意外に違う
Trema と Trema-Edge
oshiba
自己紹介


PythonとRubyが好きで、
色々遊んでます!



フレームワークをあれこれ触って楽しん
でます。



会社でOpenFlowスイッチ扱ってます。
今日お話しする内容
TremaEdgeを使ってみて分かった、
Tremaとの違いどころを色々と書きます。
Pio試したり、Sinatra試したりすると中々手ごわいこと
(ズバリ書くと不具合)があったため、その辺りを重点的に。
※ 今後修正されると思うので、そのときにはこの資料は
意味をなさないね!
(2014年2月17日で作りました)
What is TremaEdge ?
Trema

TremaEdge

OF v1.0
対応

OF v1.3
対応
要するにTremaのOF1.3用
フレームワークです
ちなみに。。。
OF v1.3になったことによる変更点は、
あんまり解説しません。
マスタリングTCP/IPのOpenFlowとか読んでね!

解説しない理由: めんどくさいから(変更点多すぎ)
※ 混乱しそうな場所だけほんのちょっと触れるかも
※ あと、Ruby2.0になったことによる違いも触れないです
まず、起動するだけ
class TestController < Controller
def start
puts “ Hello Trema! ”
end
end
何も変化無し。
ハンドラ定義 ※

ハンドラ名はものによって変わってる
OpenFlowメッセージが変わったの

で

def packet_in dpid, message
end
def port_status dpid, message
end
def port_desc_multipart_reply dpid, message
end
def packet_in dpid, message
end

ポート情報一覧の
取得用
※FeaturesRequest/Re
plyで受け取れなくなっ
た情報

今まで通りメソッドを定義。
タイマ定義とかメッセージ送信
とか
# タイマ定義
add_timer_event :discover_neighbor, 5, :periodic
# フロー追加
send_flow_mod_add( dpid, options)
#メッセージ送信
send_message dpid, PortMultipartRequest.new

基本、今まで通り。
※ フロー追加はoptionについて変更
有(instructionとか)
ここから変更点とか問題点とか
・
・
・
・
・
・

PacketInのときのパケット情報取得
PortStatusのポート情報受け取り
PacketOutでデータのみのパケット出力
色々な便利メソッドの有無
Sinatraと連携
Trema::Pioと連携
PacketInのときのパケット情報取得


マッチ条件の名前がベース
eth_dst、eth_src、ipv4_dst

とか

https://github.com/trema/tremaedge/blob/develop/ruby/trema/match.rb
をチェック!
PortStatusのポート情報受け取り
Trema
def port_status dpid, message
message.phy_port.port_no
end
 TremaEdge
def port_status dpid, message
phy_port
message.port_no
がない
end


理由:TremaEdgeでは、
PortStatusがPortクラスを継承する形で作られている
PacketOutでデータのみのパケット出力
# PacketInベースのリアクティブな処理
send_packet_out( dpid,
:packet_in => packet_in,
:actions => SendOutPort.new( OFPP_ALL )
# バイナリデータを渡す形の処理
send_packet_out( dpid,
:data => packet,
:buffer_id => OFP_NO_BUFFER,
:actions => SendOutPort.new( OFPP_ALL )

)

)

An Ethernet frame must be provided if buffer_id
is equal to 0xffffffff

データを渡してもNG…
PacketOutでデータのみのパケット出力
VALUE r_opt_message = HASH_REF( options, packet_in );
~中略~
if ( buffer_id == OFP_NO_BUFFER && !NIL_P( r_opt_message ) ) {
~中略~
else {
packet_out = create_packet_out(
get_transaction_id(),
:packet_inオプションが指定さ
buffer_id,
れていないと、ちゃんと動く
in_port,
ようになっていない。
actions,
NULL
);
}

一部改変が必要
PacketOutでデータのみのパケット出力
~改変例~
VALUE r_opt_message = HASH_REF( options, packet_in );
VALUE r_opt_data = HASH_REF( options, data ); //データオプション追加
~中略~
if ( !NIL_P( r_opt_message ) ) {
~中略~
else if( !NIL_P(r_opt_data) ){ //データオプション追加
data = r_array_to_buffer( r_opt_data );
dataに対するfree自体は既に処理
があるため、追記はしない
}
~中略~
if ( buffer_id == OFP_NO_BUFFER && //条件を1つ追加
( !NIL_P( r_opt_message ) || !NIL_P(r_opt_data) )) {
~中略~
else {
:dataのオプション指定があった場合を想定
~中略~
PacketOutでデータのみのパケット出力


これだけではNG!

send_packet_out( dpid,
#
:data => packet,
:data => packet.unpack(“C*”),
:buffer_id => OFP_NO_BUFFER,
:actions => SendOutPort.new( OFPP_ALL )
Arrayで渡さないといけないので、
unpackをする必要がある

)
Sinatraと連携
普通に使うと以下のようなメッセージが出
てしまい、Sinatraが動かない。。。

「Logger」に問題がありそう?
クラスじゃないというメッセー
ジが出てる。
Sinatraと連携
Sinatra のLoggerクラスと
TremaのLoggerモジュールがバッティングしてる。

Sinatra

Trema-Edge
Logger
クラス

競合

Logger
モジュール

Tremaだと、「DefaultLogger」だったのでOKだった。。。
Sinatraと連携(回避策)
Loggerモジュールの名前を変えてあげる。
# logger.cについて(145行目)
mLogger = rb_define_module_under( mTrema, "TremaLogger" );
# logger.rbについて(20行目)
module TremaLogger
# controller.rbについて(33行目)
include TremaLogger
Sinatraと連携(変更後)
エラーは出ない。ルートを書けば問題なく動作

少なくとも、GET、POST、DELETEは動作
Trema::Pioと連携
require "pio"
class TestPacket < Controller
def start
puts "start"
end
end

require しただけでエラー
動かない。。。

error: field '[:octets, {:type=>:uint8, :initial_length=>6}]' is an
illegal fieldname in Pio::Type::MacAddress
Trema::Pioと連携
どうやら、Trema-Edgeの問題。。。
# Trema-Edgeで動かした場合
class Fuga
end
p Fuga.superclass
p Fuga.superclass.respond_to? "string"
p Fuga.superclass.respond_to? “array"

Object
true
true

Objectクラスに対して、stringやarrayがクラスメソッ
ドとして存在してしまっている。
Trema::Pioと連携
この”string”や”array”が、bindataを使ったPioのソースにお
ける、arrayやstringの宣言的な箇所で問題を起こしている。

こういう箇所で問題になる
Trema::Pioと連携(回避策)
arrayとかstringが使えないので、とりあえずPio側を書き換え。
pio_arrayとかpio_stringにする。
class PioString < Bindata::String
end
class PioArray < Bindata::Array
end

こんな感じの宣言箇所を
arrayから、pio_arrayに変更
stringから、pio_stringに変更

sugyoさんからもっとよさそうな回避策も出てました。
でも試してないからここでは書くのをやめました。
色々な便利メソッドの有無
message-helperに今後は纏められる?
(sugyoさんがissueあげてた)
でも、今はまだ、ほとんどない。。。

send_flow_mod_add、
send_group_mod_addぐら
いしかない。。。
色々な便利メソッドの有無
残念ながら以下みたいなのは自分で定義する必要あり
・ send_flow_mod_delete
・ Portクラスの port.up? もしくは port.down?
def up?
if(self.state | 1 == 0)
return true
end
return false
end
def down?
return (not self.up?)
end
Portクラスに
追加する

def send_flow_mod_delete datapath_id, options
options[ :command ]
= OFPFC_DELETE
options[ :table_id ]
= OFPTT_ALL if options[ :table_id ].nil?
options[ :match ]
= Match.new if options[ :match ].nil?
options[ :cookie ]
=0
if options[ :cookie ].nil?
options[ :cookie_mask ] = 0
if options[ :cookie_mask ].nil?
options[ :out_port ]
= OFPP_ANY if options[ :out_port ].nil?
options[ :out_group ]
= OFPG_ANY if options[ :out_group ].nil?
send_flow_mod datapath_id, options
end

message-helperに追加するなど
Statsメッセージについて
Multipartメッセージになったことにより、
名前が変わっているので注意。
# PortStatsを取る場合
send_message dpid, PortMultipartRequest.new
# GroupStatsを取る場合
send_message dpid, GroupDescMultipartRequest.new
# FlowStatsを取る場合
send_message dpid, FlowMultipartRequest.new( cookie: 0x0 )
def flow_multipart_reply dpid, message # FlowStatsのハンドラ
end
def group_desc_multipart_reply dpid, message # GroupStatsのハンドラ
end
def port_multipart_reply dpid, message # PortStatsのハンドラ
end
最後に注意事項(OF 1.3関連)













FeaturesReplyにはポート情報は入ってない
(PortMultipartRequest/Replyが必要になります)
フローエントリにはInstructionsというものが増えてる
フローの削除でクッキー番号の指定とout_groupの指定を考えてあげる
必要がある
PacketIn用のフローを入れないと、PacketInしない
ポート番号以外に、物理ポート番号とかあるから気をつけて
OFPP_NONEとかなくなった
アクションについてset_fieldとかpush/pop tagとか色々細かくなったか
ら気をつけて
アクションは即時実行(APPLY_ACTION)とパイプライン終了後に
実行するWRITE_ACTIONがある
WRITE_ACTIONには実行順序が決められている
APPLY_ACTIONは、今まで通りセットした順番どおりに実行される
以上です!
有難うございました。
oshiba

Tremaとtrema edgeの違い