Openflowを使ってL3ヘアピン機能を実装してみる
Posted on 2014/03/15(Sat) 18:22 in technical
はじめに
「openvswitchのインストールとかそういうのいいから参考になりそうなアプリ書けオルァ」的な圧力を感じます。
そこで、今回はVEPAとかそういう感じのアレを実装して、部分最適を考えたSDNアプリケーションの実装をしてみる。
「お前がSDNだと思うものがSDNだ」を胸に抱いて。
そんな私たちは2軍のファンタジスタであーる
あ、今BGMがGJ部なんで許してください。
前回使ったトポロジを大体そのまま流用することにする。
え、前回ってこれでしょ?(https://www.ainoniwa.net/pelican/wp/1162)
...どんだけ時間経ってるんだ死ぬる。でもryuのバージョン以外は基本的に変化してないからまだまだ現役だもん!
Router-01 : BSDRP 1.4 on VirtualBox node-01 : BSDRP 1.4 on VirtualBox node-02 : BSDRP 1.4 on VirtualBox ovs01 : Ubuntu-12.04.3 + openvswitch-2.0.0 on VirtualBox ryu : Ubuntu-12.04.3 + ryu-3.7 on VirtualBox
Ubuntu-12.04.3のインストールは、こんな感じ(https://www.ainoniwa.net/pelican/2014/0301a.html)で作ってるので。
GNS3でSerial画面を出すことを前提に作るから当然だね!やっほい!
これから作ろうとしているもの
通常は、ルータまで行ってVLANタグを外してルーティングしてVLANタグつけて同じポートから出てくる動作をする。(オレンジ色の線)
でも、これくらいの動作なら間のopenvswitchでも出来るよね?(青色の線)ってことです。
これは今は個別のノードに実装をばらしているけど、KVMのbr0にopenvswitchを使っている場合は、ホストの内部で折り返す機構を準備できて、さらに有利だ。と、思う。
設定するよー
はいはーい、周辺部分の設定しちゃいますよー。前回と大体一緒ですよー。
node-01
# ifconfig em1 192.168.10.10/24 # route add default 192.168.10.1
node-02
# ifconfig em1 192.168.20.10/24 # route add default 192.168.20.1
Router-01
# ifconfig em1 up # ifconfig vlan10 create # ifconfig vlan10 vlan 10 vlandev em1 # ifconfig vlan10 192.168.10.1/24 # ifconfig vlan20 create # ifconfig vlan20 vlan 20 vlandev em1 # ifconfig vlan20 192.168.20.1/24 # kldload ipfw # kldload dummynet # ipfw add 10 pipe 1 ip from any to any # ipfw pipe 1 config delay 10ms # ipfw add 65534 pass ip from any to any # ipfw list 00010 pipe 1 ip from any to any 65534 allow ip from any to any 65535 deny ip from any to any # ipfw pipe show 00001: unlimited 10 ms burst 0 q131073 50 sl. 0 flows (1 buckets) sched 65537 weight 0 lmax 0 pri 0 droptail sched 65537 type FIFO flags 0x0 0 buckets 0 active
ryu
前はryu-3.3だったのに、もうryu-3.7になっててビビる。
そしてlibxml2-devとlibxslt1-devが必要になったので、今後は要るよって話。
$ sudo ip link set up dev eth1 $ sudo ip add add 192.168.56.11/24 dev eth1 $ sudo apt-get install python-virtualenv gcc python-dev libxml2-dev libxslt1-dev $ virtualenv ryu $ source ryu/bin/activate (ryu)$ pip install ryu (ryu)$ ryu-manager --version ryu-manager 3.7
ovs01
$ sudo ip add add 192.168.56.31/24 dev eth1 $ sudo ovs-vsctl add-br ovs-01 $ sudo ovs-vsctl add-port ovs-01 eth2 $ sudo ovs-vsctl add-port ovs-01 eth3 $ sudo ip link set up eth1 $ sudo ip link set up eth2 $ sudo ip link set up eth3 $ sudo ovs-vsctl set bridge ovs-01 other-config:datapath-id=0000000000000001 $ sudo ovs-vsctl get bridge ovs-01 datapath-id "0000000000000001" $ sudo ovs-vsctl set bridge ovs-01 protocols=OpenFlow10,OpenFlow13 $ sudo ovs-vsctl show 91158e4f-1bd0-4f50-b6c9-32f669ee0dee Bridge "ovs-01" Port "eth3" Interface "eth3" Port "eth2" Interface "eth2" Port "ovs-01" Interface "ovs-01" type: internal ovs_version: "2.0.0" $ sudo ovs-vsctl list bridge _uuid : 4da1186e-a99f-4ecb-8fa5-3c8bfc9de37f controller : [] datapath_id : "0000000000000001" datapath_type : "" external_ids : {} fail_mode : [] flood_vlans : [] flow_tables : {} ipfix : [] mirrors : [] name : "ovs-01" netflow : [] other_config : {datapath-id="0000000000000001"} ports : [a0f228c5-a438-4ba7-9d80-556449c14479, c1ed065e-f571-4a0c-b6f1-9a8e592b0cad, dba34730-aba1-4dfa-8345-920ab7b49817] protocols : ["OpenFlow10", "OpenFlow13"] sflow : [] status : {} stp_enable : false $ sudo ovs-ofctl dump-flows ovs-01 NXST_FLOW reply (xid=0x4): cookie=0x0, duration=2.196s, table=0, n_packets=0, n_bytes=0, idle_age=2, priority=0 actions=NORMAL
ここまででまずは動作確認
node-01
まずはぶら下がってるnode同士。
# ping -c 4 192.168.20.10 PING 192.168.20.10 (192.168.20.10): 56 data bytes 64 bytes from 192.168.20.10: icmp_seq=0 ttl=63 time=40.442 ms 64 bytes from 192.168.20.10: icmp_seq=1 ttl=63 time=38.783 ms 64 bytes from 192.168.20.10: icmp_seq=2 ttl=63 time=37.459 ms 64 bytes from 192.168.20.10: icmp_seq=3 ttl=63 time=38.811 ms --- 192.168.20.10 ping statistics --- 4 packets transmitted, 4 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 37.459/38.874/40.442/1.057 ms
RTTは大体40ms位ですね。はい。
ovs-01
# ping -c 4 192.168.56.11 PING 192.168.56.11 (192.168.56.11) 56(84) bytes of data. 64 bytes from 192.168.56.11: icmp_req=1 ttl=64 time=1.45 ms 64 bytes from 192.168.56.11: icmp_req=2 ttl=64 time=0.621 ms 64 bytes from 192.168.56.11: icmp_req=3 ttl=64 time=0.624 ms 64 bytes from 192.168.56.11: icmp_req=4 ttl=64 time=0.611 ms --- 192.168.56.11 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3005ms rtt min/avg/max/mdev = 0.611/0.827/1.455/0.363 ms
はい、オッケーデース。 遅延の方もまぁ丁度いい感じですかね。
レッツ開発
ではー、ここにー、開発済みのソフトウェアがありまーす。
https://www.ainoniwa.net/gitlab/snippets/12
すーぱー適当な動作概要
- ARPフレームをPacket-In
- VLANの異なる通信をOpenflow Switchで折り返せる場合、VLANと宛先MACを書き換えるフローを注入する
今回の場合、VLAN間ルーティングのためにRouter-01まで到達しないといけない通信を、ovs-01で折り返すことを目的にしている。
動かしてみよう
ryu
(ryu)$ ryu-manager ryu-app/l3_hairpin.py loading app ryu-app/l3_hairpin.py loading app ryu.controller.ofp_handler instantiating app ryu-app/l3_hairpin.py of L3Hairpin instantiating app ryu.controller.ofp_handler of OFPHandler
ovs-01
$ sudo ovs-vsctl set-controller ovs-01 tcp:192.168.56.11
残念ながらARPが飛ばないとL3ヘアピン用のフローが注入されないので、最初に消しておこう。
node-01
# arp -d 192.168.10.1
node-02
# arp -d 192.168.20.1
Router-01
# arp -d 192.168.10.10 # arp -d 192.168.20.10
ovs-01
初期状態のフロー:
$ sudo ovs-ofctl dump-flows ovs-01 NXST_FLOW reply (xid=0x4): cookie=0x1, duration=1.38s, table=0, n_packets=0, n_bytes=0, idle_age=1, priority=100,arp actions=CONTROLLER:65509,NORMAL cookie=0x1, duration=1.38s, table=0, n_packets=0, n_bytes=0, idle_age=1, priority=0 actions=NORMAL
node-01
# ping -c 4 192.168.20.10 PING 192.168.20.10 (192.168.20.10): 56 data bytes 64 bytes from 192.168.20.10: icmp_seq=0 ttl=64 time=31.400 ms 64 bytes from 192.168.20.10: icmp_seq=1 ttl=64 time=2.273 ms 64 bytes from 192.168.20.10: icmp_seq=2 ttl=64 time=1.489 ms 64 bytes from 192.168.20.10: icmp_seq=3 ttl=64 time=1.528 ms --- 192.168.20.10 ping statistics --- 4 packets transmitted, 4 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 1.489/9.172/31.400/12.837 ms
わーい、遅延が減ったよー。特に何もしてないからTTLすら64のままだよー。やったね!
ovs-01
L3ヘアピン用フロー注入後:
$ sudo ovs-ofctl dump-flows ovs-01 NXST_FLOW reply (xid=0x4): cookie=0x1, duration=7.389s, table=0, n_packets=0, n_bytes=0, idle_timeout=60, idle_age=7, priority=50,ip,in_port=1,nw_dst=192.168.10.1 actions=load:0xa->OXM_OF_VLAN_VID[],load:0x800272d120b->NXM_OF_ETH_DST[],output:2 cookie=0x1, duration=7.389s, table=0, n_packets=4, n_bytes=396, idle_timeout=60, idle_age=4, priority=50,ip,in_port=1,nw_dst=192.168.10.10 actions=load:0xa->OXM_OF_VLAN_VID[],load:0x80027099735->NXM_OF_ETH_DST[],IN_PORT cookie=0x1, duration=7.393s, table=0, n_packets=0, n_bytes=0, idle_timeout=60, idle_age=7, priority=50,ip,in_port=2,nw_dst=192.168.10.10 actions=load:0xa->OXM_OF_VLAN_VID[],load:0x80027099735->NXM_OF_ETH_DST[],output:1 cookie=0x1, duration=7.393s, table=0, n_packets=0, n_bytes=0, idle_timeout=60, idle_age=7, priority=50,ip,in_port=1,nw_dst=192.168.20.1 actions=load:0x14->OXM_OF_VLAN_VID[],load:0x800272d120b->NXM_OF_ETH_DST[],output:2 cookie=0x1, duration=7.393s, table=0, n_packets=0, n_bytes=0, idle_timeout=60, idle_age=7, priority=50,ip,in_port=2,nw_dst=192.168.10.1 actions=load:0xa->OXM_OF_VLAN_VID[],load:0x800272d120b->NXM_OF_ETH_DST[],IN_PORT cookie=0x1, duration=7.394s, table=0, n_packets=0, n_bytes=0, idle_timeout=60, idle_age=7, priority=50,ip,in_port=2,nw_dst=192.168.20.1 actions=load:0x14->OXM_OF_VLAN_VID[],load:0x800272d120b->NXM_OF_ETH_DST[],IN_PORT cookie=0x1, duration=7.39s, table=0, n_packets=3, n_bytes=298, idle_timeout=60, idle_age=4, priority=50,ip,in_port=1,nw_dst=192.168.20.10 actions=load:0x14->OXM_OF_VLAN_VID[],load:0x80027ae5fd5->NXM_OF_ETH_DST[],IN_PORT cookie=0x1, duration=7.389s, table=0, n_packets=0, n_bytes=0, idle_timeout=60, idle_age=7, priority=50,ip,in_port=2,nw_dst=192.168.20.10 actions=load:0x14->OXM_OF_VLAN_VID[],load:0x80027ae5fd5->NXM_OF_ETH_DST[],output:1 cookie=0x1, duration=35.251s, table=0, n_packets=4, n_bytes=256, idle_age=7, priority=100,arp actions=CONTROLLER:65509,NORMAL cookie=0x1, duration=35.251s, table=0, n_packets=2, n_bytes=204, idle_age=7, priority=0 actions=NORMAL
現実のネットワークではもう少し多くのホストが存在する上に、通信途中でもARPが飛んだりするので、考慮すべき問題はいくつかあるものの、 L3ヘアピン用のフローが注入されなかったとしても通信を阻害しないという点で、導入の敷居は低いと思う。
idle_timeoutは限界まで長くしておいても、実害は無いかもしれない。
おしまい
参考になるかは分からないけど、大体こんな程度の機能をこんな程度のコード量で書けそうです、と言う感じ。
今度はVXLANとかそういうものと組み合わせて、より大きめの試験環境を準備していく必要がありそうだよね。