前回はOpenFlowスイッチの実装であるOpen vSwitchを動かしてみまし
た。今回はOpenFlowコントローラを導入して、Open vSwitchと接続
して制御してみます。
OpenFlowコントローラにはNOX, Tremaなどいくつかのオープンソース
実装があります。今回はおもにNECで開発されているTremaを利用して
みます。Rubyで容易に記述できるらしいということから選んでみました。
今回の目標は、
「マルチホーム環境でopenvswitchの配下にあるクライアントからの
上流へのアクセス時に、特定のTCPポート(今回はSMTP=25)の通信
だけ異なるrouterを経由するようにする」
ということです。
前回はOpen vSwitch単体動作で上記を実現しましたが、今回はOpenFlow
コントローラ経由でこれを実現したいと思います。
◆OpenFlowコントローラTrema導入
まずはTremaの導入に必要なものをインストールします。
# aptitude install gcc make ruby ruby-dev irb file libpcap-dev libsqlite3-dev rubygems rake arpingなおarpingはTrema導入には必要ないですが、今回利用するためイン
ストールしています。
今回、Tremaはgemを利用してインストールします。
ただ、そのまま試してみるとrake1.8がないというエラーになるため
以下の対処をしておきます。
# cd /usr/bin
# ln -s rake rake1.8
# gem install trema
2012年3月11日時点ではtrema 0.2.2.1がインストールされました。
# gem list trema
*** LOCAL GEMS ***実行ファイルtremaは/var/lib/gems/1.8/binにインストールされるので
trema (0.2.2.1)
ここにPATHを通しておきます。
# PATH=/var/lib/gems/1.8/bin:$PATH
◆TremaでOpen vSwitchを制御
まずはTremaを起動してみます。
Tremaにはいくつかサンプルが附属しています。
L2スイッチ機能を実現したlearning_switchというサンプルを試して
みることにします。
# cd /var/lib/gems/1.8/gems/trema-0.2.2.1/src/examples/learning_switch
# trema run learning-switch.rb
である6633でOpenFlowスイッチからの接続を待っている状態になります。
# lsof -i:6633通常はOpenFlowスイッチとOpenFlowコントローラは別ホストですが、
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
switch_ma 32074 root 5u IPv4 11451496 0t0 TCP *:6633 (LISTEN)
今回は同一ホスト(OpenBlockS 600D)でOpen vSwitchとTrema両者を実行します。
なお、tremaを起動したコンソールはアタッチされたままなので、別
コンソールでOpen vSwitchの作業を行います。
Open vSwitchは起動している状態になっているとします。
Open vSwitchからOpenFlowコントローラ(Trema)への接続はovs-vsctlの
set-controllerコマンドで行います。同一ホスト内なのでlocalhost(127.0.0.1)
に接続します。
# ovs-vsctl set-controller br0 tcp:127.0.0.1なお、コントローラからの離脱は
# ovs-vsctl del-controller br0で行います。
前回と同様に以下のようなネットワークを構成します。
tremaとopenvswitchはobs600-3で動いていて、openvswitchはtremaに
接続している状態になっているとします。
ここでobs600-1上でrouter-1へのpingを実行します。
obs600-1# ping -c 1 192.168.100.1このときtremaからopenvswitchへはARP REPLY, ICMP ECHO REQUEST,
ICMP ECHO REPLYの3つのフローが登録されています。なお、ARP REQUEST
についてはtremaのlearning-switch.rbでは出力先のポートが未定
なためこの時点ではフローは登録されません。
# ovs-ofctl dump-flows br0
NXST_FLOW reply (xid=0x4):
cookie=0x2, duration=4.156s, table=0, n_packets=1, n_bytes=98, priority=65535,icmp,in_port=2,vlan_tci=0x0000,dl_src=00:0a:85:04:93:99,dl_dst=00:00:00:00:00:01,nw_src=192.168.100.123,nw_dst=192.168.100.1,nw_tos=0,icmp_type=8,icmp_code=0 actions=output:1
cookie=0x3, duration=4.146s, table=0, n_packets=1, n_bytes=98, priority=65535,icmp,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:0a:85:04:93:99,nw_src=192.168.100.1,nw_dst=192.168.100.123,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:2
cookie=0x1, duration=4.17s, table=0, n_packets=0, n_bytes=0, priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:0a:85:04:93:99,nw_src=192.168.100.1,nw_dst=192.168.100.123,arp_op=2 actions=output:2
Trema確認中に少し気になった点があります。TremaはCtrl-Cで終了
するのですが、switch_managerプロセスが動作したままで、次回の
trema起動がうまくいかない状況になりました。とりあえずtrema run
の前にkillall switch_managerを行うことで暫定対処としました。
◆コントローラ用コード作成
今回の目的の
「マルチホーム環境でopenvswitchの配下にあるクライアントからの
上流へのアクセス時に、特定のTCPポート(今回はSMTP=25)の通信
だけ異なるrouterを経由するようにする」
を実現することを考えます。
基本的にはL2スイッチなのでtremaサンプルのlearning-switch.rbを
ベースにします。
上記を実現するためのコードを少し加えます(modified-learning-switch.rb)。
なお、既存部分で変更を加えていない箇所は掲載を省略しています。
ざっくり動作を説明します。
class ModifiedLearningSwitch < Trema::Controlleradd_timer_event :age_fdb, 5, :periodicadd_timer_event :periodic_arp_request, 10, :periodicdef start@fdb = FDB.new@target_routes = []@target_routes.push({ :route_dst => "192.168.100.1",:route_dst_modified => "192.168.100.2",:ip_src => "192.168.100.123",:port => 25})periodic_arp_requestenddef packet_in datapath_id, message@fdb.learn message.macsa, message.in_portmatched_route = nil@target_routes.each do |route|if message.tcp? &&message.tcp_dst_port == route[:port] &&message.ipv4_saddr.to_s == route[:ip_src] &&message.macda.to_s == route[:mac_dst]matched_route = routeendendport_no = @fdb.port_no_of( message.macda )if matched_route.nil?if port_noflow_mod datapath_id, message, port_nopacket_out datapath_id, message, port_noelseflood datapath_id, messageendelseif port_noflow_mod_dstmac datapath_id, message, port_no, matched_route[:mac_dst_modified]packet_out_dstmac datapath_id, message, port_no, matched_route[:mac_dst_modified]elseflood_dstmac datapath_id, message, matched_route[:mac_dst_modified]endendend##############################################################################private##############################################################################def periodic_arp_request@target_routes.each do |route|reply=`arping -0 -c 1 -w 100 #{route[:route_dst]}`if /from (\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2}) \(([\d\.]+)\)/ =~ replyinfo "arping " + $1 + "=" + $2route[:mac_dst] = $1endreply=`arping -0 -c 1 -w 100 #{route[:route_dst_modified]}`if /from (\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2}) \(([\d\.]+)\)/ =~ replyinfo "arping " + $1 + "=" + $2route[:mac_dst_modified] = $1endendenddef flow_mod_dstmac datapath_id, message, port_no, new_mac_dstsend_flow_mod_add(datapath_id,:match => ExactMatch.from( message ),:actions => [Trema::ActionSetDlDst.new( :dl_dst => Mac.new( new_mac_dst )),Trema::ActionOutput.new( :port => port_no )])enddef packet_out_dstmac datapath_id, message, port_no, new_mac_dstsend_packet_out(datapath_id,:packet_in => message,:actions => [Trema::ActionSetDlDst.new( :dl_dst => Mac.new( new_mac_dst )),Trema::ActionOutput.new( :port => port_no )])enddef flood_dstmac datapath_id, message, new_mac_dstpacket_out_dstmac datapath_id, message, OFPP_FLOOD, new_mac_dstendend
まずは変更点として、periodic_arp_request methodで定期的にarp requestをrouter-1
とrouter-2に送信してMACアドレスを解決しておきます。これは処理するパケットのDST MAC
がrouter-1のMACにマッチした場合にrouter-2のMACに変更するということを実現する
ために必要になるためです。
openvswitchでフローが未解決パケットが到着したときには、OpenFlow プロト
コルでtremaに送信します。そしてtremaではpacket_inメソッドが呼ばれます。
learning-switch.rbではpacket_inでは通常は宛先MACアドレスを学習している
場合は、
1. フローをOpenFlowスイッチに登録(flow_mod)
2. 受信したパケットを該当ポートへ出力(packet_out)
という処理を行います。
今回の変更点として、特定の条件(ip_src = 192.168.100.123, dst_port = 25)
にマッチする場合は、
1. フローをOpenFlowスイッチに登録
ただし、DST MACアドレスをrouter-2のものに変更(flow_mod_dstmac)
2. 受信したパケットを該当ポートへ出力
ただし、DST MACアドレスをrouter-2のものに変更(packet_out_dstmac)
という処理を行うように修正しました。
◆動作実験
上記で作成したmodified-learning-switch.rbを実行します。
# trema run modified-learning-switch.rb
# ovs-vsctl set-controller br0 tcp:127.0.0.1obs600-1から外部のホスト(10.0.0.1)にsmtp接続します。
obs600-1# telnet 10.0.0.1 25このときのOpen vSwitchのフローの状態を確認します。
Connected to 10.0.0.1.
Escape character is '^]'.
220 smtp.example.org. ESMTP Postfix
# ovs-ofctl dump-flows br0dump-flowsでの確認結果には、mod_dl_dstが表示されています。
- 省略 -
cookie=0x9, duration=5.993s, table=0, n_packets=5, n_bytes=336, priority=65535,tcp,in_port=2,vlan_tci=0x0000,dl_src=00:0a:85:04:93:99,dl_dst=00:00:00:00:00:01,nw_src=192.168.100.123,nw_dst=10.0.0.1,nw_tos=16,tp_src=50729,tp_dst=25 actions=mod_dl_dst:00:00:00:00:00:02,output:1
- 省略 -
また、10.0.0.1のmailログでもrouter-2経由でアクセスされたログを
確認できので期待通りの動作を実現できました。
◆まとめ
今回はOpenFlowコントーラのTremaを利用し、Open vSwitchをTremaに
接続してOpenFlowプロトコルでフローが設定されることを確認しました。
また、Tremaのコードに修正を加えることで、特定の条件にマッチする場合
には異なるノードへ転送するようなフローをOpen vSwitchに設定する
ことができることを確認しました。
0 件のコメント:
コメントを投稿