在阅读本篇文章前,需要先前置阅读:
在之前介绍 Bridge 的文章中,我们讲到 Bridge 是一个虚拟网桥,工作在二层,功能类似于传统的物理交换机。然后也通过 Bridge 完成了容器间网络打通和容器访问外部网络的实验,但是由于篇幅有限,对于 Bridge 内具体的数据包是如何流转的并没有一个直观的认识,所以本文我们再对 Bridge 进行一点扩展实验,观察其工作方式。
实验环境概览
首先介绍一下实验环境,中间主要有三个 namespace 组成:
root
:系统默认 namespace,里面包含物理网卡enp0s5
,和 Linux Bridgebr0
;ns0
:用于模拟容器网络,通过 veth pairveth0
和veth0_br
与root
的网络打通;ns1
:用于模拟容器网络,通过 veth pairveth1
和veth1_br
与root
的网络打通;
在本实验中,br0
上会陆续接入veth0_br
、veth1_br
、tap0
以及enp0s5
设备,用于在二层打通各 namespace 间的网络以及物理网络。
我们在之前介绍 TUN/TAP 设备的文章中有讲到,TAP 工作在二层,可以接收到以太网帧,所以我们创建了
tap0
设备,在它绑定的用户程序进程中仅打印接收到的数据包,不做任何回包。这里仅仅是使用tap0
的特性来观察br0
的运作是否符合预期。
观察容器间网络
最基础的设备是网桥,这里我们先创建网桥:
$ brctl addbr br0
$ ip link set br0 up
先来创建namespace ns0
,新增 veth pair 设备veth0
和veth0_br
,其中veth0
这端放入ns0
:
$ ip netns add ns0
$ ip link add veth0 type veth peer name veth0_br
$ ip link set dev veth0 netns ns0
$ ip netns exec ns0 ip addr add 10.1.1.100/24 dev veth0
$ ip netns exec ns0 ip link set veth0 up
$ ip link set veth0_br up
veth pair 设置好后,我们将veth0_br
插入网桥br0
:
$ brctl addif br0 veth0_br
我们知道网桥是有 mac table 的,它会学习并记录 mac 与 port 的关联关系,所以这里我们也查看一下br0
的 mac table。
可以看到veth0_br
的 mac 已经记录在表中:
# `veth0_br`的 mac 地址是:ce:85:09:8a:b1:7b
$ ip a
...
5: veth0_br@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UP group default qlen 1000
link/ether ce:85:09:8a:b1:7b brd ff:ff:ff:ff:ff:ff link-netns ns0
inet6 fe80::cc85:9ff:fe8a:b17b/64 scope link
valid_lft forever preferred_lft forever
$ brctl showmacs br0
port no mac addr is local? ageing timer
1 ce:85:09:8a:b1:7b yes 0.00
1 ce:85:09:8a:b1:7b yes 0.00
veth0_br的 mac 对应 port 1,这是符合预期的。但同时我们也知道,在 port 1 除了
veth0_br外,还应该有逻辑上与它直接连接的
veth0设备,只是此时
veth0还没有任何请求发起,所以
br0`不知道它的存在。
我们在ns0
发起一个 ping 请求,虽然我们知道不会通,但是内核仍然会发起 arp 广播,这个 arp 请求在ns0
中会发送到veth0
网卡,同时立刻被投递到插入了br0
的veth0_br
,br0
就会知道 port 1 还有另外一个设备:
# shell-0
$ ip netns exec ns0 ping 10.1.1.200
# shell-1
# `veth0`的 mac 地址是:da:ff:de:36:22:8e
$ ip netns exec ns0 ip a
...
9: veth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether da:ff:de:36:22:8e brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.1.1.100/24 scope global veth0
valid_lft forever preferred_lft forever
inet6 fe80::d8ff:deff:fe36:228e/64 scope link
valid_lft forever preferred_lft forever
$ brctl showmacs br0
port n