---
# System prepended metadata

title: 網路模擬與分析(3/22)：在 Mininet 環境中實現 Router & Bridge 功能 + 應用
tags: [單臂路由, trunk link, vlan, access link, NAT 轉換, Mininet]

---

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

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


```
#!/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 (私人網域)，如下圖：

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

其中特別注意的是，在私人網域轉換成公有網域，需要進行 **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。

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

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

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

而當發生了 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 環境中去達到互相通訊呢？ 

拓樸如下：

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

特別注意的是，我們在開始實驗之前，我們需要先安裝一個套件`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`

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


> 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`去查看，可以看到：

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

現在 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，可以看到：

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

當我們在 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的網域，反之亦然。

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

因此我們將`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 結合，並且讓不同網域之間能互相通訊，拓樸如下：

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

我們將`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 稱為***單臂路由***。

拓樸如下：

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

我們將`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 去建立起來。

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

除此之外，我們可以開啟 wireshark，去監控 br1-eth4。

結果可以看到，在 192.168.10.1 發送封包到 192.168.20.1 時，會在封包內打上一個 “10” 的標籤，同理，當 192.168.20.1 發送封包回 192.168.10.1 時，也會被打上一個 “20” 的標籤。

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

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

其中要注意的是，**透過單臂路由去連接 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