Packet Out は OpenFlow で定義されたメッセージの 1 つで、スイッチの指定したポートからパケットを送信させるためのものです。送信するときにはパケットを書き換えることもできます。よく使われる用途として、Packet In でコントローラにパケットが上がってきたときに Packet Out でこのパケットを書き換えてスイッチのポートから送り出す場合があります
Trema の Packet Out API は Controller#send_packet_out メソッドで定義されています。なお Controller クラスはすべてのコントローラの親クラスなので、コントローラはこの send_packet_out メソッドをクラス内で直接呼び出すことができます。それでは、API 定義を見ていきましょう。
API 定義
send_packet_out メソッドは次の 2 つの引数を取ります。
send_packet_out( datapath_id, options )
それぞれの引数の意味は次のとおりです。
- datapath_id Packet Out の届け先となるスイッチの Datapath ID です。
- options Packet Out メッセージの中身を決めるためのオプションで、アクションによるパケットの書き換えや出力するポートの指定が行われます。これは Hash で定義されていて、必要なオプションのみを指定すればいいことになっています。
具体的な利用例は次のとおりです。
パケットを作って出す
任意のパケットを作ってスイッチの特定のポートに出したい場合、次のように :data にパケットの中身を指定してスイッチの port_number 番ポートへと出力します。この呼び出しはコントローラのコードのどこからでもできます。
send_packet_out( 0x1, :data => packet_data, :actions => ActionOutput.new( port_number ) )
パケットを送り出すときには、ポートへの出力だけでなく Modify-Field タイプのアクションを指定して書き換えることもできます。
packet_in ハンドラで使う
packet_in ハンドラから使う場合、Packet In メッセージとして入ってきたパケットの内容をそのままスイッチのポートから送り出す場合がほとんどです。この場合、パケットの送信にスイッチのバッファを使う場合と、バッファを使わずにコントローラからパケットを送る場合で呼び出しかたが変わります。
スイッチのバッファを使って Packet Out する場合
通信量が少なくパケットがスイッチのバッファに乗っていることが期待できる場合には、次のように :buffer_id オプションを指定してやることでバッファに乗っているパケットデータを ID で指定して Packet Out できます。この場合コントローラからスイッチへのパケットデータのコピーが起こらないため、若干のスピードアップが期待できます。ただし、バッファがすでに消されている場合にはエラーが返ります (このエラーのハンドリングについては別のポストで解説予定です)。
def packet_in datapath_id, message # ... send_packet_out( 0x1, :buffer_id => message.buffer_id, :actions => ActionOutput.new( port_number ) )
スイッチのバッファを使わずに Packet Out する場合
スイッチのバッファを使わずに Packet Out する場合、次のように :data を指定する必要があります。バッファに乗っているかいないかにかかわらず Packet Out できるので、若干遅くなりますが安全です。
def packet_in datapath_id, message # ... send_packet_out( 0x1, :data => message.data, :actions => ActionOutput.new( port_number ) )
フローテーブルでパケットを制御する
パケットの出力や書き換えをスイッチのフローテーブルにまかせたい場合には、ActionOutput の出力先ポート番号として特殊なポート番号である OFPP_TABLE を指定することができます。この場合、フローテーブルでの検索に使う入力ポートは、:in_port オプションを使って次のように指定できます。
def packet_in datapath_id, message # ... send_packet_out( 0x1, :in_port => message.in_port, :data => message.data, :actions => ActionOutput.new( OFPP_TABLE ) )
このコードは、:packet_in オプションを使って次のように短く書けます。:packet_in オプションは :data と :in_port オプションを PacketIn オブジェクトを使って自動的にセットしてくれます。
def packet_in datapath_id, message # ... send_packet_out( 0x1, :packet_in => message, :actions => ActionOutput.new( OFPP_TABLE ) )
オプション一覧
次が options に指定できるオプション一覧です。
- :buffer_id スイッチでバッファされているパケットの ID を指定します。この値を使うと、スイッチでバッファされているパケットを指定して Packet Out できるので効率が良くなります。ただし、スイッチにバッファされていない時はエラーになります。この値を 0xffffffff に指定した場合、バッファされているパケットは使われず :data オプションに指定した値を Packet Out することになります。デフォルトは 0xffffffff です。
- :in_port :actions オプションに OFPP_TABLE ポートへのアウトプットが指定されている場合にのみ有効です。指定されている場合、フローテーブルのルックアップにはこの値が使われます。デフォルトは OFPP_NONE です。
- :data Packet Out するパケットの中身を指定します。もし buffer_id オプションが指定されておりスイッチにバッファされたパケットを Packet Out する場合、この値は使われません。buffer_id オプションが 0xfffffff のときは、:data に指定された値を使うので指定する必要があります。デフォルトで nil です。
- :packet_in in_port, buffer_id, data を指定するためのショートカットです。packet_in ハンドラの引数として渡される PacketIn メッセージを指定します。
- :actions Packet Out のときに実行したいアクションの配列を指定します。アクションが一つの場合は配列でなくてかまいません。
まとめ
Trema の Packet Out API を解説しました。スイッチのバッファを使う場合と使わない場合での Packet Out の違いを説明しました。
buffer_id を指定した場合、data の指定は不要では?
あと、OFPP_TABLE を使うのはお勧めできないですね。
予期せぬパケット転送が発生する可能性があるので。
ああ! まちがえてましたすみません。> data の指定
記事のほう修正しました。
OFPP_TABLE で予期せぬパケット転送が発生するっていうのは、なんとなくわかる気がします。だいぶ昔に打ったフローにひっかかったりして、どう転送されるかわかりづらくなるって意味で合ってますか?
はい。フローテーブルの状態を正確に把握しないと OFPP_TABLE を使えないです。
– Packet-Out /w OFPP_TABLE を送信した瞬間に、タイムアウトなどの理由で(OFPP_TABLEでヒットさせようとしていた)
フローエントリが削除された
とか、とても嫌ですね。
そもそも、フローテーブルの状態を把握しているなら、OFPP_TABLE を使わず、実行したいアクションを
列挙できますよね。わざわざ、危険な OFPP_TABLE を使う必要ないですね。
たしかにたしかに。
フローテーブルがあらかじめ設計されていて、起動時にスタティックに書き込まれる & タイムアウトしないという設計ならば OFPP_TABLE 使うのかなと思いました。OFPP_TABLE はたぶんこういう意図なんだろうと思いますが、用途はこういう場合しか思い付かないですな。
後で、記事のほうにも注釈として書き足しておきます。
PacketOutで送出するパケットの数を指定したりすることはできないのでしょうか?
例えば,特定のポートから10個のパケットを対象のホストへ向けてパケットを送出するといった感じです.
もし,なにかPacketOut以外で方法があれば教えていただきたいです.
単純に、Packet Out を 10 回やってみるというのではダメでしょうか?
スイッチ間のスループットを計測したいと思っています.大量のパケットを特定のポートから送出し,受信側スイッチの特定ポートの統計情報より算出しようと試みているのですが.,Packet Out を繰り返す方法でも可能でしょうか?
それですと、Packet Out で buffer_id を使わない方法で Packet Out を繰り返すとスイッチ・コントローラ間での転送の影響が大きいので正しく計測できない気がします。buffer_id を指定すればこのコピーは起こらないのですが、同じ buffer_id を繰り返し使えるかどうかはやったことが無いのでわかりません。スペックには、同じ buffer_id は繰り返し使えないと書いてあった気がします。
ちょっと試していただいて、もし良ろしければ結果をご報告してもらえるとうれしいです。
ご返信ありがとうございます.はい,一度試みて,可能かどうかわかりましたら,報告させていただきます.
同一のbuffer idを指定して,複数回,packet_outを実行してみましたが,パケットは一回しか送信されませんでした..やはり,この方法でスループット計測は難しそうです..なにかまた良い方法をご存知でしたら,教えていただければ幸いです.