---
# System prepended metadata

title: 網路模擬與分析(3/15)： mn --link、mn --wifi 介紹 & 用 Python 程式撰寫 Mininet 的環境
tags: [gnuplot, Containernet, Iperf, P4, Mininet-Wifi, Mininet]

---

# 網路模擬與分析(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 進行測試

![](https://i.imgur.com/LiAFqha.png)

**我們可以看到說，每一筆封包 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)。**

![](https://i.imgur.com/a8x1WjJ.png)

> 因此若 loss rate 設成2，封包的遺失率會為8%
**“1-(98%x98%x98%x98%)”**

**而每筆封包的 delay time 都約比4%(99%x99%x99%x99%)再多一點，是因為封包經過每個節點時會有 Processing time，因此這些數值要再加上去。**

---

### TCP/UDP Packet 比較

我們保留上面所設計的環境，來做個實驗

將 TCP/UDP 封包結果測出後，用 gnuplot 繪製成圖，並且進行比較

![](https://i.imgur.com/qwSRjt5.png)

> tee: 將結果顯示在螢幕並且儲存起來


Client 分別發送100個 TCP/UDP 封包到 Server 上，其中 UDP 的封包 bandwidth 我們設為3M。

![](https://i.imgur.com/7WNt71S.png)

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

![](https://i.imgur.com/c9Vkr1M.png)

接著我們可以透過上週教過的 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"
```

![](https://i.imgur.com/ikFn9gf.png)

---

## 透過 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，拓樸如下：

![](https://i.imgur.com/TzyPs2t.png)

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

![](https://i.imgur.com/hnhKlIY.png)

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

![](https://i.imgur.com/Hw4uGl6.png)

> 當 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 所構成的。

![](https://i.imgur.com/Fhtdds1.png)

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

![](https://i.imgur.com/qmD19JB.png)

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

![](https://i.imgur.com/HDAM4oW.png)


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

![](https://i.imgur.com/I0dh5jm.png)

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

![](https://i.imgur.com/5FQsDgA.png)

![](https://i.imgur.com/uRtiybV.png)

因此透過 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 的情形，還提供了動態分析圖去觀看。

![](https://i.imgur.com/eVnaAyi.png)

> 右方的分析圖適合用於車載、物聯網等研究

---

### 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台不同且獨立的主機。

![](https://i.imgur.com/ss4ZsCR.png)

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

![](https://i.imgur.com/1KqYEBB.png)

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

![](https://i.imgur.com/nS7mBFo.png)


而我們可以測試一下兩台容器的通訊狀況：

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