【NetDevOps】新一代网工需要了解的那点事儿(三)---Linux Bridge
3 Linux bridge
和veth-pari一样,Linux bridge也是一种虚拟网络设备。这里可以将bridge看作是一个普通的二层交换机,因为它具备二层交换机的所有功能。对于普通的物理设备来说一般只有两端,从一端进来的数据会从另一端出去,比如物理网卡从外面网络中收到的数据会转发到内核协议栈中,而从协议栈过来的数据会转发到外面的物理网络中。而bridge有多个端口,数据可以从多个端口进,也可以从多个端口出去。Bridge的这个特性使得它主要被用于和其他网络设备接入使用,比如虚拟网络设备、物理网卡等。
bridge是建立在从设备上(物理设备、虚拟设备、vlan设备等,即attach一个从设备,类似于现实世界中的交换机和一个用户终端之间连接了一根网线),并且可以为bridge配置一个IP(参考LinuxBridge MAC地址行为),这样该主机就可以通过这个bridge设备与网络中的其他主机进行通信了。另外它的从设备被虚拟化为端口port,它们的IP及MAC都不在可用,且它们被设置为接受任何包,最终由bridge设备来决定数据包的去向:接收到本机、转发、丢弃、广播。
3.1 实现原理
Bridge的功能主要在内核里实现。当一个从设备被 attach 到 Bridge 上时,相当于现实世界里交换机的端口被插入了一根连有终端设备的网线。这时在内核程序里,一个用于接受数据的回调函数(netdev_rx_handler_register())被注册。以后每当这个从设备收到数据时都会调用这个函数用来把数据转发到Bridge上。当Bridge接收到此数据时,br_handle_frame()被调用,进行一个和现实世界中的交换机类似的处理过程:判断包的类别(广播/单点),查找内部MAC端口映射表,定位目标端口号,将数据转发到目标端口或丢弃,自动更新内部 MAC 端口映射表以自我学习。
Bridge和一般的二层交换机有一个区别:数据被直接发到 Bridge上,而不是从一个端口接收。这种情况可以看做 Bridge自己有一个MAC可以主动发送报文,或者说Bridge自带了一个隐藏端口和宿主Linux系统自动连接,Linux上的程序可以直接从这个端口向Bridge上的其他端口发数据。所以当一个Bridge拥有一个网络设备时,如 bridge0 加入了 eth0 时,实际上bridge0拥有两个有效MAC地址,一个是bridge0的,一个是eth0的,他们之间可以通讯。这里还有一个有意思的事情是,Bridge可以设置IP地址。通常来说IP地址是三层协议的内容,不应该出现在二层设备Bridge上。但是Linux里Bridge是通用网络设备抽象的一种,只要是网络设备就能够设定IP地址。当一个bridge0拥有IP后,Linux便可以通过路由表或者IP表规则在三层定位bridge0,此时相当于Linux拥有了另外一个隐藏的虚拟网卡和Bridge的隐藏端口相连,这个网卡就是名为bridge0的通用网络设备,IP可以看成是这个网卡的。当有符合此IP的数据到达bridge0时,内核协议栈认为收到了一包目标为本机的数据,此时应用程序可以通过Socket接收到它。一个更好的对比例子是现实世界中的带路由的交换机设备,它也拥有一个隐藏的MAC地址,供设备中的三层协议处理程序和管理程序使用。设备里的三层协议处理程序,对应名为bridge0的通用网络设备的三层协议处理程序,即宿主Linux系统内核协议栈程序。设备里的管理程序,对应bridge0所在宿主Linux 系统里的应用程序。
Bridge的实现在当前有一个限制:当一个设备被attach到Bridge上时,那个设备的IP会变的无效,Linux不再使用那个IP在三层接收数据。举例如下:如果 veth0 本来的IP是 12.1.1.2,此时如果收到一个目标地址是12.1.1.2 的数据,Linux的应用程序能通过Socket操作接收到它。而当veth0被attach到一个bridge0后,尽管veth0的IP还在,但应用程序是无法接收到上述数据的。此时若想收到该数据则应该把IP 12.1.1.2 赋bridge0。
3.2 创建bridge
如下图所示,我们创建一对veth-pari:veth0、veth1;然后创建一个Bridge:br0,其中veth0配置IP地址为12.1.1.1/24,veth1配置IP地址为12.1.1.2/24。然后我们将veth0加入到br0中,此时我们在veth1上ping12.1.1.1测试网络是否连通。为验证1.5.1中介绍原理,我们将veth0的IP地址移至br0,此时再通过veth1去ping12.1.1.1测试网络是否连通。实验过程如下:
+----------------------------------------------------------+
| By:[f0rGeEk] |
| +--------------------------------------------------+ |
| | | |
| | Network Protocal Stack | |
| | | |
| +---+--------------+--------------+------------+---+ |
| ^ ^ | ^ |
| | | | | |
+----------------------------------------------------------+
| | | | | |
| | | | | |
| v v v v |
| +--+---+ +----+---+ +--+---+ +--+---+ |
| | | | | | | | | |
| | eth0 | | br0 |<----->+ veth0| | veth1| |
| | | |12.1.1.1/24 | | |12.1.1.2/24
| +---+--+ +--------+ +--+---+ +---+--+ |
| ^ ^ ^ |
| | | | |
| | +-------------+ |
| | |
+----------------------------------------------------------+
|
v
Physical Network
- 创建veth-pair、bridge
[root@d1 ~]# ip link add veth0 type veth peer name veth1
[root@d1 ~]# ip link set dev veth0 up
[root@d1 ~]# ip link set dev veth1 up
[root@d1 ~]# ip addr add 12.1.1.1/24 dev veth0
[root@d1 ~]# ip addr add 12.1.1.2/24 dev veth1
# 创建并激活一个bridge
[root@d1 ~]# ip link add name br0 type bridge
[root@d1 ~]# ip link set br0 up
[root@d1 ~]# ip link list br0
9: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether a2:47:b8:b7:21:9d brd ff:ff:ff:ff:ff:ff
# 将veth0加入bridge中
[root@d1 ~]# ip link set dev veth0 master br0
[root@d1 ~]# ip link list br0
9: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 22:d0:1d:fd:5d:9b brd ff:ff:ff:ff:ff:ff
# 查看桥接信息
[root@d1 ~]# bridge link
8: veth0 state UP @veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2
# 首先修改各虚拟网络设备的access_local和rp_filter
[root@d1 ~]# echo 1 > /proc/sys/net/ipv4/conf/br0/accept_local
[root@d1 ~]# echo 1 > /proc/sys/net/ipv4/conf/veth1/accept_local
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/br0/rp_filter
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/veth1/rp_filter
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/default/rp_filter
仔细观察上述过程,我们发现一个有趣的现象,当veth0加入bridge br0后,br0的MAC地址则变成了veth0的MAC地址。下面我们在veth1上ping12.1.1.1测试连通性:
- 测试
[root@d1 ~]# ping -c 3 -I veth1 12.1.1.1
ping: Warning: source address might be selected on device other than veth1.
PING 12.1.1.1 (12.1.1.1) from 10.10.10.137 veth1: 56(84) bytes of data.
--- 12.1.1.1 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2009ms
- 为bridge配置IP地址
[root@d1 ~]# ip addr del 12.1.1.1/24 dev veth0
[root@d1 ~]# ip addr add 12.1.1.1/24 dev br0
- 测试
[root@d1 ~]# ping -c 2 -I veth1 12.1.1.1
PING 12.1.1.1 (12.1.1.1) from 12.1.1.2 veth1: 56(84) bytes of data.
64 bytes from 12.1.1.1: icmp_seq=1 ttl=64 time=0.049 ms
64 bytes from 12.1.1.1: icmp_seq=2 ttl=64 time=0.072 ms
3.3 连接两个NS
如下图所示,我们创建两个NS:ns1、ns2,同时创建两对veth-pari:veth0>br-veth0;veth>br-veth1。创建一个bridge:br0,并将veth0和veth1加入至br0然后为br-veth0和br-veth1分别配置IP地址。最后在br-veth0接口上ping12.1.1.2测试网络是否连通。
+-----------------------------------------------------------------------------+
| By:[F0rGeEk] |
| Linux Bridge connetc NS |
| +--------------------+ +-------------------+ |
| | NameSpace NS1| | NameSpace NS2| |
| | | | | |
| | br-veth1 | | br-veth0 | |
| | +---+ | | +---+ | |
| | | | | | | | | |
| +-------+---+--------+ +--------+---+------+ |
| |12.1.1.2/24 12.1.1.1/24| |
| | | |
| | +---------------+ | |
| | veth-pari | | veth-pari | |
| | +--+ +--+ | |
| +------------------+ | | +-------------------+ |
| veth1+--+ +--+ veth0 |
| | Bridge 0 | |
| +---------------+ |
+-----------------------------------------------------------------------------+
- 创建ns1、ns2、br0 两对veth-pair
[root@d1 ~]# ip netns add ns1
[root@d1 ~]# ip netns add ns2
[root@d1 ~]# ip link add veth0 type veth peer name br-veth0
[root@d1 ~]# ip link add veth1 type veth peer name br-veth1
[root@d1 ~]# ip link add name br0 type bridge
[root@d1 ~]# ip link set dev veth0 up
[root@d1 ~]# ip link set dev veth1 up
[root@d1 ~]# ip link set dev br-veth0 up
[root@d1 ~]# ip link set dev br-veth1 up
[root@d1 ~]# ip link set dev br0 up
- 将veth-pair分别加入NS和bridge中,并激活NS中的所有接口
[root@d1 ~]# ip link set br-veth0 netns ns2
[root@d1 ~]# ip link set br-veth1 netns ns1
[root@d1 ~]# ip link set dev veth0 master br0
[root@d1 ~]# ip link set dev veth1 master br0
# 激活NS中的接口
[root@d1 ~]# ip netns exec ns1 ifconfig br-veth1 up
[root@d1 ~]# ip netns exec ns2 ifconfig br-veth0 up
[root@d1 ~]# ip netns exec ns1 ifconfig lo up
[root@d1 ~]# ip netns exec ns2 ifconfig lo up
- 配置IP地址
[root@d1 ~]# ip netns exec ns1 ifconfig br-veth1 12.1.1.1/24
[root@d1 ~]# ip netns exec ns2 ifconfig br-veth0 12.1.1.2/24
- 检查上述配置
# 查看NS中网卡IP配置
[root@d1 ~]# ip netns exec ns1 ip addr show br-veth1
5: br-veth1@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 1a:36:ef:1e:8b:98 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 12.1.1.1/24 brd 12.1.1.255 scope global br-veth1
valid_lft forever preferred_lft forever
inet6 fe80::1836:efff:fe1e:8b98/64 scope link
valid_lft forever preferred_lft forever
[root@d1 ~]# ip netns exec ns2 ip addr show br-veth0
3: br-veth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether d2:45:60:46:ab:77 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 12.1.1.2/24 brd 12.1.1.255 scope global br-veth0
valid_lft forever preferred_lft forever
inet6 fe80::d045:60ff:fe46:ab77/64 scope link
valid_lft forever preferred_lft forever
# 检查bridge配置
[root@d1 ~]# bridge link
4: veth0 state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2
6: veth1 state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2
- 测试两个NS之间的连通性
# ipv4连通测试
[root@d1 ~]# ip netns exec ns2 ping 12.1.1.1 -c3 -I br-veth0
PING 12.1.1.1 (12.1.1.1) from 12.1.1.2 br-veth0: 56(84) bytes of data.
64 bytes from 12.1.1.1: icmp_seq=1 ttl=64 time=0.058 ms
64 bytes from 12.1.1.1: icmp_seq=2 ttl=64 time=0.062 ms
64 bytes from 12.1.1.1: icmp_seq=3 ttl=64 time=0.064 ms
--- 12.1.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.058/0.061/0.064/0.006 ms
# ipv6连通测试
[root@d1 ~]# ip netns exec ns2 ping6 fe80::1836:efff:fe1e:8b98 -c 3 -I br-veth0
PING fe80::1836:efff:fe1e:8b98(fe80::1836:efff:fe1e:8b98) from fe80::d045:60ff:fe46:ab77%br-veth0 br-veth0: 56 data bytes
64 bytes from fe80::1836:efff:fe1e:8b98%br-veth0: icmp_seq=1 ttl=64 time=0.057 ms
64 bytes from fe80::1836:efff:fe1e:8b98%br-veth0: icmp_seq=2 ttl=64 time=0.067 ms
64 bytes from fe80::1836:efff:fe1e:8b98%br-veth0: icmp_seq=3 ttl=64 time=0.069 ms
--- fe80::1836:efff:fe1e:8b98 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.057/0.064/0.069/0.008 ms