Skip to content

Latest commit

 

History

History
523 lines (355 loc) · 41.2 KB

openflow_spec.adoc

File metadata and controls

523 lines (355 loc) · 41.2 KB

OpenFlow の仕様

OpenFlow の概要がわかったところで、もう少し細かい仕様に進みましょう。実用重視で OpenFlow バージョン 1.3 仕様のポイントとよく使う用語を押さえます。

説明する範囲とバージョン

OpenFlow標準仕様が主に定義するのは次の 2 つです。

  1. コントローラとスイッチの間の通信プロトコル

  2. 書き込んだフローエントリに対するスイッチの動作

本章ではこの 2 つの中でも、特によく使う機能のみを重点的に説明します。主に対象とするバージョンは、執筆時の安定バージョンであるバージョン 1.3 です。なお、バージョン 1.3 と以前の安定バージョンである 1.0 では、考え方やモデルに大きな違いはありません。そのため 1.3 を理解すれば 1.0 も理解しやすいでしょう。

なお OpenFlow が初めての方は、最初は難しい部分を読み飛ばしても構いません。後からより詳しく知りたくなった時に、いつでも読み直してください。

スイッチとコントローラ間のやりとり

OpenFlow スイッチとコントローラは、OpenFlow 仕様の規定するメッセージをやりとりしながら動作します。ここでは具体的にどのようなメッセージを送受信するか、順を追って説明します。1 章「OpenFlow の仕組み」で見た、カスタマーサポートセンターでのやりとりを思い出しながら読んでください。

スイッチ・コントローラ間の接続

最初にコントローラとスイッチは TCP 接続を確立します。これを OpenFlow チャンネルと呼びます。この TCP 接続は、仕様によるとスイッチとコントローラのどちらが始めてもよいことになっています。しかし多くの場合、図 2-1 のようにスイッチからコントローラへと接続する実装となっています。

openflow channel
図 2-1: スイッチがコントローラに接続し OpenFlow チャンネルを確立

なお OpenFlow チャンネルには普通の TCP 接続だけでなく、よりセキュアな TLS (Transport Layer Security) も使えます。ただし、コントローラとスイッチの両方が TLS に対応している必要があります。また、TLS は暗号化・復号化に多くのリソースを必要とするので、大量にメッセージをやりとりする場合には性能が低下します。

バージョンの確認

次にスイッチとコントローラは、使う OpenFlow バージョンをお互いに確認します。これを一般にバージョンネゴシエーションと呼びます。OpenFlow チャンネルの確立後、スイッチとコントローラは自分のしゃべれるバージョン番号を乗せた Hello メッセージをお互いに出し合います (図 2-2)。

version negotiation
図 2-2: Hello メッセージを出し合うことで、お互いの OpenFlow プロトコルバージョンを確認

もしここで、相手と同じバージョンを話せるようであればネゴシエーションに成功です。成功すると、Hello 以外のメッセージもやりとりできるようになります。

スイッチの Datapath ID の確認

次にコントローラは接続したスイッチの Datapath ID を確認します。コントローラがスイッチに Features Request メッセージを送ると、スイッチは Datapath ID とスペックを乗せた Features Reply メッセージを返答します。

features request reply
図 2-3: Features Request メッセージでスイッチの Datapath ID を確認

Features Reply メッセージには Datapath ID に加えて、主に次のスペック情報が入っています。

  • 一度にバッファできるパケットの数

  • サポートするテーブルの数

  • サポートする機能の一覧

コントローラへの受信パケットの通知

スイッチは、受信したパケットと関連情報を Packet In メッセージでコントローラへ通知できます。たとえば、フローテーブルに登録していない通信を検知した場合など、Packet In メッセージを使ってパケットの情報をコントローラへ送ります (図 2-4)。

packet in
図 2-4: 受信パケットとその情報を Packet In メッセージとしてコントローラに上げる

パケットの出力

Packet Out メッセージは Packet In メッセージの逆で、スイッチからパケットを出力するためのメッセージです (図 2-5)。

packet out
図 2-5: Packet Out メッセージでパケットをスイッチから出力

Packet Out の典型的な利用例は、Packet In でコントローラへ届いたパケットを宛先に届ける場合です。もしも Packet In の後に Packet Out をやらないと、パケットはコントローラに残ったままで宛先には届きません。

フローテーブルの更新

Flow Mod メッセージはスイッチのフローエントリを追加・削除・変更するためのメッセージです (図 2-6)。Flow Mod メッセージを受け取ったスイッチは、メッセージ内容に従って自身のフローテーブルを更新します。

flow mod
図 2-6: Flow Mod メッセージでフローテーブルを更新

OpenFlow 仕様によると、スイッチはフローテーブルの更新完了をコントローラに通知しません。その理由は、もしも Flow Mod メッセージごとに応答メッセージを返すことにすると、多くのフローエントリを設定する場合に時間がかかってしまうためです。

Note
禁じ手: Flow ModとPacket Outを同時にやる方法

実は OpenFlow の仕様には、1つの Flow Mod メッセージで同時に Packet Out もまとめてやってしまう方法があります。しかし、これは危険なプログラミングスタイルです。

この Flow Mod & Packet Out は図 2-7 のように動作します。スイッチは Packet In を起こすと、スイッチのバッファ領域に Packet In を起こしたパケットの中身をバッファします。そしてコントローラに送る Packet In メッセージに、このバッファ領域の ID (Buffer ID と呼ぶ) 情報を入れて送ります。コントローラは Flow Mod のときにこの Buffer ID を指定すると、スイッチがフローテーブルの更新と同時に、コントローラの代わりに Packet Out してくれます。

flow mod and packet out
図 2-7: Flow Mod に Buffer ID を指定することで同時に Packet Out する

しかし、この方法は禁じ手です。これは次の 3 つの理由によります。

  • スイッチのバッファにパケットが残っているかどうかはスイッチの外からわからない。つまり指定した Buffer ID のパケットがまだバッファに残っているかどうかは、イチかバチかである

  • もしスイッチのバッファに残っているとわかったとしても、Flow Mod を打った時には消えているかもしれない

  • 格安のスイッチには、そもそもバッファがないかもしれない

というわけで、やはり Packet Out は Flow Mod と独立して打つのが良い方法です。

フローテーブル更新完了の確認

Flow Mod メッセージによるフローテーブルの更新完了を確認するには Barrier メッセージを使います (図 2-8)。コントローラが Barrier Request メッセージを送ると、それ以前に送った Flow Mod メッセージの処理が全て完了した後、スイッチは Barrier Reply メッセージを返します。

barrier
図 2-8: Barrier Request/Reply メッセージによってフローテーブルの更新完了を確認

フローエントリ削除の通知

フローエントリが消えると、消えたフローエントリーの情報は Flow Removed メッセージとしてコントローラに届きます。Flow Removed メッセージには、消えたフローエントリの内容とそのフローエントリにしたがって処理したパケットの統計情報が入っています。これを使えば、たとえばネットワークのトラフィック量の集計ができます。

flow removed
図 2-9: フローエントリが消えると、フローエントリの内容と転送したパケットの統計情報が Flow Removed としてコントローラへ上がる

フローエントリの中身

1章で見たようにフローエントリは次の 6 要素から成ります。

  • 優先度

  • カウンタ (統計情報)

  • タイムアウト (寿命)

  • クッキー

  • マッチフィールド

  • インストラクション

優先度

フローエントリには、優先度 (0 〜 65535) が設定できます。受信パケットが、フローテーブル中に複数のフローエントリにマッチする場合、この優先度の値が高いフローエントリが優先されます。

カウンタ (統計情報)

OpenFlow 1.3 ではフローエントリごとにカウンタを持っており、次の統計情報を取得できます。

  • 受信パケット数

  • 受信バイト数

  • フローエントリが作られてからの経過時間 (秒)

  • フローエントリが作られてからの経過時間 (ナノ秒)

タイムアウト (寿命)

フローエントリにはタイムアウト (寿命) を設定できます。寿命の指定には次の 2 種類があります。

  • アイドルタイムアウト: 参照されない時間がこの寿命に逹すると、そのフローエントリを消す。パケットが到着し、フローエントリが参照された時点で 0 秒にリセットされる。

  • ハードタイムアウト: 参照の有無を問わず、フローエントリが書き込まれてからの時間がこの寿命に逹すると、そのフローエントリを消す。

どちらのタイムアウトも 0 にして打ち込むと、そのフローエントリは明示的に消さない限りフローテーブルに残ります。

クッキー

フローエントリには、クッキーを設定できます。クッキーに設定された値は、スイッチにおけるパケット処理には全く影響を与えません。例えば、フローエントリを管理するために、コントローラがクッキーフィールドに管理用の ID を付与するといった使い方ができます。

マッチフィールド

マッチフィールドとは、OpenFlow スイッチがパケットを受け取ったときにアクションを起こすかどうかを決める条件です。たとえば「パケットの宛先が http サーバだったら」とか「パケットの宛先がブロードキャストアドレスだったら」などという条件に適合したパケットにだけ、スイッチがアクションを起こすというわけです。

OpenFlow 1.3 では、40 種類の条件が使えます。主な条件を 表2-1 に示します。これらの条件はイーサネットや TCP/UDP でよく使われる値です。

Note
コラム マッチフィールドの別名

OpenFlow が使われ始めたころ、フローエントリの要素の1つであるマッチフィールドには、"OpenFlow 12 タプル"、"ヘッダフィールド" 等、さまざまな別の呼び方がありました。混乱を避けるため、本書の前版では "マッチングルール" という呼び方に統一しました。パケットがきたときにルールに従ってマッチする、という役割をすなおに表現していて、いちばんわかりやすい名前だったからです。

その後、OpenFlow バージョン 1.3 で正式な呼び名が "マッチフィールド" に決まりました。そのため、本書では仕様に従い "マッチフィールド" という呼び方を使っています。

Table 1. マッチフィールドで指定できる主な条件
名前 説明

In Port

スイッチの論理ポート番号

In Phy Port

スイッチの物理ポート番号

Ether Src

送信元 MAC アドレス

Ether Dst

宛先 MAC アドレス

Ether Type

イーサネットの種別

VLAN ID

VLAN ID

VLAN Priority

VLAN PCP の値 (CoS)

IP DSCP

DiffServ コードポイント

IP ECN

IP ECN ビット

IP Src

送信元 IP アドレス

IP Dst

宛先 IP アドレス

IP Proto

IP のプロトコル種別

TCP Src Port

TCP の送信元ポート番号

TCP Dst Port

TCP の宛先ポート番号

UDP Src Port

UDP の送信元ポート番号

UDP Dst Port

UDP の宛先ポート番号

ICMPv4 Type

ICMP 種別

ICMPv4 Code

ICMP コード

IPv6 Src

送信元 IPv6 アドレス

IPv6 Dst

宛先 IPv6 アドレス

IPv6 Flowlabel

IPv6 フローラベル

ICMPv6 Type

ICMPv6 種別

ICMPv6 Code

ICMPv6 コード

MPLS Label

MPLS ラベル

MPLS TC

MPLS トラフィッククラス

PBB ISID

PBB ISID

OpenFlow の世界では、このマッチフィールドで指定できる条件を自由に組み合わせて通信を制御します。たとえば、

  • スイッチの物理ポート 1 番から届く、宛先が TCP 80 番 (= HTTP) のパケットを書き換える

  • MAC アドレスが 02:27:e4:fd:a3:5d で宛先の IP アドレスが 192.168.0.0/24 は遮断する

などといった具合です。

Note
OSI ネットワークモデルが壊れる?

あるネットワークの経験豊富な若者がこんな事を言っていました。

「OpenFlow のようにレイヤをまたがって自由に何でもできるようになると、OSI ネットワークモデル(よく「レイヤ 2」とか「レイヤ 3」とか呼ばれるアレのこと。正確には ISO によって制定された、異機種間のデータ通信を実現するためのネットワーク構造の設計方針)が壊れるんじゃないか?」

その心配は無用です。OSI ネットワークモデルは正確に言うと「OSI 参照モデル」と言って、通信プロトコルを分類して見通しを良くするために定義した "参照用" の階層モデルです。たとえば自分が xyz プロトコルというのを作ったけど人に説明したいというときに、どう説明するか考えてみましょう。「これはレイヤ 3 のプロトコルで、…」という風に階層を指して (参照して) 説明を始めれば相手に通りがよいでしょう。つまり、OSI ネットワークモデルはネットワーク屋同士で通じる「語彙」として使える、まことに便利なものなのです。

でも、これはあくまで「参照」であって「規約」ではないので、すべてのネットワークプロトコル、ネットワーク機器がこれに従わなければいけない、というものではありません。さっき言ったように「この ○○ は、仮に OSI で言うとレイヤ4 にあたる」のように使うのが正しいのです。

そして、OpenFlow はたまたまいくつものレイヤの情報が使える、ただそれだけのことです。

インストラクション

インストラクションには、そのフローエントリにマッチしたパケットを、次にどのように扱うかを指定します。OpenFlow 1.3 では主に、以下のインストラクションを利用可能です。

  • Apply-Actions: 指定されたアクションを実行します。

  • Write-Actions: 指定されたアクションをアクションセットに追加します。

  • Clear-Actions: アクションセット中のアクションをすべてクリアします。

  • Write-Metadata: 受信したパケットに、メタデータを付与します。

  • Goto-Table: 指定のフローテーブルに移動します。

これらのうち Write-Actions, Clear-Actions, Write-Metadata, Goto-Table は、マルチプルテーブルを使う際に用いるインストラクションです。そのため、マルチプルテーブルを説明する際に、合わせて詳しく説明します。

Apply-Actions にて指定するアクションとは、スイッチに入ってきたパケットをどう料理するか、という 動詞 にあたる部分です。よく「OpenFlow でパケットを書き換えて曲げる」などと言いますが、こうした書き換えなどはすべてアクションで実現できます。OpenFlow 1.3 では、次の 7 種類のアクションがあります。

  • Output: パケットを指定したポートから出す

  • Group: パケットに対し、指定したグループテーブルの処理を適用する

  • Drop: パケットを捨てる

  • Set-Queue: ポートごとに指定されたスイッチのキューに入れる。QoS 用

  • Push-Tag/Pop-Tag: パケットに対し MPLS/VLAN タグの付与/除去を行う

  • Set-Field: 指定のフィールドの中身を書き換える

  • Change-TTL: TTL フィールドの値を書き換える

アクションは動詞と同じく指定した順番に実行されます。「おにぎりを作って、食べて、片付ける」といったふうに。たとえば、パケットを書き換えて指定したポートから出したいときには、

[Set-Field, Output]

と、複数のアクション並べて指定します。この実行順に並べられた複数のアクションのことを、アクションリストと呼びます。Apply-Actions インストラクションや Write-Actions インストラクションには、アクションリストを用いることで、複数のアクションを指定できます。

ここで、アクションリストは指定された順番に実行されることに注意してください。アクションリストの順番を変えてしまうと、違う結果が起こります。たとえば「おにぎりを食べてから、おにぎりを作る」と最後におにぎりが残ってしまいます。同様に先ほどの例を逆にしてしまうと、まず先にパケットがフォワードされてしまいます。その後 Set-Field が実行されても、書き換えられた後、そのパケットは破棄されるだけです。

# パケットを書き換える前にフォワードされてしまう。
[Output, Set-Field]

同じ動詞を複数指定することもできます。

[Set-Field A, Set-Field B, Output A, Output B]

この場合は、フィールド A と B を書き換えて、ポート A と B へフォワードする、と読めます。このように、複数のフィールドを書き換えたり、複数のポートにパケットを出したりする場合には、アクションを複数連ねて指定します[1]

Drop は特殊なアクションで、実際に Drop アクションというものが具体的に定義されているわけではありません。アクションのリストに Output アクションを1つも入れなかった場合、そのパケットはどこにもフォワードされずに捨てられます。これを便宜的に Drop アクションと呼んでいるわけです。

それでは、もっともよく使われる Output アクションと Set-Field アクションで具体的に何が指定できるか見て行きましょう。

Output アクション

Output アクションでは指定したポートからパケットを出力します。出力先にはポート番号を指定しますが、特殊用途のために定義されている論理ポートを使うこともできます。

  • ポート番号: パケットを指定した番号のポートに出す。

  • IN_PORT: パケットを入ってきたポートに出す。

  • ALL: パケットを入ってきたポート以外のすべてのポートに出す。

  • FLOOD: パケットをスイッチが作るスパニングツリーに沿って出す。

  • CONTROLLER: パケットをコントローラに明示的に送り、Packet In を起こす。

  • NORMAL: パケットをスイッチの機能を使って転送する。

  • LOCAL: パケットをスイッチのローカルスタックに上げる。ローカルスタック上で動作するアプリケーションにパケットを渡したい場合に使う。あまり使われない。

この中でも FLOOD や NORMAL は OpenFlow スイッチ機能と既存のスイッチ機能を組み合わせて使うための論理ポートです。

Set-Field アクション

Set-Field アクションでは、パケットのさまざまな部分を書き換えられます。パケットで書き換えられるフィールドは、マッチフィールドで指定可能なフィールドと同じです (表2-1)。例えば、以下に示す書き換えが可能です。

  • 送信元/宛先 MAC アドレスの書き換え

  • 送信元/宛先 IP アドレスの書き換え

  • ToS フィールドの書き換え

  • TCP/UDP 送信元/宛先ポートの書き換え

  • VLAN ID/プライオリティの書き換え

それでは Set-Field アクションの代表的な使い道を順に見ていきましょう。

MAC アドレスの書き換え

MAC アドレス書き換えの代表的な例がルータです。OpenFlow はルータの実装に必要な、送信元と宛先 MAC アドレスの書き換えをサポートしています。

rewrite mac
図 2-10: ルータでの送信元と宛先 MAC アドレスの書き換え

ルータは 2 つのネットワークの間で動作し、ネットワーク間で行き交うパケットの交通整理を行います。ホスト A が異なるネットワークに属するホスト B にパケットを送ると、ルータはそのパケットを受け取りその宛先 IP アドレスから転送先のネットワークを決定します。そして、パケットに記述された宛先 MAC アドレスを次に送るべきホストの MAC アドレスに、送信元を自分の MAC アドレスに書き換えてデータを転送します。

IP アドレスの書き換え

IP アドレス書き換えの代表的な例が NAT (Network Address Transition) です。OpenFlow は NAT の実装に必要な、送信元と宛先 IP アドレスの書き換えをサポートしています。

rewrite ip address
図 2-11: NAT での送信元と宛先 IP アドレスの書き換え

インターネットと接続するルータでは、プライベート/グローバルネットワーク間での通信を通すために IP アドレスを次のように変換します。プライベートネットワーク内のクライアントからインターネット上のサーバに通信をする場合、ゲートウェイはプライベートネットワークから届いたパケットの送信元 IP アドレスを自分のグローバルな IP アドレスに変換して送信します。逆にサーバからの返信は逆の書き換えを行うことによりプライベートネットワーク内のクライアントに届けます。

ToS フィールドの書き換え

ToS フィールドは通信のサービス品質 (QoS) を制御する目的でパケットを受け取ったルータに対して処理の優先度を指定するために使われます。OpenFlow はこの ToS フィールドの書き換えをサポートしています。

TCP/UDP ポート番号の書き換え

TCP/UDP ポート番号書き換えの代表的な例が IP マスカレードです。OpenFlow は IP マスカレードの実装に必要な、送信元と宛先の TCP/UDP ポート番号の書き換えをサポートしています。

rewrite port
図 2-12: IP マスカレードでの送信元と宛先 TCP/UDP ポート番号の書き換え

ブロードバンドルータなど 1 つのグローバルアドレスで複数のホストが同時に通信を行う環境では、NAT だけだと TCP/UDP のポート番号が重複する可能性があります。そこで、IP マスカレードではプライベートネットワーク側のポート番号をホストごとに適当に割り当て、通信のつどポート番号を変換することで解決します。

VLAN ヘッダの書き換え

既存のタグ付き VLAN で構築したネットワークと OpenFlow で構築したネットワークを接続するという特別な用途のために、VLAN ヘッダの書き換えができます。VLAN をひとことで説明すると、既存のスイッチで構成されるネットワーク (ブロードキャストが届く範囲のネットワーク) を複数のネットワークに分割して使用するための仕組みです。この分割したネットワーク自体を VLAN と呼ぶ場合もあります。どの VLAN に所属するかを区別するのが VLAN ID で、パケットに付与される VLAN タグがこの VLAN ID を含みます。Set-Field アクションを用いることで、以下に示す 2 種類の VLAN ヘッダ操作ができます。

strip vlan
図 2-13: VLAN ヘッダを書き換えるアクションの使い道
VLAN ID の書き換え

VLAN パケットが属する VLAN の ID を書き換えます。たとえば VLAN ID を 3 に書き換えるといったアクションを指定できます。また、VLAN ヘッダがついていないパケットに 指定した VLAN ID を持つ VLAN ヘッダを付与することもできます。

VLAN プライオリティの書き換え

VLAN 上でのパケットを転送する優先度を変更します。このプライオリティはトラフィックの種類 (データ、音声、動画など) を区別する場合などに使います。指定できる値は 0 (最低) から 7 (最高) までです。

Change-TTL アクション

Chante-TTL アクションは、パケット中の TTL (Time-To-Live) の値を変更するためのアクションです。TTL は、なんらかの不具合によりネットワーク中でパケットがループすることを防ぐための仕組みです。パケットを受信したネットワーク機器は、ヘッダ中の TTL の値を一つ減らしてからパケットを転送します。もし、受信したパケットの TTL の値が 0 だった場合、そのパケットを破棄します。このようにすることで、パケットがループ中を転送され続けることを防ぎます。Change-TTL アクションでは、以下に示す TTL の書き換えが可能です。

  • MPLS ヘッダの TTL に指定の値を設定 (Set MPLS TTL)

  • IP ヘッダの TTL に指定の値を設定 (Set IP TTL)

  • MPLS ヘッダの TTL の値を一つ減算 (Decrement MPLS TTL)

  • IP ヘッダの TTL の値を一つ減算 (Decrement IP TTL)

  • 内側ヘッダの TTL の値を外側ヘッダの TTL のフィールドにコピー (Copy TTL outwards)

  • 外側ヘッダの TTL の値を内側ヘッダの TTL のフィールドにコピー (Copy TTL inwards)

例えば、内側が IP ヘッダで外側が MPLS ヘッダである時、Copy TTL outwards では、IP ヘッダの TTL 値を MPLS ヘッダの TTL のフィールドに設定します。一方、Copy TTL inwards では、MPLS ヘッダの TTL 値を IP ヘッダの TTL のフィールドに設定します。

マルチプルテーブル

OpenFlow バージョン 1.3 では、OpenFlow スイッチがフローテーブルを複数持てます。この複数のフローテーブルのことを、マルチプルテーブルと呼びます。マルチプルテーブルをうまく活用することで、複雑なパケット処理を行えます。

宮坂部長グループの社内ネットワーク運用について考えてみましょう(図 2-14)。

multiple table example
図 2-14: 宮坂部長グループの社内ネットワーク
  • 宮坂部長 (192.168.0.1) の端末からのパケットは、MailとWebのトラフィックの場合のみ、ポート 2 に出力したい。

  • 一般社員の端末 (192.168.0.2 ~ 192.168.0.254) からは、すべてのトラフィックを、ポート 2 に出力したい。

Mail のトラフィックを許可する場合には、TCP で Destination Port 番号が 25 と 110 のパケットを通過するようにします。同様に Web では、TCP で Destination Port 番号 80 と 443 を許可します。

これをフローテーブルに設定すると、表2-2 の様になります。先頭の 5 つのが宮坂部長の端末 (192.168.0.1) からのパケット向けのフローエントリです。

Table 2. マルチプルテーブルを使わない場合のフローテーブルの例1
マッチ インストラクション 優先度 備考

src_ip = 192.168.0.1/32, dst_port = 25

Apply-Actions (Output 2)

10000

宮坂部長用

src_ip = 192.168.0.1/32, dst_port = 110

Apply-Actions (Output 2)

10000

src_ip = 192.168.0.1/32, dst_port = 80

Apply-Actions (Output 2)

10000

src_ip = 192.168.0.1/32, dst_port = 443

Apply-Actions (Output 2)

10000

src_ip = 192.168.0.1/32

Apply-Actions (Drop)

5000

src_ip = 192.168.0.0/24

Apply-Actions (Output 2)

1000

一般社員用

宮坂部長の仕事が多くなったため、事務員を雇うことになりました。事務員は宮坂部長の業務を手伝う必要があるため、事務員に割り当てられた端末 (192.168.0.2) は宮坂部長の端末と同じポリシーで運用することとします。この場合、フローテーブルを 表2-3 のように書き換える必要があります。

Table 3. マルチプルテーブルを使わない場合のフローテーブルの例2
マッチ インストラクション 優先度 備考

src_ip = 192.168.0.1/32, dst_port = 25

Apply-Actions (Output 2)

10000

宮坂部長用

src_ip = 192.168.0.1/32, dst_port = 110

Apply-Actions (Output 2)

10000

src_ip = 192.168.0.1/32, dst_port = 80

Apply-Actions (Output 2)

10000

src_ip = 192.168.0.1/32, dst_port = 443

Apply-Actions (Output 2)

10000

src_ip = 192.168.0.1/32

Apply-Actions (Drop)

5000

src_ip = 192.168.0.2/32, dst_port = 25

Apply-Actions (Output 2)

10000

事務員用

src_ip = 192.168.0.2/32, dst_port = 110

Apply-Actions (Output 2)

10000

src_ip = 192.168.0.2/32, dst_port = 80

Apply-Actions (Output 2)

10000

src_ip = 192.168.0.2/32, dst_port = 443

Apply-Actions (Output 2)

10000

src_ip = 192.168.0.2/32

Apply-Actions (Drop)

5000

src_ip = 192.168.0.0/24

Apply-Actions (Output 2)

1000

一般社員用

wildcard

Apply-Actions (Drop)

0

表2-2表2-3 を比較すると、フローエントリが 5 つ増えているのがわかります。もし事務員をもう一人雇うことになった場合、さらに 5 つのエントリを追加する必要があります。

このようにフローテーブルの内容が複雑になるケースでも、マルチプルテーブルを使うことですっきりできます。まず Table1 の内容は 表2-4 の様になります。宮坂部長および事務員の端末からのパケットを、TCP のポート番号を見てからどのように処理するか判断すべきです。そのための判断をするために、これらのパケットは次に Table 2 を見るように Goto-Table インストラクションが指定されています。

Table 4. マルチプルテーブルを使う場合のフローテーブルの例 1 (Table1)
マッチ インストラクション 優先度 備考

src_ip = 192.168.0.1/32

Goto-Table 2

10000

宮坂部長用

src_ip = 192.168.0.2/32

Goto-Table 2

10000

事務員用

src_ip = 192.168.0.0/24

Apply-Actions (Output 2)

1000

一般社員用

wildcard

Apply-Actions (Drop)

0

Table2 の内容は、表2-5 のようになっています。このテーブルを参照するのは、宮坂部長、事務員の端末からのパケットが到着した場合のみなので、あとは Mail, Web のトラフィックのみ通過できるようなエントリを記述すれば良いことになります。

Table 5. マルチプルテーブルを使う場合のフローテーブルの例 2 (Table2)
マッチ インストラクション 優先度 備考

dst_port = 25

Apply-Actions (Output 2)

10000

宮坂部長、事務員用

dst_port = 110

Apply-Actions (Output 2)

10000

dst_port = 80

Apply-Actions (Output 2)

10000

dst_port = 443

Apply-Actions (Output 2)

10000

wildcard

Apply-Actions (Drop)

5000

マルチプルテーブルを使ったほうが、図2-3 と比べ、シンプルになることがわかります。もし、事務員をもう一人雇うことになった場合でも、Table1 に一つエントリを追加するだけで済みます。

Write-Actions と Clear-Actions

Apply-Actions に指定されたアクションは、フローテーブルが参照された段階で即座に実行されます。一方で、Write-Actions を使うと、一旦アクションセットに格納されます。そしてフローテーブルの参照が全て終わった段階で、アクションセットに格納されたアクションが実行されます。

例えば、表2-6表2-7 のようにフローエントリが格納されていたとします。宛先ポート番号 25 のパケットを受信した時、このパケットは Table1 の 1 番目のエントリにマッチします。そのため、Write-Actions インストラクションで指定されている Set-Field A というアクションがアクションセットに格納されます。1 番目のエントリには、Goto-Table インストラクションも指定されていますので、次に Table2 の参照を行います。受信パケットは Table2 の 1 番目のエントリにもマッチしますので、同様にアクションセットに Output 2 というアクションが格納されます。最終的にアクションセットには、Set-Field A および Output 2 という二つのアクションが格納されている状態になります。

Table 6. Write-Actions を含むフローテーブルの例 1 (Table1)
マッチ インストラクション 優先度

dst_port = 25

Write-Actions (Set-Field A), Goto-Table 2

10000

dst_port = 110

Write-Actions (Set-Field B), Goto-Table 2

10000

Table 7. Write-Actions を含むフローテーブルの例 2 (Table2)
マッチ インストラクション 優先度

wildcard

Write-Actions (Output 2)

10000

アクションセットに格納された複数のアクションは、次の優先順位に従って実行されます。格納された順に実行されるわけではない点に注意が必要です。

  1. copy TTL inwards : 外側ヘッダの TTL を内側ヘッダの TTL へコピーするアクションを実行します。

  2. pop : 指定されたタグを除去するアクションを実行します。

  3. push-MPLS : MPLS tag をパケットに付与するアクションを実行します。

  4. push-PBB : PBB tag をパケットに付与するアクションを実行します。

  5. push-VLAN : VLAN tag をパケットに付与するアクションを実行します。

  6. copy TTL outwards : 内側ヘッダの TTL を外側ヘッダの TTL へコピーするアクションを実行します。

  7. decrement TTL : TTL を 1 減らすアクションを実行します。

  8. set : Set-Field アクションを実行します。

  9. qos : Set-Queue アクションを実行します。

  10. group : Group アクションを実行します。

  11. output : group の指定がない場合のみ、Output アクションを実行します。

表2-6表2-7 で示した例の場合、Output アクションより優先度が高い Set-Field アクションが先に実行され、その後 Output アクションが実行されます。

アクションセットは、一連の処理が終わった後にクリアされます。前に受信したパケットのアクションがアクションセットに入ったままになり、次のパケットの処理に用いられることは起こりません。

マルチプルテーブルを使ったパケットの処理中であっても、Clear-Actions インストラクションを使うことで、アクションセットの中身をクリアできます。Clear-Actions インストラクションを使えば、Write-Actions で格納したアクションをアクションセット中から全て消去できます。

メタデータの利用

Write-Metadata インストラクションを使って、メタデータを付与できます。付与されたメタデータは、Goto-Table インストラクションで次のフローテーブルを参照する際に、マッチフィールドの一部として利用できます。

例えば、送信元 IP アドレスが 192.168.1.101, 102 の場合、宛先ポート番号が 25, 110 のパケットのみをポート 2 から出力し、また送信元 IP アドレスが 192.168.1.103, 104 の場合、宛先ポートが 80, 443 のパケットのみをポート 2 から出力することを考えます。この例をメタデータを使って実現したのが 表2-8表2-9 です。

表2-8 には、送信元 IP アドレスをマッチとしたフローエントリが格納しています。表2-9 には、宛先ポートをマッチとしたフローエントリが格納されています。このように、メタデータを用いることで、複雑な条件であっても、シンプルなフローエントリの組み合わせで設定できます。

Table 8. メタデータを含むフローテーブルの例 1 (Table1)
マッチ インストラクション 優先度

src_ip = 192.168.1.101

Write-Metadata 1, Goto-Table 2

10000

src_ip = 192.168.1.102

Write-Metadata 1, Goto-Table 2

10000

src_ip = 192.168.1.103

Write-Metadata 2, Goto-Table 2

10000

src_ip = 192.168.1.104

Write-Metadata 2, Goto-Table 2

10000

Table 9. メタデータを含むフローテーブルの例 2 (Table2)
マッチ インストラクション 優先度

metadata = 1, dst_port = 25

Apply-Actions (Output 2)

10000

metadata = 1, dst_port = 110

Apply-Actions (Output 2)

10000

metadata = 2, dst_port = 80

Apply-Actions (Output 2)

10000

metadata = 2, dst_port = 443

Apply-Actions (Output 2)

10000

メタデータは 64bit 長のビット列で、初期値は All 0 です。Write-Matadata インストラクションは、各ビットの値を変更します。Write-Metadata インストラクションを使うときは、値とマスクの組を指定します。マスクで指定されたビットの値がメタデータに反映されます。

例を使って説明します。実際にはメタデータは 64bit ですが、ここでは 8bit であるとします。メタデータの現在の値が 11111111 であり、Write-Metadata インストラクションでの指定した値は 00001010、マスクは 00001111 であったとします。マスクは下位 4bit が 1 であるため、値の下位 4bit 分だけをメタデータに反映します。その結果、メタデータは 11111010 となります。

また、メタデータをマッチフィールドで用いる場合にも、値とマスクを指定します。マスクで指定されたビットのみ、マッチに用います。

まとめ

OpenFlow 仕様の中でもとくにポイントとなる部分を見てきました。ここまでの章で学んできた内容だけで、すでに OpenFlow 専門家と言ってもよいほどの知識が身に付いたはずです。次の章では OpenFlow コントローラを開発するためのプログラミングフレームワークである Trema (トレマ) に触れてみましょう。


1. 指定できるアクション数の上限は OpenFlow スイッチとコントローラの実装に依存します。普通に使う分にはまず問題は起こらないでしょう