はじめに
「そろそろ頃合い」
セイウチは言った
「あれやこれやの積もる話」
ルイス・
「不思議の国のアリス」
今回は盛りだくさんです! まずは身近な例を使って、
では前置きはこのぐらいにして、
OpenFlowの動作モデル
OpenFlowの動作を現実世界にたとえると、
電話サポートの業務手順
友太郎
簡単ですね? 信じられないかもしれませんが、

OpenFlowに置き換えると……
OpenFlowでは、

スイッチはホストからのパケットを受信すると、
電話サポートとの大きな違いは、
それではしくみの話はこのぐらいにして、
「トラフィック集計スイッチ」コントローラの概要
トラフィック集計スイッチは、
設計と実装
「L2スイッチ機能」
① FDBクラス
FDBクラス
class FDB
def initialize
@db = {} ← 連想配列(MACアドレス→スイッチポート番号)
end
def lookup mac ← MACアドレスからスイッチポート番号を引く
@db[ mac ]
end
def learn mac, port_number ← MACアドレス+スイッチポートを学習
@db[ mac ] = port_number
end
end
② Counterクラス
Counterクラス
class Counter
def initialize
@db = {} ← ホストごとの集計情報を記録する連想配列
end
def add mac, packet_count, byte_count ← ホスト(MACアドレス=mac)の送信パケット数、バイト数を追加
@db[ mac ] ||= { :packet_count => 0, :byte_count => 0 }
@db[ mac ][ :packet_count ] += packet_count
@db[ mac ][ :byte_count ] += byte_count
end
def each_pair &block ← 集計情報の表示用
@db.each_pair &block
end
end
③ TrafficMonitorクラス
Traffi cMonitorクラスはコントローラの本体です
- ① packet_
inメッセージが到着したとき、 パケットを宛先のスイッチポートに転送し、 フローテーブルを更新する部分 - ② flow_
removedメッセージが到着したとき、 トラフィック集計情報を更新する部分 - ③ タイマーで10秒ごとにトラフィックの集計情報を表示する部分
require "counter"
require "fdb"
class TrafficMonitor periodic_timer_event :show_counter, 10 ――――③
def start
@counter = Counter.new ← Counterオブジェクト
@fdb = FDB.new ← FDBオブジェクト
end
def packet_in datapath_id, message ――――①
macsa = message.macsa ← パケットを送信したホストのMACアドレス
macda = message.macda ← パケットの宛先ホストのMACアドレス
@fdb.learn macsa, message.in_port
@counter.add macsa, 1, message.total_len
out_port = @fdb.lookup( macda )
if out_port
packet_out datapath_id, message, out_port
flow_mod datapath_id, macsa, macda, out_port
else
flood datapath_id, message
end
end
def flow_removed datapath_id, message ――――②
@counter.add message.match.dl_src,message.packet_count, message.byte_count
end
private ← 以下、プライベートメソッド
def show_counter ← カウンタを表示
puts Time.now
@counter.each_pair do | mac, counter |
puts "#{ mac } #{ counter[ :packet_count ] } packets (#{ counter[ :byte_count ] } bytes)"
end
end
def flow_mod datapath_id, macsa, macda, out_port ← macsaからmacdaへのパケットをout_portへ転送するflow_modを打つ
send_flow_mod_add(
datapath_id,
:hard_timeout => 10, ← flow_modの有効期限は10秒
:match => Match.new( :dl_src => macsa, :dl_dst => macda ),
:actions => Trema::ActionOutput.new( out_port )
)
end
def packet_out datapath_id, message, out_port ← packet_inしたメッセージをout_portへ転送
send_packet_out(
datapath_id,
:packet_in => message,
:actions => Trema::ActionOutput.new( out_port )
)
end
def flood datapath_id, message ← packet_inしたメッセージをin_port以外の全スイッチポートへ転送
packet_out datapath_id, message, OFPP_FLOOD
end
end
それでは、
以下の説明では図3に示すホスト2台+スイッチ1台からなるネットワーク構成を使います。host1からhost2にパケットを送信したときの動作シーケンスは図4のようになります。


- ➊ host1からhost2を宛先としてパケットを送信すると、
まずはスイッチにパケットが届く - ➋ スイッチのフローテーブルは最初はまっさらで、
どう処理すればよいかわからない状態なので、 コントローラであるTrafficMonitorにpacket_ inメッセージを送る - ➌ Traffi cMonitorのpacket_
inメッセージハンドラでは、 packet_ inメッセージのin_ port (host1のつながるスイッチポート) とhost1のMACアドレスをFDBに記録する - ➍ また、
Counterに記録されたhost1の送信トラフィックを1パケット分増やす - ➎ packet_
inメッセージの宛先MACアドレスから転送先のスイッチポート番号をFDBに問い合わせる。この時点ではhost2のスイッチポートは学習していないので、 結果は 「不明」 - ➏ そこで、
パケットをin_ port以外のすべてのスイッチポートに出力するpacket_ outメッセージ (FLOODと呼ばれる) をスイッチに送り、 host2が受信してくれることを期待する - ➐ スイッチは、
パケットをin_ port以外のすべてのポートに出す
これで、

- ➎ packet_
inメッセージの宛先MACアドレスから、 転送先のスイッチポート番号をFDBに問い合わせる。これは、 先ほどhost1からhost2にパケットを送った時点でFDBに学習させているので、 送信先はスイッチポート1番ということがわかる - ➏ そこで、
TrafficMonitorはパケットをスイッチポート1番へ出力するpacket_ outメッセージをスイッチに送る。スイッチはこれを受け取ると、 パケットをスイッチポート1番に出し、 最終的にhost1がパケットを受信する - ➐ 「送信元=00:00:00:00:00:02、
送信先=00:00:00:00:00:01となるパケットはスイッチポート1番に転送せよ」 というflow_ modメッセージをスイッチに送信する
最後の➐によって、
実行してみよう
それでは、
% ./trema run ./traffic-monitor.rb
-c ./traffic-monitor.conf
vswitch { ← 仮想スイッチ0xabcを定義
datapath_id 0xabc
}
vhost ("host1") { ← 仮想ホストhost1を定義
ip "192.168.0.1"
mac "00:00:00:00:00:01"
}
vhost ("host2") { ← 仮想ホストhost2を定義
ip "192.168.0.2"
mac "00:00:00:00:00:02"
}
link "0xabc", "host1" ← ホストhost1、host2をスイッチ0xabcに接続
link "0xabc", "host2"
実行すると、
それでは、
% ./trema send_packets --source host1 --dest host2
--n_pkts 10 --pps 10← host1からhost2宛にパケットを10個送る
% ./trema send_packets --source host2 --dest host1
--n_pkts 10 --pps 10← host2からhost1宛にパケットを10個送る
trema runを実行した元のターミナルに次のような出力が出ていれば成功です
……
00:00:00:00:00:01 10 packets (640 bytes)
↑host1からパケットが10個送信された
00:00:00:00:00:02 10 packets (640 bytes)
↑host2からパケットが10個送信された
……
まとめ
今回は
- 電話サポートの例を使ってOpenFlowの動作モデルを学びました。パケットの転送はスイッチ上のフローテーブルによって行われ、
fl ow_ modメッセージによって書き換えることができます。また、 フローテーブルに登録されていないパケットによってpacket_ inメッセージがコントローラに届きます。 - 仮想ネットワークを使ったコントローラの動作テスト方法を学びました。仮想スイッチと仮想ホストを起動してつなぎ、
send_ packetsコマンドを使って仮想ホスト間でパケットを送受信することで、 コントローラの簡単な動作テストができます。
次回はTremaを使ったテストファースト開発を紹介します。RailsやSinatraを使ったWebアジャイル開発ではお馴染みのテストファースト開発ですが、