# 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
```

這邊看到它安裝的是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**
點選之後又方會彈出詳細資訊,並點選右上角"啟動"按鈕

:::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

接著再執行:
```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的終端機開始輸出訊息,並在最後出現:

即代表ONOS成功讀取到了Mininet建立的拓樸
此時進入ONOS Web GUI
會發現在拓樸頁面出現了三個Switch,按下鍵盤**H**鍵可以開啟/關閉顯示Host
這時你會發現沒看到Host
這是因為我們所建立的h1、h2還沒有流量產生,所以ONOS的controller還沒找到Host的位置
此時回去mininet的終端機
執行```pingall```

可以發現ping成功了
而此時可以輸入
```console
mininet> h1 ping h2 -i 0.05
```
-i 後面放的是一個數值,代表多久ping一次,單位是秒,在此為0.05秒,即50ms
你會看到mininet console不斷跑出ping的結果
這時回去ONOS Web GUI拓樸頁面
並按按鍵**A**,你會看到跑出流量監控的選項,可以持續點選**A**選到**封包/秒**
此時你會看到:

可以看到流量預設是走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*

此時你會發現Flow Table中出現了App Name為**SDN**的rule
即為我們剛剛上傳的rule,且狀態為**Added**
此時回到拓樸頁面
照前面開啟mininet 每0.05秒ping一次並監控流量的方式
可以看到現在流量走向了上方的路徑:

如此便完成路徑的控制了~
---
The end.