はじめに
「そろそろ頃合い」セイウチは言った
「あれやこれやの積もる話」
ルイス・キャロル
「不思議の国のアリス」
今回は盛りだくさんです! まずは身近な例を使って、OpenFlowの動作モデルを説明します。これが理解できれば、OpenFlowの基本概念はバッチリです。次に、「トラフィック集計付きスイッチ」を実現するコントローラを実際に作ります。これはOpenFlowの重要な処理をすべて含んでいるので、応用するだけでさまざまなタイプのコントローラが作れるようになります。最後に、作成したコントローラをTremaの仮想ネットワーク上で実行します。すばらしいことに、Tremaを使えば開発から動作テストまでを開発マシン1台だけで完結できます!
では前置きはこのぐらいにして、まずはOpenFlowでスイッチを制御するしくみを理解しましょう。
OpenFlowの動作モデル
OpenFlowの動作を現実世界にたとえると、製品の電話サポートサービスに似ています。
電話サポートの業務手順
友太郎(ゆうたろう)君は、エアコンが故障したので修理に出そうと考えました(図1)。電話サポートに問い合わせると、サポート係の葵(あおい)さんはエアコンの症状を聞き、手元のマニュアルに対処方法が載っている場合にはこれをすぐに教えてくれます。問題は、マニュアルに対処法が載っていない場合です。このようなときは少し時間がかかりますが、上司の宮坂主任にどうしたらよいか聞きます。そして、宮坂主任からの回答が得られたら、葵さんは友太郎君に折り返し電話をします。また、次からの同じ問い合わせにはすばやく答えられるようにするため、葵さんは教わった対処法を手元のマニュアルに追加しておきます。
簡単ですね? 信じられないかもしれませんが、あなたはすでにOpenFlowの95%を理解したも同然なのです。
OpenFlowに置き換えると……
OpenFlowでは、お客さんがパケットを発生させるホスト、電話サポート係がスイッチ、上司がコントローラ、マニュアルがスイッチのフローテーブル(後述)に対応します(図2)。
スイッチはホストからのパケットを受信すると、最初はその処理方法がわかりません。そこで、上司にあたるコントローラに問い合わせます。この問い合わせをpacket_inメッセージと呼びます。コントローラはこれを受け取ると、同様のパケットが届いた場合にスイッチでどう処理すべきか(パケットを転送する、書き換えるなど)を決めます。これをアクションと呼びます。そして「スイッチで処理すべきパケットの特徴」+「アクション」の組(フローと呼びます)をスイッチのマニュアルに追加します。この命令をflow_modメッセージと呼び、スイッチのマニュアルをフローテーブルと呼びます。処理すべきパケットの特徴とアクションをフローテーブルに書いておくことで、以後、これに当てはまるパケットはスイッチ側だけですばやく処理できます。忘れてはならないのが、packet_inメッセージで上がってきた最初のパケットです。これはコントローラに上がってきて処理待ちの状態になっているので、packet_outメッセージで適切な宛先に転送してあげます。
電話サポートとの大きな違いは、フローテーブルに書かれたフローには期限があり、これを過ぎると消えてしまうということです。これは、「マニュアルに書かれた内容は徐々に古くなるので、古くなった項目は消す必要がある」と考えるとわかりやすいかもしれません。フローが消えるタイミングでコントローラにはflow_removedメッセージが送信されます。これには、あるフローに従ってパケットがどれだけ転送されたか──電話サポートの例で言うと、マニュアルのある項目が何回参照されたか──つまり、トラフィックの集計情報が記録されています。
それではしくみの話はこのぐらいにして、早速実践に移りましょう。もし途中でわからなくなったら、この節の頭から読み直してください。
「トラフィック集計スイッチ」コントローラの概要
トラフィック集計スイッチは、パっと見は普通のL2スイッチとして動作します。しかし、裏では各ホストが送信したトラフィックをカウントしており、定期的に集計情報を表示してくれます。これを使えば、ネットワークを無駄に使いすぎているホストを簡単に特定できます。
設計と実装
「L2スイッチ機能」と「トラフィックの集計機能」のためにはどんな部品が必要でしょうか? まずは、スイッチに指示を出す上司にあたるコントローラクラスが必要です。これをTrafficMonitorクラスと名付けましょう。また、パケットを宛先のスイッチポートへ届けるためのFDBクラス[1]、あとはトラフィックを集計するためのCounterクラスの3つが最低限必要です。
① FDBクラス
FDBクラス(リスト1)は、ホストのMACアドレスとホストが接続しているスイッチポートの対応を学習するデータベースです。このデータベースを参照することで、packet_inメッセージで入ってきたパケットの宛先MACアドレスからパケット送信先のスイッチポートを決定できます。
② Counterクラス
Counterクラス(リスト2)は、ホスト(MACアドレスで区別します)ごとの送信パケット数およびバイト数をカウントします。また、カウントした集計情報を表示するためのヘルパメソッドを提供します。
③ TrafficMonitorクラス
Traffi cMonitorクラスはコントローラの本体です(リスト3)。メインの処理はリスト3①~③の3つになります。
- ① packet_inメッセージが到着したとき、パケットを宛先のスイッチポートに転送し、フローテーブルを更新する部分
- ② flow_removedメッセージが到着したとき、トラフィック集計情報を更新する部分
- ③ タイマーで10秒ごとにトラフィックの集計情報を表示する部分
それでは、とくに重要な①の処理を詳しく見ていきましょう。なお、リスト3中で使われているメソッドの引数などAPIの詳細については、「TremaRuby APIドキュメント」を参照してください。
以下の説明では図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以外のすべてのポートに出す
これで、最終的にhost2がパケットを受信できます。逆に、この状態でhost1を宛先としてhost2からパケットを送信したときの動作シーケンスは次のとおりになります(図5)。➍までの動作は図4と同じですが、➎からの動作が次のように異なります。
- ➎ 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メッセージをスイッチに送信する
最後の➐によって、以降のhost2からhost1へのパケットはすべてスイッチ側だけで処理されるようになります。
実行してみよう
それでは、早速実行してみましょう[2]。リスト4の内容の仮想ネットワーク設定をtraffic-monitor.confとして保存し、次のように実行してください。
実行すると、図3に示した仮想ネットワークが構成され、Traffi cMonitor コントローラが起動します。
それでは、実際にトラフィックを発生させて集計されるか見てみましょう。Tremaのsend_packetsコマンドを使うと、仮想ホスト間で簡単にパケットを送受信できます。別ターミナルを開き、次のコマンドを入力してください。
trema runを実行した元のターミナルに次のような出力が出ていれば成功です[3]。
まとめ
今回は「トラフィック集計機能付きスイッチ」を実現するコントローラを書きました。学んだことは次の2つです。
- 電話サポートの例を使ってOpenFlowの動作モデルを学びました。パケットの転送はスイッチ上のフローテーブルによって行われ、fl ow_modメッセージによって書き換えることができます。また、フローテーブルに登録されていないパケットによってpacket_inメッセージがコントローラに届きます。
- 仮想ネットワークを使ったコントローラの動作テスト方法を学びました。仮想スイッチと仮想ホストを起動してつなぎ、send_packetsコマンドを使って仮想ホスト間でパケットを送受信することで、コントローラの簡単な動作テストができます。
次回はTremaを使ったテストファースト開発を紹介します。RailsやSinatraを使ったWebアジャイル開発ではお馴染みのテストファースト開発ですが、もちろんTremaでもサポートしています。とくにOpenFlowコントローラのように複雑な動作シーケンスを持つソフトウェアの実装には、テストファーストによるインクリメンタルな開発が有効です。