@@ -18,7 +18,7 @@ OpenFlow の動作を現実世界にたとえると,製品の電話サポー
18
18
19
19
## 電話サポートの業務手順
20
20
21
- 友太郎 ( ゆうたろう) 君は,エアコンが故障したので修理に出そうと考えました (図1)。電話サポートに問い合わせると,サポート係の葵 (あおい) さんはエアコンの症状を聞き,手元のマニュアルに対処方法が載っている場合にはこれをすぐに教えてくれます。問題は,マニュアルに対処法が載っていない場合です。このようなときは少し時間がかかりますが,上司の宮坂主任にどうしたらよいか聞きます。そして,宮坂主任からの回答が得られたら,葵さんは友太郎君に折り返し電話をします。また,次からの同じ問い合わせにはすばやく答えられるようにするため,葵さんは教わった対処法を手元のマニュアルに追加しておきます。
21
+ 友太郎 ( ゆうたろう) 君は,エアコンが故障したので修理に出そうと考えました (図1)。電話サポートに問い合わせると,サポート係の葵 (あおい) さんはエアコンの症状を聞き,手元のマニュアルに対処方法が載っている場合にはこれをすぐに教えてくれます。問題は,マニュアルに対処法が載っていない場合です。このようなときは少し時間がかかりますが,上司の宮坂主任にどうしたらよいか聞きます。そして,宮坂主任からの回答が得られたら,葵さんは友太郎君に折り返し電話をします。また,次からの同じ問い合わせにはすばやく答えられるようにするため,葵さんは教わった対処法を手元のマニュアルに追加しておきます。
22
22
23
23
簡単ですね? 信じられないかもしれませんが,あなたはすでに OpenFlow の 95% を理解したも同然なのです。
24
24
@@ -28,13 +28,13 @@ OpenFlow の動作を現実世界にたとえると,製品の電話サポー
28
28
29
29
## OpenFlowに置き換えると……
30
30
31
- OpenFlow では,お客さんがパケットを発生させるホスト,電話サポート係がスイッチ,上司がコントローラ,マニュアルがスイッチのフローテーブル (後述) に対応します (図2)。
31
+ OpenFlow では,お客さんがパケットを発生させるホスト,電話サポート係がスイッチ,上司がコントローラ,マニュアルがスイッチのフローテーブル (後述) に対応します (図2)。
32
32
33
33
![ OpenFlowの動作モデル] ( https://github.com/trema/Programming-Trema/raw/master/images/2_002.png )
34
34
35
35
図2 OpenFlowの動作モデル
36
36
37
- スイッチはホストからのパケットを受信すると,最初はその処理方法がわかりません。そこで,上司にあたるコントローラに問い合わせます。この問い合わせを `packet_in` メッセージと呼びます。コントローラはこれを受け取ると,同様のパケットが届いた場合にスイッチでどう処理すべきか ( パケットを転送する,書き換えるなど) を決めます。これをアクションと呼びます。そして 「スイッチで処理すべきパケットの特徴」+「アクション」 の組 (フローと呼びます) をスイッチのマニュアルに追加します。この命令を `flow_mod` メッセージと呼び,スイッチのマニュアルをフローテーブルと呼びます。処理すべきパケットの特徴とアクションをフローテーブルに書いておくことで,以後,これに当てはまるパケットはスイッチ側だけですばやく処理できます。忘れてはならないのが,`packet_in` メッセージで上がってきた最初のパケットです。これはコントローラに上がってきて処理待ちの状態になっているので,`packet_out` メッセージで適切な宛先に転送してあげます。
37
+ スイッチはホストからのパケットを受信すると,最初はその処理方法がわかりません。そこで,上司にあたるコントローラに問い合わせます。この問い合わせを `packet_in` メッセージと呼びます。コントローラはこれを受け取ると,同様のパケットが届いた場合にスイッチでどう処理すべきか ( パケットを転送する,書き換えるなど) を決めます。これをアクションと呼びます。そして 「スイッチで処理すべきパケットの特徴」+「アクション」 の組 (フローと呼びます) をスイッチのマニュアルに追加します。この命令を `flow_mod` メッセージと呼び,スイッチのマニュアルをフローテーブルと呼びます。処理すべきパケットの特徴とアクションをフローテーブルに書いておくことで,以後,これに当てはまるパケットはスイッチ側だけですばやく処理できます。忘れてはならないのが,`packet_in` メッセージで上がってきた最初のパケットです。これはコントローラに上がってきて処理待ちの状態になっているので,`packet_out` メッセージで適切な宛先に転送してあげます。
38
38
39
39
電話サポートとの大きな違いは,フローテーブルに書かれたフローには期限があり,これを過ぎると消えてしまうということです。これは,「マニュアルに書かれた内容は徐々に古くなるので,古くなった項目は消す必要がある」 と考えるとわかりやすいかもしれません。フローが消えるタイミングでコントローラには ` flow_removed ` メッセージが送信されます。これには,あるフローに従ってパケットがどれだけ転送されたか -- 電話サポートの例で言うと,マニュアルのある項目が何回参照されたか -- つまり,トラフィックの集計情報が記録されています。
40
40
@@ -47,18 +47,18 @@ OpenFlow では,お客さんがパケットを発生させるホスト,電
47
47
48
48
## 設計と実装
49
49
50
- 「L2 スイッチ機能」と「トラフィックの集計機能」のためにはどんな部品が必要でしょうか? まずは,スイッチに指示を出す上司にあたるコントローラクラスが必要です。これを ` TrafficMonitor ` クラスと名付けましょう。また,パケットを宛先のスイッチポートへ届けるための ` FDB ` クラス (注1) ,あとはトラフィックを集計するための ` Counter ` クラスの 3 つが最低限必要です。
50
+ 「L2 スイッチ機能」と「トラフィックの集計機能」のためにはどんな部品が必要でしょうか? まずは,スイッチに指示を出す上司にあたるコントローラクラスが必要です。これを ` TrafficMonitor ` クラスと名付けましょう。また,パケットを宛先のスイッチポートへ届けるための ` FDB ` クラス (注1) ,あとはトラフィックを集計するための ` Counter ` クラスの 3 つが最低限必要です。
51
51
52
- 注1) FDB とは Forwarding DataBase の略で,スイッチの一般的な機能です。詳しくは続く実装で説明します。
52
+ 注1) FDB とは Forwarding DataBase の略で,スイッチの一般的な機能です。詳しくは続く実装で説明します。
53
53
54
54
### FDBクラス
55
55
56
- ` FDB ` クラス ( リスト1) は,ホストの MAC アドレスとホストが接続しているスイッチポートの対応を学習するデータベースです。このデータベースを参照することで,` packet_in ` メッセージで入ってきたパケットの宛先 MAC アドレスからパケット送信先のスイッチポートを決定できます。
56
+ ` FDB ` クラス ( リスト1) は,ホストの MAC アドレスとホストが接続しているスイッチポートの対応を学習するデータベースです。このデータベースを参照することで,` packet_in ` メッセージで入ってきたパケットの宛先 MAC アドレスからパケット送信先のスイッチポートを決定できます。
57
57
58
58
``` ruby
59
59
class FDB
60
60
def initialize
61
- @db = {} # <- 連想配列( MACアドレス→スイッチポート番号)
61
+ @db = {} # <- 連想配列( MACアドレス→スイッチポート番号)
62
62
end
63
63
64
64
def lookup mac # <- MACアドレスからスイッチポート番号を引く
@@ -71,19 +71,19 @@ class FDB
71
71
end
72
72
```
73
73
74
- リスト1 MACアドレス→スイッチポートのデータベースFDBクラス( fdb.rb)
74
+ リスト1 MACアドレス→スイッチポートのデータベースFDBクラス( fdb.rb)
75
75
76
76
### Counter クラス
77
77
78
- ` Counter ` クラス ( リスト2) は,ホスト (MAC アドレスで区別します) ごとの送信パケット数およびバイト数をカウントします。また,カウントした集計情報を表示するためのヘルパメソッドを提供します。
78
+ ` Counter ` クラス ( リスト2) は,ホスト (MAC アドレスで区別します) ごとの送信パケット数およびバイト数をカウントします。また,カウントした集計情報を表示するためのヘルパメソッドを提供します。
79
79
80
80
``` ruby
81
81
class Counter
82
82
def initialize
83
83
@db = {} # <- ホストごとの集計情報を記録する連想配列
84
84
end
85
85
86
- def add mac , packet_count , byte_count # <- ホスト ( MAC アドレス = mac) の送信パケット数、バイト数を追加
86
+ def add mac , packet_count , byte_count # <- ホスト ( MAC アドレス = mac) の送信パケット数、バイト数を追加
87
87
@db [ mac ] ||= { :packet_count => 0 , :byte_count => 0 }
88
88
@db [ mac ][ :packet_count ] += packet_count
89
89
@db [ mac ][ :byte_count ] += byte_count
@@ -95,15 +95,15 @@ class Counter
95
95
end
96
96
```
97
97
98
- リスト2 トラフィックを記録し集計する ` Counter ` クラス ( ` counter.rb ` )
98
+ リスト2 トラフィックを記録し集計する ` Counter ` クラス ( ` counter.rb ` )
99
99
100
100
### TrafficMonitor クラス
101
101
102
- ` TrafficMonitor ` クラスはコントローラの本体です ( リスト3) 。メインの処理はリスト 3 1-3 の 3 つになります。
102
+ ` TrafficMonitor ` クラスはコントローラの本体です ( リスト3) 。メインの処理はリスト 3 1-3 の 3 つになります。
103
103
104
- ① ` packet_in ` メッセージが到着したとき,パケットを宛先のスイッチポートに転送し,フローテーブルを更新する部分
105
- ② ` flow_removed ` メッセージが到着したとき,トラフィック集計情報を更新する部分
106
- ③ タイマーで 10 秒ごとにトラフィックの集計情報を表示する部分
104
+ 1 . ` packet_in ` メッセージが到着したとき,パケットを宛先のスイッチポートに転送し,フローテーブルを更新する部分
105
+ 2 . ` flow_removed ` メッセージが到着したとき,トラフィック集計情報を更新する部分
106
+ 3 . タイマーで 10 秒ごとにトラフィックの集計情報を表示する部分
107
107
108
108
109
109
``` ruby
@@ -169,7 +169,7 @@ class TrafficMonitor < Controller
169
169
end
170
170
```
171
171
172
- リスト3 本体 ` TrafficMonitor ` クラス ( ` traffic-monitor.rb ` )
172
+ リスト3 本体 ` TrafficMonitor ` クラス ( ` traffic-monitor.rb ` )
173
173
174
174
それでは,とくに重要な (1) の処理を詳しく見ていきましょう。なお,リスト 3 中で使われているメソッドの引数など API の詳細については,「Trema Ruby API ドキュメント」 を参照してください。
175
175
@@ -186,21 +186,21 @@ end
186
186
187
187
1 . ` host1 ` から ` host2 ` を宛先としてパケットを送信すると,まずはスイッチにパケットが届く
188
188
2 . スイッチのフローテーブルは最初はまっさらで,どう処理すればよいかわからない状態なので,コントローラである ` TrafficMonitor ` に ` packet_in ` メッセージを送る
189
- 3 . ` TrafficMonitor ` の ` packet_in ` メッセージハンドラでは,` packet_in ` メッセージの ` in_port ` ( ` host1 ` のつながるスイッチポート) と ` host1 ` の MAC アドレスを FDB に記録する
189
+ 3 . ` TrafficMonitor ` の ` packet_in ` メッセージハンドラでは,` packet_in ` メッセージの ` in_port ` ( ` host1 ` のつながるスイッチポート) と ` host1 ` の MAC アドレスを FDB に記録する
190
190
4 . また,` Counter ` に記録された ` host1 ` の送信トラフィックを 1 パケット分増やす
191
191
5 . ` packet_in ` メッセージの宛先 MAC アドレスから転送先のスイッチポート番号を FDB に問い合わせる。この時点では ` host2 ` のスイッチポートは学習していないので,結果は 「不明」
192
- 6 . そこで,パケットを ` in_port ` 以外のすべてのスイッチポートに出力する ` packet_out ` メッセージ ( FLOOD と呼ばれる) をスイッチに送り,` host2 ` が受信してくれることを期待する
192
+ 6 . そこで,パケットを ` in_port ` 以外のすべてのスイッチポートに出力する ` packet_out ` メッセージ ( FLOOD と呼ばれる) をスイッチに送り,` host2 ` が受信してくれることを期待する
193
193
7 . スイッチは,パケットを ` in_port ` 以外のすべてのポートに出す
194
194
195
- これで,最終的に ` host2 ` がパケットを受信できます。逆に,この状態で ` host1 ` を宛先として ` host2 ` からパケットを送信したときの動作シーケンスは次のとおりになります (図5) 。4 までの動作は図 4 と同じですが,5 からの動作が次のように異なります。
195
+ これで,最終的に ` host2 ` がパケットを受信できます。逆に,この状態で ` host1 ` を宛先として ` host2 ` からパケットを送信したときの動作シーケンスは次のとおりになります (図5) 。4 までの動作は図 4 と同じですが,5 からの動作が次のように異なります。
196
196
197
197
![ host1 から host2 宛にパケットを送信したときの動作シーケンス] ( https://github.com/trema/Programming-Trema/raw/master/images/2_005.png )
198
198
199
199
図5 host1 から host2 宛にパケットを送信したときの動作シーケンス
200
200
201
201
1 . ` host1 ` から ` host2 ` を宛先としてパケットを送信すると,まずはスイッチにパケットが届く
202
202
2 . スイッチのフローテーブルは最初はまっさらで,どう処理すればよいかわからない状態なので,コントローラである ` TrafficMonitor ` に ` packet_in ` メッセージを送る
203
- 3 . ` TrafficMonitor ` の ` packet_in ` メッセージハンドラでは,` packet_in ` メッセージの ` in_port ` ( ` host1 ` のつながるスイッチポート) と ` host1 ` の MAC アドレスを FDB に記録する
203
+ 3 . ` TrafficMonitor ` の ` packet_in ` メッセージハンドラでは,` packet_in ` メッセージの ` in_port ` ( ` host1 ` のつながるスイッチポート) と ` host1 ` の MAC アドレスを FDB に記録する
204
204
4 . また,` Counter ` に記録された ` host1 ` の送信トラフィックを 1 パケット分増やす
205
205
5 . ` packet_in ` メッセージの宛先 MAC アドレスから,転送先のスイッチポート番号を FDB に問い合わせる。これは,先ほど ` host1 ` から ` host2 ` にパケットを送った時点で FDB に学習させているので,送信先はスイッチポート 1 番ということがわかる
206
206
6 . そこで,` TrafficMonitor ` はパケットをスイッチポート 1 番へ出力する ` packet_out ` メッセージをスイッチに送る。スイッチはこれを受け取ると,パケットをスイッチポート 1 番に出し,最終的に ` host1 ` がパケットを受信する
211
211
212
212
# 実行してみよう
213
213
214
- それでは,早速実行してみましょう (注2) 。リスト 4 の内容の仮想ネットワーク設定を ` traffic-monitor.conf ` として保存し,次のように実行してください。
214
+ それでは,早速実行してみましょう (注2) 。リスト 4 の内容の仮想ネットワーク設定を ` traffic-monitor.conf ` として保存し,次のように実行してください。
215
215
216
216
% ./trema run ./traffic-monitor.rb -c ./traffic-monitor.conf
217
217
@@ -244,7 +244,7 @@ link "0xabc", "host2"
244
244
245
245
% ./trema send_packets --source host2 --dest host1 --n_pkts 10 --pps 10← host2からhost1宛にパケットを10個送る
246
246
247
- ` trema run ` を実行した元のターミナルに次のような出力が出ていれば成功です(注3) 。
247
+ ` trema run ` を実行した元のターミナルに次のような出力が出ていれば成功です(注3) 。
248
248
249
249
...
250
250
00:00:00:00:00:01 10 packets (640 bytes)
@@ -254,9 +254,9 @@ link "0xabc", "host2"
254
254
↑host2からパケットが10個送信された
255
255
...
256
256
257
- 注2) Trema のセットアップが済んでいない人は,前回もしくは Trema のドキュメントを参考にセットアップしておいてください。なお,Trema は頻繁に更新されていますので,すでにインストールしている人も最新版にアップデートすることをお勧めします。
257
+ 注2) Trema のセットアップが済んでいない人は,前回もしくは Trema のドキュメントを参考にセットアップしておいてください。なお,Trema は頻繁に更新されていますので,すでにインストールしている人も最新版にアップデートすることをお勧めします。
258
258
259
- 注3) その他のトラフィック情報も出るかもしれませんが,これは Linux カーネルが送っている IPv6 のパケットなので,` host1 ` , ` host2 ` とは関係ありません。
259
+ 注3) その他のトラフィック情報も出るかもしれませんが,これは Linux カーネルが送っている IPv6 のパケットなので,` host1 ` , ` host2 ` とは関係ありません。
260
260
261
261
262
262
# まとめ
0 commit comments