Network namespace
Motivation
Linux のネットワーク系のコマンドを試す時にネットワークホストが複数欲しかったりするので、それを手軽に準備できる方法はないかと考えている。たぶん Docker とかで環境準備するのが良いのかもしれないがよくわからないので色々調べてみる。
Docker を使ってネットワーク構築を楽できるのではないかとも思ったが、Docker 側の仕組みに気を取られてしまったので、結局 Docker で CentOS 立てて Network namespace で仮想ネットワークを構築するのが一番お手軽だった。
ip netnsコマンドの使い方(ネットワークの実験の幅が広がるなぁ~)
ネットワーク初心者の新卒がDockerでネットワークの勉強をしてみた
Network namespace とは
Linux ホストの上に仮想的なネットワーク環境を複数作成できるもの。
ip netns コマンド
Network namespace を操作するコマンドは、ip netns コマンド。
code:bash
ip netns help
Usage: ip netns list # ネームスペースの一覧
ip netns add NAME # ネームスペースの追加
ip netns set NAME NETNSID # ネームスペースに名前を追加
ip -all netns delete NAME # ネームスペースの削除
ip netns identify PID # プロセスが動作しているネームスペースを調べる
ip netns pids NAME # ネームスペースで動作しているプロセスを調べる
ip -all netns exec NAME cmd ... # ネームスペースでコマンドを実行する
ip netns monitor # ネームスペースに対する操作のモニター
ip netns list-id
使い方
code:bash
# ネームスペースの表示
ip netns list
# ネームスペースを追加
ip netns add <ネームスペース名>
# ネームスペース
使ってみる
まずは、CentOS を Docker で立てる。Docker で立てなくても Linux 環境さえあれば OK。
code:bash
$ docker run -itd --privileged --name test01 --net test centos:centos7.2.1511 /bin/bash
$ docker exec --privileged -it test01 /bin/bash
CentOS に入り、必要なツールをインストール。
code:bash
$ yum install -y vim net-tools iproute
code:bash
# netns add でネームスペースを追加する
$ ip netns add h1
$ ip netns add r1
$ ip netns add r2
$ ip netns add h2
$ ip netns list
r1
h1
h2
r2
# link add で veth インタフェースを作成する
# 対として定義されたインタフェースは、片方にパケットが入ると、片方から出てくる
# 下記でいうと、例えば h1-veth に入ったパケットは、r1-veth1 に出て行く。逆も然り
$ ip link add name h1-veth type veth peer name r1-veth1
$ ip link add name r1-veth2 type veth peer name r2-veth1
$ ip link add name r2-veth2 type veth peer name h2-veth
# 確認すると、ホスト上に veth インタフェースができている (10~15の2*3=6個)
$ ip link show | grep veth
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/tunnel6 :: brd ::
10: r1-veth1@h1-veth: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether ba:ec:02:9f:75:e2 brd ff:ff:ff:ff:ff:ff
11: h1-veth@r1-veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether c6:9c:d9:b3:f4:52 brd ff:ff:ff:ff:ff:ff
12: r2-veth1@r1-veth2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 4e:3c:39:b1:12:61 brd ff:ff:ff:ff:ff:ff
13: r1-veth2@r2-veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether b2:28:7c:47:0c:b7 brd ff:ff:ff:ff:ff:ff
14: h2-veth@r2-veth2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 5a:d2:e6:61:5f:c7 brd ff:ff:ff:ff:ff:ff
15: r2-veth2@h2-veth: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 7a:c8:74:a8:71:d1 brd ff:ff:ff:ff:ff:ff
17: eth0@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
# veth インタフェースをネームスペースに参加させる
$ ip link set h1-veth netns h1
$ ip link set r1-veth1 netns r1
$ ip link set r1-veth2 netns r1
$ ip link set r2-veth1 netns r2
$ ip link set r2-veth2 netns r2
$ ip link set h2-veth netns h2
# ネームスペースを参加させると、インタフェースはホストからは見えなくなる
$ ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/tunnel6 :: brd ::
17: eth0@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
# ネームスペース上からはインタフェースが見える。sh コマンドを実行し入って確認してみる
$ ip netns exec h1 sh
sh-4.2# ip link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/tunnel6 :: brd ::
11: h1-veth@if10: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether c6:9c:d9:b3:f4:52 brd ff:ff:ff:ff:ff:ff link-netnsid 1
sh-4.2# exit
$ ip netns exec r1 sh
sh-4.2# ip link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/tunnel6 :: brd ::
10: r1-veth1@if11: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether ba:ec:02:9f:75:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
13: r1-veth2@if12: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether b2:28:7c:47:0c:b7 brd ff:ff:ff:ff:ff:ff link-netnsid 2
sh-4.2# exit
# veth インタフェースに IP アドレスをつける
# 最初は、当たり前だが疎通はない
$ ip netns exec h1 ping 192.168.100.20
connect: Network is unreachable
# インタフェースに IP アドレスを付与する
$ ip netns exec h1 ip addr add 192.168.100.10/24 dev h1-veth
$ ip netns exec h1 ip link set h1-veth up
# peer 先に IP アドレスを付与していないので、100% パケットロスする
$ ip netns exec h1 ping 192.168.100.20
PING 192.168.100.10 (192.168.100.10) 56(84) bytes of data.
^C
--- 192.168.100.10 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3119ms
# peer 先のインタフェースにも IP アドレスを付与する
$ ip netns exec r1 ip addr add 192.168.100.20/24 dev r1-veth1
$ ip netns exec r1 ip link set r1-veth1 up
# 互いに ping が通るようになる
$ ip netns exec r1 ping 192.168.100.10
PING 192.168.100.10 (192.168.100.10) 56(84) bytes of data.
64 bytes from 192.168.100.10: icmp_seq=1 ttl=64 time=0.113 ms
64 bytes from 192.168.100.10: icmp_seq=2 ttl=64 time=0.089 ms
64 bytes from 192.168.100.10: icmp_seq=3 ttl=64 time=0.111 ms
^C
--- 192.168.100.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2108ms
rtt min/avg/max/mdev = 0.089/0.104/0.113/0.013 ms
$ ip netns exec h1 ping 192.168.100.20
PING 192.168.100.20 (192.168.100.20) 56(84) bytes of data.
64 bytes from 192.168.100.20: icmp_seq=1 ttl=64 time=0.048 ms
64 bytes from 192.168.100.20: icmp_seq=2 ttl=64 time=0.083 ms
64 bytes from 192.168.100.20: icmp_seq=3 ttl=64 time=0.105 ms
^C
--- 192.168.100.20 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2101ms
rtt min/avg/max/mdev = 0.048/0.078/0.105/0.025 ms
# 同じ調子で IP アドレスを振っていく
$ ip netns exec r1 ip addr add 10.0.100.10/24 dev r1-veth2
$ ip netns exec r1 ip link set r1-veth2 up
$ ip netns exec r2 ip addr add 10.0.100.20/24 dev r2-veth1
$ ip netns exec r2 ip link set r2-veth1 up
$ ip netns exec r2 ip addr add 192.168.110.10/24 dev r2-veth2
$ ip netns exec r2 ip link set r2-veth2 up
$ ip netns exec h2 ip addr add 192.168.110.20/24 dev h2-veth
$ ip netns exec h2 ip link set h2-veth up
# 現時点で、隣同士では ping は通る
# h1 -> r1
root@327c6306eb3a ~# ip netns exec h1 ping 192.168.100.20
PING 192.168.100.20 (192.168.100.20) 56(84) bytes of data.
64 bytes from 192.168.100.20: icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from 192.168.100.20: icmp_seq=2 ttl=64 time=0.110 ms
64 bytes from 192.168.100.20: icmp_seq=3 ttl=64 time=0.123 ms
^C
--- 192.168.100.20 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2099ms
rtt min/avg/max/mdev = 0.069/0.100/0.123/0.025 ms
# r1 -> r2
root@327c6306eb3a ~# ip netns exec r1 ping 10.0.100.20
PING 10.0.100.20 (10.0.100.20) 56(84) bytes of data.
64 bytes from 10.0.100.20: icmp_seq=1 ttl=64 time=0.137 ms
64 bytes from 10.0.100.20: icmp_seq=2 ttl=64 time=0.067 ms
64 bytes from 10.0.100.20: icmp_seq=3 ttl=64 time=0.108 ms
^C
--- 10.0.100.20 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2077ms
rtt min/avg/max/mdev = 0.067/0.104/0.137/0.028 ms
# r2 -> h2
root@327c6306eb3a ~# ip netns exec r2 ping 192.168.110.20
PING 192.168.110.20 (192.168.110.20) 56(84) bytes of data.
64 bytes from 192.168.110.20: icmp_seq=1 ttl=64 time=0.064 ms
64 bytes from 192.168.110.20: icmp_seq=2 ttl=64 time=0.116 ms
64 bytes from 192.168.110.20: icmp_seq=3 ttl=64 time=0.107 ms
^C
--- 192.168.110.20 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2109ms
rtt min/avg/max/mdev = 0.064/0.095/0.116/0.025 ms
# 逆方向
# h2 -> r2
root@327c6306eb3a ~# ip netns exec h2 ping 192.168.110.10
PING 192.168.110.10 (192.168.110.10) 56(84) bytes of data.
64 bytes from 192.168.110.10: icmp_seq=1 ttl=64 time=0.095 ms
64 bytes from 192.168.110.10: icmp_seq=2 ttl=64 time=0.111 ms
64 bytes from 192.168.110.10: icmp_seq=3 ttl=64 time=0.102 ms
^C
--- 192.168.110.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2121ms
rtt min/avg/max/mdev = 0.095/0.102/0.111/0.013 ms
# r2 -> r1
root@327c6306eb3a ~# ip netns exec r2 ping 10.0.100.10
PING 10.0.100.10 (10.0.100.10) 56(84) bytes of data.
64 bytes from 10.0.100.10: icmp_seq=1 ttl=64 time=0.064 ms
64 bytes from 10.0.100.10: icmp_seq=2 ttl=64 time=0.161 ms
64 bytes from 10.0.100.10: icmp_seq=3 ttl=64 time=0.107 ms
^C
--- 10.0.100.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2109ms
rtt min/avg/max/mdev = 0.064/0.110/0.161/0.041 ms
# r1 -> h1
root@327c6306eb3a ~# ip netns exec r1 ping 192.168.100.10
PING 192.168.100.10 (192.168.100.10) 56(84) bytes of data.
64 bytes from 192.168.100.10: icmp_seq=1 ttl=64 time=0.077 ms
64 bytes from 192.168.100.10: icmp_seq=2 ttl=64 time=0.109 ms
64 bytes from 192.168.100.10: icmp_seq=3 ttl=64 time=0.083 ms
^C
--- 192.168.100.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2046ms
rtt min/avg/max/mdev = 0.077/0.089/0.109/0.017 ms
# ルータ(r1, r2)を介したホスト同士(h1,h2)は通らない
# h1 -> h2
root@327c6306eb3a ~# ip netns exec h1 ping 192.168.110.20
connect: Network is unreachable
# h2 -> h1
root@327c6306eb3a ~# ip netns exec h2 ping 192.168.100.10
connect: Network is unreachable
# ついでに隣り合っていないルータにも通らない
# h1 -> r2
root@327c6306eb3a ~# ip netns exec h1 ping 10.0.100.20
connect: Network is unreachable
# h2 -> r1
root@327c6306eb3a ~# ip netns exec h2 ping 10.0.100.10
connect: Network is unreachable
# まず、ルータにしたいネームスペースでは、 IP 転送を有効にする
$ ip netns exec r1 sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
$ ip netns exec r2 sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
# ルーティングテーブルの確認
root@327c6306eb3a ~# ip netns exec h1 netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
192.168.100.0 0.0.0.0 255.255.255.0 U 0 0 0 h1-veth
root@327c6306eb3a ~# ip netns exec r1 netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
10.0.100.0 0.0.0.0 255.255.255.0 U 0 0 0 r1-veth2
192.168.100.0 0.0.0.0 255.255.255.0 U 0 0 0 r1-veth1
root@327c6306eb3a ~# ip netns exec r2 netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
10.0.100.0 0.0.0.0 255.255.255.0 U 0 0 0 r2-veth1
192.168.110.0 0.0.0.0 255.255.255.0 U 0 0 0 r2-veth2
root@327c6306eb3a ~# ip netns exec h2 netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
192.168.110.0 0.0.0.0 255.255.255.0 U 0 0 0 h2-veth
例えば、h1 -> h2 へルーティングするためには...
h2 のネットワークインタフェース h2-veth(192.168.110.20) を Destination としたとき、 Gateway として r1 のネットワークインタフェース r1-veth1(192.168.100.20) を介する必要がある
現状のネットワークは以下のようになっている。
https://gyazo.com/094c1f84c127a592dbc84257b54e0b39
設定したいルーティングテーブルは以下のようになるはず。destination はレンジで設定する。今回の目的的には不要 (なはず) だが、ルータ間のプライベートネットワークにも到達できるように設定する。
table:route_table
namespace source interface dest interface destination (IP) gateway (IP)
h1 h1-veth (192.168.100.10) h2-veth (192.168.110.20) 192.168.110.0/24 r1-veth1 (192.168.100.20)
h1 h1-veth (192.168.100.10) 10.0.100.0/24 r1-veth1 (192.168.100.20)
h2 h2-veth (192.168.110.20) h1-veth (192.168.100.10) 192.10.100.0/24 r2-veth2 (192.168.110.10)
h2 h2-veth (192.168.110.20) 10.0.100.0/24 r2-veth2 (192.168.110.10)
r1 r1-veth2 (10.0.100.10) h2-veth (192.168.110.20) 192.168.110.0/24 r2-veth1 (10.0.100.20)
r2 r2-veth1 (10.0.100.20) h1-veth (192.10.110.10) 192.168.100.0/24 r1-veth2 (10.0.100.10)
上記を設定するためのコマンドは以下。
code:shell
ip netns exec h1 ip route add 192.168.110.0/24 via 192.168.100.20 dev h1-veth
ip netns exec h1 ip route add 10.0.100.0/24 via 192.168.100.20 dev h1-veth
ip netns exec h2 ip route add 192.168.100.0/24 via 192.168.110.10 dev h2-veth
ip netns exec h2 ip route add 10.0.100.0/24 via 192.168.110.10 dev h2-veth
ip netns exec r1 ip route add 192.168.110.0/24 via 10.0.100.20 dev r1-veth2
ip netns exec r2 ip route add 192.168.100.0/24 via 10.0.100.10 dev r2-veth1
設定後のルーティングテーブルを確認する。
code:shell
root@327c6306eb3a ~# ip netns exec h1 netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
10.0.100.0 192.168.100.20 255.255.255.0 UG 0 0 0 h1-veth
192.168.100.0 0.0.0.0 255.255.255.0 U 0 0 0 h1-veth
192.168.110.0 192.168.100.20 255.255.255.0 UG 0 0 0 h1-veth
root@327c6306eb3a ~# ip netns exec r1 netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
10.0.100.0 0.0.0.0 255.255.255.0 U 0 0 0 r1-veth2
192.168.100.0 0.0.0.0 255.255.255.0 U 0 0 0 r1-veth1
192.168.110.0 10.0.100.20 255.255.255.0 UG 0 0 0 r1-veth2
root@327c6306eb3a ~# ip netns exec r2 netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
10.0.100.0 0.0.0.0 255.255.255.0 U 0 0 0 r2-veth1
192.168.100.0 10.0.100.10 255.255.255.0 UG 0 0 0 r2-veth1
192.168.110.0 0.0.0.0 255.255.255.0 U 0 0 0 r2-veth2
root@327c6306eb3a ~# ip netns exec h2 netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
10.0.100.0 192.168.110.10 255.255.255.0 UG 0 0 0 h2-veth
192.168.100.0 192.168.110.10 255.255.255.0 UG 0 0 0 h2-veth
192.168.110.0 0.0.0.0 255.255.255.0 U 0 0 0 h2-veth
# ping が通るようになった!
root@327c6306eb3a ~# ip netns exec h1 ping 192.168.110.20
PING 192.168.110.20 (192.168.110.20) 56(84) bytes of data.
64 bytes from 192.168.110.20: icmp_seq=1 ttl=62 time=0.266 ms
64 bytes from 192.168.110.20: icmp_seq=2 ttl=62 time=0.170 ms
64 bytes from 192.168.110.20: icmp_seq=3 ttl=62 time=0.224 ms
^C
--- 192.168.110.20 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2114ms
rtt min/avg/max/mdev = 0.170/0.220/0.266/0.039 ms
root@327c6306eb3a ~# ip netns exec h2 ping 192.168.100.10
PING 192.168.100.10 (192.168.100.10) 56(84) bytes of data.
64 bytes from 192.168.100.10: icmp_seq=1 ttl=62 time=0.088 ms
64 bytes from 192.168.100.10: icmp_seq=2 ttl=62 time=0.217 ms
64 bytes from 192.168.100.10: icmp_seq=3 ttl=62 time=0.170 ms
^C
--- 192.168.100.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2064ms
rtt min/avg/max/mdev = 0.088/0.158/0.217/0.054 ms
root@327c6306eb3a ~# ip netns exec h1 ping 10.0.100.20
PING 10.0.100.20 (10.0.100.20) 56(84) bytes of data.
64 bytes from 10.0.100.20: icmp_seq=1 ttl=63 time=0.153 ms
64 bytes from 10.0.100.20: icmp_seq=2 ttl=63 time=0.129 ms
64 bytes from 10.0.100.20: icmp_seq=3 ttl=63 time=0.314 ms
^C
--- 10.0.100.20 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2090ms
rtt min/avg/max/mdev = 0.129/0.198/0.314/0.083 ms
root@327c6306eb3a ~# ip netns exec h2 ping 10.0.100.10
PING 10.0.100.10 (10.0.100.10) 56(84) bytes of data.
64 bytes from 10.0.100.10: icmp_seq=1 ttl=63 time=0.090 ms
64 bytes from 10.0.100.10: icmp_seq=2 ttl=63 time=0.150 ms
64 bytes from 10.0.100.10: icmp_seq=3 ttl=63 time=0.171 ms
^C
--- 10.0.100.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2066ms
rtt min/avg/max/mdev = 0.090/0.137/0.171/0.034 ms
Linux Network Namespace で遊んでみる
tracerouteコマンドの使い方