# 網路模擬與分析(3/15): mn --link、mn --wifi 介紹 & 用 Python 程式撰寫 Mininet 的環境
###### tags: `Mininet`、`Iperf`、`gnuplot`、`Mininet-Wifi`、`Containernet`、`P4`
## mn 參數補充
### link
上週 Mininet 所使用到的`mn`可以建立基本的拓樸,但由於是**封閉式**的環境,再加上每條 link 都沒有 “bandwidth”、“loss rate”,要模擬出真實的環境來說是不太可靠,因此為了在 mininet 環境中模擬出更真實的樣子,`mn`支援了其他的用法,例如: link
```
mn --link=tc,bw=10,delay='1ms',loss=0
```
> 1. tc(traffic control): 流量控制
> 2. mininet 透過 tc 去控制 bandwidth, delay, loss 等...,而其中 link 是將 tc 的功能套用到 mininet 環境中。
因此 mininet 的環境中,每條 link 都具有 bandwidth, delay, loss 等效果。
而我們進到 mininet 的環境後,使用`xterm h1 h2`開啟 h1, h2 的終端機
```
root@vm2:/home/user/mininet# mn --link=tc,bw=10,delay='1ms',loss=0
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2
*** Adding switches:
s1
*** Adding links:
(10.00Mbit 1ms delay 0.00000% loss) (10.00Mbit 1ms delay 0.00000% loss) (h1, s1) (10.00Mbit 1ms delay 0.00000% loss) (10.00Mbit 1ms delay 0.00000% loss) (h2, s1)
*** Configuring hosts
h1 h2
*** Starting controller
c0
*** Starting 1 switches
s1 ...(10.00Mbit 1ms delay 0.00000% loss) (10.00Mbit 1ms delay 0.00000% loss)
*** Starting CLI:
mininet-wifi> xterm h1 h2
```
接著可以使用上週的 Iperf 進行測試

**我們可以看到說,每一筆封包 bandwidth 的值只會接近10,原因是封包會扣除 header,且封包在傳遞過程中也會造成一些消耗。**
接著我們可以再做另一個實驗,將 loss rate 改成1
使用`mn --link=tc,bw=10,delay='1ms',loss=1`
進到環境後,使用`h1 ping -c 1000 -i 0.01 h2`
```
root@vm2:/home/user/mininet# mn --link=tc,bw=10,delay='1ms',loss=1
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2
*** Adding switches:
s1
*** Adding links:
(10.00Mbit 1ms delay 1.00000% loss) (10.00Mbit 1ms delay 1.00000% loss) (h1, s1) (10.00Mbit 1ms delay 1.00000% loss) (10.00Mbit 1ms delay 1.00000% loss) (h2, s1)
*** Configuring hosts
h1 h2
*** Starting controller
c0
*** Starting 1 switches
s1 ...(10.00Mbit 1ms delay 1.00000% loss) (10.00Mbit 1ms delay 1.00000% loss)
*** Starting CLI:
mininet-wifi> h1 ping -c 1000 -i 0.01 h2
```
> 1. -i(interval) 0.01: 封包與封包之間的間隔,**其中這個參數只能在 linux 環境中使用,且只有 root 能使用。**
> 2. 封包每一秒會送100個
```
(以上省略)
64 bytes from 10.0.0.2: icmp_seq=984 ttl=64 time=4.62 ms
64 bytes from 10.0.0.2: icmp_seq=985 ttl=64 time=4.48 ms
64 bytes from 10.0.0.2: icmp_seq=986 ttl=64 time=4.65 ms
64 bytes from 10.0.0.2: icmp_seq=987 ttl=64 time=4.46 ms
64 bytes from 10.0.0.2: icmp_seq=988 ttl=64 time=4.13 ms
64 bytes from 10.0.0.2: icmp_seq=989 ttl=64 time=4.41 ms
64 bytes from 10.0.0.2: icmp_seq=990 ttl=64 time=4.86 ms
64 bytes from 10.0.0.2: icmp_seq=991 ttl=64 time=4.16 ms
64 bytes from 10.0.0.2: icmp_seq=992 ttl=64 time=4.13 ms
64 bytes from 10.0.0.2: icmp_seq=993 ttl=64 time=4.18 ms
64 bytes from 10.0.0.2: icmp_seq=994 ttl=64 time=4.46 ms
64 bytes from 10.0.0.2: icmp_seq=995 ttl=64 time=4.34 ms
64 bytes from 10.0.0.2: icmp_seq=996 ttl=64 time=4.27 ms
64 bytes from 10.0.0.2: icmp_seq=997 ttl=64 time=4.10 ms
64 bytes from 10.0.0.2: icmp_seq=998 ttl=64 time=4.34 ms
64 bytes from 10.0.0.2: icmp_seq=999 ttl=64 time=4.18 ms
64 bytes from 10.0.0.2: icmp_seq=1000 ttl=64 time=4.74 ms
--- 10.0.0.2 ping statistics ---
1000 packets transmitted, 952 received, 4% packet loss, time 10839ms
rtt min/avg/max/mdev = 4.075/4.534/21.079/0.685 ms, pipe 2
mininet-wifi>
--------------------------------------------------------------------
(以上省略)
64 bytes from 10.0.0.2: icmp_seq=976 ttl=64 time=4.24 ms
64 bytes from 10.0.0.2: icmp_seq=977 ttl=64 time=4.23 ms
64 bytes from 10.0.0.2: icmp_seq=978 ttl=64 time=4.29 ms
64 bytes from 10.0.0.2: icmp_seq=979 ttl=64 time=4.67 ms
64 bytes from 10.0.0.2: icmp_seq=981 ttl=64 time=5.36 ms
64 bytes from 10.0.0.2: icmp_seq=982 ttl=64 time=4.23 ms
64 bytes from 10.0.0.2: icmp_seq=983 ttl=64 time=7.56 ms
64 bytes from 10.0.0.2: icmp_seq=984 ttl=64 time=4.22 ms
64 bytes from 10.0.0.2: icmp_seq=985 ttl=64 time=4.62 ms
64 bytes from 10.0.0.2: icmp_seq=986 ttl=64 time=4.61 ms
64 bytes from 10.0.0.2: icmp_seq=988 ttl=64 time=4.46 ms
64 bytes from 10.0.0.2: icmp_seq=989 ttl=64 time=4.19 ms
64 bytes from 10.0.0.2: icmp_seq=990 ttl=64 time=4.14 ms
64 bytes from 10.0.0.2: icmp_seq=991 ttl=64 time=4.53 ms
64 bytes from 10.0.0.2: icmp_seq=992 ttl=64 time=4.62 ms
64 bytes from 10.0.0.2: icmp_seq=993 ttl=64 time=4.34 ms
64 bytes from 10.0.0.2: icmp_seq=994 ttl=64 time=4.29 ms
64 bytes from 10.0.0.2: icmp_seq=995 ttl=64 time=4.18 ms
64 bytes from 10.0.0.2: icmp_seq=996 ttl=64 time=4.09 ms
64 bytes from 10.0.0.2: icmp_seq=997 ttl=64 time=4.22 ms
64 bytes from 10.0.0.2: icmp_seq=998 ttl=64 time=4.59 ms
64 bytes from 10.0.0.2: icmp_seq=999 ttl=64 time=4.19 ms
64 bytes from 10.0.0.2: icmp_seq=1000 ttl=64 time=4.22 ms
--- 10.0.0.2 ping statistics ---
1000 packets transmitted, 961 received, 3% packet loss, time 10718ms
rtt min/avg/max/mdev = 4.078/4.470/8.695/0.390 ms
mininet-wifi>
--------------------------------------------------------------------
```
**可以看到2次測試的結果,loss rate 接近3~4%,因為每個 link 的值為99%,且 TCP 封包是雙向的,因此我們可以得出 “99%x99%x99%x99%”,將其算出約為0.9606,也就是96%(成功率),再用 “100%-96%” 會得出4%(loss rate)。**

> 因此若 loss rate 設成2,封包的遺失率會為8%
**“1-(98%x98%x98%x98%)”**
**而每筆封包的 delay time 都約比4%(99%x99%x99%x99%)再多一點,是因為封包經過每個節點時會有 Processing time,因此這些數值要再加上去。**
---
### TCP/UDP Packet 比較
我們保留上面所設計的環境,來做個實驗
將 TCP/UDP 封包結果測出後,用 gnuplot 繪製成圖,並且進行比較

> tee: 將結果顯示在螢幕並且儲存起來
Client 分別發送100個 TCP/UDP 封包到 Server 上,其中 UDP 的封包 bandwidth 我們設為3M。

跑完的資料在透過`>`將其儲存到 tcp.txt/udp.txt 檔,並且透過上週的 grep, tr, awk 去篩選我們要的結果,其結果最後長這樣。

接著我們可以透過上週教過的 gnuplot 去進行繪製
```
gnuplot> plot "tcp.txt" title "tcp-flow" with linespoints, "udp.txt" title "udp-flow" with linespoints
gnuplot> set xtics 0,1,20
gnuplot> set ytics 0,1,10
gnuplot> replot
gnuplot> set yrange [2:10]
gnuplot> set ytics 2,1,10
gnuplot> replot
gnuplot> set xlabel "time(sec)"
gnuplot> set ylabel "throughput(Mbps)"
gnuplot> replot
gnuplot> set title "tcp vs udp"
gnuplot> replot
gnuplot> set terminal gif
Terminal type set to 'gif'
Options are 'nocrop enhanced size 640,480 font "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf,12" '
gnuplot> set output "result.gif"
```

---
## 透過 Python 程式建立你的第一個 Mininet 環境 (ep.1)
我們創建一個腳本`1.py`
```
#!/usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
Link(h1, h2)
net.build()
CLI(net)
net.stop()
```
> 1. mininet 主要是由 python 語言所構成的,因此第一段是描述 python 的語法
> 2. CLI 是 Command-line interface,是構成 mininet 環境中的 ‘mininet-wifi>’ 介面
> 3. TClink 將各個接口對應起來
> 4. 各節點透過`net.addHost`去產生所需的節點
儲存後,使用`python 1.py`進入 mininet 環境中的去測試 ping 封包
```
root@vm2:/home/user/mininet/examples# python 1.py
mininet> h1 ping h2 -c 3
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.071 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.048 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.043 ms
--- 10.0.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.043/0.054/0.071/0.012 ms
```
> 可以看到當沒有修改節點的 IP 時,會使用預設的 IP`10.0.0.0/24`
我們將第一個程式複製一份為`2.py`,並新增一個節點 R1,拓樸如下:

```
#!/usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
r1 = net.addHost('r1')
Link(h1, r1)
Link(r1, h2)
net.build()
CLI(net)
net.stop()
```
儲存後,我們執行`python 2.py`,並且去測試 ping 封包,結果會看到 h1 封包 ping 不過去,**代表說我們的`2.py`少了封包處理的規則,且對於各節點的 IP 並沒有設置**,因此我們可以先進行手動的增設,最後在加進 code 內。
```
root@vm2:/home/user/mininet/examples# python 2.py
mininet> h1 ping h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
^C
--- 10.0.0.2 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
```
因此對於 h1, h2 而言,我們首先要新增一個 IP 在 h1-ethernet, h2-ethernet,可以使用
```
[h1]
ifconfig h1-eth0 0
// 將 h1 網卡清空
ip addr add 192.168.1.1/24 brd + dev h1-eth0
// 新增 IP 到 h1 的網卡
[h2]
ifconfig h2-eth0 0
// 將 h2 網卡清空
ip addr add 192.168.2.1/24 brd + dev h2-eth0
// 新增 IP 到 h2 的網卡
```
當 IP 設定好之後,接著去增設路由表規則`ip route`
我們可以使用
```
[h1]
ip route add default via 192.168.1.254
// h1 新增預設路由表規則為 192.168.1.254
[h2]
ip route add default via 192.168.2.254
// h2 新增預設路由表規則為 192.168.2.254
```
而對於 R1 而言,也要修改 r1-ethernet 的 IP
```
[r1]
ifconfig r1-eth0 0
ifconfig r1-eth1 0
ip addr add 192.168.1.254/24 brd + dev r1-eth0
ip addr add 192.168.2.254/24 brd + dev r1-eth1
```
檢查 R1 路由表規則
```
ip route show
192.168.1.0/24 dev r1-eth0 proto kernel scope link src 192.168.1.254
192.168.2.0/24 dev r1-eth1 proto kernel scope link src 192.168.2.254
```
特別注意的是,R1 另外要檢查路由規則是否開啟,若值為0則需要使用`echo 1 > /proc/sys/net/ipv4/ip_forward`將路由規則打開。
```
cat /proc/sys/net/ipv4/ip_forward
1
```

因此我們可以進行 ping 測試,可以看到 h1 ping h2 是可以成功了。

> 當 ping 不通時,可以先 ping 自己,在 ping 內定路由器,最後在 ping 目標主機,以這樣的方式去進行除錯。
因此`2.py`經過修改後會變這樣
```
#!/usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
r1 = net.addHost('r1')
Link(h1, r1)
Link(h2, r1)
net.build()
h1.cmd("ifconfig h1-eth0 0")
h1.cmd("ip addr add 192.168.1.1/24 brd + dev h1-eth0")
h1.cmd("ip route add default via 192.168.1.254")
h2.cmd("ifconfig h2-eth0 0")
h2.cmd("ip addr add 192.168.2.1/24 brd + dev h2-eth0")
h2.cmd("ip route add default via 192.168.2.254")
r1.cmd("ifconfig r1-eth0 0")
r1.cmd("ifconfig r1-eth1 0")
r1.cmd("ip addr add 192.168.1.254/24 brd + dev r1-eth0")
r1.cmd("ip addr add 192.168.2.254/24 brd + dev r1-eth1")
r1.cmd("echo 1 > /proc/sys/net/ipv4/ip_forward")
CLI(net)
net.stop()
```
> h1.cmd: 在 h1 終端機下 command
---
## 介紹完 Mininet 之後還會上什麼?
### 1. 簡單介紹 Mininet-wifi
使用`mn --wifi`建立 wireless 的環境,比較不同的是,在無線網路的環境中,是由 ap 以及 station 所構成的。

```
root@vm2:/home/user/mininet/examples# mn --wifi
*** Creating network
*** Adding controller
*** Adding stations:
sta1 sta2
*** Adding access points:
ap1
*** Configuring wifi nodes...
*** Adding link(s):
(sta1, ap1) (sta2, ap1)
*** Configuring nodes
*** Starting controller(s)
c0
*** Starting switches and/or access points
ap1 ...
*** Starting CLI:
mininet-wifi> net
c0
sta1 sta1-wlan0: sta1-wlan0:
sta2 sta2-wlan0: sta2-wlan0:
ap1 lo: ap1-wlan1:wifi
mininet-wifi> sta1 ping sta2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.163 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.141 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.221 ms
^C
--- 10.0.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.141/0.175/0.221/0.033 ms
mininet-wifi>
```
與有線網路類似的是,當我們在使用 ifconfig 查看基本的 IP 設定時,也會有 default 值 10.0.0.1 的 IP。

而無線網路所使用的是`iwconfig`

另外我們可以透過`iw dev sta1-wlan0 scan`去查看無線網路的 “SSID”,由於現在的環境內只有一個 ap1,因此只會有 ap1 的資訊。

而 ap1 也有一些指令,例如`iw dev ap1-wlan1 station dump`去查看 station 的資訊。


因此透過 mininet-wifi,我們可以模擬許多無線網路的情境,在我們接著下一個實驗以前,我們可以先安裝 mininet-wifi。
在 github 將 mininet-wifi 的 package 下載下來
```
git clone https://github.com/intrig-unicamp/mininet-wifi
```
> 若尚未安裝 git,可以使用`apt install git`
接著進到 mininet-wifi 的資料夾下,使用`sudo util/install.sh -n`透過腳本進行安裝及設定,並將環境編譯成 mininet-wifi,其結果如下:
> -n: mininet-wifi dependencies
```
(以上省略)
Installed /usr/local/lib/python2.7/dist-packages/mininet_wifi-2.4.2-py2.7.egg
Processing dependencies for mininet-wifi==2.4.2
Searching for setuptools==20.7.0
Best match: setuptools 20.7.0
setuptools 20.7.0 is already the active version in easy-install.pth
Installing easy_install script to /usr/local/bin
Using /usr/lib/python2.7/dist-packages
Finished processing dependencies for mininet-wifi==2.4.2
/home/user/mininet-wifi /home/user/mininet-wifi
```
因此當你安裝好 mininet-wifi 時,可以進到 examples 目錄下,執行`python handover.py`去進行測試,除了可以測試 ping 的情形,還提供了動態分析圖去觀看。

> 右方的分析圖適合用於車載、物聯網等研究
---
### 2. 簡單介紹 Containernet (Dockernet)
mininet-wifi 課程上完之後,後面的課程會使用到 Dockernet/Containernet,因此也可以事先安裝 Containernet,安裝教學如下:
```
sudo apt-get install ansible git aptitude
git clone https://github.com/containernet/containernet.git
cd containernet/ansible
sudo ansible-playbook -i "localhost," -c local install.yml
cd ..
sudo make install
```
安裝成功後,可以使用`python3 ./setup.py install`編譯成 COnatinernet 的環境。
```
(以上省略)
Using /usr/local/lib/python2.7/dist-packages
Searching for idna==2.8
Best match: idna 2.8
Removing idna 2.0 from easy-install.pth file
Adding idna 2.8 to easy-install.pth file
Using /usr/local/lib/python2.7/dist-packages
Searching for chardet==3.0.4
Best match: chardet 3.0.4
Adding chardet 3.0.4 to easy-install.pth file
Installing chardetect script to /usr/local/bin
Using /usr/local/lib/python2.7/dist-packages
Searching for certifi==2019.11.28
Best match: certifi 2019.11.28
Adding certifi 2019.11.28 to easy-install.pth file
Using /usr/local/lib/python2.7/dist-packages
Finished processing dependencies for containernet==3.0
```
因此我們可以測試一些簡單的範例,例如:`examples/dockerhosts.py`
***在這邊要先補充一下,mininet 和 Containernet 的差別:**
***1. 以 mininet 的環境來說,來區分不同的 node 所使用的是 Network namespaces。***
***2. mininet 的檔案系統是共享的,因此在 mininet 的環境中,當你在架設多台 HTTP Server 時,你架設的 Server 所看到的網頁會是相同的。***
***3. 而 docker 可以視為是一台獨立的主機,因此除了網路命名空間隔離之外,還有其他像是 pid, ipc, mnt, uts 以及 user 等命名空間全部都隔離。***
***4. 因此 docker 能處理較複雜的環境,例如:同時處理多台 HTTP Server。***
接著可以我們使用`docker ps -a`去查看容器,可以看到透過 Containernet 建立了5台不同且獨立的主機。

而我們要進入其中一台容器可以使用`docker exec -it + 容器名稱 or ID bash`,我們以 mn.d1 為例子進入。

可以看到,d1 的 IP 為 10.0.0.251(預設),我們可以在開啟另一台終端機,並且進入到另一個容器,ex: mn.d2。

而我們可以測試一下兩台容器的通訊狀況:
```
root@d2:/# ping 10.0.0.251
PING 10.0.0.251 (10.0.0.251) 56(84) bytes of data.
64 bytes from 10.0.0.251: icmp_seq=1 ttl=64 time=5.87 ms
64 bytes from 10.0.0.251: icmp_seq=2 ttl=64 time=3.60 ms
64 bytes from 10.0.0.251: icmp_seq=3 ttl=64 time=0.743 ms
64 bytes from 10.0.0.251: icmp_seq=4 ttl=64 time=0.251 ms
^C
--- 10.0.0.251 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 0.251/2.617/5.873/2.274 ms
root@d2:/#
```
可以看到說兩個容器是可以互相通訊,且每一個容器都保留各自獨立的空間。
---
### 3. 簡單介紹 P4 language
P4 是一種程式語言,程式人員在開發時,可以透過撰寫 P4 語言去實現想要的功能,例如:hub, switch, firewall ...。
詳細的部分,例如程式組成,如何執行....,之後會在講解。
---
## Reference
1. https://www.sdnlab.com/11495.html
2. https://github.com/intrig-unicamp/mininet-wifi
3. https://matplotlib.org/1.4.2/faq/installing_faq.html
4. https://github.com/gmiotto/dockernet
5. http://csie.nqu.edu.tw/smallko/mininet-wifidockerp4.zip
6. https://github.com/containernet/containernet
7. https://github.com/intrig-unicamp/mininet-wifi
8. https://github.com/p4lang/p4c
9. https://github.com/p4lang/behavioral-model