IPフラグメントされたTCPの接続テストを行う環境をNetwork Namespaceで構築する

TCP接続の際にはIPフラグメントが発生しないように、通常はPath MTU DiscoveryによってEnd-To-Endのパス中で最小のMTUを認識して、そのサイズに収まるようにパケットを送出する。これはTCPのデータ転送を効率良く行うための最適化の結果であり、インターネットのトラフィックにおいてTCPのIPフラグメントが発生しない、ということを意味しているわけではない。TCPのオフロード機能を持つNICをテストする場合など、テストが必要になるケースもあるだろう。

手順としては、パケット送信元のホストでPath MTU Discoveryを無効にして、送信先までのパスでインターフェースのMTUを小さく設定しておきルーティングを行う、という流れになる。端末を複数台用いることができれば上記手順通りに環境構築を行うことは簡単だが、ここは仮想環境とNetwork Namespaceを用いて最小の端末数で環境構築を行ってみる。ネットワークの概要は以下のようになる。パケット送出元となる仮想環境はVirtual Box上のLinuxを使う。

+-----------------------------------------+
|host1                                    |
|+---------------------------------------+|
|| guest1(fedora on virtual box)         ||
|| +---------------+                     ||
|| |ns1            |       192.168.100.99||
|| |      veth-ns12|===veth-ns21 <=> p1p2|===+ <---(Bridged)
|| |   192.168.1.10|   192.168.1.20      ||  |
|| +---------------+                     ||  |
|+---------------------------------------+|  |
+-----------------------------------------+  |
                         +---------------+   |
                         |host2          |   |
                         |192.168.100.101|===+
                         +---------------+

===は直結、<=>はルーティングを示し、Virtual Boxのネットワーク設定はブリッジとする。NATの場合は、ns1でPath MTU Discoveryを無効化してTCPのフラグメント(IPのフラグメントビット)をクリアしていても、NAT変換される際に該当のビットはセットされてしまうため、今回の目的としては問題がある。*1

Linux上でNetwork Namespace(ns1)を以下のように設定する。ここではLinuxとしてFedora 20(Linux Kernel 3.19/iproute 3.14.0-2)を使っている。

sudo ip netns add ns1
sudo ip link add veth-ns12 type veth peer name veth-ns21
sudo ip link set veth-ns12 netns ns1
sudo ip netns exec ns1 ifconfig veth-ns12 192.168.1.10/24
sudo ip netns exec ns1 route add default gw 192.168.1.20
sudo ip netns exec ns1 sysctl -w net.ipv4.ip_no_pmtu_disc=1

sudo sysctl -w net.ipv4.ip_forward=1
sudo ifconfig veth-ns21 192.168.1.20/24
sudo ifconfig p2p1 192.168.100.99/24
sudo ifconfig p2p1 mtu 1000

sudo iptables -F

ここで、host2からns1に接続するためには、host2にns1のネットワーク(192.168.1.10/24)宛のルーティングを設定するか、guest1上でProxy Arpを有効にする。それによって、guest1はhost2からのARP RequestをProxyしてns1に渡す役目をしてくれる。

sudo sysctl -w net.ipv4.conf.p2p1.proxy_arp=1

ns1からhost2にpingが通ることを確認したら、iPerfでTCPパケットを送り、パケットをキャプチャしてみると、Don't fragmentビットが落とされ、TCPのパケットがIPフラグメントされていることが分かる。

f:id:nishidy:20160427004724p:plain

あとはLinux上でtc/cbqなどを使って好きに帯域制御を行うことで、ある程度実ネットワーク模擬をすることもできる。 なお、Ubuntu 14.04 LTS(Linux Kernel 3.13/iproute2 3.12.0-2)では、作成したNamespace内に仮想ファイル/proc/net/ipv4/ip_no_pmtu_discが無かった。

*1:もしかするとhost1側のPath MTU Discoveryの設定を無効化すればセットされないのかもしれない