再試 SDN:利用 RYU 控制 OpenFlow 交換器
Flow 表是支援 OpenFlow 的交換器用來決定封包的流量用,我嘗試用 Rest API 去移除它的預設規則,然後再加回,看看有什麼效果。
我先按著這頁的指示,將需要的組件和 Ryu 下載回來在另一台虛擬機器上安裝:
sudo apt-get install python-eventlet python-routes python-webob python-paramiko python-oslo.config git clone git://github.com/osrg/ryu.git cd ryu
然後啟動它,使用 simple_switch_13 是想支援 OpenFlow 1.3 標準:
PYTHONPATH=. ./bin/ryu-manager --verbose ryu/app/simple_switch_13.py ryu/app/ofctl_rest.py ryu/app/rest_topology.py
可見它已在 8080 埠上聆聽 HTTP RESTful 要求:
在安裝了 mininet 的電腦上執行以下指令,還是選用直線的拓樸:
$ sudo mn --topo linear,5 --mac --switch ovsk,protocols=OpenFlow13 --controller remote,ip=<控制器的 IP 地址>,port=6633
mininet 的畫面:
啟動後可見 RYU 控制器不斷跟 OpenFlow 交換器溝通:
利用 Chrome 的 Postman 程式,我們可以發送 RESTful 要求,去看看現在的拓樸:
GET http://<控制器的 IP 地址>:8080/v1.0/topology/switches
結果:
[ { "ports": [ { "hw_addr": "ca:a5:f0:9e:15:65", "name": "s1-eth1", "port_no": "00000001", "dpid": "0000000000000001" }, { "hw_addr": "52:8e:d8:12:de:07", "name": "s1-eth2", "port_no": "00000002", "dpid": "0000000000000001" } ], "dpid": "0000000000000001" }, { "ports": [ { "hw_addr": "3e:0a:77:94:58:c8", "name": "s2-eth1", "port_no": "00000001", "dpid": "0000000000000002" }, { "hw_addr": "e2:c6:cd:61:6d:f0", "name": "s2-eth2", "port_no": "00000002", "dpid": "0000000000000002" }, { "hw_addr": "8e:ce:4d:0a:54:14", "name": "s2-eth3", "port_no": "00000003", "dpid": "0000000000000002" } ], "dpid": "0000000000000002" }, { "ports": [ { "hw_addr": "52:6e:de:2f:69:d1", "name": "s3-eth1", "port_no": "00000001", "dpid": "0000000000000003" }, { "hw_addr": "92:19:fa:b1:aa:3d", "name": "s3-eth2", "port_no": "00000002", "dpid": "0000000000000003" }, { "hw_addr": "5e:d7:25:e6:59:00", "name": "s3-eth3", "port_no": "00000003", "dpid": "0000000000000003" } ], "dpid": "0000000000000003" }, { "ports": [ { "hw_addr": "fa:88:fa:db:85:b0", "name": "s4-eth1", "port_no": "00000001", "dpid": "0000000000000004" }, { "hw_addr": "2a:3c:fb:a0:d7:2b", "name": "s4-eth2", "port_no": "00000002", "dpid": "0000000000000004" }, { "hw_addr": "b6:80:d6:d7:a0:4b", "name": "s4-eth3", "port_no": "00000003", "dpid": "0000000000000004" } ], "dpid": "0000000000000004" }, { "ports": [ { "hw_addr": "d6:10:2e:f4:c0:19", "name": "s5-eth1", "port_no": "00000001", "dpid": "0000000000000005" }, { "hw_addr": "b6:93:ba:61:c7:af", "name": "s5-eth2", "port_no": "00000002", "dpid": "0000000000000005" } ], "dpid": "0000000000000005" } ]
我們見到 dpid 是交換器的 ID,它們有著不同的 Port ID,網絡上共有五台交換器。
我們去查去 dpid 為 1 的交換器的 Flow 表:
GET http://<控制器的 IP 地址>:8080/stats/flow/1
Ping 之前,任何封包都要 output 到 4294967293,這代表將封包傳送到 RYU 控制器去判斷流向:
{ "1": [ { "actions": [ "OUTPUT:4294967293" ], "idle_timeout": 0, "cookie": 0, "packet_count": 462, "hard_timeout": 0, "byte_count": 96104, "length": 80, "duration_nsec": 737000000, "priority": 0, "duration_sec": 439, "table_id": 0, "flags": 0, "match": { } } ] }
我嘗試由 h1 Ping 到 h5 後,控制器給了它兩條新的 Flow,就是目的地 MAC 地址為 00:00:00:00:00:01 從 Port 2 輸入的封包會傳到 Port 1,而目的地 MAC 地址為 00:00:00:00:00:05 從 Port 1 輸入的封包會 傳到 Port 2,這某程度就是 Ethernet 交換器的原理,防止封包廣播。而這個機制就是由 simple_switch_13.py 去進行:
{ "1": [ { "actions": [ "OUTPUT:4294967293" ], "idle_timeout": 0, "cookie": 0, "packet_count": 543, "hard_timeout": 0, "byte_count": 112371, "length": 80, "duration_nsec": 151000000, "priority": 0, "duration_sec": 486, "table_id": 0, "flags": 0, "match": { } }, { "actions": [ "OUTPUT:1" ], "idle_timeout": 0, "cookie": 0, "packet_count": 4, "hard_timeout": 0, "byte_count": 280, "length": 96, "duration_nsec": 448000000, "priority": 1, "duration_sec": 18, "table_id": 0, "flags": 0, "match": { "dl_dst": "00:00:00:00:00:01", "in_port": 2 } }, { "actions": [ "OUTPUT:2" ], "idle_timeout": 0, "cookie": 0, "packet_count": 3, "hard_timeout": 0, "byte_count": 238, "length": 96, "duration_nsec": 440000000, "priority": 1, "duration_sec": 18, "table_id": 0, "flags": 0, "match": { "dl_dst": "00:00:00:00:00:05", "in_port": 1 } } ] }
我輸入以下 Flow 規則,如果封包目的地 MAC 地址為 00:00:00:00:00:01,又從 Port 2 輸入,就什麼都不做,即是取消封包傳到 Port 1:
POST http://<控制器的 IP 地址>:8080/stats/flowentry/modify { "dpid": 1, "match":{ "dl_dst": "00:00:00:00:00:01", "in_port":2 }, "actions":[] }
結果回應封包即時不能傳送到 h1:
如果要重新給 Port 1,可以輸入以下 Flow 規則(其他 action 可以是 Group、Queue 等):
POST http://<控制器的 IP 地址>:8080/stats/flowentry/modify { "dpid": 1, "match":{ "dl_dst": "00:00:00:00:00:01", "in_port":2 }, "actions":[{"type":"OUTPUT","port":1}] }
這樣網絡又再次打通了:
這也不過是最簡單的規則改動,RYU 更可以自訂一些 Python 程式去改動 Flow 表。RYU 不足的地方就是它沒有清楚說明 REST 的用法,最多不過是打開那堆在 app 資料夾內名字帶有 "rest" 的 Python 程式碼去查看,而我也不過只能試試這個簡單功能(程式碼)。當然開發者可以利用這些 RESTful API 或它本身的 Pyhton API 去建立程式。
參考資料:
RYU Version OpenFlow Tutorial
OpenFlow
OpenFlow Technical Library
SDN Lab 5$ REST and Ryu
RYU SDN Framework - ebook
RYU SDN Framework
RYU RESTAPI 的使用
在RYU上使用Rest API控制flow entry part 1/2 (match field 沒有mask)
Instructions & Actions
本文連結