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