diff --git a/base.adoc b/base.adoc index 37cbde36..1052777a 100644 --- a/base.adoc +++ b/base.adoc @@ -4,12 +4,12 @@ include::how_does_openflow_work.adoc[] [[hello_trema]] include::hello_trema.adoc[] -[[cbench]] -include::cbench.adoc[] - [[switch_monitor]] include::switch_monitor.adoc[] +[[cbench]] +include::cbench.adoc[] + [[patch_panel]] include::patch_panel.adoc[] diff --git a/cbench.adoc b/cbench.adoc index de65c42c..508564da 100644 --- a/cbench.adoc +++ b/cbench.adoc @@ -10,10 +10,10 @@ CbenchはOpenFlow1.0コントローラのためのベンチマークです。Cbenchは「1秒あたりにコントローラが出せるFlow Modの数」を計測します。これはOpenFlowプロトコル全体のうちの一部の性能だけを対象にしているので、ベンチマークの中でもマイクロベンチマークに分類できます。 -Cbenchは<>のように動作します。まずcbenchプロセスはOpenFlowスイッチのふりをしてコントローラに接続し、コントローラにPacket Inを連続して送ります。コントローラはPacket Inを受け取るとcbenchプロセスにFlow Modを返します。cbenchプロセスは決められた時間の間に受け取ったFlow Modの数をカウントし、ベンチマークのスコアとします。つまりPacket Inに反応して素早くFlow Modを返せるコントローラほど「速い」とみなします。 +Cbenchは<>のように動作します。まずcbenchプロセスはOpenFlowスイッチのふりをしてコントローラに接続し、コントローラにPacket Inを連続して送ります。コントローラはPacket Inを受け取るとcbenchプロセスにFlow Modを返します。cbenchプロセスは決められた時間の間に受け取ったFlow Modの数をカウントし、ベンチマークのスコアとします。つまりPacket Inに反応して素早くFlow Modを返せるコントローラほど「速い」とみなします。 [[cbench_overview]] -image::cbench_overview.png[caption="図3-1",title="cbenchプロセスとコントローラの動作"] +image::cbench_overview.png[caption="図4-1",title="cbenchプロセスとコントローラの動作"] == インストール @@ -362,7 +362,7 @@ send_flow_mod_add( `send_flow_mod_add` で指定できるすべてのオプションは次の通りです。 - `:match`
 フローエントリのマッチングルールを指定する。本章で紹介した `Match` オブジェクトまたは `ExactMatch` オブジェクトを指定する -- `:actions`
 フローエントリのアクションを指定する。アクションには単体のアクションまたは複数のアクションを配列 (<>で解説) によって指定できる +- `:actions`
 フローエントリのアクションを指定する。アクションには単体のアクションまたは複数のアクションを配列 (<>で解説) によって指定できる - `:buffer_id` アクションが参照するパケットがバッファされている領域の ID を指定する - `:idle_timeout`
 フローエントリが一定時間参照されなかった場合に破棄されるまでの秒数を指定する。デフォルトは0秒で、この場合フローエントリは破棄されない - `:hard_timeout`
 フローエントリの寿命を秒数で指定する。デフォルトは0秒で、この場合フローエントリは破棄されない @@ -573,4 +573,4 @@ Packet InとFlow Modの最初の一歩として、ベンチマークツールCbe - SendOutPort アクションによるパケットの転送と、その他のアクションを学んだ - コントローラをスレッドで高速化する方法を学んだ -これまではコントローラにつなぐスイッチは一台だけでしたが、続く章ではスイッチをたくさんつなぎ集中管理を始めてみます。 +OpenFlow プログラミングの基礎はできたので、そろそろ実用的なツールを作ってみましょう。続く章では、遠隔操作可能なソフトウェアパッチパネルを作ります。ネットワークケーブルを挿し替えるためだけにラックのあるサーバルームまで出向く必要はなくなります。 diff --git a/hello_trema.adoc b/hello_trema.adoc index da019b54..389aa310 100644 --- a/hello_trema.adoc +++ b/hello_trema.adoc @@ -13,8 +13,8 @@ Trema(トレマ)を使うと楽しくSDNの世界が味わえます。これで 実装はステップバイステップで進みます。どのステップも実用的な例となっており、最初はOpenFlowやプログラミングの基礎から始めます。そしてパッチパネルやL2スイッチ、ファイアウォール、ルータの実装など徐々に複雑な機能へとステップアップし、最終的にはデータセンターでも動く本格的な「ネットワーク仮想化」の実装を目標とします。 - Hello Trema (本章): OpenFlow 版 Hello World - - cbenchベンチマーク
(<>): OpenFlow のマイクロベンチマークツール - - スイッチ監視ツール (<>): スイッチの死活監視ツール + - スイッチ監視ツール (<>): スイッチの死活監視ツール + - cbenchベンチマーク
(<>): OpenFlow のマイクロベンチマークツール - パッチパネル (<>):
ソフトウェアとして実装したインテリジェント・パッチパネル - ラーニングスイッチ
 (<>): レイヤ2スイッチをエミュレートするコントローラ - ラーニングスイッチ OpenFlow1.3 (<>): ラーニングスイッチの OpenFlow1.3 による実装 diff --git a/learning_switch.adoc b/learning_switch.adoc index 83e6be58..6cb031a2 100644 --- a/learning_switch.adoc +++ b/learning_switch.adoc @@ -147,21 +147,6 @@ include::{sourcedir}/lib/learning_switch.rb[] - `packet_in` はPacket Inメッセージを捕捉するためのハンドラ。スイッチのフローエントリにマッチしないパケットがコントローラに上がってくると、このハンドラが呼ばれる - `packet_in` ハンドラから呼ばれる `flow_mod_and_packet_out` メソッドの中では、`@fdb` を使ってポート番号を調べたり、`flow_mod` と `packet_out` メソッドでそれぞれFlow ModとPacket Outメッセージを送っている。また、先述した「パケットをばらまく(フラッディング)」処理に対応する `:flood` も見つかる -learning_switch.rb の一行目の `require` は、ご想像の通り fdb.rb を読み込んでいます。`require` はちょうど、C の `#include` や Java の `import` みたいなものと思ってください。Ruby では、たとえば fdb.rb というファイルを読み込みたいときは、拡張子の .rb を外して `require 'fdb'` と書きます。読み込む対象のファイルは、lib/ ディレクトリを起点とした相対パスで書きます。たとえば lib/learning_switch/extensions.rb を読み込みたいときには `require 'learning_switch/extensions'` と書きます。 - -fdb.rb の中身も読んでみましょう。 - -[source,ruby,subs="verbatim,attributes"] -.lib/fdb.rb ----- -include::{sourcedir}/lib/fdb.rb[] ----- - -- FDBクラスは3つのメソッド `lookup`、`learn`、`age` を持つ。 -- `lookup` メソッドを使うとMACアドレスからポート番号を検索できる -- `learn` メソッドでMACアドレスとポート番号の組をFDBに学習させる -- `age` メソッドでFDBのエントリをエージングする - ラーニングスイッチの心臓部は `packet_in` ハンドラだけで、その中身も 3 行だけと単純です。ラーニングスイッチの仕組みを思い出しながら、ソースコードを詳しく読み解いていきましょう。今回の肝となるのは、Packet In ハンドラでの次の処理です。 - FDBの更新とポート番号の検索 @@ -194,7 +179,7 @@ include::{sourcedir}/lib/learning_switch.rb[lines="12..17,22..28"] include::{sourcedir}/lib/learning_switch.rb[lines="24..28"] ---- -このflow_modメソッドとpacket_outメソッドはそれぞれTrema::Controllerクラスのsend_flow_mod_add(<>で紹介)およびsend_packet_out(Packet Outの送信)メソッドを次のように呼び出します。 +このflow_modメソッドとpacket_outメソッドはそれぞれTrema::Controllerクラスのsend_flow_mod_add(<>で紹介)およびsend_packet_out(Packet Outの送信)メソッドを次のように呼び出します。 [source,ruby,subs="verbatim,attributes"] .LearningSwitch#flow_mod, LearningSwitch#packet_out (lib/learning_switch.rb) @@ -227,27 +212,27 @@ Packet Outの使い道は、Packet Inメッセージとして入ってきたパ send_packet_out( datapath_id, buffer_id: message.buffer_id, - data: message.data, + raw_data: message.raw_data, actions: SendOutPort.new(port_number) ) ---- -この場合コントローラからスイッチへのパケットデータのコピーが起こらないため、若干のスピードアップが期待できます。ただし、<>のコラムで説明したとおり、バッファの中身は予測不能でデータがいつ消えるかわからないため、この方法は推奨しません。 +この場合コントローラからスイッチへのパケットデータのコピーが起こらないため、若干のスピードアップが期待できます。ただし、<>のコラムで説明したとおり、バッファの中身は予測不能でデータがいつ消えるかわからないため、この方法は推奨しません。 ==== スイッチのバッファを使わずにPacketOutする場合 -スイッチのバッファを使わずにPacket Outする場合、次のように:dataオプションでパケットのデータを指定する必要があります。バッファに乗っているかいないかにかかわらずPacket Outできるので、若干遅くはなりますが安全です。 +スイッチのバッファを使わずに Packet Out する場合、次のように `raw_data` オプションでパケットのデータを指定する必要があります。バッファに乗っているかいないかにかかわらず Packet Out できるので、若干遅くはなりますが安全です。 [source,ruby,subs="verbatim,attributes"] ---- send_packet_out( datapath_id, - data: message.data, + raw_data: message.raw_data, actions: SendOutPort.new(port_number) ) ---- -これは、次のように:packet_inオプションを使うことで若干短くできます (.dataを書かなくてよくなります)。 +これは、次のように `packet_in` オプションを使うことで若干短くできます (`.raw_data` を書かなくてよくなります)。 [source,ruby,subs="verbatim,attributes"] ---- @@ -260,12 +245,12 @@ send_packet_out( === 主なオプション一覧 -optionsに指定できる主なオプションは次のとおりです。 +`options` に指定できる主なオプションは次のとおりです。 -- :buffer_id
スイッチでバッファされているパケットのIDを指定する。この値を使うと、スイッチでバッファされているパケットを指定してPacket Outできるので効率が良くなる (ただし、スイッチにバッファされていない時はエラーになる) -- :data
Packet Out するパケットの中身を指定する。もし :buffer_id オプションが指定されておりスイッチにバッファされたパケットをPacket Outする場合、この値は使われない -- :packet_in
:dataおよび:in_portオプションを指定するためのショートカット。packet_inハンドラの引数として渡されるPacketInメッセージを指定する -- :actions
Packet Outのときに実行したいアクションの配列を指定する。アクションが1つの場合は配列でなくてかまわない +- `buffer_id`
スイッチでバッファされているパケットの ID を指定する。この値を使うと、スイッチでバッファされているパケットを指定して Packet Out できるので効率が良くなる (ただし、スイッチにバッファされていない時はエラーになる) +- `raw_data`
Packet Out するパケットの中身を指定する。もし `buffer_id` オプションが指定されておりスイッチにバッファされたパケットを Packet Out する場合、この値は使われない +- `packet_in`
`raw_data` および `in_port` オプションを指定するためのショートカット。Packet In ハンドラの引数として渡される Packet In メッセージを指定する +- `actions`
Packet Out のときに実行したいアクションの配列を指定する。アクションが 1 つの場合は配列でなくてかまわない ==== 宛先ポート番号が見つからなかった場合 (フラッディング) @@ -277,6 +262,20 @@ optionsに指定できる主なオプションは次のとおりです。 include::{sourcedir}/lib/learning_switch.rb[lines="24..28"] ---- +=== FDB の実装 + +learning_switch.rb の一行目の `require 'fdb'` は、同じディレクトリ内の fdb.rb を読み込みます。`require` はちょうど、C の `#include` や Java の `import` みたいなものと思ってください。Ruby では、たとえば fdb.rb というファイルを読み込みたいときは、拡張子の .rb を外して `require 'fdb'` と書きます。読み込む対象のファイルは、lib/ ディレクトリを起点とした相対パスで書きます。たとえば lib/learning_switch/extensions.rb を読み込みたいときには `require 'learning_switch/extensions'` と書きます。 + +fdb.rb もざっと目を通してみましょう。このファイルは FDB の機能をカプセル化する `FDB` クラスを提供します。 + +[source,ruby,subs="verbatim,attributes"] +.lib/fdb.rb +---- +include::{sourcedir}/lib/fdb.rb[] +---- + +`FDB` クラスは3つのメソッド `lookup`、`learn`、`age` を持ちます。`lookup` メソッドを使うと MAC アドレスからポート番号を検索できます。逆に `learn` メソッドでは MAC アドレスとポート番号の組を学習できます。タイマで定期的に呼ばれる `age` メソッドでは、FDB に入っているすべてのエントリをエージングし、寿命を過ぎたもの (`FDB::Entry#aged_out?` で判定) を消します。 + == まとめ 実用的なOpenFlowアプリケーションのベースとなるラーニングスイッチの動作と作り方を学びました。 diff --git a/switch_monitor.adoc b/switch_monitor.adoc index b9278348..f815302d 100644 --- a/switch_monitor.adoc +++ b/switch_monitor.adoc @@ -14,9 +14,9 @@ OpenFlowの特長は「たくさんのスイッチを1つのコントローラ 本章ではこの集中制御の一例として、「スイッチの監視ツール」を作ります。このツールは「今、ネットワーク中にどんなスイッチが動いていて、それぞれがどんな状態か」をリアルタイムに表示します。たくさんあるスイッチ全体が正常に動いているかを確認するのに便利です。 [[switch_monitor_overview]] -image::switch_monitor_overview.png[caption="図4-1",title="スイッチ監視ツールの動作"] +image::switch_monitor_overview.png[caption="図3-1",title="スイッチ監視ツールの動作"] -スイッチ監視ツールは<>のように動作します。OpenFlowスイッチは、起動すると指定したOpenFlowコントローラへ接続します。コントローラはスイッチの接続を検知すると、起動したスイッチの情報を表示します。逆にスイッチが予期せぬ障害など何らかの原因で接続を切った場合、コントローラはこれを検知して警告を表示します。 +スイッチ監視ツールは<>のように動作します。OpenFlowスイッチは、起動すると指定したOpenFlowコントローラへ接続します。コントローラはスイッチの接続を検知すると、起動したスイッチの情報を表示します。逆にスイッチが予期せぬ障害など何らかの原因で接続を切った場合、コントローラはこれを検知して警告を表示します。 // TODO 警告を表示するのだから、logger.info より警告レベルを上げる @@ -332,4 +332,4 @@ include::{sourcedir}/lib/switch_monitor.rb[lines="10..15,21..29"] - タイマー (`timer_event`) を使うと一定間隔ごとに指定したメソッドを起動できる - `trema start` と `trema stop` コマンドで仮想ネットワーク内のスイッチを起動/停止できる -今回作ったスイッチ監視ツールのように、比較的短いソースコードでもそこそこ実用的なツールを書けるところが Trema を使った OpenFlow プログラミングの魅力です。続く章では、もう1つの便利ツールの例として遠隔操作可能なソフトウェアパッチパネルを作ります。ネットワークケーブルを挿し替えるためだけにラックのあるサーバルームまで出向く必要はなくなります。 +続く章では、いよいよ OpenFlow の最重要メッセージである Packet In と Flow Mod を使ったプログラミングに挑戦です。