什么是Bridge
Bridge是一种虚拟网络设备,所以具备虚拟网络设备的所有特性,比如可以配置 IP、MAC 等。除此之外,Bridge 还是一个虚拟交换机,具有交换机所有的功能。
对于普通的网络设备,就像一个管道,只有两端,数据从一端进,从另一端出,如物理网卡从外面网络中收到数据转发给内核网络协议栈,从网络协议栈过来的数据转发到外部网络。而 Bridge 有多个端口,数据可以从多个端口进,从多个端口出,原理和交换机类似。
Bridge 的这个特性让它可以接入其他的网络设备,比如物理设备、虚拟设备。Bridge 通常充当主设备,其他设备为从设备,这样的效果就等同于物理交换机的端口连接了一根网线。Bridge是和主机的网络协议栈相连的,也就是说通过bridge的网络数据,是要经过host主机的内核协议栈的进行处理的(我的理解就是net_filter通过iptables设置的rules进行处理)
什么是veth-pair
veth-pair 就是一对的虚拟设备接口,它都是成对出现的。一端连着协议栈,一端彼此相连着
正因为有这个特性,它常常充当着一个桥梁,连接着各种虚拟网络设备。例如在docker中,他们连接容器所处网络namespace和bridge。
此图中veth-pair一个是连接在bridge上的veth,另一个是连接在容器网络namespace上的eth0。veth和eth0彼此相连,veth通过bridge与host的网络协议栈相连,eth0通过容器namespace和容器的网络协议栈相连。这里的容器是在host内部的容器,不考虑跨节点的容器。
模拟容器和bridge连接
安装docker成功后,我们可以看到docker在host机器上默认的创建一个docker0的bridge,这个bridge也是容器的默认的连接方式。在这里我们将用将创建网络namespace来模拟容器,让namespace和我们创建好的bridge连接起来,容器之间可以相互访问,容器可以和host所在网络访问。
首先我们要打开host机器上的ip_forwarding功能,这样host的物理网卡和bridge之间才能forward数据包。IP_FORWARD可以使连接在同一个网络协议栈上的网络接口间进行数据包的转发,适合host上有多块物理网卡,虚拟设备接口,结合iptables的 forward链一起使用。例如主机网络协议栈上连接了30.0/24这个物理网卡和88.0/24这个bridge虚拟设备,一个数据包从30.0/24这块网卡进入网络协议栈,数据包的destination是88.0/24这个网段上的某一个IP地址,内核协议栈发现这个包是去bridge子网的,于是就会把这个数据包forward给bridge连在网络协议栈上的端口并进行处理。如果forward功能没有打开,数据包无法转发到bridge,只能在drop掉。
这个重启后就失效了,要想永久有效请修改文件/etc/sysctl.conf
创建一个bridge br0,激活这个device,并给他分配一个IP地址及掩码192.168.88.1/24。
创建一个网络的namespace ns0,然后在这个网络namespace中执行bash命令(另外起一个terminal做这个事情),如果你还不清楚什么是linux内核namespace,请google相关资料。
执行命令后,我这个terminal已经进入到ns0这个namespace了,这和docker容器的网络namespace是一致的。通过ip命令查询,现在这个命令空间里面只有一个loopback的网络设备,没有其他网络设备相连。然后通过命令使loopback设备active
接下来我们就要创建veth pair,一边和这个ns0连接,一边和br0连接,并给他们分配IP地址。(这些操作不要在ns0里执行bash命令的terminal里面做)
这里创建一个veth pair,veth0和veth0p,并且把veth0和ns0相连,并分配和br0同一个网段的IP地址192.168.88.2,启动veth0p这个设备。返回到ns0下bash的terminal,可以看到多出一个网卡信息并分配到IP地址,但是这个dev是处于LOWERLAYDOWN的状态,这是由于pair另一端还没有接入到br0.
接下来就是把veth0连接到br0上,并启动这个设备。
我们可以看到br0上有一个veth设备连接上来,返回去看ns0下的bash,我们可以看到veth0p的状态已经变成UP。
现在br0的IP是192.168.88.1,ns0的IP地址为192.168.88.2,我们可以在ns0下去ping 192.168.88.1是可以通的,我们可以通过同样的步骤在创建另外一个namespace ns1并分配IP192.168.88.3。ns0,ns1, br0都是可以相互连接的,这里就不重复创建了。
目前为止连接在bridge上的namespace都是可以相互访问,这个体现了bridge虚拟交换机的功能。如果我们还需要容器能访问host所在的网络,我们需要继续配置下面步骤。
在ns0下的bash命令行下设置默认路由,不属于192.168.88.0/24这个网段的数据包全部路由到默认网关br0 192.168.88.1.
由于192.168.88.0/24这个网段和host所在的网路不是一个网络,一切从88.0/24这个网络的数据包要访问外部网络都是要通过host的主机IP去访问的,对于远端服务连接它是不知道88.0/24这个网络的,他接收的数据包的src地址都是host的IP地址,在这里我们需要对88.0/24这个网段的数据包做SNAT。在host的terminal下通过iptables的命令设置。
iptables必须是enable的,并对NAT table的POSTROUTING chain添加一条规则,所有src地址为88.0/24这个网段的数据包,全部走host机器上的物理网卡,通过MASQUERADE选项修改src地址为网卡eno16777736的IP地址。你也可以用SNAT这个参数代替指定一个特殊IP地址。
请自行参考iptables的用法和原理,如果你的host主机上实现设置了其他iptables rules从而阻止了88.0/24这个网络的forward或者routing,需要自行检查一下,每一个机器的网络状态是不一样的没办法全部覆盖。这里推荐一个系列博客很详细的讲解了iptables。
这里可以看到我们在ns0下的bash命令行,可以ping通host所在网络的机器30.134和internet上的baidu。现在就完成了整个模拟实验。