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