# 網路模擬與分析(3/22):在 Mininet 環境中實現 Router & Bridge 功能 + 應用
###### tags: `Mininet`、`NAT 轉換`、`vlan`、`單臂路由`、`trunk link`、`access link`
## 透過 Python 程式建立你的第一個 Mininet 環境 (ep.2)
### 在 mininet 的環境下建立2個 Router 的功能
我們上週有介紹了如何在 Mininet 的環境中建立2台 Hosts 和一台 Router,並能夠讓彼此通訊,接下來我們將會介紹如何在2台 Router 的環境下能夠通訊,我們將上週的`2.py`複製一份,變成`3.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')
r2 = net.addHost('r2')
Link(h1, r1)
Link(r1, r2)
Link(h2, r2)
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 10.0.0.1/24 brd + dev r1-eth1")
r1.cmd("ip route add default via 10.0.0.2")
r1.cmd("echo 1 > /proc/sys/net/ipv4/ip_forward")
r2.cmd("ifconfig r2-eth0 0")
r2.cmd("ifconfig r2-eth1 0")
r2.cmd("ip addr add 10.0.0.2/24 brd + dev r2-eth0")
r2.cmd("ip addr add 192.168.2.254/24 brd + dev r2-eth1")
r2.cmd("ip route add default via 10.0.0.1")
r2.cmd("echo 1 > /proc/sys/net/ipv4/ip_forward")
CLI(net)
net.stop()
```
> 1. `r1.cmd("ip route add default via 10.0.0.2")`=`r1.cmd("ip route add 192.168.2.0/24 via 10.0.0.2")`
> 2. `r2.cmd("ip route add default via 10.0.0.1")`=`r2.cmd("ip route add 192.168.1.0/24 via 10.0.0.1")`
儲存後,執行`python 3.py`,並且測試雙方通訊是否正常。
```
root@vm2:/home/user/mininet/examples# python 3.py
mininet> h1 ping h2 -c 3
PING 192.168.2.1 (192.168.2.1) 56(84) bytes of data.
64 bytes from 192.168.2.1: icmp_seq=1 ttl=62 time=0.159 ms
64 bytes from 192.168.2.1: icmp_seq=2 ttl=62 time=0.178 ms
64 bytes from 192.168.2.1: icmp_seq=3 ttl=62 time=0.082 ms
--- 192.168.2.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.082/0.139/0.178/0.043 ms
mininet> h2 ping h1 -c 3
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=62 time=0.112 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=62 time=0.111 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=62 time=0.159 ms
--- 192.168.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.111/0.127/0.159/0.024 ms
mininet>
```
---
### 在2台不同網路環境下的 Router 透過 NAT 轉換去實現通訊
接下來將`3.py`繼續做延伸,其中的拓樸不變,而網路環境會分成**私人網域**以及**公有網域**,其中 h1 (私人網域) 是能 ping 到 h2 (公有網域),而 h2 (公有網域) 不能 ping 到 h1 (私人網域),如下圖:

其中特別注意的是,在私人網域轉換成公有網域,需要進行 **NAT 轉換**,且在公有網域的 IP 設定以及 IP 規則設定也需要重新撰寫,因此接下來我們將`3.py`複製一份變成`4.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')
r2 = net.addHost('r2')
Link(h1, r1)
Link(h2, r2)
Link(r1, r2)
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 22.1.1.1/24 brd + dev h2-eth0")
h2.cmd("ip route add default via 22.1.1.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 12.1.1.1/24 brd + dev r1-eth1")
r1.cmd("ip route add default via 12.1.1.2")
r1.cmd("echo 1 > /proc/sys/net/ipv4/ip_forward")
r1.cmd("iptables -t nat -A POSTROUTING -o r1-eth1 -s 192.168.1.0/24 -j MASQUERADE")
r2.cmd("ifconfig r2-eth0 0")
r2.cmd("ifconfig r2-eth1 0")
r2.cmd("ip addr add 22.1.1.254/24 brd + dev r2-eth0")
r2.cmd("ip addr add 12.1.1.2/24 brd + dev r2-eth1")
r2.cmd("echo 1 > /proc/sys/net/ipv4/ip_forward")
CLI(net)
net.stop()
```
> 1. **NAT 轉換**: `r1.cmd("iptables -t nat -A POSTROUTING -o r1-eth1 -s 192.168.1.0/24 -j MASQUERADE")`
> **-t(translate): 轉換成 NAT**
> **-A(after): 轉換完之後使用 POSTROUTING**
> **-o(output): 封包傳輸選擇的出口是 r1-eth1**
> **-s(source): 來源端是 192.168.1.0/24**
> **-j(jump): jump to**
> **MASQUERADE: 做 Network Address 的轉換**
> 2. **ip route 規則說明**: 對 R1 而言,假設要通訊到 H2 的網域 22.1.1.2/24,我們選擇它的下一跳是 r2-eth0,以此類推。
測試是否成功,我們執行`python 4.py`,進入到 mininet 的環境,h1 去 ping 看看能不能通訊。
```
root@vm2:/home/user/mininet/examples# python 4.py
mininet>
mininet> h1 ping h2 -c 3
PING 22.1.1.1 (22.1.1.1) 56(84) bytes of data.
64 bytes from 22.1.1.1: icmp_seq=1 ttl=62 time=0.090 ms
64 bytes from 22.1.1.1: icmp_seq=2 ttl=62 time=0.200 ms
64 bytes from 22.1.1.1: icmp_seq=3 ttl=62 time=0.104 ms
--- 22.1.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.090/0.131/0.200/0.049 ms
mininet>
```
其中我們可以開啟2個 Router 以及 h1,使用`xterm r1 r1 h1`
而2台 Router 分別開啟 wireshark,監控的接口 選擇 r1-eth0, r1-eth1。
而 h1 去執行`ping 22.1.1. -c 3`的動作,去查看2台 wireshark IP 轉換的情形
我們在監控 r1-eth0 的接口時可以看到,來源端是 192.168.1.1,而目的端是 22.1.1.1。

而當我們看到 r1-eth1 的接口時可以看到,**來源端的 IP 原本是 192.168.1.1,轉換成了 12.1.1.1。而從 h2 發送封包回 h1 時,目的端的 IP 會先到 r1-eth0 的接口,而封包到達 r1-eth0 時,封包內的 IP 會再轉換成 h1 的 IP (可以透過上圖得知)**,因此可以驗證了當封包傳輸時,在經過 Router 時進行了 NAT 的轉換。

而當發生了 ping 不通需要 debug 的情形時,也可以透過 wireshark 這項工具進行監控,去查找哪個步驟發生了錯誤。
> 1. debug 的原則為 (以 h1 為範例): 先 ping localhost (127.0.0.1),在 ping 自己的 IP (192.168.1.1),在 ping 預設路由 (192.168.1.254),最後在 ping Internet 的 IP Address (12.1.1.1),以此類推。
而 h2 是 ping 不到 h1 的
```
mininet> h2 ping h1 -c 3
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
From 22.1.1.254 icmp_seq=1 Destination Net Unreachable
From 22.1.1.254 icmp_seq=2 Destination Net Unreachable
From 22.1.1.254 icmp_seq=3 Destination Net Unreachable
--- 192.168.1.1 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2000ms
mininet>
```
我們做完了 Router 的部分,接下來會將 Router 的網路設備會變成 Switch (bridge)!
---
### 在 mininet 的環境中實現 bridge 功能
當我們的網路設備是 **“Switch”** or **“Bridge”** 時,我們如何在 Mininet 環境中去達到互相通訊呢?
拓樸如下:

特別注意的是,我們在開始實驗之前,我們需要先安裝一個套件`bridge-utils`,使用`apt install bridge-utils`去進行安裝,而這個套件主要會將一般的 node 變成 bridge。
我們可以創建一個檔案叫`5.py`
```
#! /usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink,Intf
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
h3 = net.addHost('h3')
br1 = net.addHost('br1')
net.addLink(h1, br1)
net.addLink(h2, br1)
net.addLink(h3, br1)
net.build()
h1.cmd("ifconfig h1-eth0 0")
h2.cmd("ifconfig h2-eth0 0")
h3.cmd("ifconfig h3-eth0 0")
br1.cmd("ifconfig br1-eth0 0")
br1.cmd("ifconfig br1-eth1 0")
br1.cmd("ifconfig br1-eth2 0")
h1.cmd("ip address add 192.168.10.1/24 dev h1-eth0")
h2.cmd("ip address add 192.168.10.2/24 dev h2-eth0")
h3.cmd("ip address add 192.168.10.3/24 dev h3-eth0")
CLI(net)
net.stop()
```
**1. 那如何透過程式把 node 變成 bridge 的?**
我們可以先執行`5.py`,接著在 mininet 的環境使用`xterm br1`,接著執行`brctl -h`

> 1. brctl (bridge control): 橋接器的控制器
> 2. addbr (add bridge): 新增一個 bridge
> 3. br1: bridge 的名稱
創建好 bridge 之後,可以在 br1 執行`brctl show`,去查看 bridge 的資訊,可以看到目前的 bridge 沒有任何 interfaces 連接。
```
# brctl show
bridge name bridge id STP enabled interfaces
mybr 8000.5e25f337ee19 no
```
**2. 那如何讓 interface 去連接 bridge 呢?**
我們可以使用`brctl addif + bridge_name + device_interface`
> 1. addif (add interface): 在 bridge 新增一個 interface
將`br1-eth0`、`br1-eth1`、`br1-eth2`新增上去後,我們在使用`brctl show`去查看,可以看到:

現在 interface 已經有連接至 bridge 了,接著我們可以去使用`ifconfig`去進行查看。
可以看到,剛剛所創建的 br1 並沒有出現在`ifconfig`內。
```
mininet> br1 ifconfig
br1-eth0 Link encap:Ethernet HWaddr 3e:b2:4b:1f:87:4a
inet6 addr: fe80::3cb2:4bff:fe1f:874a/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:648 (648.0 B) TX bytes:828 (828.0 B)
br1-eth1 Link encap:Ethernet HWaddr 46:0f:3f:e3:3f:72
inet6 addr: fe80::440f:3fff:fee3:3f72/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
br1-eth2 Link encap:Ethernet HWaddr ae:6b:f4:da:29:e2
inet6 addr: fe80::ac6b:f4ff:feda:29e2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
```
事實上,br1 這個 interface 是已經有創建好了,只不過還沒啟動而已
```
mininet> br1 ifconfig -a
br1 Link encap:Ethernet HWaddr 3e:b2:4b:1f:87:4a
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
br1-eth0 Link encap:Ethernet HWaddr 3e:b2:4b:1f:87:4a
inet6 addr: fe80::3cb2:4bff:fe1f:874a/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:648 (648.0 B) TX bytes:828 (828.0 B)
br1-eth1 Link encap:Ethernet HWaddr 46:0f:3f:e3:3f:72
inet6 addr: fe80::440f:3fff:fee3:3f72/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
br1-eth2 Link encap:Ethernet HWaddr ae:6b:f4:da:29:e2
inet6 addr: fe80::ac6b:f4ff:feda:29e2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
```
因此我們可以使用`ifconfig br1 up`去啟動 br1。
而我們再去執行 ping 的動作,可以看到所有的 host 都能互相通訊了。
```
mininet> h1 ping h2
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
64 bytes from 192.168.10.2: icmp_seq=1 ttl=64 time=0.135 ms
^C
--- 192.168.10.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.135/0.135/0.135/0.000 ms
mininet> h1 ping h3
PING 192.168.10.3 (192.168.10.3) 56(84) bytes of data.
64 bytes from 192.168.10.3: icmp_seq=1 ttl=64 time=0.128 ms
^C
--- 192.168.10.3 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.128/0.128/0.128/0.000 ms
mininet> h2 ping h3
PING 192.168.10.3 (192.168.10.3) 56(84) bytes of data.
64 bytes from 192.168.10.3: icmp_seq=1 ttl=64 time=0.091 ms
^C
--- 192.168.10.3 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.091/0.091/0.091/0.000 ms
```
因此我們`5.py`的程式對於 node 設定成 bridge 只需要加上這幾行程式,就可以實現出 bridge 的功能了!
```
br1.cmd("brctl addbr mybr")
br1.cmd("brctl addif mybr br1-eth0")
br1.cmd("brctl addif mybr br1-eth1")
br1.cmd("brctl addif mybr br1-eth2")
br1.cmd("ifconfig mybr up")
```
因此這裡是完整的 code:
```
#! /usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink,Intf
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
h3 = net.addHost('h3')
br1 = net.addHost('br1')
net.addLink(h1, br1)
net.addLink(h2, br1)
net.addLink(h3, br1)
net.build()
h1.cmd("ifconfig h1-eth0 0")
h2.cmd("ifconfig h2-eth0 0")
h3.cmd("ifconfig h3-eth0 0")
br1.cmd("ifconfig br1-eth0 0")
br1.cmd("ifconfig br1-eth1 0")
br1.cmd("ifconfig br1-eth2 0")
br1.cmd("brctl addbr mybr")
br1.cmd("brctl addif mybr br1-eth0")
br1.cmd("brctl addif mybr br1-eth1")
br1.cmd("brctl addif mybr br1-eth2")
br1.cmd("ifconfig mybr up")
h1.cmd("ip address add 192.168.10.1/24 dev h1-eth0")
h2.cmd("ip address add 192.168.10.2/24 dev h2-eth0")
h3.cmd("ip address add 192.168.10.3/24 dev h3-eth0")
CLI(net)
net.stop()
```
為了要驗證這個 node 是有 bridge 的功能,我們在 mininet 使用`xterm h2`,並在 h2 的終端機執行`tcpdump -i h2-eth0`
> 1. tcpdump: 可以擷取通過某個網路介面的封包
> 2. -i: interface
接著讓 h1 去 ping h3,可以看到:

當我們在 ping h3 的時候,h2 是監聽不到任何封包,因此可以驗證 bridge 的特性。
---
### bridge 的環境中將2個不同的網域彼此獨立,同網域能進行通訊
剛剛的實驗可以看到將一般的 node 變成 bridge,接下來我們將原本的拓樸進行延伸,將 Host 數目增加為4個,其中 h1, h2 屬於 192.168.10.0/24 的網域,而 h3, h4 屬於 192.168.20.0/24 的網域,而10的網域是不能通訊到20的網域,反之亦然。

因此我們將`5.py`複製一份為`6.py`
```
#! /usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink,Intf
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
h3 = net.addHost('h3')
h4 = net.addHost('h4')
br1 = net.addHost('br1')
net.addLink(h1, br1)
net.addLink(h2, br1)
net.addLink(h3, br1)
net.addLink(h4, br1)
net.build()
h1.cmd("ifconfig h1-eth0 0")
h2.cmd("ifconfig h2-eth0 0")
h3.cmd("ifconfig h3-eth0 0")
h3.cmd("ifconfig h4-eth0 0")
br1.cmd("ifconfig br1-eth0 0")
br1.cmd("ifconfig br1-eth1 0")
br1.cmd("ifconfig br1-eth2 0")
br1.cmd("ifconfig br1-eth3 0")
br1.cmd("brctl addbr mybr1")
br1.cmd("brctl addbr mybr2")
br1.cmd("brctl addif mybr1 br1-eth0")
br1.cmd("brctl addif mybr1 br1-eth1")
br1.cmd("brctl addif mybr2 br1-eth2")
br1.cmd("brctl addif mybr2 br1-eth3")
br1.cmd("ifconfig mybr1 up")
br1.cmd("ifconfig mybr2 up")
h1.cmd("ip address add 192.168.10.1/24 dev h1-eth0")
h2.cmd("ip address add 192.168.10.2/24 dev h2-eth0")
h3.cmd("ip address add 192.168.20.1/24 dev h3-eth0")
h4.cmd("ifconfig h4-eth0 192.168.20.2/4")
CLI(net)
net.stop()
```
> 1. `ifconfig h4-eth0 192.168.20.2/4`是另一種設定 IP addr 的做法。
儲存後,執行`6.py`,可以看到說 h1 ping h2 是可以互通的,但是 h1 在 ping 20網域的 host 是 ping 不通的,而 h3 在 ping 的情形也是一樣的,因此這個實驗是成功的。
```
root@vm2:/home/user/mininet/examples# python 6.py
mininet> h1 ping h2
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
64 bytes from 192.168.10.2: icmp_seq=1 ttl=64 time=0.104 ms
^C
--- 192.168.10.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.104/0.104/0.104/0.000 ms
mininet> h1 ping h3
connect: Network is unreachable
mininet> h1 ping h4
connect: Network is unreachable
mininet> h3 ping h4
PING 192.168.20.2 (192.168.20.2) 56(84) bytes of data.
64 bytes from 192.168.20.2: icmp_seq=1 ttl=64 time=0.142 ms
^C
--- 192.168.20.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.142/0.142/0.142/0.000 ms
mininet> h3 ping h1
connect: Network is unreachable
mininet> h3 ping h2
connect: Network is unreachable
mininet>
```
---
### bridge + router 去實現在不同網域之間彼此互相通訊
上一個實驗是當有2個網域時,bridge 該怎麼去處理同一區網的連線問題,接下來我們要將 switch 與 router 結合,並且讓不同網域之間能互相通訊,拓樸如下:

我們將`6.py`複製一份為`7.py`:
```
#! /usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink,Intf
if 'main' == name:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
h3 = net.addHost('h3')
h4 = net.addHost('h4')
br1 = net.addHost('br1')
r1 = net.addHost('r1')
net.addLink(h1, br1)
net.addLink(h2, br1)
net.addLink(h3, br1)
net.addLink(h4, br1)
net.addLink(br1,r1)
net.addLink(br1,r1)
net.build()
h1.cmd("ifconfig h1-eth0 0")
h2.cmd("ifconfig h2-eth0 0")
h3.cmd("ifconfig h3-eth0 0")
h4.cmd("ifconfig h4-eth0 0")
br1.cmd("ifconfig br1-eth0 0")
br1.cmd("ifconfig br1-eth1 0")
br1.cmd("ifconfig br1-eth2 0")
br1.cmd("ifconfig br1-eth3 0")
br1.cmd("ifconfig br1-eth4 0")
br1.cmd("ifconfig br1-eth5 0")
br1.cmd("brctl addbr mybr1")
br1.cmd("brctl addbr mybr2")
br1.cmd("brctl addif mybr1 br1-eth0")
br1.cmd("brctl addif mybr1 br1-eth1")
br1.cmd("brctl addif mybr1 br1-eth4")
br1.cmd("brctl addif mybr2 br1-eth2")
br1.cmd("brctl addif mybr2 br1-eth3")
br1.cmd("brctl addif mybr2 br1-eth5")
br1.cmd("ifconfig mybr1 up")
br1.cmd("ifconfig mybr2 up")
r1.cmd('ifconfig r1-eth0 192.168.10.254 netmask 255.255.255.0')
r1.cmd('ifconfig r1-eth1 192.168.20.254 netmask 255.255.255.0')
r1.cmd("echo 1 > /proc/sys/net/ipv4/ip_forward")
h1.cmd("ip address add 192.168.10.1/24 dev h1-eth0")
h1.cmd("ip route add default via 192.168.10.254")
h2.cmd("ip address add 192.168.10.2/24 dev h2-eth0")
h2.cmd("ip route add default via 192.168.10.254")
h3.cmd("ip address add 192.168.20.1/24 dev h3-eth0")
h3.cmd("ip route add default via 192.168.20.254")
h4.cmd("ip address add 192.168.20.2/24 dev h4-eth0")
h4.cmd("ip route add default via 192.168.20.254")
CLI(net)
net.stop()
```
> 1. 對於 h1, h2 而言,它的內定路由為 192.168.10.254/24,對於 h3, h4 而言,它的內定路由為 192.168.20.254/24。
```
root@vm2:/home/user/mininet/examples# python 7.py
mininet> h1 ping h2
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
64 bytes from 192.168.10.2: icmp_seq=1 ttl=64 time=0.125 ms
^C
--- 192.168.10.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.125/0.125/0.125/0.000 ms
mininet> h1 ping h3
PING 192.168.20.1 (192.168.20.1) 56(84) bytes of data.
64 bytes from 192.168.20.1: icmp_seq=1 ttl=63 time=0.273 ms
^C
--- 192.168.20.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.273/0.273/0.273/0.000 ms
mininet> h1 ping h4
PING 192.168.20.2 (192.168.20.2) 56(84) bytes of data.
64 bytes from 192.168.20.2: icmp_seq=1 ttl=63 time=0.200 ms
^C
--- 192.168.20.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.200/0.200/0.200/0.000 ms
mininet> h3 ping h4
PING 192.168.20.2 (192.168.20.2) 56(84) bytes of data.
64 bytes from 192.168.20.2: icmp_seq=1 ttl=64 time=0.143 ms
^C
--- 192.168.20.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.143/0.143/0.143/0.000 ms
mininet> h3 ping h1
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=63 time=0.134 ms
^C
--- 192.168.10.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.134/0.134/0.134/0.000 ms
mininet> h3 ping h2
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
64 bytes from 192.168.10.2: icmp_seq=1 ttl=63 time=0.118 ms
^C
--- 192.168.10.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.118/0.118/0.118/0.000 ms
mininet>
```
執行後,雖然可以進行互 ping,但對於 router 這樣的 link 而言,還需要多一個 interface 去配置,這樣的方法不太實用,因此下方的實驗將 r1 的 link 變為一個,並具有能相互通訊的功能。
---
### 在 router + bridge 的環境中結合 vlan & trunk 去實現單臂路由的功能
在開始這個實驗之前,我們需要先安裝`vlan`,使用`apt install vlan`去進行安裝。
其中 vlan 最大的優點在於,可以建立虛擬介面,因此 bridge 的 br1-eth4 的介面會多了 “.10” 以及 “.20” 的虛擬介面,同理在 r1 的 r1-eth0 的介面中也會有兩個虛擬介面 “.10” 以及 “.20”,我們可以對 r1-eth0 的虛擬介面進行 IP addr 的設定。
而 router 以及 bridge 所連接的 link 稱為***單臂路由***。
拓樸如下:

我們將`7.py`複製一份為`8.py`
```
#! /usr/bin/env python
from mininet.cli import CLI
from mininet.net import Mininet
from mininet.link import Link,TCLink,Intf
if '__main__' == __name__:
net = Mininet(link=TCLink)
h1 = net.addHost('h1')
h2 = net.addHost('h2')
h3 = net.addHost('h3')
h4 = net.addHost('h4')
br1 = net.addHost('br1')
r1 = net.addHost('r1')
net.addLink(h1, br1)
net.addLink(h2, br1)
net.addLink(h3, br1)
net.addLink(h4, br1)
net.addLink(br1,r1)
net.build()
h1.cmd("ifconfig h1-eth0 0")
h2.cmd("ifconfig h2-eth0 0")
h3.cmd("ifconfig h3-eth0 0")
h4.cmd("ifconfig h4-eth0 0")
r1.cmd("ifconfig r1-eth0 0")
br1.cmd("ifconfig br1-eth0 0")
br1.cmd("ifconfig br1-eth1 0")
br1.cmd("ifconfig br1-eth2 0")
br1.cmd("ifconfig br1-eth3 0")
br1.cmd("ifconfig br1-eth4 0")
br1.cmd("vconfig add br1-eth4 10")
br1.cmd("vconfig add br1-eth4 20")
r1.cmd("vconfig add r1-eth0 10")
r1.cmd("vconfig add r1-eth0 20")
br1.cmd("brctl addbr mybr10")
br1.cmd("brctl addbr mybr20")
br1.cmd("brctl addif mybr10 br1-eth0")
br1.cmd("brctl addif mybr10 br1-eth1")
br1.cmd("brctl addif mybr10 br1-eth4.10")
br1.cmd("brctl addif mybr20 br1-eth2")
br1.cmd("brctl addif mybr20 br1-eth3")
br1.cmd("brctl addif mybr20 br1-eth4.20")
br1.cmd("ifconfig br1-eth4.10 up")
br1.cmd("ifconfig br1-eth4.20 up")
r1.cmd("ifconfig r1-eth0.10 up")
r1.cmd("ifconfig r1-eth0.20 up")
br1.cmd("ifconfig mybr10 up")
br1.cmd("ifconfig mybr20 up")
r1.cmd('ifconfig r1-eth0.10 192.168.10.254 netmask 255.255.255.0')
r1.cmd('ifconfig r1-eth0.20 192.168.20.254 netmask 255.255.255.0')
r1.cmd("echo 1 > /proc/sys/net/ipv4/ip_forward")
h1.cmd("ip address add 192.168.10.1/24 dev h1-eth0")
h1.cmd("ip route add default via 192.168.10.254")
h2.cmd("ip address add 192.168.10.2/24 dev h2-eth0")
h2.cmd("ip route add default via 192.168.10.254")
h3.cmd("ip address add 192.168.20.1/24 dev h3-eth0")
h3.cmd("ip route add default via 192.168.20.254")
h4.cmd("ip address add 192.168.20.2/24 dev h4-eth0")
h4.cmd("ip route add default via 192.168.20.254")
CLI(net)
net.stop()
```
執行後,開啟 r1, br1 終端機,執行`ifconfig`除了可以看到實體介面之外,虛擬介面 “.10”, “.20” 也會根據 vlan 去建立起來。

除此之外,我們可以開啟 wireshark,去監控 br1-eth4。
結果可以看到,在 192.168.10.1 發送封包到 192.168.20.1 時,會在封包內打上一個 “10” 的標籤,同理,當 192.168.20.1 發送封包回 192.168.10.1 時,也會被打上一個 “20” 的標籤。


其中要注意的是,**透過單臂路由去連接 bridge,也被稱為 trunk link**,**封包在經過 trunk link 時會被打上標籤**,**而 br1 與 host 們相連的 link 稱為 access link**,**是不帶標籤的**。而 vlan 在 bridge 與 router 相互連接的 interface 中,能夠產生出虛擬介面 “.10”, ".20",進而去實現單一條 link 進行不同網域的轉發以及相互通訊功能。
---
## sed 縮排程式的空白行
當你的程式碼是從網路上進行複製,而程式碼在編輯時會出現每行都有空白行,要解決這種問題可以使用`sed -i '/^$/d' test.py`去將空白行刪除,並縮排好程式碼,讓開發人員能夠更便利的去開發、撰寫程式。
> 1. `/^$`: ^: 起始,$: 結尾,**正則匹配**用法。
> 2. 當`^$`中間沒有任何符號或數字,代表匹配空白行,並且刪除它。
---
## Reference
1. Youtube video (Mininet ep.1 ~ ep.6): https://www.youtube.com/user/smallko2007/videos
2. bridge & switch & hub 介紹: https://notfalse.net/66/repeater-hub-bridge-switch
3. access link & trunk link: https://kknews.cc/zh-tw/code/zpnbozl.html
4. 單臂路由: https://www.itread01.com/content/1539320530.html