はじめに
グーーッモーニング、
ベトナーーーーーム!
『グッドモーニング、
今回はOpenFlowでミニ放送局気分を味わってみましょう! 動画を配信するしくみの1つにマルチキャストがあります。これはNTT東/
観たい人にだけ届けるマルチキャスト
マルチキャストを使った動画配信の動作を簡単に説明しておきましょう

マルチキャストを実現するには、
このように、
それでは、
マルチキャストの動作マルチキャスト転送を実現するために、OpenFlowスイッチはどのように動作すればよいでしょうか?
説明を簡単にするため、スイッチ1台の構成を考えましょう(図2)。ストリーミングサーバが動画パケットをスイッチに送ると、スイッチは受け取ったパケットをコピーして、受信ホストがつながっているすべてのポートに出力します。
図2 スイッチ1台のマルチキャスト
では、受信ホストの存在をスイッチはどのようにして知るのでしょうか? 受信ホストは、受信開始時にIGMP(Internet Group Management Protocol)パケットを出して「どの動画を観たいか」をスイッチに伝えます。スイッチは、このIGMPパケットを受け取ったポートに対してのみパケットを送信します。
ポイントは、IGMPを送ったホストの管理方法と、IGMPを送ったホストにだけパケットを届けるしくみです。では、OpenFlowでの実現方法を見ていきましょう。
OpenFlowでのマルチキャスト
ここでも説明を簡単にするため、スイッチ1台の小さなネットワークに、動画を送信するストリーミングサーバ1台と受信するホスト3台を接続した場合を考えます。
視聴者にだけパケットを送る
まずは、視聴者にだけパケットを送るしくみです(図3)。マルチキャストでは、送信者であるストリーミングサーバはパケットの宛先フィールドにグループアドレス(たとえば239.192.0.1)を含めます。これは「どの視聴者グループに動画を届けるか」を示すグループ名の一種と考えてください。
図3 視聴者にだけパケットを送る
コントローラは、packet_inで送られてきたパケットのグループアドレスを参照し、そのグループに参加しているホスト(視聴者)を調べます。そして、視聴者が接続しているポートにパケットを出力するフローをflow_modメッセージでスイッチに書き込みます。これで、以降のパケットは視聴者にだけ転送されます。
グループへの参加と脱退
ホストがグループに参加/脱退するためにはIGMPパケットを使います。ホストがIGMPパケットを送ると、これがpacket_inとしてコントローラに上がります。コントローラは、これがグループへの参加(MEMBERSHIP_REPORT)であるか脱退(LEAVE_GROUP)であるかを判別し、内部的に持っているグループ情報を更新します。このグループ情報は、定期的にフローを更新する際に使います(図4)。
図4 グループへの参加
実装
では、さっそくTremaで実装してみましょう。このためのコントローラは、グループを管理するクラス(MFCクラス)とコントローラ本体であるSimpleMulticastクラスから構成されます。
MFCクラス
MFC(Multicast Forwarding Cache)クラスは、グループの一覧と、グループごとにどのホストが接続しているか(ホストが接続しているポート番号で識別します)を管理します(リスト1)。
リスト1 MFCクラス(mfc.rb)
require "set"
class MFC
def initialize
@db = Hash.new do | hash, key | ← グループの一覧を初期化
hash[ key ] = Set.new
end
end
def learn group, port ← グループに受信者接続ポートを登録
members( group ).add( port )
end
def remove group, port ← グループからポートを削除
members( group ).delete( port )
end
def members group ← グループの受信ホストが接続するすべてのポートを検索
@db[ group.to_i ]
end
en
SimpleMulticastクラス
SimpleMulticastクラスは、コントローラの本体です(リスト2)。
リスト2 SimpleMulticastクラス(simple-multicast.rb)
require "mfc"
class SimpleMulticast ← MFCオブジェクトを用意
end
def packet_in datapath_id, message
if message.igmp? ← (1)packet_inしたパケットがIGMPか?
handle_igmp message
else
members = @mfc.members( message.ipv4_daddr ) ← グループのメンバー一覧
flow_mod datapath_id, members, message
packet_out datapath_id, members, message
end
end
private ← 以下プライベートメソッド
def handle_igmp message ← (2)IGMPパケット受信時の処理
group = message.igmp_group ← IGMPパケット中のグループアドレスの値
port = message.in_port
if message.igmp_v2_membership_report?
@mfc.learn group, port ← グループへ参加
elsif message.igmp_v2_leave_group?
@mfc.remove group, port ← グループを脱退
end
end
def flow_mod datapath_id, members, message ← (3)フローを設定
send_flow_mod_add(
datapath_id,
:match => ExactMatch.from( message ),
:actions => output_actions( members ), ← 視聴者のポートだけに出力
:hard_timeout => 5 ← flow_modの有効期限は5秒
)
end
def packet_out datapath_id, members, message ← packet_inパケットの転送
send_packet_out(
datapath_id,
:packet_in => message,
:actions => output_actions( members ) ← 視聴者のいるすべてのポートに出力
)
end
def output_actions members ← 視聴者のいるポートだけに出力するアクション
members.collect do | each |
ActionOutput.new( :port => each )
end
end
end
packet_in ハンドラ(リスト2(1))ではigmp?メソッドを使い、パケットがIGMPであるかを判定しています。IGMPであった場合、handle_igmpメソッド(リスト2(2))で中身を見て、グループの参加/脱退を行います。このように、Trema::PacketInクラスはパケットの種別や各フィールドを参照するための便利なメソッドをたくさん持っており[2]、パケットの詳細を知らなくても簡単に各レイヤの情報を取り出すことができます。
flow_modメソッド(リスト2(3))では、視聴者だけにパケットを転送するためのフローを設定します。output_actionsメソッドを使って、視聴者の接続するポートに出力するためのアクションの配列を作り、send_flow_mod_addの:actionに指定します。
グループの情報は刻々と変化しますので、フローの有効期限は5秒としています。フローが消えると再びマルチキャストパケットがpacket_inでコントローラまで上がってきますので、その時点でのグループ情報を使って新しくフローを張りなおします。
実行してみよう
さっそく動かしてみましょう! 構成としてストリーミングサーバ1台、受信ホスト2台に加え、コントローラを動作させるホストの合計4台を使います(図5)。
図5 動作構成
まず、TremaとOpenFlowスイッチの準備をしましょう。リスト3のネットワーク設定ファイルを用意します。
リスト3 マルチキャスト用スイッチ1台をeth0、eth1、eth2に接続(network.conf)
vswitch ( "msw" ) {
datapath_id "0xabc"
}
link "msw", "eth0"
link "msw", "eth1"
link "msw", "eth2"
今回作成したsimple-multicast.rb、mfc.rbとコンフィグレーションファイルを、tremaコマンドと同じディレクトリに置いてください。次のように今回作成したコントローラを実行します。
% ./trema run ./simple-multicast.rb -c ./network.conf
マルチキャストの送受信を行うアプリケーションとしては、VLC[3]を使います。ストリーミングサーバと受信ホストの両方に、次のようにVLCをインストールしてください。
% sudo apt-get install vlc
それでは、ストリーミングサーバの設定をします。前もって、配信したい動画ファイルを携帯やデジカメで撮影しておいてください(movie.mpg)。次のコマンドで、マルチキャストを使った動画のストリーミングを開始します[4]。
% cvlc ./movie.mpg --sout udp://239.192.0.1:60000
--miface eth0 --loop --mtu 1472 -d
次に、2台の受信ホストで動画視聴の準備をしましょう。まず、次のコマンドでIGMP Version 2[5]を使うように各ホストの設定を変更しておいてください。
% sudo sysctl -w net.ipv4.conf.all.force_igmp_version=2
次に、VLCを起動し、メニューから「ネットワークストリームを開く」を選択します。ダイアログボックスに、「udp://@239.192.0.1:60000」と入力してください(図6)。
図6 マルチキャスト受信の設定
以上を行うと、2台の受信ホストで、図7のように動画が同じタイミングで再生されるはずです。うまく再生ができたでしょうか?
まとめ
今回は、マルチキャスト転送を実現するコントローラを作りました。学んだことは、次の2点です。
- packet_
inメッセージの中のパケットの各フィールドの値を参照する方法を学びました。Trema::PacketInクラスに用意されているigmp_typeなどのアクセサメソッドを使うことで、いろいろなレイヤの値を簡単に取り出すことができます。
- マルチキャスト転送を実現するアクションの書き方を学びました。複数のアクションを指定することで、
複数のポートに複製したパケットの出力ができます。
次回は、いよいよ最終回です。Tremaについてより深く知ってもらうために、Tremaの中身、アーキテクチャについて紹介します。
説明を簡単にするため、

では、
ポイントは、
OpenFlowでのマルチキャスト
ここでも説明を簡単にするため、
視聴者にだけパケットを送る
まずは、

コントローラは、
グループへの参加と脱退
ホストがグループに参加/

実装
では、
MFCクラス
MFC
require "set"
class MFC
def initialize
@db = Hash.new do | hash, key | ← グループの一覧を初期化
hash[ key ] = Set.new
end
end
def learn group, port ← グループに受信者接続ポートを登録
members( group ).add( port )
end
def remove group, port ← グループからポートを削除
members( group ).delete( port )
end
def members group ← グループの受信ホストが接続するすべてのポートを検索
@db[ group.to_i ]
end
en
SimpleMulticastクラス
SimpleMulticastクラスは、
require "mfc"
class SimpleMulticast ← MFCオブジェクトを用意
end
def packet_in datapath_id, message
if message.igmp? ← (1)packet_inしたパケットがIGMPか?
handle_igmp message
else
members = @mfc.members( message.ipv4_daddr ) ← グループのメンバー一覧
flow_mod datapath_id, members, message
packet_out datapath_id, members, message
end
end
private ← 以下プライベートメソッド
def handle_igmp message ← (2)IGMPパケット受信時の処理
group = message.igmp_group ← IGMPパケット中のグループアドレスの値
port = message.in_port
if message.igmp_v2_membership_report?
@mfc.learn group, port ← グループへ参加
elsif message.igmp_v2_leave_group?
@mfc.remove group, port ← グループを脱退
end
end
def flow_mod datapath_id, members, message ← (3)フローを設定
send_flow_mod_add(
datapath_id,
:match => ExactMatch.from( message ),
:actions => output_actions( members ), ← 視聴者のポートだけに出力
:hard_timeout => 5 ← flow_modの有効期限は5秒
)
end
def packet_out datapath_id, members, message ← packet_inパケットの転送
send_packet_out(
datapath_id,
:packet_in => message,
:actions => output_actions( members ) ← 視聴者のいるすべてのポートに出力
)
end
def output_actions members ← 視聴者のいるポートだけに出力するアクション
members.collect do | each |
ActionOutput.new( :port => each )
end
end
end
packet_
flow_
グループの情報は刻々と変化しますので、
実行してみよう
さっそく動かしてみましょう! 構成としてストリーミングサーバ1台、

まず、
vswitch ( "msw" ) {
datapath_id "0xabc"
}
link "msw", "eth0"
link "msw", "eth1"
link "msw", "eth2"
今回作成したsimple-multicast.
% ./trema run ./simple-multicast.rb -c ./network.conf
マルチキャストの送受信を行うアプリケーションとしては、
% sudo apt-get install vlc
それでは、
% cvlc ./movie.mpg --sout udp://239.192.0.1:60000
--miface eth0 --loop --mtu 1472 -d
次に、
% sudo sysctl -w net.ipv4.conf.all.force_igmp_version=2
次に、

以上を行うと、
まとめ
今回は、
- packet_
inメッセージの中のパケットの各フィールドの値を参照する方法を学びました。Trema::PacketInクラスに用意されているigmp_ typeなどのアクセサメソッドを使うことで、 いろいろなレイヤの値を簡単に取り出すことができます。 - マルチキャスト転送を実現するアクションの書き方を学びました。複数のアクションを指定することで、
複数のポートに複製したパケットの出力ができます。
次回は、