# ONOS/Mininet 使用ONOS和mininet建立SDN網路並修改forwarding table. 參考資料: - https://chentingz.github.io/2019/10/28/%E3%80%8CONOS%20x%20Mininet%E3%80%8D%E4%BB%8E0%E5%BC%80%E5%A7%8B%E6%90%AD%E5%BB%BA%E7%8E%AF%E5%A2%83/#%E8%BF%90%E8%A1%8CONOS-Apps - https://wiki.onosproject.org/display/ONOS/Installing+required+tools - https://stackoverflow.com/questions/58491064/my-onos-controller-dont-recognize-the-hosts-mininet-topology/61307099#61307099 - https://wiki.onosproject.org/display/ONOS/Requirements - https://wiki.onosproject.org/display/ONOS/Flow+Rules --- ## 目錄 [TOC] ## 環境設置 - Ubuntu 18.04.5-desktop - ONOS 2.4.0 - mininet 2.3.0d6 - Java JDK 11 :::danger 注意! 若使用虛擬機,所配置的硬碟大小需大於10GB,建議設20或30GB,並開啟共用剪貼簿 ::: ## 前置套件安裝 先安裝git、curl、net-tools、vim ```linux sudo apt-get install git curl net-tools vim ``` ## Java 環境安裝 ### Java JDK 安裝 官方建議使用Ubuntu18以及更新的環境建議裝Java 11 Ubuntu16以及更舊版本則安裝Java 8 ```linux sudo apt-get install openjdk-11-jdk ``` 參考資料: - https://wiki.onosproject.org/display/ONOS/Requirements ### 設置環境變數 ```linux sudo gedit /etc/profile ``` 會跳出ubuntu內建文件編輯器,在裡面新增: ```linux export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 export JRE_HOME=$JAVA_HOME/jre export CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH ``` 使用```java -version```測試是否成功 ## ONOS ### 下載ONOS ```shell git clone https://gerrit.onosproject.org/onos ``` #### 版本選擇 clone完成之後可以cd進onos資料夾 使用git checkout選擇版本 也可以不用另外選擇 這邊使用的是**2.4.0-rc1** ### Bazel環境安裝 Bazel是用來建置以及編譯ONOS所使用的工具 而這邊使用Bazelisk,Bazelisk可以自動幫你選擇最適合你ONOS版本的 Bazel版本,這邊使用的Bazelisk版本為1.4.0 #### 前置套件 Bazel會需要事先安裝下列套件 ``` sudo apt-get install g++ python ``` #### 下載Bazelisk ```linux wget https://github.com/bazelbuild/bazelisk/releases/download/v1.4.0/bazelisk-linux-amd64 chmod +x bazelisk-linux-amd64 sudo mv bazelisk-linux-amd64 /usr/local/bin/bazel cd ~/onos bazel version ``` ![](https://i.imgur.com/LLuZblB.png) 這邊看到它安裝的是Bazel 3.0.0 參考資料: - https://wiki.onosproject.org/display/ONOS/Installing+required+tools ### 編譯ONOS 接下來可以開始編譯ONOS,**這邊會跑很久** ``` cd ~/onos bazel build onos ``` ## Mininet ### 下載Mininet ``` git clone git://github.com/mininet/mininet ``` 這邊和ONOS一樣可以git checkout選擇版本 這邊使用的是**2.3.0d6** ### 安裝 ``` cd mininet/util/ sudo ./install.sh ``` 完成後可以測試安裝是否成功 ``` sudo mn --test pingall ``` ## 整合與應用 ### 執行ONOS 這邊會跑一段時間 ```linux cd ~/onos bazel run onos-local ``` ONOS很方便的地方就是它有GUI可以使用 等前一步驟跑起來不再有執行訊息之後可以開啟瀏覽器 連上 http://localhost:8181/onos/ui 帳號密碼輸入預設的 *onos*/*rocks* 進入後你會看到拓樸頁面是空的 這時候就要透過Mininet建立拓樸 並把Controller指定為onos 但在這之前,需要先設定ONOS的App 以下有兩種方法: #### 1. ONOS Web GUI 進入ONOS GUI之後點選左上角選單進入應用程式 並個別搜尋: 1. **Basic Pipeline** 2. **Reactive Forwarding** 3. **OpenFlow Provider Suite** 點選之後又方會彈出詳細資訊,並點選右上角"啟動"按鈕 ![](https://i.imgur.com/BsAELBa.png) :::warning 注意: 1. 啟動Basic Pipeline之後ONOS會執行一段時間,期間Web GUI無法使用 2. 要開啟openflow這個app才找讀到mininet所建立的device和拓樸 ::: #### 2. ONOS CLI 進入終端機執行: (看你onos clone在哪裡就把~/onos取代成"<那個路徑>/onos") ``` ~/onos/tools/test/bin/onos localhost ``` 這邊如果出現需要密碼的話,可以改輸入: ``` ~/onos/tools/test/bin/onos localhost -l onos ``` 來指定使用者,並輸入密碼*rocks*來進入CLI ![](https://i.imgur.com/mmWibgY.png) 接著再執行: ```sh app activate org.onosproject.pipelines.basic app activate org.onosproject.fwd app activate org.onosproject.openflow ``` 要離開CLI可以按Ctrl+D或輸入logout ### Mininet建立自訂拓樸 在mininet中支援使用python自己建立的拓樸 以下先建立我們自訂的拓樸檔 ```linux vim mytopo.py ``` 貼上 ```= #!/usr/bin/python from mininet.topo import Topo class MyTopo( Topo ): def __init__( self ): # Initialize topology Topo.__init__( self ) # Add hosts and switches LHost = self.addHost( 'h1' ) RHost = self.addHost( 'h2' ) TSwitch = self.addSwitch( 's1' ) LSwitch = self.addSwitch( 's2' ) RSwitch = self.addSwitch( 's3' ) # Add links self.addLink( LHost, LSwitch ) self.addLink( LSwitch, TSwitch ) self.addLink( TSwitch, RSwitch ) self.addLink( RSwitch, RHost ) self.addLink( LSwitch, RSwitch ) topos = { 'mytopo': ( lambda: MyTopo() ) } ``` 參考資料: http://mininet.org/walkthrough/#custom-topologies ### 串接ONOS及Mininet 創建好自訂拓樸之後可以使用mininet創建它 在終端機輸入: ```sh sudo mn --mac --custom mytopo.py --topo mytopo --controller=remote,ip=127.0.0.1 ``` > --mac讓設備安排的mac address照順序,不加的話會是隨機mac > --custom 為指定使用自訂的python拓樸 > --topo為指定該拓樸檔中的哪一個拓樸class,即mytopo.py中line 25所定義的dict key 'mytopo' > --controller定義所使用的controller位址,因為我們ONOS建立在localhost,於是輸入127.0.0.1即可 :::warning Mininet使用remote controller時預設會監聽controller的port 6653或6633 需要修改的話可以改成ip=x.x.x.x,port=<...> ::: 此時你要看到執行ONOS的終端機開始輸出訊息,並在最後出現: ![](https://i.imgur.com/y1tilCe.png) 即代表ONOS成功讀取到了Mininet建立的拓樸 此時進入ONOS Web GUI 會發現在拓樸頁面出現了三個Switch,按下鍵盤**H**鍵可以開啟/關閉顯示Host 這時你會發現沒看到Host 這是因為我們所建立的h1、h2還沒有流量產生,所以ONOS的controller還沒找到Host的位置 此時回去mininet的終端機 執行```pingall``` ![](https://i.imgur.com/4IythxB.png) 可以發現ping成功了 而此時可以輸入 ```console mininet> h1 ping h2 -i 0.05 ``` -i 後面放的是一個數值,代表多久ping一次,單位是秒,在此為0.05秒,即50ms 你會看到mininet console不斷跑出ping的結果 這時回去ONOS Web GUI拓樸頁面 並按按鍵**A**,你會看到跑出流量監控的選項,可以持續點選**A**選到**封包/秒** 此時你會看到: ![](https://i.imgur.com/QcYUb0A.png) 可以看到流量預設是走Shortest Path 而現在我們要修改Flow Table 讓Switch把Packet route到較遠的路徑 ### 自訂Flow Table 接下來我們要自訂Flow Table ONOS上傳Flow Table的方式是使用REST API上傳 我們需要準備好規定格式的JSON檔: 可用欄位可以參考:https://wiki.onosproject.org/display/ONOS/Flow+Rules > vim 01g.json ```json= { "priority": 10, "timeout": 0, "isPermanent": true, "deviceId": "of:0000000000000001", "treatment": { "instructions": [ { "type": "OUTPUT", "port": "2" } ] }, "selector": { "criteria": [ { "type": "IN_PORT", "port": "1" }, { "type": "ETH_DST", "mac": "00:00:00:00:00:02" }, { "type": "ETH_SRC", "mac": "00:00:00:00:00:01" } ] } } ``` > vim 02g.json ```json= { "priority": 10, "timeout": 0, "isPermanent": true, "deviceId": "of:0000000000000002", "treatment": { "instructions": [ { "type": "OUTPUT", "port": "2" } ] }, "selector": { "criteria": [ { "type": "IN_PORT", "port": "1" }, { "type": "ETH_DST", "mac": "00:00:00:00:00:02" }, { "type": "ETH_SRC", "mac": "00:00:00:00:00:01" } ] } } ``` > vim 03g.json ```json= { "priority": 10, "timeout": 0, "isPermanent": true, "deviceId": "of:0000000000000003", "treatment": { "instructions": [ { "type": "OUTPUT", "port": "2" } ] }, "selector": { "criteria": [ { "type": "IN_PORT", "port": "1" }, { "type": "ETH_DST", "mac": "00:00:00:00:00:02" }, { "type": "ETH_SRC", "mac": "00:00:00:00:00:01" } ] } } ``` > vim 01f.json ```json= { "priority": 10, "timeout": 0, "isPermanent": true, "deviceId": "of:0000000000000001", "treatment": { "instructions": [ { "type": "OUTPUT", "port": "1" } ] }, "selector": { "criteria": [ { "type": "IN_PORT", "port": "2" }, { "type": "ETH_DST", "mac": "00:00:00:00:00:01" }, { "type": "ETH_SRC", "mac": "00:00:00:00:00:02" } ] } } ``` > vim 02f.json ```json= { "priority": 10, "timeout": 0, "isPermanent": true, "deviceId": "of:0000000000000002", "treatment": { "instructions": [ { "type": "OUTPUT", "port": "1" } ] }, "selector": { "criteria": [ { "type": "IN_PORT", "port": "2" }, { "type": "ETH_DST", "mac": "00:00:00:00:00:01" }, { "type": "ETH_SRC", "mac": "00:00:00:00:00:02" } ] } } ``` > vim 03f.json ```json= { "priority": 10, "timeout": 0, "isPermanent": true, "deviceId": "of:0000000000000003", "treatment": { "instructions": [ { "type": "OUTPUT", "port": "1" } ] }, "selector": { "criteria": [ { "type": "IN_PORT", "port": "2" }, { "type": "ETH_DST", "mac": "00:00:00:00:00:01" }, { "type": "ETH_SRC", "mac": "00:00:00:00:00:02" } ] } } ``` 接下來我們要寫一個把Flow Table上傳到ONOS的shell程式 > vim ./flow_onos.sh ```bash #!/bin/bash curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -u onos:rocks -d @01g.json 'http://localhost:8181/onos/v1/flows/of:0000000000000001?appId=SDN' curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -u onos:rocks -d @02g.json 'http://localhost:8181/onos/v1/flows/of:0000000000000002?appId=SDN' curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -u onos:rocks -d @03g.json 'http://localhost:8181/onos/v1/flows/of:0000000000000003?appId=SDN' curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -u onos:rocks -d @01f.json 'http://localhost:8181/onos/v1/flows/of:0000000000000001?appId=SDN' curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -u onos:rocks -d @02f.json 'http://localhost:8181/onos/v1/flows/of:0000000000000002?appId=SDN' curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -u onos:rocks -d @03f.json 'http://localhost:8181/onos/v1/flows/of:0000000000000003?appId=SDN' ``` 後方網址的格式為:<onos host>/onos/flows/<device id>?appId=<appId> device id 可以在拓樸中點選Switch則會在詳細資料中看到它的URI 接下來上傳rule: ```console chmod +x ./flow_onos.sh ./flow_onos.sh ``` 執行完如果成功則不會看到結果訊息 成功後進入ONOS Web GUI點選左上選單進入**Devices** 此時你會看到3個device,即是我們用mininet建立的3個Switch 點選**of:0000000000000001**這個只有3個port的device 並點選右上角亮起的選項中的*Show flow view for selected device* ![](https://i.imgur.com/fsYg311.png) 此時你會發現Flow Table中出現了App Name為**SDN**的rule 即為我們剛剛上傳的rule,且狀態為**Added** 此時回到拓樸頁面 照前面開啟mininet 每0.05秒ping一次並監控流量的方式 可以看到現在流量走向了上方的路徑: ![](https://i.imgur.com/xYdts81.png) 如此便完成路徑的控制了~ --- The end.