集線器和交換機是兩種典型的網路裝置,集線器 位於 物理層,而 交換機 位於於 資料鏈路層 ,行為明顯不同。本節準備了兩個簡單實驗,旨在通過實踐加深對理論知識的理解,逐步掌握 Linux 主機網路操作。
實驗一:觀察乙太網集線器
本實驗將 3 臺 Linux 主機連到一個集線器上,以此觀察集線器的工作行為,網路拓撲圖如下:
實驗環境以 docker 容器的形式提供,執行這個 docker 命令即可一鍵開啟:
docker run --name hub-lab --rm -it --privileged --cap-add=NET_ADMIN --cap-add=SYS_ADMIN -v /data -h hub-lab fasionchan/netbox:0.5 bash /script/hub-lab.sh
實驗環境開啟後,可以看到 3 個視窗,各自代表一臺主機:
這是用 tmux 命令實現的視窗劃分,按下_ Ctrl-B_ 後再按方向鍵,即可在不同主機視窗間切換。
請特別注意,按下 Ctrl-B 後要鬆手,然後再按方向鍵,才能切到想要操作的主機視窗。
還有一種更快捷的切換方法,先按下 Ctrl-B ,鬆手後再按 Q 。這時,每個視窗都會顯示一個數字。接著,按下對應的數字即可切到想要的視窗:
我們先切到主機 ant ,觀察它的網路卡資訊,ifconfig 或 ip 命令均可:
root@ant [ ~ ] ➜ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 32:90:b9:9f:35:56 txqueuelen 1000 (Ethernet)
RX packets 6 bytes 540 (540.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 270 (270.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
root@ant [ ~ ] ➜ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/tunnel6 :: brd ::
6: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 32:90:b9:9f:35:56 brd ff:ff:ff:ff:ff:ff link-netnsid 0
接著,切到主機 bee 和 cicada ,繼續觀察它們的網路卡資訊:
root@bee [ ~ ] ➜ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/tunnel6 :: brd ::
8: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether a2:17:41:bb:cd:98 brd ff:ff:ff:ff:ff:ff link-netnsid 0
root@cicada [ ~ ] ➜ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/tunnel6 :: brd ::
10: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether ee:76:f2:37:5e:69 brd ff:ff:ff:ff:ff:ff link-netnsid 0
經過觀察,3 臺主機網路卡及 MAC 地址資訊整理如下:
主機 | 網路卡 | MAC地址 |
---|---|---|
ant | eth0 | 32:90:b9:9f:35:56 |
bee | eth0 | a2:17:41:bb:cd:98 |
cicada | eth0 | ee:76:f2:37:5e:69 |
現在,我們從主機 ant 向主機 bee 傳送一句話,看主機 bee 是否可以收到這個資訊,於此同時觀察主機 cicada 是否也可以收到。開始傳送之前,我們先在 bee 和 cicada 執行抓包工具 tcpdump 命令,嗅探網路流量:
root@bee [ ~ ] ➜ tcpdump -ni eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
root@cicada [ ~ ] ➜ tcpdump -ni eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
tcpdump 命令 -i 選項指定嗅探網路卡,這裡我們嗅探每臺主機 eth0 網路卡上的流量。
一切準備就緒,我們在主機 ant 上執行自制工具 sendether 給 bee 發一段文字:
root@ant [ ~ ] ➜ sendether -i eth0 -t a2:17:41:bb:cd:98 -T 0x0900 -d 'hello, world!'
sendether 是一個自制命令,用於傳送乙太網幀。其中,-i 指定傳送網路卡,-t 指定目的地址,-T 指定資料型別,-d 指定要傳送的資料。後續的程式設計環節,我們會講解 sendether 是如何封裝、傳送乙太網幀的。
我們立馬看到主機 bee 上的 tcpdump 抓到一個乙太網幀,它就是 ant 發出來的 hello, world!
:
root@bee [ ~ ] ➜ tcpdump -ni eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
16:37:27.254658 32:90:b9:9f:35:56 > a2:17:41:bb:cd:98, ethertype Unknown (0x0900), length 27:
0x0000: 6865 6c6c 6f2c 2077 6f72 6c64 21 hello,.world!
注意到,主機 cicada 也收到這個幀,這符合集線器的行為:
root@cicada [ ~ ] ➜ tcpdump -ni eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
16:37:27.254624 32:90:b9:9f:35:56 > a2:17:41:bb:cd:98, ethertype Unknown (0x0900), length 27:
0x0000: 6865 6c6c 6f2c 2077 6f72 6c64 21 hello,.world!
由於這個幀的目的主機並不是 cicada ,cicada 協議棧將丟棄它。
實驗二:觀察乙太網交換機
本實驗將 3 臺 Linux 主機連到一個交換機上,以此觀察交換機的工作行為,網路拓撲圖如下:
實驗環境同樣通過 docker 容器提供,執行以下命令即可一鍵開啟:
docker run --name switch-lab --rm -it --privileged --cap-add=NET_ADMIN --cap-add=SYS_ADMIN -v /data -h switch fasionchan/netbox:0.5 bash /script/switch-lab.sh
實驗環境啟動後,可以看到 4 個由 tmux 命令劃分的視窗,分別代表 3 臺主機以及交換機。
為了方便觀察交換機 MAC 地址學習的過程,我們為每臺主機設定了一個很好分辨的 MAC 地址:
主機 | 網路卡 | MAC地址 | 交換機埠 |
---|---|---|---|
ant | eth0 | 40:aa:aa:aa:aa:aa | 1 |
bee | eth0 | 40:bb:bb:bb:bb:bb | 2 |
cicada | eth0 | 40:cc:cc:cc:cc:cc | 3 |
實驗環境中的交換機由 bridge 虛擬裝置模擬,裝置名為 switch0
:
root@switch [ ~ ] ➜ ip link show switch0
4: switch0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 4a:9e:f8:3c:75:40 brd ff:ff:ff:ff:ff:ff
執行 brctl 命令,可以檢視交換機當前的 MAC 地址表:
root@switch [ ~ ] ➜ brctl showmacs switch0
port no mac addr is local? ageing timer
3 4a:9e:f8:3c:75:40 yes 0.00
3 4a:9e:f8:3c:75:40 yes 0.00
2 6a:64:44:0d:d1:55 yes 0.00
2 6a:64:44:0d:d1:55 yes 0.00
1 be:24:47:bd:f2:52 yes 0.00
1 be:24:47:bd:f2:52 yes 0.00
噫?怎麼 MAC 地址表已經有一些條目了?我們明明還沒有在任何主機上發資料,地址表按理說應該是空的呀!
其實,這些 MAC 地址是交換機自己的, is local
列值都是 yes
。如果將該列值為 yes
的記錄過濾掉,就可以確認 MAC 地址表確實為空(暫未學習到任何地址):
root@switch [ ~ ] ➜ brctl showmacs switch0 | grep -v yes
port no mac addr is local? ageing timer
現在,我們在主機 ant 上往主機 bee 傳送一個乙太網幀,來觀察交換機行為。開始之前,我們先在主機 bee 和 cicada 上執行 tcpdump 命令來嗅探網路流量。
root@bee [ ~ ] ➜ tcpdump -ni eth0
root@cicada [ ~ ] ➜ tcpdump -ni eth0
root@ant [ ~ ] ➜ sendether -i eth0 -t 40:bb:bb:bb:bb:bb -d 'hello, bee!'
這個幀成功發出去後,我們同時在主機 bee 和 cicada 上觀察它。原因在於,交換機還沒學到主機 bee 的 MAC 地址,只能將這個幀轉發到其他所有埠,因此 cicada 也會收到它。
root@bee [ ~ ] ➜ tcpdump -ni eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
16:40:34.437330 40:aa:aa:aa:aa:aa > 40:bb:bb:bb:bb:bb, ethertype Unknown (0x0900), length 25:
0x0000: 6865 6c6c 6f2c 2062 6565 21 hello,.bee!
root@cicada [ ~ ] ➜ tcpdump -ni eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
16:40:34.437152 40:aa:aa:aa:aa:aa > 40:bb:bb:bb:bb:bb, ethertype Unknown (0x0900), length 25:
0x0000: 6865 6c6c 6f2c 2062 6565 21 hello,.bee!
交換機從埠 0 接到主機 ant 傳送的乙太網幀,源地址是 40:aa:aa:aa:aa:aa
,便知道以後發給這個地址的幀應該轉發給埠 0 。這樣一來,交換機機智地學習到主機 ant 的 MAC 地址:
root@switch [ ~ ] ➜ brctl showmacs switch0 | grep -v yes
port no mac addr is local? ageing timer
1 40:aa:aa:aa:aa:aa no 1.97
接著,我們在主機 bee 向 ant 回覆一個資訊:
root@bee [ ~ ] ➜ sendether -i eth0 -t 40:aa:aa:aa:aa:aa -d 'how are you?'
由於交換機已經學習到 ant 的地址,知道去往 40:aa:aa:aa:aa:aa
的幀應該轉發到埠 0 ,位於埠 3 的 cicada 主機便不會收到這個幀了。
同理,在這個過程中,交換機學習到主機 bee 的 MAC 地址 40:bb:bb:bb:bb:bb
:
root@switch [ ~ ] ➜ brctl showmacs switch0 | grep -v yes
port no mac addr is local? ageing timer
1 40:aa:aa:aa:aa:aa no 60.17
2 40:bb:bb:bb:bb:bb no 50.14
這樣一來,主機 ant 再給 bee 發資料,cicada 同樣也不會收到了:
root@ant [ ~ ] ➜ sendether -i eth0 -t 40:bb:bb:bb:bb:bb -d 'fine, thank you!'
【小菜學網路】系列文章首發於公眾號【小菜學程式設計】,敬請關注: