searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

VXLAN 简介

2024-10-31 09:28:45
4
0

基于之前的介绍,我们已经知道如何在单个物理节点中通过 Bridge 和 Veth Pair 来打通容器间的网络(即不同 namespace 间的网络),今天我们来考虑一个新的问题,如何打通多个节点间的容器网络。

Bridge & Veth Pair 打通节点间的容器网络

我们先考虑下,基于我们已有的简单的 Bridge 和 Veth Pair 设备,可以实现吗?答案是肯定的,我们可以构建出如下图所示的网络环境:
bridge & veth pair

Node1 & Node2 总共有 4 个 namespace,用于模拟 4 个不同的容器,其 IP 都是10.1.0.0/24的网段,我们通过 Node1 的 10.1.0.100 ping Node2 的 10.1.0.102,其流程如下:

  1. 首先在 Node1 的ns0中查询 arp 表项,发现并没有10.1.0.102对应的 mac,所以会发起 arp 广播;
  2. 由于 Node1 veth0的对端veth0_br插在 Linux Bridge 上,所以 arp 请求会被广播到enp1s0这个物理网口;
  3. Node1 的enp1s0在接收到 arp 广播后,会发送到物理交换机,物理交换机也会将当前 arp 请求广播到 Node2 的enp1s0接口;
  4. 同理,Node2 的enp1s0veth0_br都插在同一个 Linux Bridge 上,所以 Node2 veth0最终会收到 arp 请求,并按照同样的路径回包;
  5. Node1 的veth0在接收到 arp 响应后,记录表项,发起 ICMP 请求与 Node2 的veth0正常通信;

可以看到,通过 Linux Bridge + 物理网卡 + 物理交换机的方式,可以使得在不同节点上的各个容器之间处在同一个二层内,三层很容易就能互通。

好,现在对容器网络提出另一个合理的需求,我们希望在当前的环境里部署两套应用,应用 A应用 B。应用之间不能互通,要怎么办?也很简单,最直观的方案,我们为不同的应用划分不同的网段就行:
bridge & veth pair

我们将应用 A的服务部署到 Node1 和 Node2 的ns0,网段为10.1.0.0/24,将应用 B的服务部署到 Node1 和 Node2 的ns1,网段为10.1.1.0/24,这样由于这两个应用处在不同的网段,他们自然无法联通。
看起来是初步解决了问题,可是,如果应用 A的开发人员在容器内给自己的网卡添加了10.1.1.100的 IP,并且增加了指向10.1.1.0/24网段的路由,那在应用 A中就可以很轻易的访问应用 B的网络了,这显然是我们不愿意看到的。

我们可以发现,通过简单的 Bridge & Veth Pair 的方式虽然能打通不同节点间的容器网络,但它并不能提供很好的隔离性以及一些更高级的功能。
所以,我们需要引入新的机制,比如:VXLAN。

VXLAN 打通节点间的容器网络

我们先看 VXLAN 是如何满足我们打通不同节点的容器网络的需求的,先搭建如下的环境:
vxlan

可以看到与之前的差别是,我们插入 Linux Bridge 的并非物理网卡,而是一块 VXLAN 类型的vxlan0网卡。

具体的环境搭建流程如下,在 Node1 中:

# 添加 vxlan 设备
$ ip link add vxlan0 type vxlan id 4100 remote 192.168.31.42 local 192.168.31.92 dstport 4789 dev enp1s0
$ ip link set vxlan0 up

# `ns0`模拟容器
$ ip netns add ns0
$ ip link add veth0 type veth peer name veth0_br
$ ip link set veth0 netns ns0
$ ip netns exec ns0 ip addr add 10.1.0.100/24 dev veth0
$ ip link set veth0_br up
$ ip netns exec ns0 ip link set veth0 up

# `ns1`模拟容器
$ ip netns add ns1
$ ip link add veth1 type veth peer name veth1_br
$ ip link set veth1 netns ns1
$ ip netns exec ns1 ip addr add 10.1.0.101/24 dev veth1
$ ip link set veth1_br up
$ ip netns exec ns1 ip link set veth1 up

# 创建网桥,将设备插入网桥
$ brctl addbr br0
$ ip link set br0 up

$ brctl addif br0 veth0_br
$ brctl addif br0 veth1_br
$ brctl addif br0 vxlan0

在 Node2 上也是类似的操作:

# 添加 vxlan 设备
$ ip link add vxlan0 type vxlan id 4100 remote 192.168.31.92 local 192.168.31.42 dstport 4789 dev enp1s0
$ ip link set vxlan0 up

# `ns0`模拟容器
$ ip netns add ns0
$ ip link add veth0 type veth peer name veth0_br
$ ip link set veth0 netns ns0
$ ip netns exec ns0 ip addr add 10.1.0.102/24 dev veth0
$ ip link set veth0_br up
$ ip netns exec ns0 ip link set veth0 up

# `ns1`模拟容器
$ ip netns add ns1
$ ip link add veth1 type veth peer name veth1_br
$ ip link set veth1 netns ns1
$ ip netns exec ns1 ip addr add 10.1.0.103/24 dev veth1
$ ip link set veth1_br up
$ ip netns exec ns1 ip link set veth1 up

# 创建网桥,将设备插入网桥
$ brctl addbr br0
$ ip link set br0 up

$ brctl addif br0 veth0_br
$ brctl addif br0 veth1_br
$ brctl addif br0 vxlan0

环境搭建好后,我们通过 Node1 的veth0去 ping Node2 的veth0,可以看到是可以正常连通的:

ip netns exec ns0 ping -c1 10.1.0.102
PING 10.1.0.102 (10.1.0.102) 56(84) bytes of data.
64 bytes from 10.1.0.102: icmp_seq=1 ttl=64 time=1.02 ms

--- 10.1.0.102 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.022/1.022/1.022/0.000 ms

同时我们在 Node2 的物理网卡上抓包,可以看到有四个包,其中两个 arp 的包,两个 icmp 的包,这里为了后续的分析,我们将数据包的内容也以十六进制打印出来了:

$ tcpdump -n -vvv -X -i enp1s0udp port 4789
tcpdump: listening on enp1s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
# arp request
17:37:49.657206 IP (tos 0x0, ttl 64, id 22138, offset 0, flags [none], proto UDP (17), length 78)
    192.168.31.92.50072 > 192.168.31.42.4789: [bad udp cksum 0xc022 -> 0x33d4!] VXLAN, flags [I] (0x08), vni 4100
ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 10.1.0.102 tell 10.1.0.100, length 28
	0x0000:  4500 004e 567a 0000 4011 644e c0a8 1f5c  E..NVz..@.dN...\
	0x0010:  c0a8 1f2a c398 12b5 003a c022 0800 0000  ...*.....:."....
	0x0020:  0010 0400 ffff ffff ffff 9a63 8988 5b60  ...........c..[`
	0x0030:  0806 0001 0800 0604 0001 9a63 8988 5b60  ...........c..[`
	0x0040:  0a01 0064 0000 0000 0000 0a01 0066       ...d.........f

# arp reply
17:37:49.657271 IP (tos 0x0, ttl 64, id 35736, offset 0, flags [none], proto UDP (17), length 78)
    192.168.31.42.41498 > 192.168.31.92.4789: [bad udp cksum 0xc022 -> 0x20c5!] VXLAN, flags [I] (0x08), vni 4100
ARP, Ethernet (len 6), IPv4 (len 4), Reply 10.1.0.102 is-at 6e:08:05:aa:a6:93, length 28
	0x0000:  4500 004e 8b98 0000 4011 2f30 c0a8 1f2a  E..N....@./0...*
	0x0010:  c0a8 1f5c a21a 12b5 003a c022 0800 0000  ...\.....:."....
	0x0020:  0010 0400 9a63 8988 5b60 6e08 05aa a693  .....c..[`n.....
	0x0030:  0806 0001 0800 0604 0002 6e08 05aa a693  ..........n.....
	0x0040:  0a01 0066 9a63 8988 5b60 0a01 0064       ...f.c..[`...d

# icmp request
17:37:49.657451 IP (tos 0x0, ttl 64, id 22139, offset 0, flags [none], proto UDP (17), length 134)
    192.168.31.92.42570 > 192.168.31.42.4789: [bad udp cksum 0xc05a -> 0xd890!] VXLAN, flags [I] (0x08), vni 4100
IP (tos 0x0, ttl 64, id 20585, offset 0, flags [DF], proto ICMP (1), length 84)
    10.1.0.100 > 10.1.0.102: ICMP echo request, id 18697, seq 1, length 64
	0x0000:  4500 0086 567b 0000 4011 6415 c0a8 1f5c  E...V{..@.d....\
	0x0010:  c0a8 1f2a a64a 12b5 0072 c05a 0800 0000  ...*.J...r.Z....
	0x0020:  0010 0400 6e08 05aa a693 9a63 8988 5b60  ....n......c..[`
	0x0030:  0800 4500 0054 5069 4000 4001 d574 0a01  ..E..TPi@.@..t..
	0x0040:  0064 0a01 0066 0800 1773 4909 0001 6d4a  .d...f...sI...mJ
	0x0050:  3265 0000 0000 3000 0900 0000 0000 1011  2e....0.........
	0x0060:  1213 1415 1617 1819 1a1b 1c1d 1e1f 2021  ...............!
	0x0070:  2223 2425 2627 2829 2a2b 2c2d 2e2f 3031  "#$%&'()*+,-./01
	0x0080:  3233 3435 3637                           234567

# icmp reply
17:37:49.657486 IP (tos 0x0, ttl 64, id 35737, offset 0, flags [none], proto UDP (17), length 134)
    192.168.31.42.33888 > 192.168.31.92.4789: [bad udp cksum 0xc05a -> 0xfa7a!] VXLAN, flags [I] (0x08), vni 4100
IP (tos 0x0, ttl 64, id 60757, offset 0, flags [none], proto ICMP (1), length 84)
    10.1.0.102 > 10.1.0.100: ICMP echo reply, id 18697, seq 1, length 64
	0x0000:  4500 0086 8b99 0000 4011 2ef7 c0a8 1f2a  E.......@......*
	0x0010:  c0a8 1f5c 8460 12b5 0072 c05a 0800 0000  ...\.`...r.Z....
	0x0020:  0010 0400 9a63 8988 5b60 6e08 05aa a693  .....c..[`n.....
	0x0030:  0800 4500 0054 ed55 0000 4001 7888 0a01  ..E..T.U..@.x...
	0x0040:  0066 0a01 0064 0000 1f73 4909 0001 6d4a  .f...d...sI...mJ
	0x0050:  3265 0000 0000 3000 0900 0000 0000 1011  2e....0.........
	0x0060:  1213 1415 1617 1819 1a1b 1c1d 1e1f 2021  ...............!
	0x0070:  2223 2425 2627 2829 2a2b 2c2d 2e2f 3031  "#$%&'()*+,-./01
	0x0080:  3233 3435 3637                           234567

经过之前的学习,我们知道抓到这四个包并不奇怪,但仔细分析一下,这几个包还是有不同寻常的地方,以第一个 arp request 数据包为例:

0x0000:  4500 004e 567a 0000 4011 644e c0a8 1f5c
0x0010:  c0a8 1f2a c398 12b5 003a c022 0800 0000
0x0020:  0010 0400 ffff ffff ffff 9a63 8988 5b60
0x0030:  0806 0001 0800 0604 0001 9a63 8988 5b60
0x0040:  0a01 0064 0000 0000 0000 0a01 0066

我们在抓包的时候没有指定-e参数,所以这里打印出来的数据包已经丢弃了最外层的以太网帧头部分:

  • 第一层:IP 数据报头部分4500 004e 567a 0000 4011 644e c0a8 1f5c c0a8 1f2a,我们可以看到在最后的源 IP c0a8 1f5c -> 192.168.31.92,以及目的 IP c0a8 1f2a -> 192.168.31.42,这是物理网络的 IP,而非容器网络的10.1.0.0/2410.1.1.0/24网段;
  • 第二层:传输层部分c398 12b5 003a c022,在第一层的 IP 数据报头部分,可以看到 Protocol 的类型是11,代表传输层是 UDP 协议。这里按照 UDP 协议来解析,可知其 Source Port 是 c3 -> 50072, Dst Port 是98 -> 4789,Dst Port 的值与之前创建的vxlan0时声明的 dstport 一致;
  • 第三层:UDP Payload0800 0000 0010 0400 ffff ffff ffff 9a63 8988 5b60 0806 0001 0800 0604 0001 9a63 8988 5b60 0a01 0064 0000 0000 0000 0a01 0066,整个数据包的剩余部分都是 UDP Payload,但其实 vxlan 协议的重心都在这里面。

我们再重点分析下第三层,也就是 UDP Payload 部分:

  • VXLAN 头部:0800 0000 0010 0400,首部的08是 VXLAN 的标识,必须为此值,00 00 00是 Reserved 保留字段,00 01 04是 VNI 的值,转成十进制后就是我们在创建vxlan0时指定的 4100,最后的00也是保留字段;
  • 以太网帧部分:ffff ffff ffff 9a63 8988 5b60 0806,这个应该比较熟悉了,前 12 字节分别为 Dst MAC 和 Src MAC,最后0806代表 arp 协议;
  • arp 协议:0001 0800 0604 0001 9a63 8988 5b60 0a01 0064 0000 0000 0000 0a01 0066,这里就不再详细分析了,可以对照 arp 协议的规范来查看,请求的含义就是:who-has 10.1.0.102 tell 10.1.0.100

总之,经过刚才的分析,我们可以比较清楚的看到 VXLAN 的报文结构:
vxlan

其实所谓 VXLAN,就是在原有的三层网络的基础上搭建虚拟的二层的网络(Overlay),以刚才的请求为例,当 Node1 的容器内发起的 arp 请求经过vxlan0网卡时,会经过一层封包,简化描述如下:

[外层封包                                                                ]  [原始报文]
[Node2 MAC | Node1 MAC | Node1 IP | Node2 IP | UDP SrcPort | UDP DstPort]  [arp request]

封包后的报文再经过 Node1 的物理网卡enp1s0发送给 Node2 的4789端口,Node2 接收到该请求后,会将其进行解封装的操作,也就是将外层的封包去掉,将原始的 [arp request] 报文交给 Node2 的vxlan0网卡,由于vxlan0插在网桥上,所以对应veth0网卡也就能接收到这个原始的 arp 请求。

所以,如果我们直接在 Node2 的vxlan0网卡上抓包时,看到的是已经解封装之后的原始报文:

tcpdump -n -i vxlan0 -vvv -X
tcpdump: listening on vxlan0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
17:37:49.657226 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 10.1.0.102 tell 10.1.0.100, length 28
	0x0000:  0001 0800 0604 0001 9a63 8988 5b60 0a01  .........c..[`..
	0x0010:  0064 0000 0000 0000 0a01 0066            .d.........f
17:37:49.657259 ARP, Ethernet (len 6), IPv4 (len 4), Reply 10.1.0.102 is-at 6e:08:05:aa:a6:93, length 28
	0x0000:  0001 0800 0604 0002 6e08 05aa a693 0a01  ........n.......
	0x0010:  0066 9a63 8988 5b60 0a01 0064            .f.c..[`...d
17:37:49.657459 IP (tos 0x0, ttl 64, id 20585, offset 0, flags [DF], proto ICMP (1), length 84)
    10.1.0.100 > 10.1.0.102: ICMP echo request, id 18697, seq 1, length 64
	0x0000:  4500 0054 5069 4000 4001 d574 0a01 0064  E..TPi@.@..t...d
	0x0010:  0a01 0066 0800 1773 4909 0001 6d4a 3265  ...f...sI...mJ2e
	0x0020:  0000 0000 3000 0900 0000 0000 1011 1213  ....0...........
	0x0030:  1415 1617 1819 1a1b 1c1d 1e1f 2021 2223  .............!"#
	0x0040:  2425 2627 2829 2a2b 2c2d 2e2f 3031 3233  $%&'()*+,-./0123
	0x0050:  3435 3637                                4567
17:37:49.657480 IP (tos 0x0, ttl 64, id 60757, offset 0, flags [none], proto ICMP (1), length 84)
    10.1.0.102 > 10.1.0.100: ICMP echo reply, id 18697, seq 1, length 64
	0x0000:  4500 0054 ed55 0000 4001 7888 0a01 0066  E..T.U..@.x....f
	0x0010:  0a01 0064 0000 1f73 4909 0001 6d4a 3265  ...d...sI...mJ2e
	0x0020:  0000 0000 3000 0900 0000 0000 1011 1213  ....0...........
	0x0030:  1415 1617 1819 1a1b 1c1d 1e1f 2021 2223  .............!"#
	0x0040:  2425 2627 2829 2a2b 2c2d 2e2f 3031 3233  $%&'()*+,-./0123
	0x0050:  3435 3637                                4567

到这里,我们已经基本明白 VXLAN 的工作方式,它将容器网络的二层以太网帧封装在物理网络的传输层 UDP 中,相当于在物理网络的三层网络之上,构建了虚拟的容器二层网络,这就是它能跨节点打通容器网络的原因。

知道了 VXLAN 是怎么跨节点打通容器网络的,最后我们再来简单讨论下隔离性,即如何隔离多个应用间的网络?
答案很简单,建多个不同的 VNI 的 VXLAN 网卡即可,如下图所示:
vxlan

我们可以看到在如上所示的环境里,我们通过 VXLAN 划分出两个虚拟网络,分别对应VNI 4100VNI 4200。虽然对于这两个虚拟网络而言,他们都有共同的网段10.1.0.0/24,但是由于他们关联的 Bridge 的 VXLAN 网卡不同(VNI 不同),所以经过 VXLAN 封包后的数据包在对端也会被分发到不同的 VXLAN 网卡,所以并不会出现任何的冲突的情况(可以按照之前分析的流程来推导下,可以加深对此的印象)。

0条评论
0 / 1000
LLL
10文章数
0粉丝数
LLL
10 文章 | 0 粉丝
原创

VXLAN 简介

2024-10-31 09:28:45
4
0

基于之前的介绍,我们已经知道如何在单个物理节点中通过 Bridge 和 Veth Pair 来打通容器间的网络(即不同 namespace 间的网络),今天我们来考虑一个新的问题,如何打通多个节点间的容器网络。

Bridge & Veth Pair 打通节点间的容器网络

我们先考虑下,基于我们已有的简单的 Bridge 和 Veth Pair 设备,可以实现吗?答案是肯定的,我们可以构建出如下图所示的网络环境:
bridge & veth pair

Node1 & Node2 总共有 4 个 namespace,用于模拟 4 个不同的容器,其 IP 都是10.1.0.0/24的网段,我们通过 Node1 的 10.1.0.100 ping Node2 的 10.1.0.102,其流程如下:

  1. 首先在 Node1 的ns0中查询 arp 表项,发现并没有10.1.0.102对应的 mac,所以会发起 arp 广播;
  2. 由于 Node1 veth0的对端veth0_br插在 Linux Bridge 上,所以 arp 请求会被广播到enp1s0这个物理网口;
  3. Node1 的enp1s0在接收到 arp 广播后,会发送到物理交换机,物理交换机也会将当前 arp 请求广播到 Node2 的enp1s0接口;
  4. 同理,Node2 的enp1s0veth0_br都插在同一个 Linux Bridge 上,所以 Node2 veth0最终会收到 arp 请求,并按照同样的路径回包;
  5. Node1 的veth0在接收到 arp 响应后,记录表项,发起 ICMP 请求与 Node2 的veth0正常通信;

可以看到,通过 Linux Bridge + 物理网卡 + 物理交换机的方式,可以使得在不同节点上的各个容器之间处在同一个二层内,三层很容易就能互通。

好,现在对容器网络提出另一个合理的需求,我们希望在当前的环境里部署两套应用,应用 A应用 B。应用之间不能互通,要怎么办?也很简单,最直观的方案,我们为不同的应用划分不同的网段就行:
bridge & veth pair

我们将应用 A的服务部署到 Node1 和 Node2 的ns0,网段为10.1.0.0/24,将应用 B的服务部署到 Node1 和 Node2 的ns1,网段为10.1.1.0/24,这样由于这两个应用处在不同的网段,他们自然无法联通。
看起来是初步解决了问题,可是,如果应用 A的开发人员在容器内给自己的网卡添加了10.1.1.100的 IP,并且增加了指向10.1.1.0/24网段的路由,那在应用 A中就可以很轻易的访问应用 B的网络了,这显然是我们不愿意看到的。

我们可以发现,通过简单的 Bridge & Veth Pair 的方式虽然能打通不同节点间的容器网络,但它并不能提供很好的隔离性以及一些更高级的功能。
所以,我们需要引入新的机制,比如:VXLAN。

VXLAN 打通节点间的容器网络

我们先看 VXLAN 是如何满足我们打通不同节点的容器网络的需求的,先搭建如下的环境:
vxlan

可以看到与之前的差别是,我们插入 Linux Bridge 的并非物理网卡,而是一块 VXLAN 类型的vxlan0网卡。

具体的环境搭建流程如下,在 Node1 中:

# 添加 vxlan 设备
$ ip link add vxlan0 type vxlan id 4100 remote 192.168.31.42 local 192.168.31.92 dstport 4789 dev enp1s0
$ ip link set vxlan0 up

# `ns0`模拟容器
$ ip netns add ns0
$ ip link add veth0 type veth peer name veth0_br
$ ip link set veth0 netns ns0
$ ip netns exec ns0 ip addr add 10.1.0.100/24 dev veth0
$ ip link set veth0_br up
$ ip netns exec ns0 ip link set veth0 up

# `ns1`模拟容器
$ ip netns add ns1
$ ip link add veth1 type veth peer name veth1_br
$ ip link set veth1 netns ns1
$ ip netns exec ns1 ip addr add 10.1.0.101/24 dev veth1
$ ip link set veth1_br up
$ ip netns exec ns1 ip link set veth1 up

# 创建网桥,将设备插入网桥
$ brctl addbr br0
$ ip link set br0 up

$ brctl addif br0 veth0_br
$ brctl addif br0 veth1_br
$ brctl addif br0 vxlan0

在 Node2 上也是类似的操作:

# 添加 vxlan 设备
$ ip link add vxlan0 type vxlan id 4100 remote 192.168.31.92 local 192.168.31.42 dstport 4789 dev enp1s0
$ ip link set vxlan0 up

# `ns0`模拟容器
$ ip netns add ns0
$ ip link add veth0 type veth peer name veth0_br
$ ip link set veth0 netns ns0
$ ip netns exec ns0 ip addr add 10.1.0.102/24 dev veth0
$ ip link set veth0_br up
$ ip netns exec ns0 ip link set veth0 up

# `ns1`模拟容器
$ ip netns add ns1
$ ip link add veth1 type veth peer name veth1_br
$ ip link set veth1 netns ns1
$ ip netns exec ns1 ip addr add 10.1.0.103/24 dev veth1
$ ip link set veth1_br up
$ ip netns exec ns1 ip link set veth1 up

# 创建网桥,将设备插入网桥
$ brctl addbr br0
$ ip link set br0 up

$ brctl addif br0 veth0_br
$ brctl addif br0 veth1_br
$ brctl addif br0 vxlan0

环境搭建好后,我们通过 Node1 的veth0去 ping Node2 的veth0,可以看到是可以正常连通的:

ip netns exec ns0 ping -c1 10.1.0.102
PING 10.1.0.102 (10.1.0.102) 56(84) bytes of data.
64 bytes from 10.1.0.102: icmp_seq=1 ttl=64 time=1.02 ms

--- 10.1.0.102 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.022/1.022/1.022/0.000 ms

同时我们在 Node2 的物理网卡上抓包,可以看到有四个包,其中两个 arp 的包,两个 icmp 的包,这里为了后续的分析,我们将数据包的内容也以十六进制打印出来了:

$ tcpdump -n -vvv -X -i enp1s0udp port 4789
tcpdump: listening on enp1s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
# arp request
17:37:49.657206 IP (tos 0x0, ttl 64, id 22138, offset 0, flags [none], proto UDP (17), length 78)
    192.168.31.92.50072 > 192.168.31.42.4789: [bad udp cksum 0xc022 -> 0x33d4!] VXLAN, flags [I] (0x08), vni 4100
ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 10.1.0.102 tell 10.1.0.100, length 28
	0x0000:  4500 004e 567a 0000 4011 644e c0a8 1f5c  E..NVz..@.dN...\
	0x0010:  c0a8 1f2a c398 12b5 003a c022 0800 0000  ...*.....:."....
	0x0020:  0010 0400 ffff ffff ffff 9a63 8988 5b60  ...........c..[`
	0x0030:  0806 0001 0800 0604 0001 9a63 8988 5b60  ...........c..[`
	0x0040:  0a01 0064 0000 0000 0000 0a01 0066       ...d.........f

# arp reply
17:37:49.657271 IP (tos 0x0, ttl 64, id 35736, offset 0, flags [none], proto UDP (17), length 78)
    192.168.31.42.41498 > 192.168.31.92.4789: [bad udp cksum 0xc022 -> 0x20c5!] VXLAN, flags [I] (0x08), vni 4100
ARP, Ethernet (len 6), IPv4 (len 4), Reply 10.1.0.102 is-at 6e:08:05:aa:a6:93, length 28
	0x0000:  4500 004e 8b98 0000 4011 2f30 c0a8 1f2a  E..N....@./0...*
	0x0010:  c0a8 1f5c a21a 12b5 003a c022 0800 0000  ...\.....:."....
	0x0020:  0010 0400 9a63 8988 5b60 6e08 05aa a693  .....c..[`n.....
	0x0030:  0806 0001 0800 0604 0002 6e08 05aa a693  ..........n.....
	0x0040:  0a01 0066 9a63 8988 5b60 0a01 0064       ...f.c..[`...d

# icmp request
17:37:49.657451 IP (tos 0x0, ttl 64, id 22139, offset 0, flags [none], proto UDP (17), length 134)
    192.168.31.92.42570 > 192.168.31.42.4789: [bad udp cksum 0xc05a -> 0xd890!] VXLAN, flags [I] (0x08), vni 4100
IP (tos 0x0, ttl 64, id 20585, offset 0, flags [DF], proto ICMP (1), length 84)
    10.1.0.100 > 10.1.0.102: ICMP echo request, id 18697, seq 1, length 64
	0x0000:  4500 0086 567b 0000 4011 6415 c0a8 1f5c  E...V{..@.d....\
	0x0010:  c0a8 1f2a a64a 12b5 0072 c05a 0800 0000  ...*.J...r.Z....
	0x0020:  0010 0400 6e08 05aa a693 9a63 8988 5b60  ....n......c..[`
	0x0030:  0800 4500 0054 5069 4000 4001 d574 0a01  ..E..TPi@.@..t..
	0x0040:  0064 0a01 0066 0800 1773 4909 0001 6d4a  .d...f...sI...mJ
	0x0050:  3265 0000 0000 3000 0900 0000 0000 1011  2e....0.........
	0x0060:  1213 1415 1617 1819 1a1b 1c1d 1e1f 2021  ...............!
	0x0070:  2223 2425 2627 2829 2a2b 2c2d 2e2f 3031  "#$%&'()*+,-./01
	0x0080:  3233 3435 3637                           234567

# icmp reply
17:37:49.657486 IP (tos 0x0, ttl 64, id 35737, offset 0, flags [none], proto UDP (17), length 134)
    192.168.31.42.33888 > 192.168.31.92.4789: [bad udp cksum 0xc05a -> 0xfa7a!] VXLAN, flags [I] (0x08), vni 4100
IP (tos 0x0, ttl 64, id 60757, offset 0, flags [none], proto ICMP (1), length 84)
    10.1.0.102 > 10.1.0.100: ICMP echo reply, id 18697, seq 1, length 64
	0x0000:  4500 0086 8b99 0000 4011 2ef7 c0a8 1f2a  E.......@......*
	0x0010:  c0a8 1f5c 8460 12b5 0072 c05a 0800 0000  ...\.`...r.Z....
	0x0020:  0010 0400 9a63 8988 5b60 6e08 05aa a693  .....c..[`n.....
	0x0030:  0800 4500 0054 ed55 0000 4001 7888 0a01  ..E..T.U..@.x...
	0x0040:  0066 0a01 0064 0000 1f73 4909 0001 6d4a  .f...d...sI...mJ
	0x0050:  3265 0000 0000 3000 0900 0000 0000 1011  2e....0.........
	0x0060:  1213 1415 1617 1819 1a1b 1c1d 1e1f 2021  ...............!
	0x0070:  2223 2425 2627 2829 2a2b 2c2d 2e2f 3031  "#$%&'()*+,-./01
	0x0080:  3233 3435 3637                           234567

经过之前的学习,我们知道抓到这四个包并不奇怪,但仔细分析一下,这几个包还是有不同寻常的地方,以第一个 arp request 数据包为例:

0x0000:  4500 004e 567a 0000 4011 644e c0a8 1f5c
0x0010:  c0a8 1f2a c398 12b5 003a c022 0800 0000
0x0020:  0010 0400 ffff ffff ffff 9a63 8988 5b60
0x0030:  0806 0001 0800 0604 0001 9a63 8988 5b60
0x0040:  0a01 0064 0000 0000 0000 0a01 0066

我们在抓包的时候没有指定-e参数,所以这里打印出来的数据包已经丢弃了最外层的以太网帧头部分:

  • 第一层:IP 数据报头部分4500 004e 567a 0000 4011 644e c0a8 1f5c c0a8 1f2a,我们可以看到在最后的源 IP c0a8 1f5c -> 192.168.31.92,以及目的 IP c0a8 1f2a -> 192.168.31.42,这是物理网络的 IP,而非容器网络的10.1.0.0/2410.1.1.0/24网段;
  • 第二层:传输层部分c398 12b5 003a c022,在第一层的 IP 数据报头部分,可以看到 Protocol 的类型是11,代表传输层是 UDP 协议。这里按照 UDP 协议来解析,可知其 Source Port 是 c3 -> 50072, Dst Port 是98 -> 4789,Dst Port 的值与之前创建的vxlan0时声明的 dstport 一致;
  • 第三层:UDP Payload0800 0000 0010 0400 ffff ffff ffff 9a63 8988 5b60 0806 0001 0800 0604 0001 9a63 8988 5b60 0a01 0064 0000 0000 0000 0a01 0066,整个数据包的剩余部分都是 UDP Payload,但其实 vxlan 协议的重心都在这里面。

我们再重点分析下第三层,也就是 UDP Payload 部分:

  • VXLAN 头部:0800 0000 0010 0400,首部的08是 VXLAN 的标识,必须为此值,00 00 00是 Reserved 保留字段,00 01 04是 VNI 的值,转成十进制后就是我们在创建vxlan0时指定的 4100,最后的00也是保留字段;
  • 以太网帧部分:ffff ffff ffff 9a63 8988 5b60 0806,这个应该比较熟悉了,前 12 字节分别为 Dst MAC 和 Src MAC,最后0806代表 arp 协议;
  • arp 协议:0001 0800 0604 0001 9a63 8988 5b60 0a01 0064 0000 0000 0000 0a01 0066,这里就不再详细分析了,可以对照 arp 协议的规范来查看,请求的含义就是:who-has 10.1.0.102 tell 10.1.0.100

总之,经过刚才的分析,我们可以比较清楚的看到 VXLAN 的报文结构:
vxlan

其实所谓 VXLAN,就是在原有的三层网络的基础上搭建虚拟的二层的网络(Overlay),以刚才的请求为例,当 Node1 的容器内发起的 arp 请求经过vxlan0网卡时,会经过一层封包,简化描述如下:

[外层封包                                                                ]  [原始报文]
[Node2 MAC | Node1 MAC | Node1 IP | Node2 IP | UDP SrcPort | UDP DstPort]  [arp request]

封包后的报文再经过 Node1 的物理网卡enp1s0发送给 Node2 的4789端口,Node2 接收到该请求后,会将其进行解封装的操作,也就是将外层的封包去掉,将原始的 [arp request] 报文交给 Node2 的vxlan0网卡,由于vxlan0插在网桥上,所以对应veth0网卡也就能接收到这个原始的 arp 请求。

所以,如果我们直接在 Node2 的vxlan0网卡上抓包时,看到的是已经解封装之后的原始报文:

tcpdump -n -i vxlan0 -vvv -X
tcpdump: listening on vxlan0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
17:37:49.657226 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 10.1.0.102 tell 10.1.0.100, length 28
	0x0000:  0001 0800 0604 0001 9a63 8988 5b60 0a01  .........c..[`..
	0x0010:  0064 0000 0000 0000 0a01 0066            .d.........f
17:37:49.657259 ARP, Ethernet (len 6), IPv4 (len 4), Reply 10.1.0.102 is-at 6e:08:05:aa:a6:93, length 28
	0x0000:  0001 0800 0604 0002 6e08 05aa a693 0a01  ........n.......
	0x0010:  0066 9a63 8988 5b60 0a01 0064            .f.c..[`...d
17:37:49.657459 IP (tos 0x0, ttl 64, id 20585, offset 0, flags [DF], proto ICMP (1), length 84)
    10.1.0.100 > 10.1.0.102: ICMP echo request, id 18697, seq 1, length 64
	0x0000:  4500 0054 5069 4000 4001 d574 0a01 0064  E..TPi@.@..t...d
	0x0010:  0a01 0066 0800 1773 4909 0001 6d4a 3265  ...f...sI...mJ2e
	0x0020:  0000 0000 3000 0900 0000 0000 1011 1213  ....0...........
	0x0030:  1415 1617 1819 1a1b 1c1d 1e1f 2021 2223  .............!"#
	0x0040:  2425 2627 2829 2a2b 2c2d 2e2f 3031 3233  $%&'()*+,-./0123
	0x0050:  3435 3637                                4567
17:37:49.657480 IP (tos 0x0, ttl 64, id 60757, offset 0, flags [none], proto ICMP (1), length 84)
    10.1.0.102 > 10.1.0.100: ICMP echo reply, id 18697, seq 1, length 64
	0x0000:  4500 0054 ed55 0000 4001 7888 0a01 0066  E..T.U..@.x....f
	0x0010:  0a01 0064 0000 1f73 4909 0001 6d4a 3265  ...d...sI...mJ2e
	0x0020:  0000 0000 3000 0900 0000 0000 1011 1213  ....0...........
	0x0030:  1415 1617 1819 1a1b 1c1d 1e1f 2021 2223  .............!"#
	0x0040:  2425 2627 2829 2a2b 2c2d 2e2f 3031 3233  $%&'()*+,-./0123
	0x0050:  3435 3637                                4567

到这里,我们已经基本明白 VXLAN 的工作方式,它将容器网络的二层以太网帧封装在物理网络的传输层 UDP 中,相当于在物理网络的三层网络之上,构建了虚拟的容器二层网络,这就是它能跨节点打通容器网络的原因。

知道了 VXLAN 是怎么跨节点打通容器网络的,最后我们再来简单讨论下隔离性,即如何隔离多个应用间的网络?
答案很简单,建多个不同的 VNI 的 VXLAN 网卡即可,如下图所示:
vxlan

我们可以看到在如上所示的环境里,我们通过 VXLAN 划分出两个虚拟网络,分别对应VNI 4100VNI 4200。虽然对于这两个虚拟网络而言,他们都有共同的网段10.1.0.0/24,但是由于他们关联的 Bridge 的 VXLAN 网卡不同(VNI 不同),所以经过 VXLAN 封包后的数据包在对端也会被分发到不同的 VXLAN 网卡,所以并不会出现任何的冲突的情况(可以按照之前分析的流程来推导下,可以加深对此的印象)。

文章来自个人专栏
李浩
10 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0