使用 Veth Pair 虚拟网卡对不同的网络空间进行通信

宝剑锋从磨砺出,梅花香自苦寒来。这篇文章主要讲述使用 Veth Pair 虚拟网卡对不同的网络空间进行通信相关的知识,希望能为你提供帮助。
使用 Veth Pair 虚拟网卡对不同的网络空间进行通信解决问题:由于资源隔离带来的名称空间的网络隔离,解决不同的网络隔离的通信,隔离的目的是为了资源部署,我们诉求是要达到互联互通,不能只隔离,不通信,所以需要使用虚拟网卡对的方式来实现网络的互联互通。
如图是一个传统的同一个 node 节点的,不同的 pod 之间互相通信模型
brctl 命令

# 安装命令
~# apt install bridge-utils ethtools -y

# 查看当前服务器 bridge 类型的网卡
~# brctl show
bridge name bridge idSTP enabled interfaces
docker08000.0242910296ce no

# 查看当前网卡下的 mac 地址
~# brctl showmacs docker0
port no mac addris local?(是否是本地的mac地址,no表示为非本地的地址)ageing timer
1 12:88:4c:d1:3b:24 yes0.00
1 12:88:4c:d1:3b:24 yes0.00

模拟 Linux bridge
我们通过本地创建一个网桥,然后连接不同的网络空间,来达到模拟同一个 node 节点的 pod 通过网桥通信的目的。网桥的本质其实就是一个模拟二层的交换机,通过 MAC 地址来进行数据链路层的通信。
1.创建 br0 网桥
# 添加 br0 bridge 网桥
~# ip link add br0 type bridge

# 启动网桥
~# ip link set br0 up

# 查看 bridge
~# brctl show
bridge name bridge idSTP enabled interfaces
br08000.000000000000 no
docker08000.0242910296ce noveth3ea8f48

2.创建名称空间 ns1 和 ns2
~# ip netns add ns1
~# ip netns add ns2

3.创建 veth 对
~# ip link add veth0 type veth peer br-veth0
~# ip link add veth1 type veth peer br-veth1

# 查看创建的 veth 对,通过查看此时 veth对 在 root名称空间下
~# ip address show
······
6: br0: < BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether ae:60:62:d9:67:aa brd ff:ff:ff:ff:ff:ff
inet6 fe80::ac60:62ff:fed9:67aa/64 scope link
valid_lft forever preferred_lft forever
7: br-veth0@if8: < BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 5e:6b:c5:04:47:52 brd ff:ff:ff:ff:ff:ff link-netns ns1
9: br-veth1@if10: < BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 16:b4:3a:d1:88:e6 brd ff:ff:ff:ff:ff:ff link-netns ns2

4.将创建的 veth对 的一端插入到指定的名称空间
~# ip link set veth0 netns ns1
~# ip link set veth1 netns ns2

# 通过进入不同的名称空间查看网卡的一端
~# ip netns exec ns1 ip a
1: lo: < LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
8: veth0@if7: < BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 7e:c7:8f:64:79:e0 brd ff:ff:ff:ff:ff:ff link-netnsid 0

~# ip netns exec ns2 ip a
1: lo: < LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
10: veth1@if9: < BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 36:19:69:64:ab:8c brd ff:ff:ff:ff:ff:ff link-netnsid 0

5.将创建的 veth对 的另一端插入到 br0 的桥接网卡
~# ip link set br-veth0 master br0
~# ip link set br-veth1 master br0

# 查看网卡对 和 绑定情况
~# brctl show br0
bridge name bridge idSTP enabled interfaces
br08000.16b43ad188e6 nobr-veth0
br-veth1

6.启动网卡 veth0 veth1 br-veth0 br-veth1,并配置 ip 地址
~# ip link set br-veth0 up
~# ip link set br-veth1 up

~# ip netns exec ns1 ip link set veth0 up
~# ip netns exec ns2 ip link set veth1 up

~# ip netns exec ns1 ifconfig veth0 192.168.100.10/24
~# ip netns exec ns2 ifconfig veth1 192.168.100.20/24
~# ip netns exec ns1 ifconfig
lo: flags=73< UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1netmask 255.0.0.0
inet6 ::1prefixlen 128scopeid 0x10< host>
looptxqueuelen 1000(Local Loopback)
RX packets 0bytes 0 (0.0 B)
RX errors 0dropped 0overruns 0frame 0
TX packets 0bytes 0 (0.0 B)
TX errors 0dropped 0 overruns 0carrier 0collisions 0

veth0: flags=4099< UP,BROADCAST,MULTICAST> mtu 1500
inet 192.168.100.10netmask 255.255.255.0broadcast 192.168.100.255
ether 7e:c7:8f:64:79:e0txqueuelen 1000(Ethernet)
RX packets 0bytes 0 (0.0 B)
RX errors 0dropped 0overruns 0frame 0
TX packets 0bytes 0 (0.0 B)
TX errors 0dropped 0 overruns 0carrier 0collisions 0
< /sub> # ip netns exec ns2 ifconfig
lo: flags=73< UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1netmask 255.0.0.0
inet6 ::1prefixlen 128scopeid 0x10< host>
looptxqueuelen 1000(Local Loopback)
RX packets 0bytes 0 (0.0 B)
RX errors 0dropped 0overruns 0frame 0
TX packets 0bytes 0 (0.0 B)
TX errors 0dropped 0 overruns 0carrier 0collisions 0

veth1: flags=4099< UP,BROADCAST,MULTICAST> mtu 1500
inet 192.168.100.20netmask 255.255.255.0broadcast 192.168.100.255
ether 36:19:69:64:ab:8ctxqueuelen 1000(Ethernet)
RX packets 0bytes 0 (0.0 B)
RX errors 0dropped 0overruns 0frame 0
TX packets 0bytes 0 (0.0 B)
TX errors 0dropped 0 overruns 0carrier 0collisions 0



# 注意:也需要启动网卡 lo,否则 ping 本身的 IP地址不通,没有环回接口,ping 自己是走的 lo网卡
~# ip netns exec ns1 ip link set lo up
~# ip netns exec ns2 ip link set lo up

# 效果展示,我们发现 ping 自己可以通,但是 ping 另一个名称空间的 IP地址不通,通过抓包查看
root@tf:< sub> # ip netns exec ns2 ping 192.168.100.20
PING 192.168.100.20 (192.168.100.20) 56(84) bytes of data.
64 bytes from 192.168.100.20: icmp_seq=1 ttl=64 time=0.029 ms
64 bytes from 192.168.100.20: icmp_seq=2 ttl=64 time=0.066 ms
^C
--- 192.168.100.20 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1014ms
rtt min/avg/max/mdev = 0.029/0.047/0.066/0.018 ms
root@tf:< /sub> # ip netns exec ns2 ping 192.168.100.10
PING 192.168.100.10 (192.168.100.10) 56(84) bytes of data.
From 192.168.100.20 icmp_seq=1 Destination Host Unreachable
From 192.168.100.20 icmp_seq=2 Destination Host Unreachable
From 192.168.100.20 icmp_seq=3 Destination Host Unreachable
^C
--- 192.168.100.10 ping statistics ---
4 packets transmitted, 0 received, +3 errors, 100% packet loss, time 3073ms
pipe 4

7.抓包
# 对 br-veth0 抓包,我们发现地址可达,已经通过 arp 广播正确学习了 MAC 地址
tcpdump -pne -i br-veth0


# 对 ns1 名称空间下的 veth0 进行抓包,看看是否有返回的报文
# 我们发现并没有返回的报文,那么都能正确解析了,却没有返回的报文
ip netns exec ns1 tcpdump -pne -i veth0


# 对 ns2 名称空间下的 veth1 进行抓包,可以正常返回 ARP 报文
ip netns exec ns2 tcpdump -pne -i veth1


通过上边抓包可以判断,我们的配置是没有的问题的,问题应该出在 ??? netfilter???这个 hook 上,也就是 iptables 的 FORWARD 链


8. iptables FORWARD链开启这个问题其实是 docker 修改了我们默认的 FORWARD 链的策略,可以查看官方文档的解释
【使用 Veth Pair 虚拟网卡对不同的网络空间进行通信】??docker-on-a-router??
# 查看 FORWARD 链是否是 DROP 状态
~# iptables-save | grep DROP
:FORWARD DROP [29:2436]

# 对 br0 网桥开启 FORWARD 转发
~# iptables -A FORWARD -i br0 -j ACCEPT

# 再次进行连通性测试,可以正常转发
~# ip netns exec ns1 ping 192.168.100.20
PING 192.168.100.20 (192.168.100.20) 56(84) bytes of data.
64 bytes from 192.168.100.20: icmp_seq=1 ttl=64 time=0.075 ms
64 bytes from 192.168.100.20: icmp_seq=2 ttl=64 time=0.065 ms
64 bytes from 192.168.100.20: icmp_seq=3 ttl=64 time=0.150 ms
64 bytes from 192.168.100.20: icmp_seq=4 ttl=64 time=0.137 ms


    推荐阅读