先日 Wakame Advent Calendar の記事として コマンドラインからだけで OpenVNet の機能を推測してみる という記事を書きましたが、その後実際に動かしていませんでした。
最初は、Ubuntu で OpenVNet を動かして、いんちきで OpenStack と接続してみようと思ったのですが、Ubuntu では 12.04 でも 13.04 でも 13.10 でも VNA が起動できなかったので、もう少し軽く動かしてみます。 Ubuntu 13.10 では wakame-edge のコンパイルも失敗しました (trema-edge では修正されています)。
今回は Network Namespace を使って OpenVNet の動作を確認してみようと思います。動作確認するのは以下の3つです。
- 仮想ネットワークを作成
- IPアドレスが重複する仮想ネットワークでの動作
- 仮想ネットワーク間のルーティング
Network Namespace は 1 つの Linux ホストの中に仮想的なネットワーク環境を複数作れる機能です。これを使うと、ネットワークの実験を VM などリソースをたくさん消費する手段を使わずにできるので便利です。仮想ネットワークを複数作ってみる方法については、OpenVNet Installation Guideには、VM 環境を使って OpenVNet を動かしてみる方法が紹介されていますが、Network Namespace を使うとお手軽に実験できるので、今回はその方法を紹介します。
今回の動作確認の中でいくつかスクリプトを作りました。OpenVNet test tools with network namespace においてありますので、適当に参考にしてください。
CentOS6 で Network Namespace を使う準備
Network Namespace は最近新しめの Linux Kernel を採用している Ubuntu などではそのままで利用できますが、OpenVNet の推奨環境となっている CentOS6 では残念ながら、まだそのままでは動きません。設定方法を CentOS6 で Linux Network Namespace を使うにまとめましたので、こちらを参照して、必要な iproute パッケージ (ip コマンド) を入れておいて下さい。
OpenVNet のインストール
OpenVNet Installation Guideにしたがって、インストールを行います。
"Let's try 1Box OpenVNet" の前までを実施します。
注意点としては、OpenVNet のサービスを開始する前に redis も開始しておく必要があります。
# chkconfig redis on # service redis start
CentOS6.4 が推奨となっていますが、今回は CentOS6.5 を使いましたが問題なく動作しました。
仮想ネットワークの作成
準備ができたので、2つの VM が接続された仮想ネットワーク net1 を作成してみます。
- 仮想ネットワーク net1 : 172.16.1.0/24
- インタフェース1 (VM1 相当) : 172.16.1.1 (@netns ns1)
- インタフェース1 (VM2 相当) : 172.16.1.2 (@netns ns2)
手順を順番に見ていきます。 Installation Guide に説明がある 1 ホストで VM を使って動かす例についている設定スクリプト db.sh を参考に整理してみました。
まず、最初に OpenFlow として動作する Open vSwitch bridge を OpenVNet に登録します。Open vSwitch bridge は br0 です。今回は 1 ホストなので、最初に一回 datapath を登録します。
https://github.com/amotoki/openvnet-test-tools/blob/master/vnet-register-datapath.sh#L17# uuid は dp- で始める必要あり # dpid は 0x を先頭に付けること (OVS の出力とは違うので注意) ./vnctl datapath add ¥ --uuid dp-br0 ¥ --display-name=br0 ¥ --dc-segment-id=seg1 ¥ --dpid=0x000002fdee8e0a44 ¥ --ipv4-address=$HOST_IP192.168.122.207 ¥ --node-id=192.168.122.207openvnet にネットワークを作成します。
https://github.com/amotoki/openvnet-test-tools/blob/master/vnet-register-net1.sh#L20
# ネットワークの作成 # 仮想ネットワークなので network-mode は virtual を指定する # uuid は nw- で始める必要あり ./vnctl network add ¥ --uuid nw-net1 --display-name=net1 --ipv4-network 172.16.1.0 --ipv4-prefix=24 ¥ --domain-name=dom1 --network-mode=virtual # datapath に作成したネットワークを関連付ける # broadcast-mac-address は MAC2MAC でブロードキャストパケットを転送する際に使用されるはず ./vnctl datapath networks add dp-$BRNAME nw-net1 --broadcast-mac-address=08:00:27:10:03:01 # ネットワークに接続されるインタフェースを登録する # uuid は単なる ID ではなく、OVS に追加するポートの名前と一致している必要があるみたい。また、if- で始める必要あり。 # IPv4 address と MAC はそのインタフェースの値を指定する。VM の場合は VM の中の NIC、network namespace の場合は # namespace 内の NIC の値になる。OVS に add-port する network device の値ではない点に注意。 ./vnctl interface add --uuid=if-veth1 --ipv4-address=$IP1 --network-uuid=nw-net1 --mac-address=$MAC1 ./vnctl interface add --uuid=if-veth2 --ipv4-address=$IP2 --network-uuid=nw-net1 --mac-address=$MAC2上記で OpenVNet 側の設定は終わりです。
次に、実際のデバイスを作成します。
./ovs-create.sh ./if-create-net1.shovs-create.sh は OVS bridge を作成し、datapath ID 設定、OpenFlow 設定などを行っています。今回は OpenVNet の設定を行った後でないと、うまく動かなかったので、このタイミングで行っています。
/var/log/wakame-vnet/vna.log を見ると、OVS から接続や、スイッチポートが追加されていることが分かります。
ping を実行してみましょう。
network namespace ns1 の 172.16.1.1 側から ns2 の 172.16.1.2 に ping を送ります。
# ip netns ns2 ns1 # ip netns exec ns1 ip -o addr 207: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN \ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 207: lo inet 127.0.0.1/8 scope host lo 207: lo inet6 ::1/128 scope host \ valid_lft forever preferred_lft forever 208: ns-veth1:ns2 の 172.16.1.2 側で tcpdump を仕掛けておくとパケットが見えます。mtu 1500 qdisc pfifo_fast state UP qlen 1000\ link/ether 52:54:00:0d:84:01 brd ff:ff:ff:ff:ff:ff 208: ns-veth1 inet 172.16.1.1/24 brd 172.16.1.255 scope global ns-veth1 208: ns-veth1 inet6 fe80::5054:ff:fe0d:8401/64 scope link \ valid_lft forever preferred_lft forever # ip netns exec ns1 ping -c 5 172.16.1.2 PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data. 64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=0.466 ms 64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=0.134 ms 64 bytes from 172.16.1.2: icmp_seq=3 ttl=64 time=0.058 ms 64 bytes from 172.16.1.2: icmp_seq=4 ttl=64 time=0.066 ms 64 bytes from 172.16.1.2: icmp_seq=5 ttl=64 time=0.063 ms --- 172.16.1.2 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4000ms rtt min/avg/max/mdev = 0.058/0.157/0.466/0.157 ms # ip netns exec ns1 arp -na ? (172.16.1.2) at 52:54:00:0d:84:02 [ether] on ns-veth1
# ip netns exec ns2 tcpdump -i ns-veth2 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ns-veth2, link-type EN10MB (Ethernet), capture size 65535 bytes 01:16:35.234573 ARP, Request who-has 172.16.1.2 tell 172.16.1.1, length 28 01:16:35.234610 ARP, Reply 172.16.1.2 is-at 52:54:00:0d:84:02 (oui Unknown), length 28 01:16:35.234709 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 12927, seq 1, length 64 01:16:35.234758 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 12927, seq 1, length 64 01:16:36.234855 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 12927, seq 2, length 64 01:16:36.234917 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 12927, seq 2, length 64 01:16:37.234633 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 12927, seq 3, length 64 01:16:37.234651 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 12927, seq 3, length 64 01:16:38.234501 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 12927, seq 4, length 64 01:16:38.234522 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 12927, seq 4, length 64 01:16:39.234528 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 12927, seq 5, length 64 01:16:39.234550 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 12927, seq 5, length 64 01:16:40.234515 ARP, Request who-has 172.16.1.1 tell 172.16.1.2, length 28 01:16:40.234793 ARP, Reply 172.16.1.1 is-at 52:54:00:0d:84:01 (oui Unknown), length 28 ^C 14 packets captured 14 packets received by filter 0 packets dropped by kernel
IPアドレスが重複する仮想ネットワークでの動作
仮想ネットワークといえば、IP アドレス重複に対応できるか!です。もちろん試してみましょう。
先ほどの net1 に追加で、同じ IP アドレスを持つ net3 を作成します。
- 仮想ネットワーク net1 : 172.16.1.0/24
- インタフェース : 172.16.1.1 (@netns ns1)
- インタフェース : 172.16.1.2 (@netns ns2)
- 仮想ネットワーク net3 : 172.16.1.0/24
- インタフェース : 172.16.1.1 (@netns ns5)
- インタフェース : 172.16.1.2 (@netns ns6)
./vnet-register-net3.sh ./if-create-net3.shns1 の 172.16.1.1 と ns5 の 172.16.1.1 からそれぞれ 172.16.1.2 に ping を送ってみます。ns1 から送った時は同じ net1 側の ns2 の 172.16.1.2で、ns5 から送った時は net3 側の ns6 の 172.16.1.2 にパケットが届いていることが確認できます。片方の結果だけ載せておきます。
(net3 側の 172.16.1.1 から ping) # ip netns exec ns5 ping -c 3 172.16.1.2 PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data. 64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=1.68 ms 64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=0.058 ms 64 bytes from 172.16.1.2: icmp_seq=3 ttl=64 time=0.048 ms --- 172.16.1.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2001ms rtt min/avg/max/mdev = 0.048/0.596/1.683/0.768 ms (net1 側の 172.16.1.2) # ip netns exec ns2 tcpdump -i ns-veth2 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ns-veth2, link-type EN10MB (Ethernet), capture size 65535 bytes ^C 0 packets captured 0 packets received by filter 0 packets dropped by kernel (net3 側の 172.16.1.2) # ip netns exec ns5 tcpdump -i ns-veth5 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ns-veth5, link-type EN10MB (Ethernet), capture size 65535 bytes 01:34:38.493450 ARP, Request who-has 172.16.1.2 tell 172.16.1.1, length 28 01:34:38.493998 ARP, Reply 172.16.1.2 is-at 52:54:00:0d:84:06 (oui Unknown), length 28 01:34:38.494005 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 57983, seq 1, length 64 01:34:38.494093 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 57983, seq 1, length 64 01:34:39.493507 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 57983, seq 2, length 64 01:34:39.493546 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 57983, seq 2, length 64 01:34:40.493471 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 57983, seq 3, length 64 01:34:40.493503 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 57983, seq 3, length 64 01:34:43.493577 ARP, Request who-has 172.16.1.1 tell 172.16.1.2, length 28 01:34:43.493588 ARP, Reply 172.16.1.1 is-at 52:54:00:0d:84:05 (oui Unknown), length 28 ^C 10 packets captured 10 packets received by filter 0 packets dropped by kernelIP アドレス帳服がある場合でも、期待通り仮想ネットワークが機能しています。
仮想ネットワーク間のルーティング
最後に、仮想ネットワーク間でのルーティングをさせてみたいと思います。先日、コマンドラインからだけで OpenVNet の機能を推測してみる記事を書いた時にはいまいちよく分からなかったのですが、サンプルスクリプトの db.sh などと睨めったことしつつなんとなく分かって来ましたので、試してみました。IP アドレスの違う論理ネットワークを2つ用意し、それらを論理ルータで接続する構成です。論理ルータは 172.16.1.254 と 172.16.2.254 という2つのインタフェースを持ちます。
- 仮想ネットワーク net1 : 172.16.1.0/24
- インタフェース : 172.16.1.1 (@netns ns1)
- インタフェース : 172.16.1.2 (@netns ns2)
- ルータインタフェース : 172.16.1.254
- 仮想ネットワーク net2 : 172.16.2.0/24
- インタフェース : 172.16.2.3 (@netns ns3)
- インタフェース : 172.16.2.4 (@netns ns4)
- ルータインタフェース : 172.16.2.254
./vnet-register-net2.sh ./if-create-net2.shOpenVNet 側にルータを作成していきます。次のような手順になります。
- ルータインタフェースの作成
- ルータインタフェースに対して network_service を関連付け (なくても動く)
- route_link の作成
- route_link の datapath への関連付け (なくても動く)
- route 情報の登録
ざっくり言うと、route_link が論理ルータに対応します。 route は論理ルータの経路情報です。名前が分かりにくいですね。
サンプルスクリプト db.sh に書かれている手順を参考にしていますが、network_service の作成、route_link の datapath への関連付けの2つはなくても動きました。どういうことでしょう?
実際のコマンドベースで説明します。一部のコマンドは CLI では提供されていないので、curl を直接叩いています。
https://github.com/amotoki/openvnet-test-tools/blob/master/vnet-register-router.sh#L29
# net1 の論理ルータに対応するインタフェースを作成 # OpenVNet が機能を提供するので mode=simulated を指定する # vnctl CLI では mode が指定できないので、curl を使う curl -s -X POST --data-urlencode uuid=if-vnet1gw ¥ --data-urlencode network_uuid=nw-net1 ¥ --data-urlencode mac_address=52:54:00:74:00:00 ¥ --data-urlencode ipv4_address=172.16.1.254 ¥ --data-urlencode mode=simulated ¥ http://localhost:9090/api/interfaces # ルータインタフェースの終端を OpenVNet が行うことの宣言のようです。 # ★ 1 ホストでのテストでは、なくても動きました。 ./vnctl network_service add --uuid=ns-vnet1gw --interface-uuid=if-vnet1gw --display-name=router # net2 の論理ルータに対応するインタフェースを作成 # OpenVNet が機能を提供するので mode=simulated を指定する # vnctl CLI では mode が指定できないので、curl を使う curl -s -X POST --data-urlencode uuid=if-vnet2gw ¥ --data-urlencode network_uuid=nw-net2 ¥ --data-urlencode mac_address=52:54:00:74:22:22 ¥ --data-urlencode ipv4_address=172.16.2.254 ¥ --data-urlencode mode=simulated ¥ http://localhost:9090/api/interfaces # ルータインタフェースの終端を OpenVNet が行うことの宣言のようです。 # ★ 1 ホストでのテストでは、なくても動きました。 ./vnctl network_service add --uuid=ns-vnet2gw --interface-uuid=if-vnet2gw --display-name=router # 論理ルータの作成 # OpenVNet では route_link という概念として表現される # MAC address が何に利用されるのかは不明 ./vnctl route_link add --uuid=rl-vnetlink1 --mac-address=52:54:00:60:11:11 # 作成した route_link を datapath に登録する # ★ 1 ホストでのテストでは、なくても動きました。 curl -s -X POST --data-urlencode route_link_uuid=rl-vnetlink1 \ --data-urlencode mac_address=08:00:27:20:01:01 \ http://localhost:9090/api/datapaths/dp-br0/route_links/rl-vnetlink1 # 論理ルータでの経路情報を登録する curl -s -X POST --data-urlencode uuid=r-vnet1 ¥ --data-urlencode interface_uuid=if-vnet1gw ¥ --data-urlencode route_link_uuid=rl-vnetlink1 ¥ --data-urlencode ipv4_network=172.16.1.0 ¥ http://localhost:9090/api/routes curl -s -X POST --data-urlencode uuid=r-vnet2 ¥ --data-urlencode interface_uuid=if-vnet2gw ¥ --data-urlencode route_link_uuid=rl-vnetlink1 ¥ --data-urlencode ipv4_network=172.16.2.0 ¥ http://localhost:9090/api/routes上記を実行して、OpenVNet に登録します。ルータ機能は OpenVNet が提供するので、実際のデバイスの設定は必要ありません。
./vnet-register-router.shnet1 と net2 の間で ping を行ってみます。 net1 の 172.16.1.1 (@ns1) から net2 の 172.16.2.3 (@ns3) に ping を実行しています。ちゃんと ping が通りました。成功です。
# ip netns exec ns1 ping 172.16.2.3 PING 172.16.2.3 (172.16.2.3) 56(84) bytes of data. 64 bytes from 172.16.2.3: icmp_seq=1 ttl=64 time=20.0 ms 64 bytes from 172.16.2.3: icmp_seq=2 ttl=64 time=0.060 ms 64 bytes from 172.16.2.3: icmp_seq=3 ttl=64 time=0.056 ms ^C --- 172.16.2.3 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2550ms rtt min/avg/max/mdev = 0.056/6.711/20.018/9.409 ms/var/log/wakame-vnet/vna.log を見ると、vna が ARP に応答している記録もあります。
I, [2013-12-24T02:41:50.725318 #2463] INFO -- : 0x000002fdee8e0a44 interfaces/simulated: simulated arp reply (arp_tpa:172.16.1.254) I, [2013-12-24T02:41:50.735063 #2463] INFO -- : 0x000002fdee8e0a44 interfaces/simulated: simulated arp reply (arp_tpa:172.16.2.254)ちなみに、ルータインタフェースへの ping は通りませんでした。
# ip netns exec ns1 ping 172.16.1.254 PING 172.16.1.254 (172.16.1.254) 56(84) bytes of data. ^C --- 172.16.1.254 ping statistics --- 5 packets transmitted, 0 received, 100% packet loss, time 4590ms
まとめ
今回は OpenVNet の基本的な使い方を Network Namespace を使って実験してみました。仮想ネットワークの作成、重複 IP アドレスの扱い、仮想ルータを使った通信をどうやって行うのかは分かりました。外部の物理ネットワークとの通信方法は時間があれば調査してみようと思います。テスト中には、コマンドの投入順序の違いなどで動かず、やり直してみたら動いた、といった点もありました。全体的に、OpenVNet に情報 (datapath や interface) を登録してから、OVS から VNA への接続やインタフェースの OVS への追加を行う必要があるみたいです。今後そのあたりは改善されていくと思います。また、一部の機能は CLI からは制御できないようです。そのうち対応されるのではないかと思います。