Trema で OpenFlow ネットワークプログラミング

こんにちは、インフラストラクチャ本部の大山です。
このエントリはGREE Advent Calendar 2013 22日目の記事です。

はじめに

"ネットワークプログラミング" という言葉は、恐らくシステム屋さんにとって TCP/UDP あるいは IP といった L4, L3 の世界のプログラミングを想起させるのではないかと思います。ですが OpenFlow によって、そのレイヤが一気に L1 まで落ちました。つまり Layer-1 (物理層)までがプログラマブルに扱える領域になったということです。

これは主に Ethernet と IP に限定されるものの、従来 L1 から L3 の領域はネットワーク屋さんの領分で L4 以上がシステム屋さん、あるいはアプリケーション屋さんの領分という暗黙の了解を OpenFlow が無くしてしまいました。

今日は OpenFlow ネットワークを制御するソフトウェア(コントローラ)を実装するためのフレームワーク "Trema" について、お話をさせていただきます。

Trema とは何か?

3 年くらい前から OpenFlow や SDN といった言葉がもてはやされ、その実何がどう上手くできるのか、モヤッとしててよくわからないバズワードとなって久しいです。更にここへきて、通信事業者などが中心となって、こうした技術を利用した、ファイヤーウォールや諸所の認証機能などのネットワーク機能を仮想化する NFV なるモノが打ち出されました。

厄介なバズワードがどんどん増え、加えて SDN と NFV を同一レベルのものとして扱う記事などによって混乱が進み、この話題に縁遠いエンジニアにとってはそろそろ訳がわからなくなってきてるんじゃないかと思います。

憶測ですが、こうした用語が次々と登場する背景にネットワーク屋さん(とかく欧米圏)の特徴に起因するところがあるのではないかと思っています。彼らは何にでも名前を付けたがります。仮にそれが別の言葉で一般的に認識されているモノや事象であっても、名前っぽい名前が無いと分かると、それらを一般化した総体として長々しい名前を付けます。そしてその名前の各単語の頭文字をくっつけてそいつを指すようにします。"Virtual eXtensible Local Area Network" で VXLAN、"Network Virtualization using Generic Routing Encapsulation" で NVGRE などなど。

実に普通です。GNU や Linux, YAML などといったネーミングと比較しても、システム屋さんとネットワーク屋さんの性質の違いが現れます。

閑話休題。

「じゃあ Trema とか OpenFlow って何なんじゃ」という話ですが、ここでこれらの用語をざっくりと整理してみます。先の SDN, NFV 云々の話の流れで言うと、OpenFlow とは SDN を実現するプリミティブな部分の実装をサポートするモノで、Trema とは OpenFlow ネットワークを制御する OpenFlow コントローラと呼ばれるソフトウェアを実装するフレームワークになります。これをソフトウェアとの対比で表したものが下の表になります。

NFV => ある目的に特化したアプリケーション (Ruby on Rails 相当)
SDN => アプリケーションを作るためのプログラミング言語 (Ruby 相当)
OpenFlow => プログラミング言語を支える技術 (libc 相当)
Trema => 標準 C ライブラリ (libc) の実装の一つ (glibc 相当)

何故 Trema なのか?

標準 C ライブラリと一口に言っても、その実装は glibc の他にも BSD 系のモノや組み込み用に専科したものなど様々あります。

同じように、OpenFlow コントローラフレームワーク (と呼ばれているモノ、あるいはそう分類されるもの) には Trema 以外にも様々あります。主だった OpenFlow コントローラフレームワークの特徴をまとめてみました。

Trema : 開発・テスト・デバッグツールを自前で備えたオールインワンな OpenFlow コントローラフレームワーク。NEC のメンバーが中心となって開発を推進。
Floodlight : 米 Big Switch 社が開発する商用のオープンソースの OpenFlow コントローラ。高度にモジュール化された設計が特徴。
Ryu : OpenStack Neutron に Ryu プラグインがあり、Neutron と REST API を使って協調動作可能。NTT コミュニケーションズのメンバーが中心となって開発を推進。
OpenDaylight : OpenFlow 以外の SDN 技術をサポートする SDN コントローラ(これについては後述します)。大規模エンタープライズ向け。主に cisco などのネットワークベンダーが中心となって開発を推進。

次に、Trema が唯一と謳うオールインワンフレームワークを成す、テストフレームワークとテストツール、そして最近 Trema プロジェクト内部で話題のサブプロジェクト Trema::Pio についてご紹介します。

テストフレームワーク:仮想ネットワーク

ここで言う仮想ネットワークとは「物理の上に仮想ネットワークを構築して、マルチテナントなネットワークを作りましょ」的なモノではなく、一つの PC 上で仮想的に任意のトポロジーの IP ネットワークを構築して、そこに仮想的なホストを接続させる事が出来るモノのことを言っています。なので「ネットワークシュミレータ」といった方が意味的に近いのかもしれませんが、Trema グループがこれを「仮想ネットワーク」と呼んでいるので、ここでもそう呼びます。

Trema では、こうした仮想ネットワークの構築、及び仮想ネットワークに接続する仮想ホスト間でのパケット転送ができる環境を提供しています。これによって、物理的に OpenFlow ネットワークを用意しなくても、手元にある PC で手軽に大規模かつ複雑な OpenFlow ネットワークを構築し、そこで自前のコントローラの動作をテストできます。

では実際に動かしながらこいつを見てみましょう。

まず、Trema を動かせる環境を用意します。GitHub の Trema リポジトリから最新のソースコードを取得しビルドします(詳しくは、Trema の GitHub ページ などを参考に実施してください)。

環境構築が済んだら、以下のコマンドで実際に動かしてみます。仮想ネットワークを含む Trema に対する操作は、リポジトリ配下の trema コマンドによって行われます。"trema --help" で使い方が確認できます。Trema のコントローラアプリケーションを起動するには、run コマンドの後ろに C もしくは Ruby で作成したコントローラアプリケーションのパスを指定します。

今回は仮想ネットワークの動作を確認するだけなので、用意されているサンプルプログラムを実行します。ここではサンプルプログラム dumper (src/examples/dumper/dumper.rb) を使用します。dumper は、コントローラに通知される各種イベントに対して、イベントとイベントに付随するメタデータ (メッセージデータ) の中身を標準出力に書き出すコントローラアプリケーションです。

仮想ネットワークを使用するには -c オプションに対して、Trema 仮想ネットワーク用の設定ファイルのパスを渡します。サンプルプログラム dumper と同じディレクトリに、仮想ネットワーク用の設定ファイル (dumper.conf) が置かれているのでこれを指定して Trema を起動します。尚、仮想ネットワークを使用する場合、TAP デバイスを作成するため root 権限を要求してくるため、root 権限を与えてやります。

ここで、仮想ネットワークの設定ファイルの中身を見てみます。

ここでは、二つの仮想ホストが一つの仮想スイッチに接続する仮想ネットワークを作る設定をしています。また見ての通り、Trema 仮想ネットワークの設定ファイルは Ruby の内部 DSL で記述されます。

dumper.conf には、vswitch, vhost, link の 3 種類のメソッドコールが記述されており、それぞれが「仮想スイッチ」「仮想ホスト」「仮想リンク」の定義になります。vswitch と vhost の引数でそれぞれ仮想スイッチ、仮想ホストにラベルをつけてやれます。またブロックで内部で、それぞれのメタ情報を設定してやることもできます。

vswitch では、OpenFlow スイッチを識別する 64 ビットの識別子 ( datapath_id ) を設定しています。サンプルでは何も指定していませんが、vhost に対して仮想ホストの IP アドレス、MAC アドレスを以下のように指定してやる事もできます。

作成した仮想ネットワークに対してトラフィックを流してやるには、trema コマンドのサブコマンドの一つ send_packets コマンドを使用します。Trema のリポジトリで "trema send_packets --help" と実行すると、send_packets コマンドの使用法を教えてくれます。

それでは試しに host1 からトラフィックを出して見たいと思います。trema を起動したのとは別のコンソールから、以下コマンドを実行してみてください。以下のコマンドで dumper.conf で定義した host1 から host2 に対して UDP パケットが送られます。

trema run コマンドを実行したコンソールで、パケットがキャプチャされている様子が分かると思います。

また仮想ホスト、及び仮想スイッチの送受信状況、転送設定状況についてはそれぞれ、trema のサブコマンド "show_stats" と "dump_flows" によって確認できます。使い方については、これまでと同様にサブコマンドの後ろに "--help" をつけて実行すると使用法を教えてくれます。

ここまでだと、仮想ネットワークの使い方のお話だけのよくある内容になってしまうので、ここあらもうちょっとツッ込んだ「じゃあこいつをどうやって実現しているんだ」という部分について少し見ています。

Trema 実行中に /sbin/ifconfig を実行してみてください。trema- という名前のついた仮想 NIC があるのがわかります。それぞれが、仮想ネットワークの設定ファイルで記述した仮想スイッチと仮想ホストが接続する TAP デバイスになります。

ここで、仮想スイッチと仮想ホストの実体 (プロセス) を ps コマンドで見て行きましょう。trema 関連のプロセスを確認できます。

switch_manager というプロセスは、スイッチのフロントエンドのプロセスになり、コントロールプレーンにおける OpenFlow スイッチからの TCP コネクションを受け付けます。またスイッチから送られてくる OpenFlow メッセージをコントローラアプリケーションに通知する処理もこのプロセスが行います。こいつは仮想ネットワークの仕事には関与しません。

仮想ネットワークを実現するのが、その下で起動する phost と ovs-openflowd というプロセスです。ovs-openflowd はその名の通り、Open vSwitch の openflow スイッチのソフトウェア実装になります。Open vSwitch は、OpenFlow の他 SDN を実現する様々なトンネリングプロトコルや多彩なネットワーク機能を提供するソフトウェアですが、ovs-openflowd は、v1.2 系で存在したユーザランドで動く openflow スイッチの機能を切り出したソフトウェアになります。その後 test-openflowd と名前が変わり、v1.3 系で消滅しました。Trema のリポジトリに置かれている openvswitch は ovs-openflowd が存在していた最後のバージョンになります (*)。

そして、仮想ホストを実現しているのが phost というソフトウェアで、Trema が提供しています。機能としては、非常に単純なパケットの送受信の機能だけ提供しており、現時点では ARP と ICMP (ECHO), そして UDP プロトコルに対応しています。

仮想ホストに対する操作は、直接的には同じ階層にある cli コマンドによって実施されますが、この辺りの操作を trema コマンドがラッピングしてくれています。cli コマンドを生で触ると、まだ色々と残念な目に遭うのでまだそっとしておきましょう。

ここで ps コマンドの結果を改めて見てください。仮想スイッチ及び仮想ホストの実行プロセスのサイズが小さい事がわかってもらえると思います。僕の実行環境ではそれぞれ、1.1M と 700K しかメモリを使っていません。実に小食です。

仮想ネットワークを構成する個々の要素の実装が軽量なので、ノート PC 一つで大規模なネットワーク環境をも構築することができてしまいます。仮想ホストの作りははかなり簡素ですが、TCP コネクションを張ってゴニョゴニョするような検証はできませんが、ホスト間の接続性や経路設定を検証するのであれば、これで十分かもしれません。

(*) 仮想ネットワークにおける仮想スイッチ部分における実装がメンテされなくなってしまったため、Trema プロジェクトでは Trema 仮想ネットワーク用の OpenFlow スイッチのソフトウェア実装を昨年から開発していました。そのソフトウェアスイッチは OpenFlow v1.3.x をサポートし、今年 1 月にリリースされ、同じく OpenFlow v1.3.x に対応したコントローラフレームワーク trema-edge で利用可能になりました (Nick さんありがとう)。

Trema デバッグツール : Tremashark

仮想ネットワークについてかなり長々書いてしまいましたが、ここで Trema が提供するデバッグツール Tremashark について見てゆきます。

Tremashark のインストール方法については、@stereocat さんのブログ でハマり所とその対処も含め、詳しく解説してくれています。

Tremashark は、コントローラに到着する OpenFlow メッセージを解析し、Wireshark に出力する Trema のプラグインになります。Wireshark で読み込ませる為に、OpenFlow メッセージを一旦 libpcap ファイルフォーマットに変換するという処理を行なっています。また、OpenFlow メッセージだけではなく、コントローラが吐き出すログ出力も syslog 経由で同時に表示できる機構になっているみたいです(http://www.slideshare.net/chibayasunobu/tremashark)。

Tremashark の便利な所は、物理ネットワーク上の何処で何が、何処に対して、何を行ったのをがひと目で把握できるところにあります。

trema の run コマンドに -s オプションを指定することによって、Tremashark プラグインを有効化できます。以下が実行例になります。

今回は、Trema のサンプルアプリケーションの一つ learning-switch.rb を実行します。learning-switch.rb は、各 OpenFlow スイッチを L2 Learning Switch にするコントローラアプリケーションです。仮想ネットワークの設定ファイルもサンプルで用意されているモノを指定しています。-s オプション付きで trema run コマンドを実行すると、objects/tremashark/tremashark を起動します。こいつが、OpenFlow スイッチから上がってくる OpenFlow メッセージをキャプチャして Wireshark に出力します。

下の図は、仮想ホスト (host1) から別の仮想ホスト (host2) に対するパケット送信を実行した後の表示になります。

host1 から host2 宛のパケットが、スイッチ (dpid: 0xabc) に送られ、その後フローエントリのマッチングに失敗して、PacketIn メッセージが送られ、コントローラからスイッチに PacketOut メッセージが下りてくるという一連の処理を構造的に把握することができ、コントローラのデバッグに非常に重宝します。

余談ですが、かつてある人に『優秀なプログラマは、気合と根性と printf デバッグでプログラムを書けるが、gdb によって誰もが同じ事を出来るようになる』と教えられました。良いプログラミング環境には、良いデバッグツールは欠かせません。設定が複雑だったり、機能を有効化させるためにプロセスを再起動させなければ行けなかったりと制限がいろいろとありますが、トラブった時の強力な手として、Tremashark は力を発揮しれくれます。

Trema::Pio について

最後にトレンディな話題として、地味ながら Trema で話題になっている Trema Pio について紹介したいと思います。

コントローラを開発していると、コントロールプレーンでパケットを生成してデータプレーンに流したいといった要求が出てきます。例えば、コントローラ側からデータプレーンのトポロジーを知りたいといった場合や、ネットワークにどんな IP を持ったホストが接続されているかを一定周期で知りたいといった場合、更にはホストの疎通を確認したい場合などなど。これらに対してこれまでは、C で Ruby の拡張ライブラリを書く、あるいは以下のように気合で Ethernet フレームを作って送り出すといった荒業を繰り出すといった事をしてかと思います。

ちなみこいつは、Ethernet ヘッダの送信元と宛先用の MAC アドレスフィールド (12 byte) の先頭 8 byte 部分に datapath_id を、余った 4 byte 部分に物理ポートを指定したフレームを OpenFlow スイッチに送り出すコードになります。

また、こいつを受け取った側で必要な情報を取り出す為のロジックが必要になってきます。今回のように Ethernet ヘッダのアドレスフィールドに仕込む場合には以下のようにします。

ここでは、packet_in オブジェクトの送信元と宛先アドレスフィールドをそれぞれマージしてバイナリに変換し、先頭 64 bit だけ取り出して、数値に戻すという処理をしなければなりません。メンテナンス性を著しく害した非常にダメな感じのやり方です。

加えて、こうしたオレオレなトラフィックをデータプレーンに流して、既存のネットワーク機器に入ったりなんかしちゃと、ビックリさせてエラーとか吐かせちゃうかもしれないので、ここは LLDP などの既存のプロトコルのパケットを生成し、また取得したパケットを解析できるようにしたいです。

そこで役に立つのが Trema::Pio です。例えば先程のコードを LLDP パケットを送り出すモノに書き換えたいと思います。 Trema Pio を使うと以下のようにしてできます。

邪魔なオレオレコードを無くすことができました。受信側のパース処理においても同様です。

オレオレ解析処理に加えて、解析に伴う判定処理も無くなりました。

Trema::Pio は Trema のサブプロジェクトであることもあり、Trema のデータ構造とも連携しているので、解析に伴う判定処理も packet_in オブジェクトのチェックメソッドで行えます。これにより、CONTROLLER_DEFINED_TYPE などといった酷たらしい、定数定義も排除でき、コードが非常にスッキリしました。

この他にも現在までに ARP, ICMP プロトコルに対応しており、今後は DHCP やタグ VLAN などのプロトコルの対応も進めていくみたいです。

まとめ

かなり長くなりましたが、Trema が一体どんなモノで何に使えそうかといったことが、今回の内容で少しでもわかっていただけたら幸いです。

Trema は、何かオモシロそうなテクニックが登場した際に陥りがちな「このテクニックだったらこんな事出来るよね」といったテクニック先行型のアプローチでモノを作るのではなく、実際のユースケースを考え抜いた上で設計するユースケース先行型のアプローチでモノを作るので、個々の機能が非常に洗練されています。

最後に

ここまで OpenFlow の話を中心にしてきましたが、世間的には 2013 年の中頃から『SDN コントローラ』と呼ばれるモノが登場し、現在非常に多くの注目を集めています。SDN コントローラは、単純な開発フレームワーク機能だけを提供するのではなく、Firewall や VPN, Network Isolation といった諸所のネットワーク機能を実装したコントローラアプリケーションの機能を REST API などによって外部からポリシーレベルで操作できるようにしているのが特徴です。また、ポリシーを実現するためのメカニズムは SDN コントローラと連携するソフトウェアにとっては何でも構いませんし、また実現するためのメカニズムも必ずしも OpenFlow だけではないので、OpenFlow 以外の SDN 手法も取り入れられています。

Trema などの OpenFlow コントローラフレームワークは、コントローラアプリケーションの開発にフォーカスしたソフトウェアであるのに対して、SDN コントローラはコントローラアプリケーションと連携するソフトウェアにフォーカスしたソフトウェアになります。こうした SDN コントローラとして、OpenContrailOpenDaylight などが有名です。
こうした SDN コントローラは、ネットワーク屋さんの OpenFlow に対する不満に上手くこたえ、同時にシステム屋さんの要求も満足させる、非常に巧いやり方だと思います。

明日は Rubyist の荒井さんによる Chef についての投稿です。お楽しみに!