◆ 【TremaでOpenFlowプログラミング】VLAN ID コンバータ
OpenFlow フレームワーク Trema を使って OpenFlow スイッチで VLAN ID を変換する機能を実装してみました。
テスト用の仮想ネットワークは、前回の記事で作った共通ファイル vnetcommon.sh を利用して Open vSwitch を VLAN スイッチと OpenFlow スイッチの両方で使った構成としています。
使用した環境: (前回の記事と同じです)
OS: Lubuntu 12.04 Desktop (i386) (※ 13.04 ではありません)
Trema: Trema 0.3.19
補足:
Open vSwitch のインストール方法は以下の記事の「■ステップ3. OpenVSwitch のインストール」を参照ください:
記事: 「2万円で OpenFlow スイッチを自作しよう」
http://ranosgrant.cocolog-nifty.com/blog/2013/02/2-openflow-4b98.html
■今回の仮想ネットワークの構成図
こんなストーリーを想定しています:
・2つの拠点があって、拠点ごとに別々に VLAN ID を管理していた。
・拠点1の VLAN ID 10, 20 の L2 ネットワークを拠点2 にも延ばす必要が生じた。
・しかし、拠点2 では VLAN ID 10, 20 は使えず、代わりに 拠点2 では LAN ID 30, 40 を割り当て、拠点間で VLAN ID を変換することにした。
・拠点1 の VLAN ID: 10 と 拠点2 の VLAN ID: 30 を接続する。
・拠点1 の VLAN ID: 20 と 拠点2 の VLAN ID: 40 を接続する。
■ 構成の確認
後述するシェルスクリプトで仮想ネットワークを作成したら OpenFlow ポートの1番が拠点1側、ポートの2番が拠点2側との接続となっていることを確認します。
$ sudo ovs-ofctl show ofs1 OFPT_FEATURES_REPLY (xid=0x1): ver:0x1, dpid:0000a67c7e48d940 n_tables:255, n_buffers:256 features: capabilities:0xc7, actions:0xfff 1(vlinkSW1-1): addr:c6:9c:ee:c6:c9:15 config: 0 state: 0 current: 10GB-FD COPPER 2(vlinkSW2-1): addr:46:d6:29:cd:8d:3f config: 0 state: 0 current: 10GB-FD COPPER LOCAL(ofs1): addr:a6:7c:7e:48:d9:40 config: PORT_DOWN state: LINK_DOWN OFPT_GET_CONFIG_REPLY (xid=0x3): frags=normal miss_send_len=0
■ OpenFlow コントローラ
OpenFlow フレームワーク Trema を用いて VLAN ID コンバータを実装しています。ソースコードは以下:
class VLANIDConverter < Controller @conversion_table def start @convesion_table = [ [ { :port => 1, :tag => 10 }, { :port => 2, :tag => 30 } ], [ { :port => 1, :tag => 20 }, { :port => 2, :tag => 40 } ] ] end def switch_ready( datapath_id ) puts "switch #{ datapath_id.to_hex } is connected." @convesion_table.each do | each | flow_mod( datapath_id, each[0], each[1] ) flow_mod( datapath_id, each[1], each[0] ) end send_flow_mod_add( datapath_id, :priority => 100, :actions => [ ] ) end def flow_mod( datapath_id, e1, e2 ) send_flow_mod_add( datapath_id, :match => Match.new( :in_port => e1[ :port ] , :dl_vlan => e1[ :tag ] ), :actions => [ SetVlanVid.new( e2[ :tag ] ), SendOutPort.new( e2[ :port ] ) ] ) end end
やっていることを文字にすると以下の通り:
・ポート1(拠点1) から来た VLAN ID: 10 のパケットなら、VLAN ID を 30 にすげかえてポート2(拠点2)へ出力。
・ポート2(拠点2) から来た VLAN ID: 30 のパケットなら、VLAN ID を 10 にすげかえてポート1(拠点1)へ出力。
・ポート1(拠点1) から来た VLAN ID: 20 のパケットなら、VLAN ID を 40 にすげかえてポート2(拠点2)へ出力。
・ポート2(拠点2) から来た VLAN ID: 40 のパケットなら、VLAN ID を 20 にすげかえてポート1(拠点1)へ出力。
・上記に当てはまらないパケットが来たら破棄する。(すべてのパケットに合致するフローエントリですが優先度 priority を他のフローエントリより下げることで「上記に当てはまらない」という動きにしています。)
ソース中の @conversion_table を変更すれば異なる変換ルールにできます。
コントローラは仮想ネットワークを作成した後に、以下で起動します:
trema run コントローラのソースコードのファイル
■ OpenFlow スイッチのフローテーブルの確認
$ sudo ovs-ofctl dump-flows ofs1 NXST_FLOW reply (xid=0x4): cookie=0x4, duration=22.022s, table=0, n_packets=0, n_bytes=0, priority=65535,in_port=2,dl_vlan=40 actions=mod_vlan_vid:20,output:1 cookie=0x1, duration=22.022s, table=0, n_packets=0, n_bytes=0, priority=65535,in_port=1,dl_vlan=10 actions=mod_vlan_vid:30,output:2 cookie=0x3, duration=22.022s, table=0, n_packets=0, n_bytes=0, priority=65535,in_port=1,dl_vlan=20 actions=mod_vlan_vid:40,output:2 cookie=0x2, duration=22.022s, table=0, n_packets=0, n_bytes=0, priority=65535,in_port=2,dl_vlan=30 actions=mod_vlan_vid:10,output:1 cookie=0x5, duration=22.022s, table=0, n_packets=0, n_bytes=0, priority=100 actions=drop
■ 動作確認
vhost1(192.168.0.1, 拠点1, VLAN ID: 10) から ping で vhost3(192.168.0.3, 拠点2, VLAN ID: 30) とのみ通信できることを確認:
$ sudo ip netns exec vhost1 ping -c2 192.168.0.2 PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data. From 192.168.0.1 icmp_seq=1 Destination Host Unreachable From 192.168.0.1 icmp_seq=2 Destination Host Unreachable --- 192.168.0.2 ping statistics --- 2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1008ms pipe 2
$ sudo ip netns exec vhost1 ping -c2 192.168.0.3 PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data. 64 bytes from 192.168.0.3: icmp_req=1 ttl=64 time=1.13 ms 64 bytes from 192.168.0.3: icmp_req=2 ttl=64 time=0.160 ms --- 192.168.0.3 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 0.160/0.649/1.138/0.489 ms $ sudo ip netns exec vhost1 ping -c2 192.168.0.4 PING 192.168.0.4 (192.168.0.4) 56(84) bytes of data. From 192.168.0.1 icmp_seq=1 Destination Host Unreachable From 192.168.0.1 icmp_seq=2 Destination Host Unreachable --- 192.168.0.4 ping statistics --- 2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1007ms pipe 2
vhost2(192.168.0.2, 拠点1, VLAN ID: 20) から ping で vhost4(192.168.0.4, 拠点2, VLAN ID: 40) とのみ通信できることを確認:
$ sudo ip netns exec vhost2 ping -c2 192.168.0.1 PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data. From 192.168.0.2 icmp_seq=1 Destination Host Unreachable From 192.168.0.2 icmp_seq=2 Destination Host Unreachable --- 192.168.0.1 ping statistics --- 2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1008ms pipe 2 $ sudo ip netns exec vhost2 ping -c2 192.168.0.3 PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data. From 192.168.0.2 icmp_seq=1 Destination Host Unreachable From 192.168.0.2 icmp_seq=2 Destination Host Unreachable --- 192.168.0.3 ping statistics --- 2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1008ms pipe 2 $ sudo ip netns exec vhost2 ping -c2 192.168.0.4 PING 192.168.0.4 (192.168.0.4) 56(84) bytes of data. 64 bytes from 192.168.0.4: icmp_req=1 ttl=64 time=1.05 ms 64 bytes from 192.168.0.4: icmp_req=2 ttl=64 time=0.153 ms --- 192.168.0.4 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 0.153/0.605/1.058/0.453 ms
■今回の仮想ネットワークの作成・削除シェルスクリプト
引数 delete で削除、add で追加です。
共通ファイル vnetcommon.sh がカレントディレクトリにおいてあることを前提としています。
#!/bin/sh . ./vnetcommon.sh delete() { deleteVSwitch "ofs1" "vswitch1" "vswitch2" deleteVLink "vlinkSW1" "vlinkSW2" deleteVHost "vhost1" "vhost2" "vhost3" "vhost4" deleteVLink "vlink1" "vlink2" "vlink3" "vlink4" } add() { addOFSwitch "ofs1" addVSwitch "vswitch1" addVSwitch "vswitch2" connectVSwitchToVSwitch "vswitch1" "vlinkSW1" "ofs1" connectVSwitchToVSwitch "vswitch2" "vlinkSW2" "ofs1" addVHost "vhost1" connectVHostToVSwitch "vhost1" "vlink1" "vswitch1" tag=10 vhostExec "vhost1" ifconfig "vlink1" "192.168.0.1" addVHost "vhost2" connectVHostToVSwitch "vhost2" "vlink2" "vswitch1" tag=20 vhostExec "vhost2" ifconfig "vlink2" "192.168.0.2" addVHost "vhost3" connectVHostToVSwitch "vhost3" "vlink3" "vswitch2" tag=30 vhostExec "vhost3" ifconfig "vlink3" "192.168.0.3" addVHost "vhost4" connectVHostToVSwitch "vhost4" "vlink4" "vswitch2" tag=40 vhostExec "vhost4" ifconfig "vlink4" "192.168.0.4" } case "$1" in add) sudo sh -c exit set -x add ;; delete) sudo sh -c exit delete > /dev/null 2>&1 ;; *) echo "usage: $0 { add | delete }" esac
vnetcommon.sh のコードは、前回記事の末尾を参照ください。
最近のコメント