プログラマのためのSDN基礎講座 2:OpenFlowコントローラとスイッチ

# はじめに
前回はSDN(Software Defined Network)の簡単な説明をしました。
ネットワーク機器をソフトウェアで制御できるメリットは皆さんも大まかですが想像できると思います。今回はSDNを実現するための技術を説明します。
今回からネットワークに関連する用語を少しずつ使っていきます。用語についてはプログラマにも分かるよう、簡単な補足を入れます。

# OpenFlowとは
SDNの思想を実現するためONFの前身であるOpenFlow Switch Consortiumが中心となって2009年に標準化したプロトコルが”OpenFlow”です。「プロトコル」という言葉はプログラマにはなじみのない言葉ですが、機器同士が通信するための共通ルールのことを指します。例えば「英語」「日本語」はそれぞれの言語圏の人たちがコミュニケーションをとるためのプロトコルと言えます。同様に、ネットワークを構成する機器がそれぞれOpenFlowプロトコルに対応していれば、SDNを実現できます。
言語に英語・日本語があるようにSDNを実現するプロトコルもOpenFLow以外にも存在します。
[ONFのFAQ](https://www.opennetworking.org/ja/about-ja/faqs-ja)には

```
Q: OpenFlowを使わなくてもSDNが構成できますか?
A: はい、可能です。しかしながら、あまりお勧めできません。OpenFlowの代替技術である独自仕様を用いれば、ベンダーが定義するソリューションと価格体系に従わなくてはなりません。OpenFlowはオープンなSDNアーキテクチャの中心的役割を担う、制御レイヤーと転送レイヤーの間の通信機能を提供する唯一の標準プロトコルです。
```

とあるように、その名のとおりオープンであること、制御のためのAPIを外部に公開していることから今後も広く普及していくものと思われます。

# OpenFlowコントローラとスイッチ
OpenFlowスイッチは、受信したデータをどうするか―特定の宛先に送るのか、廃棄するのか、内容を書き換えて送信するのか、などを持つ『フローテーブル』を根拠に決定します。データの流れ（フロー）をジャッジするためのcase文を想像してもらえば分かりやすいと思われます。そのフローテーブルに”条件”と”処理”を配布する機能を持つものがOpenFlowコントローラです。コントローラとスイッチの間でやり取りをするためのルールがOpenFlowプロトコルとなります。

# スイッチとは
OpenFlowスイッチの話をする前に、スイッチ自体の説明をします。タイトルからあるように「プログラマのための」SDN基礎講座なので。
ここでいうスイッチとは「スイッチングハブ」のことを指します。「レイヤ2スイッチ」「ネットワークスイッチ」とも言います。

![スイッチングハブ.png](https://qiita-image-store.s3.amazonaws.com/0/64230/6b00beb8-9b2c-4f23-af99-397b44743215.png)

研究室やオフィスで見たことがあるかと思います。複数のホスト（コンピュータ）を接続し、通信する際に使用します。
スイッチは内部に”MACアドレステーブル”というキャッシュを持ちます。MACアドレステーブルには、『スイッチに接続されているホストを識別するための”MACアドレス”』と、『ホストが接続されているポート（ネットワークケーブルが接続されている穴の位置）』の組み合わせが記録されています。スイッチはこのMACアドレステーブルを見て、流れてきたデータを送付する宛先ホストが分かる場合はポートから送信し、分からない場合はスイッチのすべてのポートからそのデータを送付します。OpenFlowスイッチはMACアドレステーブルの代わりに、フローテーブルを見て宛先を決めたり、あるいはデータを破棄することが可能です。

OpenFlowスイッチとはその名のとおりOpenFlowに対応したスイッチを意味します。OpenFlowスイッチはNECなどが発売している物理スイッチと、Open vSwitchなどの仮想スイッチがあります。物理スイッチはOpenFlow スイッチとしてのみ動作する機器と、従来のスイッチとしても使用できるハイブリッド型があります。物理スイッチは転送能力に優れ、大規模ネットワーク向けですが、OpenFlowへの対応は仮想スイッチと比べるとどうしても遅くなってしまい、柔軟性に欠けます。

# OpenFlowのメッセージ

## Packet In メッセージ
OpenFlowスイッチに接続されたコンピュータ（サーバやPC、以降本記事では「ホスト」と呼び方を統一します）からパケットが流れてきた際、OpenFlowスイッチはこのパケットと関連する情報をPacket In メッセージでOpenFlowコントローラに送付することができます。例えば、フローテーブルの条件に合致しないパケットが届いた場合、OpenFlowスイッチはOpenFlowコントローラにパケットを送り、「どうすればいいの？」と問い合わせます。

```
パケット：ネットワーク上を流れるデータを指します。OSI参照モデルのどの層の話をしているかで、呼称が変化します。データリンク層では「フレーム」、ネットワーク層では「パケット」と呼びますが、ネットワーク上を流れるデータという点では同じです。SDNの場合ネットワーク層の話が中心のため、以後当記事でも「パケット」で統一します。
```

## Packet Out メッセージ
逆にPacket Out メッセージはOpenFlowスイッチからPacket In メッセージで受け取ったパケットに対して、「このホストに送ってください」「破棄してください」などの命令を出します。


## Flow Mod メッセージ
OpenFlowコントローラからOpenFlowスイッチに対してフローテーブルへのエントリの追加・更新・削除を命令します。

主なOpenFlowメッセージは上記3つです。以下にOpenFlowメッセージの表を記載します。

|状態|説明|
|:---|:---|
|（スイッチ・コントローラ間の接続）|OpenFlowスイッチとOpenFlowコントローラは最初にTCPでコネクションを設立します。これをOpenFlowチャンネルといいます。このコネクションは仕様ではどちらから接続しても良いですが、多くの場合スイッチからコントローラに接続します。|
|Hello|OpenFlowスイッチとOpenFlowコントローラは互いにHelloメッセージを出し合い、互いのOpenFlowのバージョンを確認します。両者が通信可能と判断した場合、ネゴシエーションに成功し、Helloメッセージ以外のメッセージをやり取りすることが可能になります。|
|Features|OpenFlowコントローラはOpenFlowスイッチのDatapathID（スイッチを識別するID）を確認するためのFeatures Requestメッセージを送信します。スイッチはこれに対してDatapath IDや自身のスペックを伝えます。|
|Packet In|Packet InメッセージによりOpenFlowスイッチからOpenFLowコントローラにパケット情報を上げた場合の通信です。|
|Packet Out|OpenFLowコントローラからOpenFlowスイッチにPacket Outメッセージによりパケットを出力した場合の通信です。|
|Flow Mod|OpenFLowコントローラからOpenFlowスイッチへのフローエントリーの追加・更新・削除命令です。スイッチはフローテーブル更新の報告をコントローラには上げません。Flow Modメッセージの都度応答メッセージを返すと、大量のメッセージを送った際に時間がかかってしまうからです。|
|Barrier|OpenFLowコントローラからOpenFlowスイッチへフローテーブルの更新完了を確認するメッセージです。OpenFlowスイッチは更新が完了した場合、Barrier Replyメッセージを送ります。|
|Flow Removed|フローエントリーが消えた時、OpenFlowスイッチからOpenFLowコントローラに消えたフローエントリーと統計情報をFlow Removedメッセージとして送信します。統計情報を使ってOpenFlowコントローラはパケットの集計や分析ができます。|

```
TCP：送信元のホスト上のプログラムから、別のホスト上の別のプログラムへとデータを転送する際に使用するプロトコルです。IPアドレスが特定のホストを宛先として指定し、TCPがポート番号を指定することで、ホスト上で動作しているサービス（メールサーバ、Webサーバなど）へ正しく通信することが可能になります。
```

## フローエントリ
フローテーブルに登録するフローエントリには以下の6種類の情報が含まれます。

|種類|説明|
|:---|:---|
|優先度|フローテーブルの条件に複数マッチするパケットがスイッチに届いた場合、優先度が高いエントリが優先されます。|
|マッチフィールド|OpenFlowスイッチがどのようなパケットを受け取ったときに処理を起こすかの条件。重要な要素その1です。|
|処理|マッチフィールドに合致したパケットに対しての処理。重要な要素その2です。|
|統計情報|このエントリに合致したパケット数、バイト数などのデータを保持します。|
|タイムアウト|このエントリの有効期限です。|
|クッキー|この値自体は条件や処理に影響を及ぼさず、コントローラがフローエントリを識別するために付与するIDとなります。|

### マッチフィールド
OpenFlow 1.0では12種類だったマッチフィールドも、1.3では40種類に増大しています。よく使いそうなものを列挙します。

|マッチフィールド名|説明|
|:---|:---|
|in_port|スイッチの論理ポート番号|
|in_phy_port|スイッチの物理ポート番号|
|eth_dst|宛先MACアドレス|
|eth_dst|送信元MACアドレス|
|eth_type|Ethernetタイプ番号|
|vlan_vid|VLANID|
|vlan_pcp|VLAN優先度|
|ip_proto|IPプロトコル番号|
|ipv4_src|IPv4の送信元IPアドレス|
|ipv4_dst|IPv4の宛先IPアドレス|
|tcp_src|TCPの送信元ポート番号|
|tcp_dst|TCPの宛先ポート番号|
|udp_src|UDPの送信元ポート番号|
|udp_dst|UDPの宛先ポート番号|

プログラマの方でもIPアドレス、ポート番号などは聞いたことがあるかと思います。OpenFlowはネットワークを流れるパケットの”ヘッダ”と呼ばれる、データ以外の箇所に格納された上記表に記された属性の値を基に、合致するパケットを条件指定できます。

### 処理
マッチフィールドで合致したパケットに対して以下のようなアクションを指定することができます。処理はパイプライン式で設定可能なので、入力→変換（Set-Field)→出力(Output)で使用することが多いのではないでしょうか。

|種類|説明|
|:---|:---|
|Output|指定したポートからパケットを出力します。本来のスイッチングハブの役割です。しかし、通常のスイッチングハブにはない「パケットを送ってきたポートに出力する」「フローテーブルの中にエントリがあるにもかかわらずコントローラにPacket Inする」など、様々なアクションを起こすことができます|
|Set-Field|パケット内の様々な部分を書き換えられます。先ほど挙げたマッチフィールドの条件判定に使用したMACアドレスやIPアドレスを書き換える以外にも様々な更新が可能です|
|Group|テーブル（グループ毎にアクションを指定した情報）により処理を分岐させます|
|Drop|パケットを破棄します。実際にDropというアクションが用意されているわけではなく、処理の中にOutputを記載しない場合、パケットは捨てられます。これをDropアクションと便宜的に呼びます。|

ネットワークに詳しい人ならばお分かりになるかと思いますが、OpenFlowを使うことでスイッチがファイアウォールになったり、ルータになったり、NATサーバになったり、ロードバランサになることもできます。

```
ファイアウォール：社内ネットワークとインターネットの間を出入りするパケットを監視し、決められたルールを基にパケットを通過or破棄を決定します。ハードウェアとしてインターネットに接続する端に設置する以外に、ソフトウェアでPCにインストールすることもあります。
```

```
ルータ：スイッチは同一ネットワーク（例えば、同じ会社拠点のオフィス内）のコンピュータを接続するのに対して、ルータは異なるネットワークにあるコンピュータを接続する際にルートを決定し、パケットを伝送します。
```

```
NATサーバ：ネットワークはそれぞれIPアドレスを持ちます。皆さんがよく目にする、自分のPCに付与されているIPアドレス「192.168.～」はローカル内でのみ使用可能なIPアドレス（＝プライベートIPアドレス）で、ネットワーク間で通信するためには世界で一意となるIPアドレス（＝グローバルIPアドレス）が必要です。ネットワークを超えて通信を行うために、ローカルIPアドレスをIPアドレスを変換する必要があります。このネットワークアドレス変換サーバをNAT（Network Address Translation）サーバと呼びます
```

```
ロードバランサ：ネットワーク上を流れるパケットを複数の機器向けに分散させることで、負荷を分散させる装置です。
```

## 終わりに
OpenFlowができることを一通り説明しました。OpenFlowスイッチがフローテーブルに記述されたルールに従いパケットの転送に専念し、判断に迷ったパケットはコントローラに問い合わせ、回答をもらい、同じ質問をしないようフローテーブルに条件と処理を追加する、までの一連の流れが分かっていただけたと思います。
次回はいよいよプログラミングに入ります。OpenFlowコントローラを実装するために様々なフレームワークが用意されています。

|フレームワーク|プログラミング言語|特徴|
|:---|:---|:---|
|Trema|Ruby|NECが開発。OpenFlowコントローラとOpenFlowスイッチがセットでシミュレーション可能。日本国内で人気のフレームワーク|
|OpenDaylight|Java|Linux Foundationが主体。主要ベンダが共同で開発。OpenFlow以外のSDNプロトコルにも対応し、広範囲のSDNを実現しようとしている|
|Brocade Vyatta Controller|Java|OpenDaylightのブロケード版ディストリビューション|
|Floodlight|Java|オープンなコミュ二ティで開発が進められている。Eclipseを使用できる。OSを問わず動作する|
|Ryu|Python|NTT研究所が開発。多くのOpenFlowバージョンに対応し、OpenFlowのバージョンアップにすぐに対応する。仮想スイッチのOpen vSwitch を制御可能|
|POX|Python|世界最初のOpenFlowコントローラNOX(C++/Pythonの両言語で書かれていた)をPythonに特化させたもの。OSを問わず動作が可能|
|ONOS|Java|通信事業者のネットワーク向けに構築されたOpenFlowコントローラ|

次回はRyuを使ってOpenFlowコントローラを実装し、SDNとOpenFlowの基礎に触ってみます。

----
[プログラマのためのSDN基礎講座 1:SDNとは](http://qiita.com/Usek/items/cac38a67659207620c19)
[プログラマのためのSDN基礎講座 2:OpenFlowコントローラとスイッチ](http://qiita.com/Usek/items/6c56ecac56ac8febebfa)
[プログラマのためのSDN基礎講座 3:Ryuでスイッチングハブを作ってみる](http://qiita.com/Usek/items/31010eac154d5831872d)