はじめに
先進サービス開発事業部の山岡です。
最近Raw Socketで通信するプログラムを書いているのですが、試しにパケットを送って他所と上手く通信できるか確認したい時に実機を使うのは大変面倒です。こういった場合にLinuxのNetwork Namespaceを使えば手軽にルーティング環境を作ることができるので手順を残したいと思います。
構築する環境
以下のようにルーター1台を挟んでサーバー同士が通信できるネットワークを作ります。ちなみにアイコンはShownet Iconをお借りしました *1。ありがとうございます。
前提条件
以下の環境で構築しました。
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=18.04 DISTRIB_CODENAME=bionic DISTRIB_DESCRIPTION="Ubuntu 18.04.2 LTS"
手順
特に複雑でもないコマンドの羅列なのでシェルスクリプトにしました。コピペ&実行すれば一発で構築できるはずです。
# !/bin/bash set -eu # Network Namespaceの作成 sudo ip netns add server1 sudo ip netns add router sudo ip netns add server2 # インターフェースの作成 # peer で指定した相手と接続関係になる sudo ip link add name server1-veth1 type veth peer name router-veth1 sudo ip link add name router-veth2 type veth peer name server2-veth1 # インターフェースを各Namespaceに所属させる sudo ip link set server1-veth1 netns server1 sudo ip link set router-veth1 netns router sudo ip link set router-veth2 netns router sudo ip link set server2-veth1 netns server2 # 各インターフェースへIPアドレスを付与 sudo ip netns exec server1 ip addr add 10.0.0.1/24 dev server1-veth1 sudo ip netns exec router ip addr add 10.0.0.254/24 dev router-veth1 sudo ip netns exec router ip addr add 10.0.1.254/24 dev router-veth2 sudo ip netns exec server2 ip addr add 10.0.1.1/24 dev server2-veth1 # 各インターフェースの起動 # lo には自動的に 127.0.0.1/8 が割り当てられます sudo ip netns exec server1 ip link set server1-veth1 up sudo ip netns exec router ip link set router-veth1 up sudo ip netns exec router ip link set router-veth2 up sudo ip netns exec server2 ip link set server2-veth1 up sudo ip netns exec server1 ip link set lo up sudo ip netns exec router ip link set lo up sudo ip netns exec server2 ip link set lo up # サーバーのデフォルトルートを設定+ルーターでルーティングを有効化 sudo ip netns exec server1 ip route add 0.0.0.0/0 via 10.0.0.254 sudo ip netns exec server2 ip route add 0.0.0.0/0 via 10.0.1.254 sudo ip netns exec router sysctl -w net.ipv4.ip_forward=1
結果確認
インターフェースが以下のようになっていればOKです( lo は省略)。
$ sudo ip netns exec server1 ip addr && sudo ip netns exec server1 ip route 39: server1-veth1@if38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether ce:d6:72:f0:c8:03 brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet 10.0.0.1/24 scope global server1-veth1 valid_lft forever preferred_lft forever inet6 fe80::ccd6:72ff:fef0:c803/64 scope link valid_lft forever preferred_lft forever default via 10.0.0.254 dev server1-veth1 10.0.0.0/24 dev server1-veth1 proto kernel scope link src 10.0.0.1 $ sudo ip netns exec server2 ip addr && sudo ip netns exec server2 ip route 40: server2-veth1@if41: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether a6:36:cb:38:ff:14 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.0.1.1/24 scope global server2-veth1 valid_lft forever preferred_lft forever inet6 fe80::a436:cbff:fe38:ff14/64 scope link valid_lft forever preferred_lft forever default via 10.0.1.254 dev server2-veth1 10.0.1.0/24 dev server2-veth1 proto kernel scope link src 10.0.1.1 $ sudo ip netns exec router ip addr && sudo ip netns exec router ip route 38: router-veth1@if39: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether aa:6d:fb:d1:82:89 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.0.0.254/24 scope global router-veth1 valid_lft forever preferred_lft forever inet6 fe80::a86d:fbff:fed1:8289/64 scope link valid_lft forever preferred_lft forever 41: router-veth2@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 5e:a0:a5:5e:17:0c brd ff:ff:ff:ff:ff:ff link-netnsid 2 inet 10.0.1.254/24 scope global router-veth2 valid_lft forever preferred_lft forever inet6 fe80::5ca0:a5ff:fe5e:170c/64 scope link valid_lft forever preferred_lft forever 10.0.0.0/24 dev router-veth1 proto kernel scope link src 10.0.0.254 10.0.1.0/24 dev router-veth2 proto kernel scope link src 10.0.1.254
サーバー1からサーバー2へのtracepathの結果(きちんとルーティングされているのがわかります)。
$ sudo ip netns exec server1 tracepath -n 10.0.1.1 1?: [LOCALHOST] pmtu 1500 1: 10.0.0.254 0.055ms 1: 10.0.0.254 0.169ms 2: 10.0.1.1 0.114ms reached Resume: pmtu 1500 hops 2 back 2
使い方
既に散々手順に出てきていますが、各ネットワーク内部で何かをしたい場合は sudo ip netns exec <namespace> <command>
で実行できます。これはVMやコンテナと違いあくまでネットワークだけが分離されているので、ローカルで使用可能なものは各Namespace内でもそのまま実行することができます。
例えばルーターでパケットキャプチャーをしつつサーバー1からサーバー2にPingを打ちたい場合は以下のようになります(シェルを2つ起動するといいでしょう)。
# シェル1 $ sudo ip netns exec server1 ping 10.0.1.1 PING 10.0.1.1 (10.0.1.1) 56(84) bytes of data. 64 bytes from 10.0.1.1: icmp_seq=1 ttl=63 time=0.027 ms
# シェル2 $ sudo ip netns exec router tcpdump -nn -l [sudo] password for ca: tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on router-veth1, link-type EN10MB (Ethernet), capture size 262144 bytes 00:20:02.180211 IP 10.0.0.1 > 10.0.1.1: ICMP echo request, id 14454, seq 1, length 64 00:20:02.180230 IP 10.0.1.1 > 10.0.0.1: ICMP echo reply, id 14454, seq 1, length 64
オマケ:ベンチマークしてみた
仮想的なネットワークでハードウェアの制約を受けないためか凄い速度が出ます。これは仮想環境(VirtualBox)の結果ですが、実機に直接OSをインストールした環境では50Gbpsくらい出ました。
# シェル1 $ sudo ip netns exec server1 iperf3 -c 10.0.1.1
# シェル2 $ sudo ip netns exec server2 iperf3 -s ----------------------------------------------------------- Server listening on 5201 ----------------------------------------------------------- Accepted connection from 10.0.0.1, port 48870 [ 5] local 10.0.1.1 port 5201 connected to 10.0.0.1 port 48872 [ ID] Interval Transfer Bandwidth [ 5] 0.00-1.00 sec 3.74 GBytes 32.1 Gbits/sec [ 5] 1.00-2.00 sec 3.93 GBytes 33.7 Gbits/sec [ 5] 2.00-3.00 sec 3.85 GBytes 33.1 Gbits/sec [ 5] 3.00-4.00 sec 3.95 GBytes 33.9 Gbits/sec [ 5] 4.00-5.00 sec 3.93 GBytes 33.8 Gbits/sec [ 5] 5.00-6.00 sec 4.01 GBytes 34.4 Gbits/sec [ 5] 6.00-7.00 sec 4.03 GBytes 34.6 Gbits/sec [ 5] 7.00-8.00 sec 4.01 GBytes 34.4 Gbits/sec [ 5] 8.00-9.00 sec 3.98 GBytes 34.2 Gbits/sec [ 5] 9.00-10.00 sec 4.02 GBytes 34.5 Gbits/sec [ 5] 10.00-10.04 sec 158 MBytes 34.3 Gbits/sec - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth [ 5] 0.00-10.04 sec 0.00 Bytes 0.00 bits/sec sender [ 5] 0.00-10.04 sec 39.6 GBytes 33.9 Gbits/sec receiver