3月も半ばになり、暖かい日も増えてきました。これだけ暖かくなってくると、ちょっとしたアプリで少し特殊なネットワークフレームを流したり、普段使わないネットワークプロトコルを試したくなりますよね。でも本番環境でそれをやってしまうと、変質者としてしかるべき場所に通報されてしまいます。そこで今回は他人に迷惑をかけずに隔離されたネットワークテスト環境を構築できる
Open vSwitchとネットワーク名前空間で気軽にテスト環境を構築する
Linuxカーネルには
Ubuntuだとip netns
」ip netns
」
「ip netns
」
そこで登場するのが今回紹介する
さらにmininetはPythonモジュールでもあるため、任意のネットワーク構成を構築するためのPythonスクリプトとして記述も可能です。もし複雑な構成を試したくなった場合は、その構成情報をPythonで記述していくことになるでしょう。
ちなみに
mininetは、このあたりのネットワーク名前空間やOpenFlow/
mininetのインストール
さっそくmininetをインストールしましょう。いくつかの方法が存在しますが、Ubuntuの場合は次の2種類が主な手段となります。
- mininetが提供する仮想マシンイメージを使う
- aptでインストールする
仮想マシンイメージは、mininetが提供するUbuntuベースのイメージとなっています。mininet本体だけでなく、mininet用のWiresharkなどもインストールされており、とりあえず使ってみるだけならOSを問わないのでおすすめです。ただしイメージ自体はOVF
mininet自体はUbuntu/
ところでmininetが仮想マシンイメージを推奨する理由のひとつが、
これはパッケージ版をインストールする場合も同様です。お試しで使うなら、ホストに直接インストールするのもアリではあるものの、本格的に使うなら任意の仮想マシンに閉じ込めてしまうのが良いでしょう。ちなみにコンテナ上で動かすというものひとつの案ではありますが、Open vSwitchを動くようにするのはいろいろと手間がかかります[2]。よって結局のところは何らかの方法でUbuntuの仮想マシンを用意した上で、そこのパッケージ版のmininetをインストールすることをおすすめします。
今回はLXD/
$ lxc launch ubuntu:22.04 mininet $ lxc shell mininet $ sudo -i -u ubuntu
仮想マシンでもホストでも良いので、次のようにmininetパッケージをインストールしてください。
$ sudo apt install mininet
これでmininetの準備完了です。
mininetを試してみる
mininetではmn --help
」
$ mn --help Usage: mn [options] (type mn -h for details) (中略) --topo=TOPO linear|minimal|reversed|single|torus|tree[,param=value ...] minimal=MinimalTopo linear=LinearTopo reversed=SingleSwitchReversedTopo single=SingleSwitchTopo tree=TreeTopo torus=TorusTopo
もっとも気にするであろうオプションがこの--topo
」minimal
」
実際に試してみましょう。mnを実行するには管理者権限が必要です。
$ sudo mn *** No default OpenFlow controller found for default switch! *** Falling back to OVS Bridge *** Creating network *** Adding controller *** Adding hosts: h1 h2 *** Adding switches: s1 *** Adding links: (h1, s1) (h2, s1) *** Configuring hosts h1 h2 *** Starting controller *** Starting 1 switches s1 ... *** Starting CLI: mininet>
Open vSwitch
このCLIを操作して、このネットワーク上にパケットを流していくことになります。またhelpで使い方を表示し、exitでmininet環境を終了します。mininet環境を終了すると、作成した仮想ネットワークは削除されるので注意してください。
試しにノードの情報を表示するコマンドをいくつか実行してみましょう。
ノードの一覧 mininet> nodes available nodes are: h1 h2 s1 ノード間の接続 mininet> links h1-eth0<->s1-eth1 (OK OK) h2-eth0<->s1-eth2 (OK OK) 各ノードのネットワーク情報 mininet> net h1 h1-eth0:s1-eth1 h2 h2-eth0:s1-eth2 s1 lo: s1-eth1:h1-eth0 s1-eth2:h2-eth0 IPアドレスも含めた表示 mininet> dump <Host h1: h1-eth0:10.0.0.1 pid=5260> <Host h2: h2-eth0:10.0.0.2 pid=5262> <OVSBridge s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=5267>
起動時に表示された情報の通り、2台のホストと1台のスイッチが繋がっています。またホストh1にはIPアドレス
ホストの詳細なネットワーク設定は、ipコマンドでも表示できます。
mininet> h1 ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: h1-eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 9e:2e:22:20:20:a2 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.0.0.1/8 brd 10.255.255.255 scope global h1-eth0 valid_lft forever preferred_lft forever inet6 fe80::9c2e:22ff:fe20:20a2/64 scope link valid_lft forever preferred_lft forever mininet> h2 ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: h2-eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 62:c8:b1:39:10:e0 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.0.0.2/8 brd 10.255.255.255 scope global h2-eth0 valid_lft forever preferred_lft forever inet6 fe80::60c8:b1ff:fe39:10e0/64 scope link valid_lft forever preferred_lft forever
mininetでは
ちなみにホストから見ると次のようになります。
$ ip addr (中略) 23: s1-eth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master ovs-system state UP group default qlen 1000 link/ether da:18:8b:07:1a:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::d818:8bff:fe07:1a02/64 scope link valid_lft forever preferred_lft forever 24: s1-eth2@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master ovs-system state UP group default qlen 1000 link/ether 46:c7:13:95:e0:2a brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet6 fe80::44c7:13ff:fe95:e02a/64 scope link valid_lft forever preferred_lft forever 25: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 7e:23:05:a8:6e:6e brd ff:ff:ff:ff:ff:ff 26: s1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 1a:cf:12:d1:66:49 brd ff:ff:ff:ff:ff:ff
スイッチ
ホストノード間のpingを実行するにはpingallコマンドやpingfullコマンドが便利です。これらは単に表示される情報が異なるだけとなります。
mininet> pingall *** Ping: testing ping reachability h1 -> h2 h2 -> h1 *** Results: 0% dropped (2/2 received) mininet> pingallfull *** Ping: testing ping reachability h1 -> h2 h2 -> h1 *** Results: h1->h2: 1/1, rtt min/avg/max/mdev 0.388/0.388/0.388/0.000 ms h2->h1: 1/1, rtt min/avg/max/mdev 0.010/0.010/0.010/0.000 ms
もちろん普通のpingコマンドも使えます。
mininet> h1 ping -c 3 h2 PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data. 64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.140 ms 64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.098 ms 64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.097 ms --- 10.0.0.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2031ms rtt min/avg/max/mdev = 0.097/0.111/0.140/0.020 ms
専用のターミナルを起動する
mininetでネットワークを構築したあとは、個々のホストノードで端末を立ち上げてそれぞれのホストノードで異なるコマンドを実行したいことがあります。一番手っ取り早いのは
そこでmnexec
」
まずはdumpコマンドで各ノードのPIDを確認しておきます。
mininet> dump <Host h1: h1-eth0:10.0.0.1 pid=6185> <Host h2: h2-eth0:10.0.0.2 pid=6187> <OVSBridge s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=6192>
ここでh1がPID=6185、h2がPID=6187になっていることがわかります。ホスト上でプロセスリストを表示すると次のようになっています。
$ ps -fe -q 6185,6187 UID PID PPID C STIME TTY TIME CMD root 6185 6177 0 16:28 pts/2 00:00:00 bash --norc --noediting -is mininet:h1 root 6187 6177 0 16:28 pts/3 00:00:00 bash --norc --noediting -is mininet:h2
「--norc
」--noediting
」-is
」
たとえば前述した
さて、mnexecコマンドはこのmnが作ったホストノードのbashプロセスと同じ名前空間に
$ sudo mnexec -a 6185 bash root@mininet:/home/ubuntu# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: h1-eth0@if27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether a2:c5:ae:b5:c9:49 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.0.0.1/8 brd 10.255.255.255 scope global h1-eth0 valid_lft forever preferred_lft forever inet6 fe80::a0c5:aeff:feb5:c949/64 scope link valid_lft forever preferred_lft forever
具体的には-a PID
」
名前空間にアタッチしてコマンドを実行するというシンプルな作りであるため、もちろん他のシェルでも使えますし、任意のコマンドを実行可能です。また複数のシェルインスタンスを立ち上げて複雑なこともできるようになっています。たとえばmnexecでtcpdumpしつつ、他のmnexecで任意のネットワークプログラムを動かす、といった定番操作が考えられるでしょう。
トポロジーの設定について
mnコマンドでmininetを起動する際、トポロジーオプションを使うことで、複雑な構成を構築可能です。
たとえば何も指定しなければ、ホストノード1台とスイッチ1台になりますが、これをホストノードを10台にするには次のようにsingleトポロジーを指定します。
$ sudo mn --topo=single,k=10 *** No default OpenFlow controller found for default switch! *** Falling back to OVS Bridge *** Creating network *** Adding controller *** Adding hosts: h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 *** Adding switches: s1 *** Adding links: (h1, s1) (h2, s1) (h3, s1) (h4, s1) (h5, s1) (h6, s1) (h7, s1) (h8, s1) (h9, s1) (h10, s1) *** Configuring hosts h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 *** Starting controller *** Starting 1 switches s1 ... *** Starting CLI: mininet> links h1-eth0<->s1-eth1 (OK OK) h2-eth0<->s1-eth2 (OK OK) h3-eth0<->s1-eth3 (OK OK) h4-eth0<->s1-eth4 (OK OK) h5-eth0<->s1-eth5 (OK OK) h6-eth0<->s1-eth6 (OK OK) h7-eth0<->s1-eth7 (OK OK) h8-eth0<->s1-eth8 (OK OK) h9-eth0<->s1-eth9 (OK OK) h10-eth0<->s1-eth10 (OK OK)
何も指定しない場合のminimalトポロジーは、
単純にスイッチの台数を増やしたければ、linearトポロジーが使えます。
$ sudo mn --topo=linear,k=4,n=2 *** No default OpenFlow controller found for default switch! *** Falling back to OVS Bridge *** Creating network *** Adding controller *** Adding hosts: h1s1 h1s2 h1s3 h1s4 h2s1 h2s2 h2s3 h2s4 *** Adding switches: s1 s2 s3 s4 *** Adding links: (h1s1, s1) (h1s2, s2) (h1s3, s3) (h1s4, s4) (h2s1, s1) (h2s2, s2) (h2s3, s3) (h2s4, s4) (s2, s1) (s3, s2) (s4, s3) *** Configuring hosts h1s1 h1s2 h1s3 h1s4 h2s1 h2s2 h2s3 h2s4 *** Starting controller *** Starting 4 switches s1 s2 s3 s4 ... *** Starting CLI: mininet> links h1s1-eth0<->s1-eth1 (OK OK) h1s2-eth0<->s2-eth1 (OK OK) h1s3-eth0<->s3-eth1 (OK OK) h1s4-eth0<->s4-eth1 (OK OK) h2s1-eth0<->s1-eth2 (OK OK) h2s2-eth0<->s2-eth2 (OK OK) h2s3-eth0<->s3-eth2 (OK OK) h2s4-eth0<->s4-eth2 (OK OK) s2-eth3<->s1-eth3 (OK OK) s3-eth3<->s2-eth4 (OK OK) s4-eth3<->s3-eth4 (OK OK)
linearトポロジーは
スイッチをツリー構造でつなげるには、その名の通りtreeトポロジーを使います。
$ sudo mn --topo=tree,depth=2,fanout=3 *** No default OpenFlow controller found for default switch! *** Falling back to OVS Bridge *** Creating network *** Adding controller *** Adding hosts: h1 h2 h3 h4 h5 h6 h7 h8 h9 *** Adding switches: s1 s2 s3 s4 *** Adding links: (s1, s2) (s1, s3) (s1, s4) (s2, h1) (s2, h2) (s2, h3) (s3, h4) (s3, h5) (s3, h6) (s4, h7) (s4, h8) (s4, h9) *** Configuring hosts h1 h2 h3 h4 h5 h6 h7 h8 h9 *** Starting controller *** Starting 4 switches s1 s2 s3 s4 ... *** Starting CLI: mininet> links s1-eth1<->s2-eth4 (OK OK) s1-eth2<->s3-eth4 (OK OK) s1-eth3<->s4-eth4 (OK OK) s2-eth1<->h1-eth0 (OK OK) s2-eth2<->h2-eth0 (OK OK) s2-eth3<->h3-eth0 (OK OK) s3-eth1<->h4-eth0 (OK OK) s3-eth2<->h5-eth0 (OK OK) s3-eth3<->h6-eth0 (OK OK) s4-eth1<->h7-eth0 (OK OK) s4-eth2<->h8-eth0 (OK OK) s4-eth3<->h9-eth0 (OK OK)
ツリーの頂点にs1スイッチが存在し、そこからfanoutのパラメーターの数だけ子ノードを生やし、depthで指定した深さの終端ノードとして、ホストノードを構築します。つまりホストノードの数は
最後のtorusトポロジーは2次元トーラスを構築するトポロジーです。mn --topo=torus,3,3
」
Pythonプログラムを使えば複雑化・自動化もできる
mininetは--topo
」
mininetは元々Python製のソフトウェアであることもあって、外部のプログラムから利用しやすいPython APIを用意しています。mnコマンド自体がこのAPIを利用しており、たとえばトポロジーの作り方などは/usr/
」
さらに、いくつかのよくある機能を実現するためのPythonプログラムは/usr/
にサンプルとしてインストールされています。そちらも参考にすると良いでしょう。ただしこれらのサンプルはうまく動かないことも多いです。
ちなみにmininetのAPIに対応し、より複雑なIPネットワークを構成するための
mininetは、残念ながらアクティブにメンテナンスされているソフトウェアとは言えませんが、それでも必要な機能が十分に揃っています。ちょっとしたことをやるのであればこれでも問題ありませんし、多少複雑なこともPythonスクリプトを書けば実現できます。ちょっとしたテストを作るために、お手軽に環境を作りたい場合であれば、相応の役に立つはずです。