以上、ネット上でよく紹介されているRubyによるOpenFlowコントローラを先に紹介しました。
しかし、Trema/Appsに含まれるコードを見てもお分かりのとおり、実はC言語で描かれたコードも多数あります。しかし、C言語での実装についての紹介をあまり見ないので、ここで軽く紹介しておきたいと思います。
まず、Rubyのコードと同様にnullコントローラを作ってみます。null_ofc.cという名前でファイルを作成します。
#include "trema.h" int main( int argc, char *argv[] ) { init_trema( &argc, &argv ); start_trema(); return 0; }
行数 | 内容 |
---|---|
4行目 | init_trema() は、Tremaフレームワークの初期化を行います。 |
5行目 | start_trema() は、Tremaのメインループになります。プログラムはこの中を動き続けることになります。 |
このたった数行のコードが、前節で紹介した「何もしないOpenFlowコントローラ」のC言語版となります。
では、作成したコードをビルドしてみましょう。以下のコマンドのうち、`trema-config --cflags --libs`の個所が、ビルドに必要なヘッダファイルディレクトリの指定などを補完してくれます。
$ gcc -o null_ofc null_ofc.c `trema-config --cflags --libs`
作成したコントローラの起動方法はRubyで作成したものと同じです(もちろん、シミュレータも利用できます)。
$ trema run ./null_ofc
ここまで、「何もしないOpenFlowコントローラ」を作成し、コントローラの起動とシミュレータの起動を試しました。次は実際にOpenFlowの基本動作(Packet-In/Flow-Mod/Packet-Out)を含んだコントローラを作成してみましょう。
今回は、RubyとC言語の両方で、リピータハブ(通称・馬鹿ハブ)を作成してみます。
まず、パケットが入力されたら、入力ポート以外からパケットを出すだけのコードを書いてみます。これをhub.rbという名前で保存してください。
class MyController < Controller def packet_in datapath_id, message send_flow_mod_add( datapath_id, :match => ExactMatch.from( message ), :actions => ActionOutput.new( OFPP_ALL ) ) send_packet_out( datapath_id, :packet_in => message, :actions => ActionOutput.new( OFPP_ALL ) ) end end
行数 | 内容 |
---|---|
2行目 | def packet_in datapath_id、messageの個所が、Packet-Inメッセージを受信した際に動作することになります。 |
3行目 | send_flow_mod_add(datapath_id, options = {})の個所は、Flow-Modメッセージの個所になります。今回はフローエントリの追加を行っています。引数のdatapath_idはPacket-Inメッセージを送信したスイッチを指定し、optionsには、マッチングルールにPacket-In時のmessage(パケット)とアクション(入力ポート以外の全てに送信)を指定しています。 |
8行目 | send_packet_out(datapath_id, options = {})の個所は、Packet-Outメッセージの個所になります。引数のdatapath_idはPacket-Inメッセージを送信したスイッチを指定し、optionsには、送信パケットにPacket-In時のmessage(パケット)とアクション(入力ポート以外の全てに送信)を指定しています(注)。 |
注:「packet_in => message」としている個所について、1点補足しておきたいと思います。この個所は以下の2行と同じ内容を指定していることになります。この2行の方が指定している内容がイメージしやすいと思います。
:in_port => message.in_port,
:data => message.data,
なお、パケットを転送するには、Flow-Modでフローエントリに追加すると同時にPacket-Outも明示的に行う必要がある点に注意が必要です。OpenFlowとしては、Flow-ModとPacket-Outは違うモノであることを意識しておく必要があります。
では次に、Rubyで書いたリピータハブをC言語でも書いてみましょう。以下の内容をhub.cいう名前で保存してください。
#include "trema.h" static void call_packet_in( uint64_t, packet_in ); int main( int argc, char *argv[] ) { init_trema( &argc, &argv ); set_packet_in_handler( call_packet_in, NULL ); start_trema(); return 0; } static void call_packet_in( uint64_t datapath_id, packet_in message ) { openflow_actions *actions = create_actions(); append_action_output( actions, OFPP_FLOOD, UINT16_MAX ); struct ofp_match match; set_match_from_packet( &match, message.in_port, 0, message.data ); buffer *flow_mod = create_flow_mod( get_transaction_id(), match, get_cookie(), OFPFC_ADD, 60, 0, UINT16_MAX, -1, OFPP_NONE, OFPFF_SEND_FLOW_REM, actions ); send_openflow_message( datapath_id, flow_mod ); free_buffer( flow_mod ); buffer *frame = duplicate_buffer( message.data ); fill_ether_padding( frame ); buffer *packet_out = create_packet_out( get_transaction_id(), message.buffer_id, message.in_port, actions, frame ); send_openflow_message( datapath_id, packet_out ); free_buffer( packet_out ); free_buffer( frame ); delete_actions( actions ); }
行数 | 内容 |
---|---|
7行目 | main()で呼び出しているset_packet_in_handler();は、Packet-Inメッセージを受信した際に呼び出すハンドラを指定します。このような書き方は、イベントドリブン型なプログラム(Win32プログラムなど)を書いたことがあればなじみがあるのではないでしょうか。すぐ後にstart_trema();でTremaメインループに入りますが、Packet-Inメッセージ受信時は、call_packet_in();が処理される形になります。 |
19行目 | create_flow_mod()を使い、Flow-Modメッセージを作成し、作成したメッセージをsend_openflow_message()で送信する事でFlow-Mod処理を実行しています。 |
36行目 | create_packet_out()を使い、Packet-Outメッセージを作成し、作成したメッセージをsend_openflow_message()で送信する事でPacket-Out処理を実行しています。 |
Rubyで書いたコードと比較すると後処理が面倒ですが、C言語慣れしている方にはコチラの方が分かりやすいかもしれません。
残念ながらC言語APIリファレンスが見つからないので、Tremaのコード(具体的には/var/lib/gems/1.8/gems/trema-*/src/libのあたりのコード)をうろうろする必要はありますが、OpenFlowを詳しく知るにはよい試練かもしれません。
では、作成した「リピータハブOpenFlowコントローラ」をシミュレータで動かしてみます。起動するだけでは動作が分かりませんので、シミュレータ内にパケットを生成してみましょう。
上記で作成したシミュレータを起動します。
$ sudo trema run -c sim_ofn.conf hub.rb
今回は、host1からhost2にパケットを送信してみます。
$ sudo trema send_packets --source host1 --dest host2 --n_pkts 10
パケット送信の結果を見てみましょう。以下で、パケットがきちんと転送できたことを確認できます(もちろん、ifconfigでRXやTXの値を見ても確認できます)。
$ sudo trema show_stats host1 Sent packets: ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets 192.168.0.2,1,192.168.0.1,1,10,500 Received packets:
これで、「リピータハブOpenFlowコントローラ」の動作を確認できました。
ここではRubyとC言語の両方でTremaを利用したOpenFlowコントローラを作成してみましたが、どちらも非常にきれいに収まったコードになっていることがお分かりいただけたと思います。
インターネットでは、TremaはRubyでのOpenFlowコントローラ作成が多く目に付きますが、ここで説明したとおり、C言語でも簡潔なコードでOpenFlowコントローラを作成できます。「オブジェクト指向が苦手で……」という方も、臆することなくTremaを使ってSDN/OpenFlowな世界に踏み出してはいかがでしょうか(ちなみに僕は、オブジェクト指向が分からないC言語派です)。
今回作成した「リピータハブなOpenFlowコントローラ」から一歩踏み出してコントローラをプログラミングする際に非常に役立つのが「APIリファレンス」になります。このクラス一覧を眺めつつ、独自のOpenFlowコントロール作成を楽しんでみてはいかがでしょうか。
1つ残念なのは、前述のとおり、C言語のAPI一覧らしきモノが見つからないことです。Trema/Appsに含まれるコードにはC言語のモノが多いため、ぜひC言語版のAPIリファレンスの登場を期待したいところです。
OpenFlowプログラミングフレームワーク、Tremaを利用することで、OpenFlowコントローラの作成、OpenFlowネットワークでの動作も確認できてしまいました。しかし、それでもやはり物理的なスイッチに心惹かれたりしませんか?(実は、心奪われてしまった僕がここにいますw)
どうやら、OpenFlow対応物理スイッチに興味を持つエンジニアは僕だけでなく多いようです。今をさかのぼること2012年になりますが、物理スイッチを入手するためのカオスな勉強会が秋葉原の居酒屋にて開催されました。それが「自宅ラック勉強会 #2.5 秋葉原出張編」です。
これは、「WHR-G301N」という市販ルータをOpenFlowスイッチにしてしまおう、という何とも馬鹿げた目的を掲げた勉強会だったのですが、にも関わらず20名を越える参加者がありました。
注:自宅ラック勉強会は「自宅ラック友の会」が開催している勉強会です。OpenFlowに限らずさまざまな内容の勉強会を実施しています。
このOpenFlowスイッチの作り方、ファームウェアについては、最新の情報がhttp://openflow.inthebox.info/から入手可能になっています。
今回はTremaの使い方と簡単なOpenFlowコントローラの作り方を紹介しました。これであなたもSDN/OpenFlowデビューできたのではないでしょうか。
さて次回は、もう一歩踏み出して、小さな「机上OpenFlowネットワーク」を作ってみましょう。
Copyright © ITmedia, Inc. All Rights Reserved.