OpenFlow とは
OpenFlow はスタンフォード大学を中心として2008年に設立された「OpenFlow スイッチングコンソーシアム」が提唱した、スイッチを制御するためのプロトコルです。現在は、2011年に発足した ONF (Open Networking Foundation) が中心となって OpenFlow 技術の規格策定を実施し、標準化や普及を促進しています。
本稿では OpenFlow の概要を紹介します。
OpenFlowプロトコル
OpenFlowプロトコルは L2スイッチを制御するためのプロトコルです。
通常の L2スイッチでは、制御 (MACアドレスの学習や VLAN制御など) 機能とパケット転送機能が一体となって実装されていますが、OpenFlow の世界では両者が分離されており、前者をコントローラー、後者をスイッチと呼びます。
OpenFlowスイッチと OpenFlowコントローラーの間での通信に使用されるプロトコルが、OpenFlowプロトコルです。
スイッチとコントローラーの間の通信路を OpenFlowチャンネルと呼びます。
通常、スイッチ側からコントローラーに対して TCP接続を張ることで確立されます。使用する TCPポート番号は 6653 と IANA で定められていますが、歴史的経緯からデフォルトで 6633 を使用する実装が多いです。
OpenFlow 実装例
代表的なソフトウェア実装には以下のようなものがあります。
- Open vSwitch (http://www.openvswitch.org )
スイッチのソフトウェア実装です。事実上のリファレンス実装となっています。 - Ryu SDN Framework (http://osrg.github.io/ryu/)
pythonで書かれたコントローラーの実装です。コントローラーのOSS実装は他にも沢山ありますが、ここではRyuを推しておきます。
(筆者はOpen vSwitchやRyuの開発に関っていますので、多少バイアスがかかっています。)
バージョン
OpenFlowプロトコルの仕様策定は Open Networking Foundation (ONF) という団体が行なっています。議論は非公開ですが、決定した仕様はPDFの形で公開されています。以下URLを参考にしてください。
OpenFlow はいくつかのバージョンが公開されています。バージョン間の互換性は基本的にありません。OpenFlowチャンネル確立時にスイッチとコントローラーがお互いのサポートするバージョンを交換する仕組みはありますが、使用するバージョンを人間が事前に決めて設定しておくのが無難です。
現在公開されている主なバージョンは以下のとおりです。
- 1.0
- 1.1
- 1.2
- 1.3
- 1.4
よく使用されるバージョンは 1.0 です。1.0 では機能が足りない場合、1.0 + ベンダー拡張、または 1.3 が使用される事が多いです。
1.1 と 1.2 はほとんど使用されません。また、1.4 は実装があまりありません。
現在、1.5 の策定が進められており、1.3 の次は、1.4 を飛ばして 1.5 が主流になりそうな雰囲気です。
本稿では、特に断りがない場合は OpenFlow 1.3 を前提とします。
フローテーブルとメッセージ
フローテーブル
スイッチは、コントローラーによって設定された処理内容に従い、パケットの転送を行ないます。この処理内容の記述、また、それを格納する領域を、フローテーブルと呼びます。
メッセージ
OpenFlowプロトコルで使用される転送の単位を OpenFlowメッセージと呼びます。
OpenFlowチャンネル確立後、スイッチとコントローラーが OpenFlowメッセージを非同期に送り合います。
各 OpenFlowメッセージは、固定長のヘッダーが付いています。ヘッダーを見ると、メッセージ種別とメッセージの長さがわかります。
OpenFlowメッセージは大きく以下の4種類に分類できます。
- コントローラーからスイッチへのメッセージ
コントローラーからスイッチへの要求です。
例. フローテーブルの更新要求
例. スイッチの機能の照会要求
例. 統計情報の要求
例. Packet-Out - スイッチからコントローラーへのメッセージ
コントローラーからの要求に対する返答です。
例. 統計情報 - 非同期メッセージ
スイッチからコントローラーへ、状態変更を通知するために使われます。
例. Packet-In
例. エラー - 対称的メッセージ
双方向で同じ形式が使用されるメッセージもあります。
例. サポートするバージョンの広告
例. 生死確認
OpenFlowの動作
よくある処理の流れは以下のようなものです。
- OpenFlowチャンネル確立、バージョンの交換
- コントローラーがスイッチにメッセージを送る
– スイッチ情報の取得
– フローテーブルの設定 - スイッチが転送処理を行なう
OpenFlowの主要なメッセージ
- Flow Mod
コントーラーからスイッチへ送られる、フローテーブル更新要求です。 - Packet-In
前述の「3. スイッチが転送処理を行なう」の段階では、スイッチが単体でパケット転送を行なうことができ、コントローラーの介入は必要としません。これには性能上の利点がありますが、フローテーブルの記述能力により処理内容が制限されてしまいます。
非同期メッセージの一つ、Packet-In メッセージを使用すると、フローテーブルに直接記述できないような例外的な処理を実装できます。コントローラーは、パケットをコントローラーに転送するよう、スイッチのフローテーブルを設定できます。スイッチは Packet-In メッセージを使用して パケットを転送します。コントローラーは、転送されたパケットの内容を吟味して処理を決定できます。
- Packet-Out
コントローラーは、Packet-Out メッセージを使用して、スイッチにパケットの送信を要求できます。このメッセージには、送信するパケット全体のバイト列を与えることができます。
- Echo Request
ofp_echo_replyの送信を要求。生死確認などに使用します。
- Features Request
コントローラーからスイッチへのメッセージです。Datapath IDやFlow tableの数などのスイッチ情報 (ofp_switch_features_reply) を要求します。
- Multipart Request / Reply
メッセージサイズの上限(64KB)を回避するため、要求や回答を複数のメッセージに分割して送信します。ポート一覧の取得 (OFPMP_PORT_DESC) や、Flow table (OFPMP_FLOW) の取得などの要求に使用します
- Port Status
スイッチからコントローラーへのメッセージで、ポートの追加・削除・状態変更を通知します。
フローテーブル
フローテーブルはスイッチの処理内容を記述するプログラムで、受信したパケットをどのように処理するかを記述します。また、ハードウェア実装への考慮か、ループ処理は書けないようになっています。
スイッチには 1 個以上のフローテーブル (単にテーブルとも呼びます) があります。各テーブルは、table-id と呼ばれる 0 以上の整数値で指定されており、テーブル 0 は必ず実装されています。
フローテーブルは 0 個以上のフローエントリーから成ります。通常、初期状態では、フローテーブルは空、つまり 0 個のフローエントリーを持っています。コントローラーからの更新要求によってフローエントリーが追加されます。
各フローエントリーは、主にマッチとインストラクションから成ります。
・マッチはパケットに適合する条件の記述です。
例. ポート 0 からの入力、かつ宛先が 192.0.2.1 のIPパケット
・インストラクションは処理内容の記述です。
例. 宛先を 192.0.2.2 に書き換え、ポート 1 から出力
スイッチはパケットを受信すると、テーブル 0 に適合するエントリーが無いか探します。無ければ、デフォルトではパケットは捨てられます (※補足1)。
エントリーが見付かった場合、そのエントリーのインストラクションに従った処理を行ないます。
※ 補足1
仕様上はこのように定められているのですが、Open vSwitch では Packet-In を生成するようになっています。これが問題になる場合は、明示的に drop するフローエントリーを作成するなどの回避策が必要です。
- マッチ
マッチは 0個以上の OXM (OpenFlow Extensible Match) から成ります。あるパケットに全ての OXM が適合する場合に、マッチ全体が適合します。
OXM にはパケットのフィールドとその値を記述します。フィールドには、パケット自体の内容から抽出される通常のフィールドと、そうでないもの (pipeine match field) の 2 種類があります。
・フィールド
例. パケットを受信したポート (pipeline match field)
例. トンネルID (pipeline match field)
例. ethertype
例. IPv4宛先アドレス
例. IPプロトコル番号
例. TCPポート番号
・値、マスク
例. 192.168.0.0 / 255.255.255.0
- インストラクション
マッチには、制限はありますが、0 個以上のインストラクションを記述できます。
インストラクションにはパケットの処理内容を記述します。
・例. アクションの記述
・例. Goto-Table
良く使われるのはアクションの記述です (古いバージョンの OpenFlowプロトコルにはインストラクションという概念は無く、直接アクションを記述していました)。Goto-Table というのは、table-id を指定して、そのテーブルからマッチをやり直すという機能です。現在のテーブルの table-id より大きい値しか指定できませんので、ループは書けません。
- アクション
インストラクションには、制限はありますが、複数のアクションを記述できます。アクションには大きく分けて2種類あります。
・パケットの内容の書き換え
例. TTL を減らす
例. IPv4 宛先アドレスの変更
例. VLAN ヘッダーをつける
・転送処理
例. 指定ポートからの出力
例. コントローラーへの転送 (Packet-In)
- フローエントリーのその他フィールド
フローエントリーにはマッチとインストラクションの他に以下のようなフィールドがあります。
・優先度
どのエントリーが優先的に選ばれるかの記述ができます。パケットがどのエントリーに適合するか、優先度とマッチで一意に定まらない場合、どのエントリーが選ばれるかは仕様上不定とされています。
・カウンター
どれくらいエントリーが選ばれたかの統計です。
・タイムアウト
指定された時間が経過したエントリーは消されます。
・クッキー
コントローラーからフローエントリーを指定する際に使用できる値です。パケットの転送処理には使われません。
・フラグ
細かい動作を指定します。
【XID】
各メッセージのヘッダーには 32 ビットの Transaction ID (XID) が含まれています。メッセージの要求-応答の関係は、メッセージ種別に加え、応答に要求と同じXIDを使用することで表現されます
【barrier】
メッセージは、基本的に受信側の任意の順序で処理されます。コントローラーは、barrierメッセージを使用することで、スイッチ側のメッセージ処理順序をある程度制御できます。
barrierメッセージを受信したスイッチは、barrierメッセージへの返答を送信する前に、barrierメッセージより前に受信したメッセージへの返答を、もしあれば全て送信しなければいけません。
【エラー処理】
OpenFlowメッセージの多くは正常完了時に何も返答を返しません。そのため、エラーが無い事のチェックを行なうには、barrierメッセージを使用する必要があります。
・例. エラーが無い場合
・例. エラーがある場合
※ 補足2
Open vSwitch の ovs-ofctl コマンドや Ryu ofctl application などは、このような処理を行なっています。
以下 URL を参考にしてください。
- Open vSwitch ovs−ofctl
- Ryu ofctl application
関連プロトコル
OF-Config
OpenFlowプロトコル自体には、スイッチ管理の機能はありません。スイッチ管理のために良く使われるプロトコルとして NETCONF がありますが、この上で使われるスキーマはベンダー依存です。
OF-Config は、ONF の策定した OpenFlowスイッチのための NETCONFスキーマです。
以下 URL に仕様等に関する資料 (PDF) が掲載されていますので、参考にしてください。
- ONF OF-Config
OVSDB 管理プロトコル
OVSDB は Open vSwitch で提供されるスイッチ管理機能です。機能的には OF-Config と重なりますが、OF-Config 策定以前から提供されており、事実上もう一つのスタンダードです。
トランスポートは RFC 7047 として規格化されていますが、スキーマは実装依存のようです。
以下URLに詳細な情報が掲載されていますので、参考にしてください。
まとめ
早足でしたが、OpenFlow の概要を紹介しました。現時点では、低レベルのフローロジックを直接作り込む必要があるなど、導入の敷居はあまり低くありません。
OpenFlow の仕様自体にもあいまいな点や誤りが散見されます。また、実装依存が大きく、機能差異を実行時に吸収するのは大変なので、使用するバージョンやスイッチの実装はあらかじめ決めうちにすることをお勧めします。