Keep learning, keep living...

0%

动态维护FDB表项实现VXLAN通信

之前的文章<<VXLAN原理介绍与实例分析>>简要介绍了VXLAN的基本概念和基于组播的通信过程实例。其中控制面是混合在数据面中的,由洪泛(flood)和源地址学习实现。本文通过实例将控制面从数据面中分离,手动维护FDBARP表项实现通信过程。

对于大规模的VXLAN网络中,最核心的问题一般有两个:

  1. 如何发现网络中其他VTEP
  2. 如何降低BUM(Broadcast, Unknown unicast, Multicast)流量

在对于问题一来说,之前的文章中的解决方法是洪泛,对于问题二,则通过源地址学习来确定MAC地址的归属。VXLAN的转发过程主要依赖FDB(Forwarding Database)实现。二层网桥的FDB表项格式可表达为:

1
<MAC> <VLAN> <DEV PORT>

VXLAN设备的表项与之类似,可以表达为:

1
<MAC> <VNI> <REMOTE IP>

VXLAN设备根据MAC地址来查找相应的VTEP IP地址,继而将二层数据帧封装发送至相应VTEP。

如果我们能够从集中的Controller或者存储中获取VTEP信息以及MAC地址与VTEP的对应信息,则问题一和问题二都可以通过根据相应信息动态更新FDB表项来解决,OpenStack的Neutron, VMware的NSX,Docker Overlay都有类似的思路。

下面我们通过实例来手动更新FDB表来实现VXLAN通信。我们的实验环境如下图, VTEP本地使用Linux bridge来挂载连接到network namespace中的veth pair虚拟网卡,我们要实现3.3.3.3二层访问3.3.3.4

分别两台主机执行命令构建环境,Host1上的命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
sysctl -w net.ipv4.ip_forward=1
ip netns add ns1
ip link add veth1 type veth peer name eth0 netns ns1
ip netns exec ns1 ip link set eth0 up
ip netns exec ns1 ip link set lo up
ip netns exec ns1 ip addr add 3.3.3.3/24 dev eth0
ip link set up dev veth1
ip link add br1 type bridge
ip link set br1 up
ip link set veth1 master br1
ip link add vxlan100 type vxlan id 100 dstport 4789 local 192.168.33.15 nolearning
ip link set vxlan100 master br1
ip link set up vxlan100

在创建VXLAN设备时指定了nolearning来禁用源地址学习。在Host2中修改相应IP同样进行配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
sysctl -w net.ipv4.ip_forward=1
ip netns add ns1
ip link add veth1 type veth peer name eth0 netns ns1
ip netns exec ns1 ip link set eth0 up
ip netns exec ns1 ip link set lo up
ip netns exec ns1 ip addr add 3.3.3.4/24 dev eth0
ip link set up dev veth1
ip link add br1 type bridge
ip link set br1 up
ip link set veth1 master br1
ip link add vxlan100 type vxlan id 100 dstport 4789 local 192.168.33.16 nolearning
ip link set vxlan100 master br1
ip link set up vxlan100

在Host1的ns1中访问Host2中的3.3.3.4, 此时无法连通:

1
2
3
4
5
6
7
8
root@ubuntu-focal:/home/vagrant/workspace# ip netns exec ns1 ping -c2 3.3.3.4
PING 3.3.3.4 (3.3.3.4) 56(84) bytes of data.
From 3.3.3.3 icmp_seq=1 Destination Host Unreachable
From 3.3.3.3 icmp_seq=2 Destination Host Unreachable

--- 3.3.3.4 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1027ms
pipe 2

原因在于,Host1的3.3.3.3接口需要先发送ARP广播包查询3.3.3.4的MAC地址。 ARP广播包到达接口vxlan100之后,根据FDB表项无法找到VTEP地址,因而ARP广播包无法发出。

我们在两个Host上分别查看两个虚拟网卡的MAC地址:

1
2
3
4
5
root@ubuntu-focal:/home/vagrant/workspace# ip netns exec ns1 ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 26:c1:ad:8d:00:73 brd ff:ff:ff:ff:ff:ff link-netnsid 0
1
2
3
4
5
root@ubuntu-bionic:/home/vagrant/workspace# ip netns exec ns1 ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether e2:ca:8e:f0:7d:79 brd ff:ff:ff:ff:ff:ff link-netnsid 0

然后在Host1上手动增加VXLAN FDB表项:

1
2
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 192.168.33.16
bridge fdb append e2:ca:8e:f0:7d:79 dev vxlan100 dst 192.168.33.16

全零表项表示没有匹配的MAC地址时,就发送到该表项中的VTEP, 用于处理BUM流量。
对于Host2中的虚拟接口的MAC地址,则直接发送到VTEP:192.168.33.16。

查看VXLAN设备vxlan100的FDB表项,两条表项都已成功添加而且永不老化(permanent):

1
2
3
4
5
root@ubuntu-focal:/home/vagrant/workspace# bridge fdb show brport vxlan100
aa:f2:de:f2:01:73 vlan 1 master br1 permanent
aa:f2:de:f2:01:73 master br1 permanent
00:00:00:00:00:00 dst 192.168.33.16 self permanent
e2:ca:8e:f0:7d:79 dst 192.168.33.16 self permanent

同样在Host2上增加FDB表项:

1
2
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 192.168.33.15
bridge fdb append 26:c1:ad:8d:00:73 dev vxlan100 dst 192.168.33.15

之后再次从Host1测试ping访问, 此时访问成功:

1
2
3
4
5
6
7
8
root@ubuntu-focal:/home/vagrant/workspace# ip netns exec ns1 ping -c2 3.3.3.4
PING 3.3.3.4 (3.3.3.4) 56(84) bytes of data.
64 bytes from 3.3.3.4: icmp_seq=1 ttl=64 time=1.30 ms
64 bytes from 3.3.3.4: icmp_seq=2 ttl=64 time=1.21 ms

--- 3.3.3.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.213/1.256/1.300/0.043 ms

Host1的虚拟接口3.3.3.3在ping访问Host2上的3.3.3.4时,需要先获得3.3.3.4对应的MAC地址,它会发送ARP广播请求。在大规模VXLAN环境中,这种ARP广播的消耗也很巨大,即我们开头所说的问题二。如果我们能获取MAC所在的VTEP,则可由VXLAN设备实现ARP代答,将ARP广播范围控制在本地,避免ARP广播请求发送到整个VXLAN网络环境中。Linux VXLAN设备支持通过proxy参数开启ARP代答。

在Host1上执行如下命令,重新创建VXLAN接口、开启ARP代答并重新添加FDB表项:

1
2
3
4
5
6
ip link del vxlan100
ip link add vxlan100 type vxlan id 100 dstport 4789 local 192.168.33.15 nolearning proxy
ip link set vxlan100 master br1
ip link set up vxlan100
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 192.168.33.16
bridge fdb append e2:ca:8e:f0:7d:79 dev vxlan100 dst 192.168.33.16

接着添加3.3.3.4的ARP表项并查看:

1
2
3
root@ubuntu-focal:/home/vagrant/workspace# ip neighbor add 3.3.3.4 lladdr e2:ca:8e:f0:7d:79 dev vxlan100
root@ubuntu-focal:/home/vagrant/workspace# ip neighbor show dev vxlan100
3.3.3.4 lladdr e2:ca:8e:f0:7d:79 PERMANENT

同样在Host2上执行命令, 重新创建VXLAN接口、开启ARP代答并重新添加FDB表项:

1
2
3
4
5
6
ip link del vxlan100
ip link add vxlan100 type vxlan id 100 dstport 4789 local 192.168.33.16 nolearning proxy
ip link set vxlan100 master br1
ip link set up vxlan100
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 192.168.33.15
bridge fdb append 26:c1:ad:8d:00:73 dev vxlan100 dst 192.168.33.15

也添加3.3.3.3的ARP表项:

1
2
3
root@ubuntu-bionic:/home/vagrant/workspace# ip neighbor add 3.3.3.3 lladdr 26:c1:ad:8d:00:73 dev vxlan100
root@ubuntu-bionic:/home/vagrant/workspace# ip neighbor show dev vxlan100
3.3.3.3 lladdr 26:c1:ad:8d:00:73 PERMANENT

此时再次从Host1测试ping, 访问成功:

1
2
3
4
5
6
7
8
9
root@ubuntu-focal:/home/vagrant/workspace# ip netns exec ns1 ip neigh flush all
root@ubuntu-focal:/home/vagrant/workspace# ip netns exec ns1 ping -c2 3.3.3.4
PING 3.3.3.4 (3.3.3.4) 56(84) bytes of data.
64 bytes from 3.3.3.4: icmp_seq=1 ttl=64 time=2.52 ms
64 bytes from 3.3.3.4: icmp_seq=2 ttl=64 time=1.01 ms

--- 3.3.3.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.012/1.765/2.519/0.753 ms

通过手动更新ARP和FDB表项可以实现VXLAN通信,但这些表项需要提前添加。为了支持更加灵活的维护ARP和FDB表,Linux的VXLAN设备还支持对于表项匹配MISS的消息通知。内核在发现在ARP或者FDB表项中找不到相应的表项,则可以通过NETLINK消息发送通知,用户态进程可以监听相应消息并补充所缺失的表项记录,从而实现动态的表项维护。VXLAN设备支持两种消息:

  • L2MISS: VXLAN设备在FDB表中找不到目的MAC地址所属的VTEP IP地址。L2MISS消息的发送需要满足如下条件:

    • 目的MAC地址未知,即在FDB表中没有相应条项
    • FDB表中没有全零表项
    • 目的MAC地址不是组播或多播地址
  • L3MISS: VXLAN设备在ARP表中找不到目的IP所对应的MAC地址

我们在Host1上删除vxlan100,重新添加开启l2missl3missvxlan100接口:

1
2
3
4
ip link del vxlan100
ip link add vxlan100 type vxlan id 100 dstport 4789 local 192.168.33.15 nolearning proxy l2miss l3miss
ip link set vxlan100 master br1
ip link set up vxlan100

接着使用ip moniotr命令去监听消息:

1
ip monitor all dev vxlan100

再次从Host1上去测试ping, 访问不通:

1
2
3
4
5
6
7
8
9
root@ubuntu-focal:/home/vagrant/workspace# ip netns exec ns1 ip neigh flush all
root@ubuntu-focal:/home/vagrant/workspace# ip netns exec ns1 ping -c2 3.3.3.4
PING 3.3.3.4 (3.3.3.4) 56(84) bytes of data.
From 3.3.3.3 icmp_seq=1 Destination Host Unreachable
From 3.3.3.3 icmp_seq=2 Destination Host Unreachable

--- 3.3.3.4 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1027ms
pipe 2

但我们从ip monitor收到了L3MISS的消息:

1
2
3
[NEIGH]miss 3.3.3.4  STALE
[NEIGH]miss 3.3.3.4 STALE
[NEIGH]miss 3.3.3.4 STALE

我们手动添加上相应ARP表项:

1
2
3
root@ubuntu-focal:/home/vagrant/workspace# ip neighbor replace 3.3.3.4 lladdr e2:ca:8e:f0:7d:79 dev vxlan100 nud reachable
root@ubuntu-focal:/home/vagrant/workspace# ip neighbor show dev vxlan100
3.3.3.4 lladdr e2:ca:8e:f0:7d:79 REACHABLE

我们这里添加了nud reachable, NUD表示: Neighbour Unreachability Detection, 意思是这个表项有过期时间,系统发现它无效后过一定时间会自动删除, 之后如果再次需要内核会再次发送L3MISS消息,这样我们就不用自己维护这些添加的表项了。

此时再次从Host1上去ping, 依然不能连通:

1
2
3
4
5
root@ubuntu-focal:/home/vagrant/workspace# ip netns exec ns1 ping -c2 3.3.3.4
PING 3.3.3.4 (3.3.3.4) 56(84) bytes of data.
^C
--- 3.3.3.4 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1025ms

但是从ip monitor能收到L2MISS消息:

1
2
[NEIGH]miss lladdr e2:ca:8e:f0:7d:79 STALE
[NEIGH]miss lladdr e2:ca:8e:f0:7d:79 STALE

接着,我们手动添加FDB表项:

1
bridge fdb append e2:ca:8e:f0:7d:79 dev vxlan100 dst 192.168.33.16

再次从Host1上ping,访问成功:

1
2
3
4
5
6
7
8
root@ubuntu-focal:/home/vagrant/workspace# ip netns exec ns1 ping -c2 3.3.3.4
PING 3.3.3.4 (3.3.3.4) 56(84) bytes of data.
64 bytes from 3.3.3.4: icmp_seq=1 ttl=64 time=2.81 ms
64 bytes from 3.3.3.4: icmp_seq=2 ttl=64 time=1.19 ms

--- 3.3.3.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.194/2.002/2.811/0.808 ms

基于这种动态更新机制,我们从集中的Controller或者某种存储处获取相应的信息,即可实现一般意义上的SDN架构,有效减少VXLAN网络中的BUM流量。Docker的libnetwork VXLAN模式以及Flannel的VXLAN模式都使用这种模式来实现Docker overlay网络, 后面再写文章具体分析。

参考: