# 網路模擬與分析(4/12): mininet 去實作出 SDN 功能(ovs) & 鏡像功能
###### tags: `Mininet`、`SDN`、`OpenFlow`、`OpenvSwitch`
## 透過不同路徑去實現封包傳輸的動作
### ARP & ICMP 封包傳輸方式
根據上週的實驗,我們可以得知,網路的通訊協定是先透過 ARP 封包去取得對方的網路卡卡號,其中 ARP 封包又可以分為 Request (Broadcast 的方式進行傳輸), Reply (unicast 的方式進行傳輸)。

值得注意的是,我們知道 ARP Request packet 是透過 broadcast 的方式傳輸,但是在較複雜的網路環境中,當封包進行傳輸時會因為 Switches (s1, s2, s3) 而充斥著廣播,因此很有可能會造成廣播的風暴。
因此我們藉由 SDN 的特性,可能將任意的規則放在所屬的節點上,而我們的環境中,因為得知每個節點的位置,因此在這個實驗的第一部分,我們將 ARP 封包的規則由 broadcast 更換成 unicast 的方式。

> 1. 當 ARP 封包送完之後,才開始傳 IP packet。
> 2. IP 的封包是需要 MAC Addr + IP Addr。
透過封包傳輸的特性我們可以得知,封包在傳輸時是 “一去一回” 的動作,而我們實驗的另一個部分要做的就是,透過以下的拓樸去進行封包傳輸,其中封包進行 Request 時是走上面的路徑,而當封包要回去時,是走下面的路徑。

---
### 初學者如何撰寫 ARP & ICMP 規則
我們要如何得知 ARP 和 ICMP 規則是怎麼寫的?
在開始實驗之前,我們可以先在終端機使用預設的環境`mm`,並執行`h1 ping h2 -c 1`
```
root@vm2:/home/user/mininet/ovs-test/lab1# 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> h1 ping h2 -c 1
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=4.94 ms
--- 10.0.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 4.949/4.949/4.949/0.000 ms
mininet-wifi>
```
接著開啟另一台終端機,執行`ovs-ofctl dump-flows s1`去查看規則
```
root@vm2:/home/user# ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=29.058s, table=0, n_packets=1, n_bytes=42, idle_timeout=60, idle_age=29, priority=65535,arp,in_port=2,vlan_tci=0x0000,dl_src=f6:99:8e:3a:fd:21,dl_dst=56:9d:6b:56:f9:96,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=2 actions=output:1
cookie=0x0, duration=24.051s, table=0, n_packets=1, n_bytes=42, idle_timeout=60, idle_age=24, priority=65535,arp,in_port=2,vlan_tci=0x0000,dl_src=f6:99:8e:3a:fd:21,dl_dst=56:9d:6b:56:f9:96,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=1 actions=output:1
cookie=0x0, duration=24.050s, table=0, n_packets=1, n_bytes=42, idle_timeout=60, idle_age=24, priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=56:9d:6b:56:f9:96,dl_dst=f6:99:8e:3a:fd:21,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,arp_op=2 actions=output:2
cookie=0x0, duration=29.058s, table=0, n_packets=1, n_bytes=98, idle_timeout=60, idle_age=29, priority=65535,icmp,in_port=1,vlan_tci=0x0000,dl_src=56:9d:6b:56:f9:96,dl_dst=f6:99:8e:3a:fd:21,nw_src=10.0.0.1,nw_dst=10.0.0.2,nw_tos=0,icmp_type=8,icmp_code=0 actions=output:2
cookie=0x0, duration=29.058s, table=0, n_packets=1, n_bytes=98, idle_timeout=60, idle_age=29, priority=65535,icmp,in_port=2,vlan_tci=0x0000,dl_src=f6:99:8e:3a:fd:21,dl_dst=56:9d:6b:56:f9:96,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:1
```
可以看到說,透過預設的環境,可以看到 ARP & ICMP 規則是如何撰寫的,因此透過這樣的規則我們就可以去撰寫出我們想要的樣子。
**[ARP 規則]**

**[ICMP 規則]**

接著我們去練習如何撰寫 ARP & ICMP 規則,我們在終端機使用`mn --controller=remote`,**其中的`--controller=remote`是將控制器放到遠端,但是控制器並沒有啟動**,因此當我們在執行 ping 的時候,會發現 ping 不通的原因是這樣。
因此根據這樣的環境我們便可以透過手動新增 ARP 規則上去:
```
root@vm2:/home/user# ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
root@vm2:/home/user# ovs-ofctl add-flow s1 arp,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,arp_op=1,actions=output:2
root@vm2:/home/user# ovs-ofctl add-flow s1 arp,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=2,actions=output:1
root@vm2:/home/user# ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=25.721s, table=0, n_packets=0, n_bytes=0, idle_age=25, arp,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,arp_op=1 actions=output:2
cookie=0x0, duration=10.390s, table=0, n_packets=0, n_bytes=0, idle_age=10, arp,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=2 actions=output:1
```
確認規則上去後,我們再執行 ping 測試,發現雖然還是 ping 不通,但是各節點的 ARP 規則表已經建立上去了,代表說我們 ARP 的規則是可行的。
```
mininet-wifi> 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
mininet-wifi> h1 arp -n
Address HWtype HWaddress Flags Mask Iface
10.0.0.2 ether 0e:f8:bd:37:4b:48 C h1-eth0
mininet-wifi> h2 arp -n
Address HWtype HWaddress Flags Mask Iface
10.0.0.1 ether fe:42:82:c2:5e:73 C h2-eth0
mininet-wifi>
```
我們在把 ICMP 規則新增上去:
```
root@vm2:/home/user# ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:2
root@vm2:/home/user# ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:1
root@vm2:/home/user# ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=534.255s, table=0, n_packets=1, n_bytes=42, idle_age=239, arp,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,arp_op=1 actions=output:2
cookie=0x0, duration=518.924s, table=0, n_packets=1, n_bytes=42, idle_age=239, arp,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=2 actions=output:1
cookie=0x0, duration=32.124s, table=0, n_packets=0, n_bytes=0, idle_age=32, icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0 actions=output:2
cookie=0x0, duration=22.125s, table=0, n_packets=0, n_bytes=0, idle_age=22, icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0 actions=output:1
root@vm2:/home/user#
```
當規則都上去後,再去測試 h1 ping h2,可以看到可以互相通訊了。

---
### 完成上面的拓樸
首先我們先創建一個資料夾`ovs-test`,進入資料夾再創建一個資料夾`lab1`,進入到 lab1 後去新增一個檔案`1.py`
```
#!/usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink,Intf
from mininet.node import Controller,RemoteController
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
s1 = net.addSwitch('s1')
s2 = net.addSwitch('s2')
s3 = net.addSwitch('s3')
c0 = net.addController('c0', controller=RemoteController)
net.addLink(h1, s1)
net.addLink(s1, s2)
net.addLink(s1, s3)
net.addLink(s3, s2)
net.addLink(s2, h2)
net.build()
c0.start()
s1.start([c0])
s2.start([c0])
s3.start([c0])
# rules for s1
# ovs-ofctl add-flow s1 arp,arp_op=1,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:2
# ovs-ofctl add-flow s1 arp,arp_op=1,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1
# ovs-ofctl add-flow s1 arp,arp_op=2,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:2
# ovs-ofctl add-flow s1 arp,arp_op=2,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1
# ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:3
# ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:1
# rules for s2
# ovs-ofctl add-flow s2 arp,arp_op=1,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:3
# ovs-ofctl add-flow s2 arp,arp_op=1,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1
# ovs-ofctl add-flow s2 arp,arp_op=2,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:3
# ovs-ofctl add-flow s2 arp,arp_op=2,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1
# ovs-ofctl add-flow s2 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:3
# ovs-ofctl add-flow s2 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:1
# rules for s3
# ovs-ofctl add-flow s3 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:2
CLI(net)
net.stop()
```
> // ARP & IP 封包處理 (以 s1 說明):
>
> 1. ovs-ofctl add-flow s1 arp,arp_op=1,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:2
> // 對於 s1 而言,當 ARP Request 封包由 h1 送出給 h2,封包會從2號埠丟。
> 2. ovs-ofctl add-flow s1 arp,arp_op=1,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1
> // 對於 s1 而言,當 ARP Request 封包由 h2 送出給 h1,封包會從1號埠丟。
> 3. ovs-ofctl add-flow s1 arp,arp_op=2,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:2
> // 對於 s1 而言,當 ARP Reply 封包從 h1 送出給 h2,封包會從2號埠丟。
> 4. ovs-ofctl add-flow s1 arp,arp_op=2,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1
> // 對於 s1 而言,當 ARP Reply 封包從 h2 送出給 h1,封包會從1號埠丟。
> 5. ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:3
> // 對於 s1 而言,當 IP Request 封包從 h1 送到 h2,封包會從3號埠丟
> 6. ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:1
> // 對於 s1 而言,當 IP Reply 封包從 h2 送到 h1,封包會從1號埠丟
儲存後,我們根據程式碼可以先手動新增規則上去,原因是因為可以透過手動的方式去進行除錯,讓封包可以順利的傳輸,我們在除錯的過程也可以搭配 wireshark 去監控。
**[s1]**
```
root@vm2:/home/user# ovs-ofctl add-flow s1 arp,arp_op=1,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:2
root@vm2:/home/user# ovs-ofctl add-flow s1 arp,arp_op=1,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1
root@vm2:/home/user# ovs-ofctl add-flow s1 arp,arp_op=2,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:2
root@vm2:/home/user# ovs-ofctl add-flow s1 arp,arp_op=2,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1
root@vm2:/home/user# ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:3
root@vm2:/home/user# ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:1
root@vm2:/home/user# ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=56.395s, table=0, n_packets=0, n_bytes=0, idle_age=56, arp,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,arp_op=1 actions=output:2
cookie=0x0, duration=39.099s, table=0, n_packets=0, n_bytes=0, idle_age=39, arp,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=1 actions=output:1
cookie=0x0, duration=31.625s, table=0, n_packets=0, n_bytes=0, idle_age=31, arp,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,arp_op=2 actions=output:2
cookie=0x0, duration=24.922s, table=0, n_packets=0, n_bytes=0, idle_age=24, arp,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=2 actions=output:1
cookie=0x0, duration=15.700s, table=0, n_packets=0, n_bytes=0, idle_age=15, icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0 actions=output:3
cookie=0x0, duration=8.679s, table=0, n_packets=0, n_bytes=0, idle_age=8, icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0 actions=output:1
```
**[s2]**
```
root@vm2:/home/user# ovs-ofctl add-flow s2 arp,arp_op=1,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:3
root@vm2:/home/user# ovs-ofctl add-flow s2 arp,arp_op=1,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1
root@vm2:/home/user# ovs-ofctl add-flow s2 arp,arp_op=2,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:3
root@vm2:/home/user# ovs-ofctl add-flow s2 arp,arp_op=2,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1
root@vm2:/home/user# ovs-ofctl add-flow s2 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:3
root@vm2:/home/user# ovs-ofctl add-flow s2 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:1
root@vm2:/home/user# ovs-ofctl dump-flows s2
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=41.815s, table=0, n_packets=0, n_bytes=0, idle_age=41, arp,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,arp_op=1 actions=output:3
cookie=0x0, duration=34.954s, table=0, n_packets=0, n_bytes=0, idle_age=34, arp,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=1 actions=output:1
cookie=0x0, duration=28.159s, table=0, n_packets=0, n_bytes=0, idle_age=28, arp,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,arp_op=2 actions=output:3
cookie=0x0, duration=20.031s, table=0, n_packets=0, n_bytes=0, idle_age=20, arp,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=2 actions=output:1
cookie=0x0, duration=12.986s, table=0, n_packets=0, n_bytes=0, idle_age=12, icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0 actions=output:3
cookie=0x0, duration=6.462s, table=0, n_packets=0, n_bytes=0, idle_age=6, icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0 actions=output:1
```
**[s3]**
```
root@vm2:/home/user# ovs-ofctl add-flow s3 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:2
root@vm2:/home/user# ovs-ofctl dump-flows s3
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=4.292s, table=0, n_packets=0, n_bytes=0, idle_age=4, icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0 actions=output:2
```
當我們在 s2-eth1, s3-eth2 分別開啟 wireshark,可以看到 IP Request 封包是走上面,而 Reply 封包是走下面的。


### 完整程式碼
```
#!/usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink,Intf
from mininet.node import Controller,RemoteController
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
s1 = net.addSwitch('s1')
s2 = net.addSwitch('s2')
s3 = net.addSwitch('s3')
c0 = net.addController('c0', controller=RemoteController)
net.addLink(h1, s1)
net.addLink(s1, s2)
net.addLink(s1, s3)
net.addLink(s3, s2)
net.addLink(s2, h2)
net.build()
c0.start()
s1.start([c0])
s2.start([c0])
s3.start([c0])
# rules for s1
s1.cmd("ovs-ofctl add-flow s1 arp,arp_op=1,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:2")
s1.cmd("ovs-ofctl add-flow s1 arp,arp_op=1,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1")
s1.cmd("ovs-ofctl add-flow s1 arp,arp_op=2,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:2")
s1.cmd("ovs-ofctl add-flow s1 arp,arp_op=2,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1")
s1.cmd("ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:3")
s1.cmd("ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:1")
# rules for s2
s2.cmd("ovs-ofctl add-flow s2 arp,arp_op=1,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:3")
s2.cmd("ovs-ofctl add-flow s2 arp,arp_op=1,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1")
s2.cmd("ovs-ofctl add-flow s2 arp,arp_op=2,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:3")
s2.cmd("ovs-ofctl add-flow s2 arp,arp_op=2,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1")
s2.cmd("ovs-ofctl add-flow s2 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:3")
s2.cmd("ovs-ofctl add-flow s2 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:1")
# rules for s3
s3.cmd("ovs-ofctl add-flow s3 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:2")
CLI(net)
net.stop()
```
### 關於程式碼...
其中特別注意的是,雖然 h1 ping h2 是成功的,但是當你在測試 h2 ping h1 時,會發現通訊是不通的,為什麼?
這個問題就留給你們了!
### Answer
```
#!/usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink,Intf
from mininet.node import Controller,RemoteController
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
s1 = net.addSwitch('s1')
s2 = net.addSwitch('s2')
s3 = net.addSwitch('s3')
c0 = net.addController('c0', controller=RemoteController)
net.addLink(h1, s1)
net.addLink(s1, s2)
net.addLink(s1, s3)
net.addLink(s3, s2)
net.addLink(s2, h2)
net.build()
c0.start()
s1.start([c0])
s2.start([c0])
s3.start([c0])
# rules for s1
s1.cmd("ovs-ofctl add-flow s1 arp,arp_op=1,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:2")
s1.cmd("ovs-ofctl add-flow s1 arp,arp_op=1,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1")
s1.cmd("ovs-ofctl add-flow s1 arp,arp_op=2,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:2")
s1.cmd("ovs-ofctl add-flow s1 arp,arp_op=2,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1")
s1.cmd("ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:2")
s1.cmd("ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=8,icmp_code=0,actions=output:1")
s1.cmd("ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=0,icmp_code=0,actions=output:2")
s1.cmd("ovs-ofctl add-flow s1 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:1")
# rules for s2
s2.cmd("ovs-ofctl add-flow s2 arp,arp_op=1,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:3")
s2.cmd("ovs-ofctl add-flow s2 arp,arp_op=1,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1")
s2.cmd("ovs-ofctl add-flow s2 arp,arp_op=2,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,actions=output:3")
s2.cmd("ovs-ofctl add-flow s2 arp,arp_op=2,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,actions=output:1")
s2.cmd("ovs-ofctl add-flow s2 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=8,icmp_code=0,actions=output:3")
s2.cmd("ovs-ofctl add-flow s2 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=8,icmp_code=0,actions=output:2")
s2.cmd("ovs-ofctl add-flow s2 icmp,nw_src=10.0.0.1,nw_dst=10.0.0.2,icmp_type=0,icmp_code=0,actions=output:3")
s2.cmd("ovs-ofctl add-flow s2 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:2")
# rules for s3
s3.cmd("ovs-ofctl add-flow s3 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=0,icmp_code=0,actions=output:1")
s3.cmd("ovs-ofctl add-flow s3 icmp,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=8,icmp_code=0,actions=output:1")
CLI(net)
net.stop()
```
---
## 透過 ovs 實現鏡像功能 (Mirror Function)
這次的實驗拓樸如下:

我們新增一個資料夾`lab2`,進入後創建一個檔案`1.py`:
```
#!/usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink,Intf
from mininet.node import Controller,RemoteController
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1', ip="10.0.0.1/24", mac="00:00:00:00:00:01")
h2 = net.addHost('h2', ip="10.0.0.2/24", mac="00:00:00:00:00:02")
h3 = net.addHost('h3', ip="10.0.0.3/24", mac="00:00:00:00:00:03")
s1 = net.addSwitch('s1')
c0 = net.addController('c0', controller=RemoteController)
net.addLink(h1, s1)
net.addLink(h2, s1)
net.addLink(h3, s1)
net.build()
c0.start()
s1.start([c0])
h1.cmd("arp -s 10.0.0.2 00:00:00:00:00:02")
h1.cmd("arp -s 10.0.0.3 00:00:00:00:00:03")
h2.cmd("arp -s 10.0.0.1 00:00:00:00:00:01")
h2.cmd("arp -s 10.0.0.3 00:00:00:00:00:03")
h3.cmd("arp -s 10.0.0.1 00:00:00:00:00:01")
h3.cmd("arp -s 10.0.0.2 00:00:00:00:00:02")
#rules for s1
#ovs-ofctl add-flow s1 priority=1,ip,nw_dst=10.0.0.1,actions=output:1
#ovs-ofctl add-flow s1 priority=1,ip,nw_dst=10.0.0.2,actions=output:2
#ovs-ofctl add-flow s1 priority=1,ip,nw_dst=10.0.0.3,actions=output:3
#ovs-ofctl add-flow s1 priority=10,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2,actions=output:2,output:3
CLI(net)
net.stop()
```
> 1. ovs-ofctl add-flow s1 priority=1,ip,nw_dst=10.0.0.1,actions=output:1
> 2. priority: 優先順序,當規則越多時,會根據優先順序的權重值分別進行,權重越大則優先處理。
先用手動的方式將規則新增上去,新增完後檢查規則
```
root@vm2:/home/user# ovs-ofctl add-flow s1 priority=1,ip,nw_dst=10.0.0.1,actions=output:1
root@vm2:/home/user# ovs-ofctl add-flow s1 priority=1,ip,nw_dst=10.0.0.2,actions=output:2
root@vm2:/home/user# ovs-ofctl add-flow s1 priority=1,ip,nw_dst=10.0.0.3,actions=output:3
root@vm2:/home/user# ovs-ofctl add-flow s1 priority=10,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2,actions=output:2,output:3
root@vm2:/home/user# ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=8.491s, table=0, n_packets=0, n_bytes=0, idle_age=8, priority=10,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2 actions=output:2,output:3
cookie=0x0, duration=66.357s, table=0, n_packets=0, n_bytes=0, idle_age=66, priority=1,ip,nw_dst=10.0.0.1 actions=output:1
cookie=0x0, duration=60.972s, table=0, n_packets=0, n_bytes=0, idle_age=60, priority=1,ip,nw_dst=10.0.0.2 actions=output:2
cookie=0x0, duration=57.354s, table=0, n_packets=0, n_bytes=0, idle_age=57, priority=1,ip,nw_dst=10.0.0.3 actions=output:3
```
我們可以在 h3 開啟 wireshark,並且去監聽封包當 h1 ping h2 時,是否有複製一份到 h3 ?

我們可以看到,當 h1 ping 10.0.0.2,也就是 h2 的 IP 時,我們 h3 的 wireshark 也有收到 h1 傳來的封包,因此這個實驗是成功的。
---
### 用 Iperf 測試鏡像功能 (Mirror Function)
我們可以用 Iperf 測試說這個規則是否也適用在 Iperf 的這項工具。
我們在終端機使用`xterm h1 h2 h3`,其中 h1 為 Client 去傳輸 UDP 封包,而 h2, h3 為 Server 去接收封包。

結果可以看到說,當 h1 ping h2 時,h3 並沒有接收到任何來自 h1 的封包。
因此我們可以發現這條規則對於 Iperf 而言是不管用的,當我們開啟 wireshark 去監聽就會發現 dst 的 IP 是不對的。

因此若要完成這個實驗,我們可以將規則修改成這樣:
```
ovs-ofctl add-flow s1 priority=100,ip,nw_src=10.0.0.1,nw_dst=10.0.0.2,actions=output:2,mod_nw_dst=10.0.0.3,mod_dl_dst=00:00:00:00:00:03,output:3
```
我們在檢查 h3 的終端機時就會發現,h1 的封包已經成功複製一份到 h3 了。

且監聽 wireshark 也會發現,dst 的 IP 也正確了。

### TCP 雙向溝通

---
## Reference
1. Youtube video (mininet ovs 2): https://www.youtube.com/watch?v=zQDtEoUrnvQ
2. Youtube video (mininet ovs 3): https://www.youtube.com/watch?v=z5h7hRPgFAE