---
# System prepended metadata

title: '網路模擬與分析(4/12): mininet 去實作出 SDN 功能(ovs) & 鏡像功能'
tags: [OpenvSwitch, SDN, OpenFlow, Mininet]

---

# 網路模擬與分析(4/12): mininet 去實作出 SDN 功能(ovs) & 鏡像功能
###### tags: `Mininet`、`SDN`、`OpenFlow`、`OpenvSwitch`

## 透過不同路徑去實現封包傳輸的動作

### ARP & ICMP 封包傳輸方式

根據上週的實驗，我們可以得知，網路的通訊協定是先透過 ARP 封包去取得對方的網路卡卡號，其中 ARP 封包又可以分為 Request (Broadcast 的方式進行傳輸), Reply (unicast 的方式進行傳輸)。

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

值得注意的是，我們知道 ARP Request packet 是透過 broadcast 的方式傳輸，但是在較複雜的網路環境中，當封包進行傳輸時會因為 Switches (s1, s2, s3) 而充斥著廣播，因此很有可能會造成廣播的風暴。

因此我們藉由 SDN 的特性，可能將任意的規則放在所屬的節點上，而我們的環境中，因為得知每個節點的位置，因此在這個實驗的第一部分，我們將 ARP 封包的規則由 broadcast 更換成 unicast 的方式。

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

> 1. 當 ARP 封包送完之後，才開始傳 IP packet。
> 2. IP 的封包是需要 MAC Addr + IP Addr。

透過封包傳輸的特性我們可以得知，封包在傳輸時是 “一去一回” 的動作，而我們實驗的另一個部分要做的就是，透過以下的拓樸去進行封包傳輸，其中封包進行 Request 時是走上面的路徑，而當封包要回去時，是走下面的路徑。

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

---

### 初學者如何撰寫 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 規則]**
![](https://i.imgur.com/yZz4AQl.png)

**[ICMP 規則]**
![](https://i.imgur.com/skujUND.png)


接著我們去練習如何撰寫 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，可以看到可以互相通訊了。 

![](https://i.imgur.com/Jg6zbxE.jpg)

---

### 完成上面的拓樸

首先我們先創建一個資料夾`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 封包是走下面的。
![](https://i.imgur.com/0NXMw9x.png)
![](https://i.imgur.com/jaBcNK7.png)


### 完整程式碼

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

這次的實驗拓樸如下：

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

我們新增一個資料夾`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 ？

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


我們可以看到，當 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 去接收封包。

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

結果可以看到說，當 h1 ping h2 時，h3 並沒有接收到任何來自 h1 的封包。

因此我們可以發現這條規則對於 Iperf 而言是不管用的，當我們開啟 wireshark 去監聽就會發現 dst 的 IP 是不對的。

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

因此若要完成這個實驗，我們可以將規則修改成這樣：

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

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

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

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

### TCP 雙向溝通

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


---

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