コントローラは小さなハンドラの集まり
os-ken は、OpenFlow のコントローラを Python で書くためのライブラリです。コントローラの中身は、「スイッチから知らせが届いたら、この関数を呼ぶ」という、小さな関数の集まりです。
読み方が分かれば、コントローラが何を決め、スイッチに何を書き込むかを、コードから言えます。
このレッスンのゴール
メモ
小さな os-ken コントローラを読み、各ハンドラが何をするか言える。
重要な言葉
| ことば | いみ | れい |
|---|---|---|
| ハンドラ | 知らせが届いたときに呼ばれる関数 | |
| packet-in | フローのない枠を、スイッチがコントローラに送る知らせ | |
| flow-mod | コントローラがスイッチにフローを書く、命令 | |
| データパス | コントローラから見た、1台のスイッチ | dp |
| os-ken | OpenFlow のコントローラを書く Python のライブラリ |
学生が入力するセルは、このプレビューでは空白です。
スイッチの知らせごとに関数がある
os-ken では、スイッチからの知らせをイベントと呼びます。イベントごとに、@set_ev_cls の印を付けた関数を1つ書きます。
代表は2つです。スイッチがつながったとき、最初の設定をします。フローのない枠が届いたとき、packet-in で受け取って決めます。
2つのイベントと、そこでする仕事
| イベント | いつ起きるか | よくある仕事 |
|---|---|---|
| スイッチがつながった | 接続のとき、1回 | 最初のフローを入れる |
| packet-in | フローのない枠が届くたび | 行き先を決め、フローを入れる |
学生が入力するセルは、このプレビューでは空白です。
ラボのコントローラの形を見る
ラボには、手を付けていないコントローラの写しが、/usr/share/sdnlab/switch.py にあります。まず、@set_ev_cls が付いた行と、関数の名前を並べます。
続いて、packet-in を受ける関数の最初の9行を見ます。ここでは読むだけで、書き換えません。
イベントの印を探す
grep -n "set_ev_cls" /usr/share/sdnlab/switch.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
関数の名前を並べる
grep -n "def " /usr/share/sdnlab/switch.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
packet-in の最初の9行
sed -n 33,41p /usr/share/sdnlab/switch.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
出力から分かること
set_ev_cls の行は3つで、最初はインポート、残る2つが、つながったときと packet-in のイベントです。関数は4つあり、add_flow はフローを入れる道具で、あとの2つがハンドラです。
packet-in の関数は、入口のポートを msg.match から取り、枠を解いて送り主と宛先を取り出し、スイッチごとの表を開きます。
コントローラは flow-mod でフローを書く
コントローラがスイッチにフローを入れる命令を、flow-mod と呼びます。ovs-ofctl add-flow の1行と、同じ内容です。
次の例は、スイッチがつながったとき、3番のポートから入る枠をすべて捨てるフローを1つ入れます。命令を1つも付けないフローは、枠を捨てます。
フローを1つ入れる、小さなコントローラ
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER, set_ev_cls
from os_ken.ofproto import ofproto_v1_3
class GuestBlocker(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_connected(self, ev):
dp = ev.msg.datapath
parser = dp.ofproto_parser
match = parser.OFPMatch(in_port=3)
# 命令を1つも付けないフローは、パケットを捨てます。
dp.send_msg(parser.OFPFlowMod(datapath=dp, priority=200, match=match, instructions=[]))
flow-mod の引数と ovs-ofctl の行
| コードの引数 | ovs-ofctl での書き方 | 意味 |
|---|---|---|
| priority=200 | priority=200 | 優先度 |
| match=OFPMatch(in_port=3) | in_port=3 | 3番ポートから入る枠 |
| instructions=[] | actions=drop | 何もしない、つまり捨てる |
学生が入力するセルは、このプレビューでは空白です。
例を動かすと、1行のフローが入る
この例をラボで動かして確かめました。接続したスイッチの表に、priority=200,in_port=3 actions=drop の1行だけが入りました。
そのあと ofproto/trace で3番ポートの枠を調べると、優先度200のフローに当たり、捨てられました。コードは例で、ラボの課題のコードではありません。
予約ポートは大きな番号
アクションの出口には、普通のポート番号のほかに、特別な予約ポートがあります。全ポートに流す FLOOD や、コントローラに送る CONTROLLER です。
予約ポートの番号は、とても大きな数です。普通のポート番号と、ぶつからないようにしてあります。
予約ポートの番号を出す
python3 -c "from os_ken.ofproto import ofproto_v1_3 as o; print(o.OFPP_CONTROLLER, o.OFPP_FLOOD, o.OFPP_NORMAL)"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
packet-in のハンドラは、パケットごとに毎回呼ばれる、と思いがちです。実際は、フローのない枠だけです。コントローラがフローを入れたあとの枠は、スイッチだけで運ばれ、ハンドラには来ません。