Bootstrap

理解linux虚拟网络设备bridge

上一节我们介绍了Linux veth设备,知道了如何把多个彼此隔离的网络命名空间(下面开始简称NS)与主机通过VETH连接起来,并且多个NS间也能相互通信,在这一节我们将介绍另一种方式,用linux bridge。

Linux bridge即linux虚拟网桥,工作方式类似于物理的网络交换机,工作在二层时,能够转发以太网报文,能够学习MAC地址与端口的映射关系,但是也能工作在三层,提供类似三层交换机的功能,想要让linux bridge工作在三层,只需要给它配置一个IP地址。

还是通过一些示例来了解一下,我们将使用linux bridge连接两个NS:

通过bridge方式连接容器和主机

通过下面的命令可以创建一个bridge并启动

ip link add br0 type bridge
ip link set br0 up

接下来我们创建两个新的NS和两对VETH网卡

ip netns add ns1
ip netns add ns2
ip link add veth0 type veth peer name veth1
ip link add veth2 type veth peer name veth3
ip link set veth0 netns ns1
ip link set veth2 netns ns2
ip netns exec ns1 ip addr add 192.168.10.10/24 dev veth0
ip netns exec ns2 ip addr add 192.168.10.11/24 dev veth2
ip netns exec ns1 ip link set veth0 up
ip netns exec ns2 ip link set veth2 up 

接下来就是把两端都插到bridge上:

ip link set veth1 master br0
ip link set veth3 master br0
ip link set veth1 up
ip link set veth3 up

就这样两个NS就可以相互PING通了,因为是相同网段,所以不需要默认网关,又因为bridge会广播arp请求,所以不用设置proxy_arp也不用写邻居表,NS1就能按照最正常方式通过ARP拿到NS2的MAC地址。

但是此时在主机上PING两个NS都是PING不通的,因为主机不知道怎么去192.168.10.0/24网段,所以先给主机加条路由:

ip route add 192.168.10.0/24 dev br0 scope link

注意主机的这条路由针对的是整个网段192.168.10.0/24,就是说后面如果继续加新的NS,只要IP还在这个段内,就不用再增加主机路由了。

来看一下现在的状态:

img

从主机ping一下NS1还是不行,想想应该是NS1不知道怎么回这个包,看一下当前NS1中的路由信息:

[root@worker3 ~]# ip netns exec ns1 ip route
192.168.10.0/24 dev veth0 proto kernel scope link src 192.168.10.10

这条路由是我们在给veth0设置ip的时候由系统自动生成的。

我们给ns1和ns2都加个默认路由,然后打开br0的arp代答:

ip netns exec ns1 ip route add 0.0.0.0/0 via 169.2.2.2 dev veth0 onlink
ip netns exec ns2 ip route add 0.0.0.0/0 via 169.2.2.2 dev veth2 onlink
echo 1 > /proc/sys/net/ipv4/conf/br0/proxy_arp

这时候应该就ping得通了。

两种方式的区别

到目前为止我们介绍了两种在同个主机连接多个ns的方法,现在来详细介绍一下他们之间的异同点:

如果只是用veth连接,就是单纯地把主机当成路由器,两个ns之间的通信数据包都是要走一遍主机的协议栈的转发流程的,而用bridge的纯二层方式(注意是纯二层,因为自始至终都没有给bridge设置ip)则在二层直接把数据包就从ns1转到了ns2,没有再走主机的协议栈,如下图所示:

img

只用veth网卡连接多个ns时,ns2到ns1数据包经过的流程,橙色部分

img

只用veth网卡加bridge连接多个ns时,ns2到ns1数据包经过的流程,橙色部分

这个我们可以用如下方式求证,关掉主机的路由转发功能:

echo 0 > /proc/sys/net/ipv4/ip_forward

然后你会发现ns1中ping ns2其实还是可以ping通的。

那这样一来,主机的防火墙策略岂不是没用了?k8s的网络策略就是在主机的iptables规则中来控制的,为了让这个规则生效,还是有办法的,可以让经过linux bridge的数据包也过一遍iptabls规则

echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables

注:开启了这个参数,数据包还是不会去主机的协议栈,只是linux bridge的__br_forward方法中显式地调用了

NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD,net, NULL, skb, indev, skb->dev,br_forward_finish)

代码在:/net/bridge/br_forward.c

这时候在主机的iptables防火墙里增加一个过滤规则,把数据包全拦截掉

iptables -P FORWARD DROP

然后ns1就ping不通ns2了。

注:linux bridge 不能跨网络命名空间,所以不要尝试把br0转到ns1中,下面的命令会报错

ip link set br0 netns ns1

RTNETLINK answers: Invalid argument

凡是netns-local为on的设备都不能跨网络命名空间,像loopback/vxlan/bridge,用下面的命名可以查看eth0的netns-local的值

ethtool -k eth0|grep netns-local

;