Tremaでコントロールプレインとデータプレインを分離する方法
初めまして、ネットワークエンジニアのHiroです。私の仕事は上位レイヤーが中心ですが、最近盛り上がってきているSDNをそこに応用していこうと思っています。
例えば、複雑な通信の最中に奇妙なパケットを投げてバグを再現させるといったことが可能になるのではないかと思っています。その第一歩としてTremaを少しずつ勉強しており、今回は下記2点を解説します。
- Tremaでコントロールプレインとデータプレインを分離する方法
- Tremaで仮想ホストではなくリアルなインターフェイスを使う方法
Tremaの解説本「OpenFlow実践入門」には記載がなくネットにも見当たらない情報です。
■■■ ネットワーク環境 ■■■
======================================================
10.10.10.1(リモートアクセスでの管理用)
eth0
eth1 --------------- Ubuntu136 --------------- eth2
|.136 .141
| |
192.168.1.0/24 192.168.2.0/24
| |
|.141 .141
eth1 --------------- Ubuntu141 --------------- eth2
eth0
10.10.10.2(リモートアクセスでの管理用)
======================================================
3つのインターフェイスを持つUbuntu2台をVM上で動かしています。
- Ubuntu136: コントロールパスとデータパス両方
- Ubuntu141: データパスのみ
■■■ Ubuntu136上でのスクリプト&設定 ■■■
ソースコードは下記の通りです。
======================================================
root@ubuntu:/home/hiro/test/trema# cat learning-switch.rb
class LearningSwitch < Controller
def start
@fdb = {}
end
def switch_ready datapath_id
info "Switch #{ datapath_id.to_hex } is UP"
end
def switch_disconnected( datapath_id )
info "Switch #{ datapath_id.to_hex } is DOWN"
end
def packet_in( datapath_id, message )
info "packet_in. dp: " + datapath_id.to_hex + ", in_port: " + message.in_port.to_s
@fdb[ message.macsa ] = message.in_port
port_no = @fdb[ message.macda ]
if port_no
flow_mod datapath_id, message, port_no
packet_out datapath_id, message, port_no
else
flood datapath_id, message
end
end
private
def flow_mod( datapath_id, message, port_no )
send_flow_mod_add(
datapath_id,
:match => ExactMatch.from( message ),
:actions => SendOutPort.new( port_no )
)
end
def packet_out( datapath_id, message, port_no )
send_packet_out(
datapath_id,
:packet_in => message,
:actions => SendOutPort.new( port_no )
)
end
def flood( datapath_id, message )
packet_out datapath_id, message, OFPP_FLOOD
end
end
root@ubuntu:/home/hiro/test/trema# cat learning-switch2.conf
vswitch("lsw") {
datapath_id "0x136"
ip "192.168.1.136"
}
link "lsw", "eth1"
link "lsw", "eth2"
======================================================
"learning-switch2.conf"には仮想ホストもそれへのリンクも設定していません。単に仮想スイッチと実インターフェイスへのリンクをしているだけです。
「ip "192.168.1.136"」が重要で、データパス側からコントローラパスに向けて接続するので、データパスだけのホストには必ずこれを設定しておく必要があります。
■■■ Ubuntu141上でのスクリプト&設定 ■■■
======================================================
root@ubuntu:/home/hiro/test/trema# cat empty.rb
class Empty < Controller
end
root@ubuntu:/home/hiro/test/trema# cat learning-switch2.conf
vswitch("lsw") {
datapath_id "0x141"
ip "192.168.1.136"
}
link "lsw", "eth1"
link "lsw", "eth2"
======================================================
このホストはコントローラとしては動かないためrubyスクリプトは空のクラスだけでOKです。設定には、
- 実インターフェイスと仮想スイッチをリンク
- コントローラのIPを指定
というキモを押さえておけばいいです。
■■■ 実行してみる@Ubuntu136(コントロールパス側) ■■■
======================================================
root@ubuntu:/home/hiro/test/trema# trema run learning-switch.rb -c learning-switch2.conf
Switch 0x136 is UP
packet_in. dp: 0x136, in_port: 1
packet_in. dp: 0x136, in_port: 2
packet_in. dp: 0x136, in_port: 1
packet_in. dp: 0x136, in_port: 1
packet_in. dp: 0x136, in_port: 1
packet_in. dp: 0x136, in_port: 1
Switch 0x141 is UP
packet_in. dp: 0x136, in_port: 1
packet_in. dp: 0x141, in_port: 1
packet_in. dp: 0x141, in_port: 1
packet_in. dp: 0x136, in_port: 1
packet_in. dp: 0x141, in_port: 1
root@ubuntu:/home/hiro/test/trema# curl 192.168.2.141
<html><body><h1>It works!</h1>
<p>This is the default web page for this server.</p>
<p>The web server software is running but no content has been added, yet.</p>
</body></html>
root@ubuntu:/home/hiro/test/trema# curl 192.168.1.141
<html><body><h1>It works!</h1>
<p>This is the default web page for this server.</p>
<p>The web server software is running but no content has been added, yet.</p>
</body></html>
======================================================
■■■ 実行してみる@Ubuntu141(データパス側) ■■■
======================================================
root@ubuntu:/home/hiro/test/trema# trema run empty.rb -c learning-switch2.conf
======================================================
上記のとおり、コントロールパス側からデータパス側にcurlでHTTPリクエストを送っています。データパス側にはもともとWebサーバが動いているのでレスポンスを返します。
その通信をイベントハンドラがキャッチしてpacket_in関数が実行されます。それにより、
info "packet_in. dp: " + datapath_id.to_hex + ", in_port: " + message.in_port.to_s
に書いたとおり、どちらのデータパスか判別するためにdatapath_idを出力させ、eth1とeth2のどちらのインターフェイスにパケットが入ってきたのか判別するためin_portを出力させています。
どうやら設定で
link "lsw", "eth1"
のようにインターフェイス名を指定しても、tremaのin_portでは1や2などとして番号で認識されているようです。
▼関連リンク:Trema のコントローラと仮想ネットワークをそれぞれ別々に動かす
http://ranosgrant.cocolog-nifty.com/blog/2013/04/trema-4cea.html