# 網路模擬與分析(3/08):透過 Python 進行自動化運維 & 在 Mininet 中使用 Iperf (封包測量工具) + gnuplot (繪圖套件)
###### tags: `Mininet`、`Iperf`、`Ansible`、`EVE-NG`、`gnuplot`
## 安裝 python 套件 : pip & 安裝 paramiko
安裝 ppa
```
add-apt-repository ppa:deadsnakes/ppa
```
更新套件&升級套件
```
apt-get update && apt-get upgrade -y
```
檢查 python 版本
```
root@vm2:/home/user# python3 --version
Python 3.5.2
```
使用`curl`下載 pip 安裝包
```
root@vm2:/home/user# curl https://bootstrap.pypa.io/pip/3.5/get-pip.py -o get-pip.py
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1863k 100 1863k 0 0 283k 0 0:00:06 0:00:06 --:--:-- 460k
```
並使用`sudo python3 get-pip.py --force-reinstall`進行安裝
**若有出現以下問題,代表你下載的套件包與 python 版本不符,需要下載正確的版本**
```
root@vm2:/home/user# sudo python3 get-pip.py --force-reinstall
ERROR: This script does not work on Python 3.5 The minimum supported Python version is 3.6. Please use https://bootstrap.pypa.io/pip/3.5/get-pip.py instead.
```
下載 paramiko
```
pip install paramiko
```
---
## 使用 EVE-NG 建立拓樸
在VMWare 開啟 EVE 虛擬機,並登入給予的網址`192.168.102.158`

在瀏覽器輸入該網址並建立一個 lab,即可看到以下畫面。

新增一個節點 Router,其配置如下

新增一個網路,其配置如下

最後我們在新增2個節點,而該拓樸圖如下

---
### 設定 R1 的 dhcp/ssh 功能
將 Router 設定成 dhcp,並且查看該 IP
```
Router>en
Router#conf t
Router(config)#hostname R1
R1(config)#int e0/2
R1(config-if)#ip addr dhcp
R1(config-if)#no sh
R1(config-if)#
*Dec 31 23:01:48.082: %DHCP-6-ADDRESS_ASSIGN: Interface Ethernet0/2 assigned DHCP address 192.168.102.159, mask 255.255.255.0, hostname R1
R1(config-if)#do show ip int brief
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 unassigned YES unset administratively down down
Ethernet0/1 unassigned YES unset administratively down down
Ethernet0/2 192.168.102.159 YES DHCP up up
Ethernet0/3 unassigned YES unset administratively down down
R1(config-if)#
Translating "pnpntpserver.localdomain"...domain server (192.168.102.2)
```
開啟一台虛擬機,與 R1 進行 ping 的傳輸
```
root@vm2:/home/user# ping 192.168.102.159
PING 192.168.102.159 (192.168.102.159) 56(84) bytes of data.
64 bytes from 192.168.102.159: icmp_seq=1 ttl=255 time=0.903 ms
64 bytes from 192.168.102.159: icmp_seq=2 ttl=255 time=2.87 ms
^C
--- 192.168.102.159 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.903/1.888/2.874/0.986 ms
root@vm2:/home/user#
```
回到 R1 設定 ssh 的帳號、密碼
```
R1(config-if)#username cisco privilege 15 password ci
sco
R1(config)#ip domain-name test.com
R1(config)#crypto key generate rsa
The name for the keys will be: R1.test.com
Choose the size of the key modulus in the range of 360 to 4096 for your
General Purpose Keys. Choosing a key modulus greater than 512 may take
a few minutes.
How many bits in the modulus [512]: 1024
% Generating 1024 bit RSA keys, keys will be non-exportable...
[OK] (elapsed time was 0 seconds)
R1(config)#
*Dec 31 23:11:23.368: %SSH-5-ENABLED: SSH 1.99 has been enabled
R1(config)#ip ssh version 2
R1(config)#line vty 0 4
R1(config-line)#login local
R1(config-line)#transport input ssh
R1(config-line)#
```
設定完後,可以回到虛擬機進行 ssh 測試
```
root@vm2:/home/user# ssh cisco@192.168.102.159
The authenticity of host '192.168.102.159 (192.168.102.159)' can't be established.
RSA key fingerprint is SHA256:RWmcEorbsP+ib7reGDGQNFogzE3rFRt8GMev9E36eMQ.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.102.159' (RSA) to the list of known hosts.
Password:
Password:
R1#
```
---
## 透過 ansible 進行多台 host 的部署
### 在 ubuntu 下載 Ansible
下載 ansible 相關指令
```
$ sudo apt update
$ sudo apt install software-properties-common
$ sudo add-apt-repository --yes --update ppa:ansible/ansible
$ sudo apt install ansible
```
下載完後,在 EVE 建立該拓樸,由於上一個實驗有先建立相同的拓樸,我們只需要重啟 R1,並重新設定 ssh 規則

設定完後,再重新進行 ssh 連線,接著就會發生以下問題。
```
root@vm2:/home/user# ssh cisco@192.168.102.159
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
SHA256:qeGdNCj60VTspA1GqXjpny0PGmd0vPO+IO6TWQsH9B8.
Please contact your system administrator.
Add correct host key in /root/.ssh/known_hosts to get rid of this message.
Offending RSA key in /root/.ssh/known_hosts:3
remove with:
ssh-keygen -f "/root/.ssh/known_hosts" -R 192.168.102.159
RSA host key for 192.168.102.159 has changed and you have requested strict checking.
Host key verification failed.
```
> 解法: ssh-keygen -f "/root/.ssh/known_hosts" -R 192.168.102.159
確認連線成功並`exit`後,先創建一個資料夾`cisco`,並且創建兩個檔案名叫 ‘hosts’, ‘config_cisco.yml’
1. 'hosts'
```
[cisco]
192.168.102.159 ansible_connection=local
```
> **設定要管理的機器,這邊我們使用的是"Router"**
2. 'config_cisco.yml'
```
- hosts: 192.168.102.159 # R1 的 ip
gather_facts: yes
vars:
- cisco_host_ip: 192.168.102.159
ssh_username: cisco
ssh_password: cisco
enable_open: yes
enable_password: cisco
roles:
- config_cisco
```
> **設定該機器的相關變數**
設定完之後,我們接著要根據 cicso_playbook 的格式進行設定

因此我們在當前目錄下再創建資料夾`config_cisco`,進入後再創建`tasks`去建立 ansible-playbook 的樹狀圖
再當前目錄下建立檔案 ‘main.yml’
3. 'main.yml'
```
- name: cisco_description_ssh_certification
set_fact:
cisco_verification:
host: "{{cisco_host_ip}}"
username: "{{ssh_username}}"
password: "{{ssh_password}}"
authorize: "{{enable_open}}"
auth_pass: "{{enable_password}}"
- name: config e0/0
ios_config:
provider: "{{cisco_verification}}"
parents: interface Ethernet 0/0
lines:
- ip addr 192.168.1.254 255.255.255.0
- no shut
- name: config e0/1
ios_config:
provider: "{{cisco_verification}}"
parents: interface Ethernet 0/1
lines:
- ip addr 192.168.2.254 255.255.255.0
- no shut
- name: show ip interface
ios_command:
provider: "{{cisco_verification}}"
commands: show ip interface brief
register: show_ip_log
- name: show_ip_log
debug:
var: show_ip_log.stdout_lines
with_items: show_ip_log.results
```
> **根據 Router 下的 Hosts 去管理多台主機的配置&設定**
儲存後,回到`cisco`的路徑下,使用`tree`查看該樹狀圖
```
root@vm2:/home/user/cisco/config_cisco# cd ..
root@vm2:/home/user/cisco# tree
.
├── config_cisco
│ └── tasks
│ └── main.yml
├── config_cisco.yml
└── hosts
2 directories, 3 files
```
> **若未安裝 `tree`,可使用`apt install tree`**
使用`ansible-playbook config_cisco.yml -i hosts`進行配置
```
PLAY [192.168.102.159] *********************************************************
TASK [Gathering Facts] *********************************************************
[DEPRECATION WARNING]: Distribution Ubuntu 16.04 on host 192.168.102.159 should
use /usr/bin/python3, but is using /usr/bin/python for backward compatibility
with prior Ansible releases. A future Ansible release will default to using the
discovered platform python for this host. See https://docs.ansible.com/ansible
/2.9/reference_appendices/interpreter_discovery.html for more information. This
feature will be removed in version 2.12. Deprecation warnings can be disabled
by setting deprecation_warnings=False in ansible.cfg.
ok: [192.168.102.159]
TASK [config_cisco : cisco_description_ssh_certification] **********************
ok: [192.168.102.159]
TASK [config_cisco : config e0/0] **********************************************
changed: [192.168.102.159]
TASK [config_cisco : config e0/1] **********************************************
changed: [192.168.102.159]
TASK [config_cisco : show ip interface] ****************************************
ok: [192.168.102.159]
TASK [config_cisco : show_ip_log] **********************************************
ok: [192.168.102.159] => (item=show_ip_log.results) => {
"ansible_loop_var": "item",
"item": "show_ip_log.results",
"show_ip_log.stdout_lines": [
[
"Interface IP-Address OK? Method Status Protocol",
"Ethernet0/0 192.168.1.254 YES manual up up ",
"Ethernet0/1 192.168.2.254 YES manual up up ",
"Ethernet0/2 192.168.102.159 YES DHCP up up ",
"Ethernet0/3 unassigned YES unset administratively down down"
]
]
}
PLAY RECAP *********************************************************************
192.168.102.159 : ok=6 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
```
接著可以回到 Router 進行配置檔是否成功?
```
R1(config)#do show ip int brief
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 192.168.1.254 YES manual up up
Ethernet0/1 192.168.2.254 YES manual up up
Ethernet0/2 192.168.102.159 YES DHCP up up
Ethernet0/3 unassigned YES unset administratively down down
```
---
## 在 Mininet 環境下使用 Iperf + gnuplot
若未安裝 mininet 可以用`sudo apt-get install mininet`
在家目錄創建資料夾`mininet`,進入後使用`mn`創建拓樸
```
root@vm2:/home/user/mininet# mn
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2
*** Adding switches:
s1
*** Adding links:
(h1, s1) (h2, s1)
*** Configuring hosts
h1 h2
*** Starting controller
c0
*** Starting 1 switches
s1 ...
*** Starting CLI:
mininet-wifi>
```
1. net:查看拓樸資訊
```
h1 h1-eth0:s1-eth1
h2 h2-eth0:s1-eth2
s1 lo: s1-eth1:h1-eth0 s1-eth2:h2-eth0
c0
```
2. xterm s1 h1 h2:在 mininet 開啟 s1, h1, h2 虛擬機


> 預設 IP 是 `10.0.0.0/24` 網域
3. pingall:測試該拓樸連線
```
mininet-wifi> pingall
*** Ping: testing ping reachability
h1 -> h2
h2 -> h1
*** Results: 0% dropped (2/2 received)
```
4. h1 ping 10.0.0.2 -c 3:在 Mininet 環境下進行封包測試

> `-c(count): + [變數]`
---
### Mininet 進行 HTTP 測試
* 對於 Mininet 的環境下,每一個 Host 都可以視為獨立的主機,因此我們可以做一個簡單的測試。
將 h1 設為 HTTPServer,使用該指令`python -m SimpleHTTPServer 80`

而 h2 可以切換成一般使用者進行 firefox 的測試,去查看 HTTPServer。

h1 的網頁伺服器情形

5. 我們也可以透過封包傳遞的過程,去執行`link h1 s1 down`來模擬網路連線中斷的情形。

使用`link h1 s1 up`可以將 link 重新連線,因此可以看到封包中斷後又重新發送封包到 h2 了。

6. 在 Mininet 執行終端機的內容,可以在 mininet 使用`sh + command`,ex:`sh pwd`

---
### Iperf 進行網路效能檢測
Iperf 一共有兩種傳輸方式:TCP/UDP,在實驗中,我們將 h1 當作 Client,h2 當作 Server。
1. TCP 封包
h2 [server] 執行`iperf -s -i 1`
> `-s`: server
> `-i(interval) 1`: 計算吞吐量,週期是一秒一次
h1 [client] 執行`iperf -c 10.0.0.2 -t 15`
> `-c`: client
> `10.0.0.2`: 指定 server 的 ip,這邊我們使用 h2 的 ip
> -t(time/duration): 根據傳輸時間去發送 TCP 封包,預設值是10秒,這邊我們設定15秒。

當 Client 發送後,可以看到 Server 收到了每一秒封包的值,還有0-15秒封包平均值。

2. UDP 封包
h2 [server] 執行`iperf -s -i 1 -u`
> `-u`: 指定 UDP 封包
h1 [client] 執行`iperf -c 10.0.0.2 -t 15 -u -b 100M`
> `-u`: 指定 UDP 封包
> `-b(badnwidth) 100M`: 封包傳送速率為100M
> * TCP 與 UDP 不同在於,TCP 是根據你網路的傳輸速率有多快就會給多快。

我們回到 Server,一樣可以看到每筆封包的數值以及平均。

3. 多台 Server 設定
假設你的環境需要多個 Server 測試封包的流量,你可以使用`-p`去區分每個 Server 所屬的埠號。
因此我們可以在 Mininet 在新增一台 h1, h2,使用`xterm h1 h2`
h2 [server] 執行`iperf -s -t 1 -u -p 5555`
h2 [server] 執行`iperf -s -t 1 -u -p 6666`

因此現在2台 Server 有了不同的 port 號後,我們的 Client 就可以對於不同的 Server 去進行連線。

---
### 將數值利用 gnuplot 進行繪圖
上一個實驗我們利用了 Iperf 取得了封包的數值,若你想將數值繪成圖,便可以使用這個套件 gnuplot 將數值繪圖起來。
但是在繪圖之前,我們需要將數據記錄起來,因此我們可以使用`>`將數據導向到所指定的檔案內。
h2 [server] 執行`iperf -s -t 1 -u -p 5555 > udp5555`
> udp5555: 所指定的檔案 ‘udp5555’
而 h1 一樣發送 UDP 封包到 Server`iperf -c 10.0.0.2 -t 10 -b 100M -p 5555`
當 h1 結束後,h2 使用`ctrl + C`結束執行,並且查看 ‘udp5555’ 的檔案內是否有 UDP 的資料?

結果可以看到資料進來了,代表 UDP 封包的數據已經記錄起來了

**接著我們可以使用3個 linux 的文字處理:grep, tr, awk 去取得數據的時間跟吞吐量。**
我們可以從 h2 得知數據後,再近一步的過濾出想要的結果。
因此 “udp5555” 過濾結果的過程會如下方所示
```
1. cat udp5555 | grep sec
// 過濾出“時間”、“吞吐量”共通有的特徵
2. cat udp5555 | grep sec | head -n 20
// 取前20筆資料
3. cat udp5555 | grep sec | head -n 20 | tr "-" " "
// 用空格取代每一筆時間軸的 “-”
4. cat udp5555 | grep sec | head -n 20 | tr "-" " " | awk '{print $4, $8}'
// 取出“時間”、“吞吐量”的數值
5. cat udp5555 | grep sec | head -n 20 | tr "-" " " | awk '{print $4, $8}' > result.txt
// 將結果映射到 “result.txt”
```
取出結果後,我們可以在終端機使用`gnuplot`的指令進入到 ‘gnuplot’ 內
```
root@vm2:/home/user# gnuplot
G N U P L O T
Version 5.0 patchlevel 3 last modified 2016-02-21
Copyright (C) 1986-1993, 1998, 2004, 2007-2016
Thomas Williams, Colin Kelley and many others
gnuplot home: http://www.gnuplot.info
faq, bugs, etc: type "help FAQ"
immediate help: type "help" (plot window: hit 'h')
Terminal type set to 'qt'
gnuplot>
```
接著選擇 result.txt 進行繪圖`plot "result.txt"`
接著依照你的結果去設定 x 軸, y 軸,以我的例子是:
```
set yrange [0:30]
set ytics 0,5,30
replot
set xrange [0:20]
set xtics 0,1,20
replot
plot "result.txt" with linespoints
set xlabel "tine (sec)"
set ylabel "throughput (Mbis/sec)"
replot
set title "udp throughput"
replot
```
> `xrange`,`yrange`: x 軸, y 軸參數的範圍
> `xtics`,`ytics`: x 軸, y 軸每個參數之間的間隔
> `xlabel`,`ylabel`: x 軸, y 軸的 label
> `title`: 圖片的 title
> `replot`: 更新
繪製完圖片後,將格式轉為 gif 檔`set terminal "gif"`
將圖片儲存成 gif 檔`set output "result.gif"`,並且`replot`。
最後你就可以得到一份完整的結果圖了

---
## Reference
1. iperf intro : https://m1016c.pixnet.net/blog/post/145780230
2. iperf command : https://linuxhint.com/iperf_command_usage/
3. p4-bmv2 download : https://github.com/p4lang/behavioral-model
4. p4c download : https://github.com/p4lang/p4c