Permalink: 2014-11-02 19:20:00+09:00 by ruy@ainoniwa.net in technical tags: openstack openvswitch openflow social:

はじまり

先日、 GNS3を用いてトポロジを作り、OpenStack環境を構築できることを確認した。

その際、VXLANを用いた仮想サブネットを構築したわけだが、これは単純に考えるとループが発生するトポロジとなる。

今回は、br-tunにインストールされたフローエントリから、トンネルを用いたOpenStackの仮想サブネットがどのようにループを防止しているかを見ていく。

初見

まずは、前回のOpenStackの仮想サブネット構成を例に、仮想スイッチの構成を見てみよう。

compute02で見た場合の仮想スイッチの構成(例)

# ovs-vsctl show
a01814aa-f4ac-4848-af80-bcc45568e4aa
    Bridge br-int
        fail_mode: secure
        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}
        Port "qvodfe516d1-4c"
            tag: 3
            Interface "qvodfe516d1-4c"
        Port br-int
            Interface br-int
                type: internal
    Bridge br-tun
        Port "vxlan-c0a86502"
            Interface "vxlan-c0a86502"
                type: vxlan
                options: {in_key=flow, local_ip="192.168.101.12", out_key=flow, remote_ip="192.168.101.2"}
        Port br-tun
            Interface br-tun
                type: internal
        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}
        Port "vxlan-c0a8650b"
            Interface "vxlan-c0a8650b"
                type: vxlan
                options: {in_key=flow, local_ip="192.168.101.12", out_key=flow, remote_ip="192.168.101.11"}
    ovs_version: "1.11.0"

問題意識

さて、普通にOpen vSwitchのVXLANを複数点で接続すると、下図のようなループが発生することが予想される。

openstack_br-tun_001.png

が、当然OpenStackではループでネットワークの身動きが取れないと言った状況は発生していない。

この問題は、単純に考えると「トンネルから入ってきたパケットは、トンネルにはフラッディングしない」ことで解決すると考えられる。

そして、OpenStackの作るフローエントリはまさにそういう状況になっているのだろう。

答え合わせ

では、同仮想スイッチのフローエントリを見てみよう。

compute02で見た場合の仮想スイッチのフローエントリ(例)

# ovs-ofctl dump-flows br-tun
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=115832.009s, table=0, n_packets=457, n_bytes=47352, idle_age=1391, hard_age=65534, priority=1,in_port=3 actions=resubmit(,3)
 cookie=0x0, duration=115902.012s, table=0, n_packets=556, n_bytes=57416, idle_age=1391, hard_age=65534, priority=1,in_port=1 actions=resubmit(,1)
 cookie=0x0, duration=115900.908s, table=0, n_packets=54, n_bytes=5064, idle_age=23814, hard_age=65534, priority=1,in_port=2 actions=resubmit(,3)
 cookie=0x0, duration=115901.967s, table=0, n_packets=4, n_bytes=300, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0x0, duration=115901.888s, table=1, n_packets=492, n_bytes=51340, idle_age=1391, hard_age=65534, priority=1,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
 cookie=0x0, duration=115901.754s, table=1, n_packets=64, n_bytes=6076, idle_age=23814, hard_age=65534, priority=1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,21)
 cookie=0x0, duration=115901.675s, table=2, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0x0, duration=110244.491s, table=3, n_packets=300, n_bytes=31148, idle_age=1391, hard_age=65534, priority=1,tun_id=0xb actions=mod_vlan_vid:3,resubmit(,10)
 cookie=0x0, duration=115901.619s, table=3, n_packets=28, n_bytes=2520, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0x0, duration=115901.569s, table=10, n_packets=483, n_bytes=49896, idle_age=1391, hard_age=65534, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:1
 cookie=0x0, duration=115901.529s, table=20, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=resubmit(,21)
 cookie=0x0, duration=110244.534s, table=21, n_packets=18, n_bytes=1724, idle_age=23814, hard_age=65534, dl_vlan=3 actions=strip_vlan,set_tunnel:0xb,output:3,output:2
 cookie=0x0, duration=115901.489s, table=21, n_packets=14, n_bytes=1036, idle_age=65534, hard_age=65534, priority=0 actions=drop

見やすくするため、不要部分を削って列を揃えて並び替えてテーブル毎で区切るとこうなる。

table=0,  priority=1, in_port=1                                  actions=resubmit(,1)
table=0,  priority=1, in_port=2                                  actions=resubmit(,3)
table=0,  priority=1, in_port=3                                  actions=resubmit(,3)
table=0,  priority=0                                             actions=drop

table=1,  priority=1, dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
table=1,  priority=1, dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,21)

table=2,  priority=0                                             actions=drop

table=3,  priority=1, tun_id=0xb                                 actions=mod_vlan_vid:3,resubmit(,10)
table=3,  priority=0                                             actions=drop

table=10, priority=1                                              actions=learn(table=20,hard_timeout=300,priority=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:1

table=20, priority=0                                             actions=resubmit(,21)

table=21,             dl_vlan=3                                  actions=strip_vlan,set_tunnel:0xb,output:3,output:2
table=21, priority=0                                             actions=drop

文字を追うのはつらいので、雑な絵に描き起こすとこうなる。

openstack_br-tun_002.png

と言うわけで、こんな風に「トンネルから入ってきたパケットはフラッディングせず、Uplinkにのみ投げる(Table=10)」と言う状況を実現している。

つまりこうだ。

openstack_br-tun_003.png

br-tunを1つのインタフェースとして見れば、これはSplit-Horizonと同義と言える。

そして、この挙動自体はbr-tunを作らない(即ち、br-intに直接VXLANインタフェースを作る)場合でも実現は出来るが、 その場合は仮想マシンの数に合わせてフローエントリを増やさなければならず、しかも既存のフローエントリのoutputを変更する必要も出てくる。

br-tunにトンネルインタフェースを専任させることで、トンネルインタフェースとUplink(br-int向)インタフェースをシンプルに分けることが出来るので、フローエントリもスッキリする。

もちろん、これはフルメッシュ構造が基本で、トンネルトポロジがスター型だったりするとパケットが届かない場所が出てきたりするので、使い分けが必要な話。

こいつは当然GREの時点から考慮されていたもので「VXLANでも使えるけどユニキャストなトンネルインタフェースで共通」なお話である。

まとめ

OpenStackはbr-tunをトンネル専用インタフェースとすることで、フローエントリを抑えてSplit-Horizonを実現している。

当然、ovs-ofctl del-flows br-tunを叩くと、トポロジを維持したままループが発生する状況を作れてしまうので、 SDNコントローラ開発中に誤ってbr-tunのフローエントリを消してしまわないよう留意する必要があるだろう。

と言うお話だったとさ。