Openflowを使ってL3ヘアピン機能を実装してみる

Posted on 2014/03/15(Sat) 18:22 in technical

はじめに

「openvswitchのインストールとかそういうのいいから参考になりそうなアプリ書けオルァ」的な圧力を感じます。

そこで、今回はVEPAとかそういう感じのアレを実装して、部分最適を考えたSDNアプリケーションの実装をしてみる。

「お前がSDNだと思うものがSDNだ」を胸に抱いて。

そんな私たちは2軍のファンタジスタであーる

あ、今BGMがGJ部なんで許してください。

前回使ったトポロジを大体そのまま流用することにする。

openflow_ryu_l3hairpin.png

え、前回ってこれでしょ?(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タグつけて同じポートから出てくる動作をする。(オレンジ色の線)

openflow_ryu_l3hairpin_002.png

でも、これくらいの動作なら間の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

すーぱー適当な動作概要

  1. ARPフレームをPacket-In
  2. 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とかそういうものと組み合わせて、より大きめの試験環境を準備していく必要がありそうだよね。