Skip to content

VXLAN:二层 Overlay

GRE三层隧道:两个站点各自一个网段,靠路由互通。 VXLAN 是二层隧道:两个站点的主机变成同一个广播域里的邻居。

这个差别不只是"更底层",它带来的是完全不同的用途。

封装格式

text
  ┌────────────┬─────────┬──────────┬────────────────────────────┐
  │ 外层 IP(20) │ UDP(8)  │ VXLAN(8) │ 完整的以太网帧(含 MAC 头)   │
  │            │ dst4789 │ VNI 100  │ hA的MAC → hB的MAC + IP 包   │
  └────────────┴─────────┴──────────┴────────────────────────────┘
                                       ↑ 连二层头都被封进去了

开销 50 字节,是 GRE(24 字节)的两倍多 —— 因为它要多带一个以太网头 和 UDP/VXLAN 头。

VNI(VXLAN Network Identifier,24 位)标识这是哪个二层网段。 一对 VTEP 之间可以跑 1600 万个 VNI,互不干扰 —— 这是它在多租户 数据中心里的核心价值。

配置

应用配置

bash
./labctl up site
./labctl apply site 02
./labctl verify site 02
bash
# 1. 两端各建一个隧道
vppctl create vxlan tunnel src 198.51.100.1 dst 198.51.100.2 vni 100

# 2. 建桥域
vppctl create bridge-domain 100 learn 1 forward 1 flood 1 uu-flood 1

# 3. 把 LAN 口和隧道口都放进桥域
vppctl set interface ip address del host-eth1 10.1.0.1/24   # 先摘掉三层地址
vppctl set interface l2 bridge host-eth1 100
vppctl set interface l2 bridge vxlan_tunnel0 100
vppctl set interface state vxlan_tunnel0 up

一个接口不能同时是三层口和二层口

进桥域前必须先 set interface ip address del。忘了这步会报错, 或者更糟 —— 看起来配上了但转发行为不对。

反过来从桥域摘出来用 set interface l3 <接口>, 摘完记得把三层地址加回去,否则那个口就成了哑口。

配好之后,两台 NAT 的 LAN 口没有 IP 地址了。它们纯粹是二层桥的成员, 不参与路由 —— 跨站点通信完全靠桥转发。

决定性证据:学到了对端的 MAC

实验把 hB 挪到和 hA 同一个网段(10.1.0.11 和 10.1.0.12),然后:

text
── 2) 决定性证据:hA 学到了 hB 的 MAC ──
  hA 的 ARP 表:10.1.0.12 dev eth1 lladdr aa:c1:ab:dc:2b:4f REACHABLE
  hB 的真实 MAC:aa:c1:ab:dc:2b:4f
✓ 两者一致 —— 这是真正的二层邻居

这一条就是 VXLAN 和 GRE 最本质的区别

text
  GRE(三层)    hA 的 ARP 表里只有网关的 MAC,
                对端主机的 MAC 它永远看不到

  VXLAN(二层)  hA 直接学到 hB 的 MAC

能学到对端 MAC,说明 ARP 广播被泛洪过去了、ARP 应答也原样 带着 MAC 头回来了 —— 整个以太网帧穿过了 WAN

转发路径完全不同

text
  → af-packet-input
  → ethernet-input
  → l2-input          ← 进的是二层处理,不是 ip4-input
  → l2-learn          ← 学源 MAC
  → l2-flood          ← 查不到目的 MAC 就泛洪(这里是 ARP 广播)
  → l2-output
  → vxlan4-encap      ← 套上 VXLAN + UDP + 外层 IP
  → ip4-rewrite
  → host-eth2-output

对比 GRE 的路径ip4-input → ip4-lookup → ip4-midchain), 从第三跳就分岔了。

在 VPP 眼里,这个包从头到尾是个以太网帧,三层信息只是载荷的一部分。

L2FIB 记录了桥学到的 MAC:

bash
./labctl vppctl site show l2fib verbose
text
 Mac-Address        BD-Idx If-Idx  Interface-Name
 aa:c1:ab:20:30:5e    1      7     vxlan_tunnel0     ← 从隧道学到的远端 MAC
 aa:c1:ab:04:32:d5    1      1     host-eth1         ← 本地 MAC

为什么跑在 UDP 上

VXLAN 用 UDP 4789 封装,而不是像 GRE 那样自己占一个 IP 协议号。 这个选择有两个实际好处:

一、能穿 NAT。 UDP 有端口,NAT 设备处理得了。 GRE 是协议 47,大多数 NAT 穿不过去。

二、支持多路径负载分担。

text
  中间设备做 ECMP 时按五元组哈希。

  GRE      没有端口字段 → 两个 VTEP 之间的所有流量
           在中间设备看来是同一条流 → 全挤在一条物理链路上

  VXLAN    把**内层流**的哈希写进**外层源端口** →
           不同的内层流有不同的外层源端口 →
           中间设备把它们哈希到不同的物理路径

第二点在数据中心里是刚需。没有它,两个机架之间买再多带宽也用不上 —— 所有流量都走同一条。

MTU 问题比 GRE 更紧张

text
  ┌──────────┬────────┬──────────────────────┐
  │ 隧道类型  │ 开销    │ 1500 物理口的可用 MTU  │
  ├──────────┼────────┼──────────────────────┤
  │ GRE      │ 24     │ 1476                 │
  │ VXLAN    │ 50     │ 1450                 │
  └──────────┴────────┴──────────────────────┘

GRE 那一篇讲的 MTU 分片问题在这里更严重。 数据中心的标准做法是把底层网络配成巨帧(9000 MTU), 这样封装 50 字节之后仍有 8950 可用,租户完全无感。

这也是为什么 VXLAN 主要用在数据中心内部而不是跨互联网 —— 你能控制底层 MTU 的时候它才好用。

一个坑:接口编号取决于链路声明顺序

netlab 按每个节点的链路顺序分配 ethN

这个坑我在写本章实验时踩了,而且症状极具迷惑性。

最初的拓扑链路顺序是:

yaml
links:
  - nat1 + hA      # 站点 A
  - nat1 + nat2    # WAN
  - nat2 + hB      # 站点 B

结果:

text
  nat1: eth1 = 站点 A LAN,  eth2 = WAN     ← 符合直觉
  nat2: eth1 = WAN,         eth2 = 站点 B LAN  ← 反过来了!

因为 nat2 参与的第一条链路是 WAN。于是我在 nat2 上 把 WAN 口放进了桥域,而它的 LAN 口还留着三层地址。

症状是:配置命令都成功、桥域看起来正常、L2FIB 也在学 MAC, 但学到的全是错误的 MAC,跨站点就是不通。

解决办法是调整链路声明顺序,让每个节点的本地 LAN 都排在第一:

yaml
links:
  - nat1 + hA      # 站点 A → nat1 的 eth1
  - nat2 + hB      # 站点 B → nat2 的 eth1
  - nat1 + nat2    # WAN    → 两台各自的 eth2

排查方法:配任何二层/隧道之前,先确认接口含义。

bash
vppctl show interface addr | grep -A1 '^host-eth'

看每个口上的地址,比看接口名可靠得多。

GRE 还是 VXLAN

text
  ✓ 用 GRE                          ✓ 用 VXLAN
  ────────────────────────────      ──────────────────────────────
  两个站点是不同的网段                两个站点要在同一个二层域
  只要三层可达                       需要跨站点做二层广播
  想省开销(24 vs 50 字节)           需要多租户隔离(VNI)
  跨互联网,MTU 不可控                需要 ECMP 多路径分担
                                    底层 MTU 可控(数据中心)

一个常见误区是"VXLAN 更现代所以更好"。二层域拉长是有代价的 —— 广播域变大、故障域变大、生成树/环路风险跨站点传播。 如果三层能解决,就不要拉二层。

VXLAN 真正不可替代的场景是:虚拟机/容器需要跨物理位置保持同一个二层网段 (在线迁移、集群心跳),或者需要大规模多租户隔离。

小结

层次二层 overlay,封装完整以太网帧
封装外层 IP(20) + UDP(8, 端口 4789) + VXLAN(8) + 内层以太网头 = 50 字节
VNI24 位,1600 万个二层域,多租户隔离的基础
配置create vxlan tunnel ... vni N + 桥域 + set interface l2 bridge
前置进桥域前必须先摘掉接口的三层地址
决定性证据主机能学到对端主机的 MAC
转发路径l2-input → l2-learn → l2-fwd/flood → vxlan4-encap
为什么用 UDP能穿 NAT;外层源端口带内层哈希,支持 ECMP
MTU比 GRE 更紧张,数据中心靠巨帧解决
实验中的坑netlab 按链路顺序分配 ethN,两端接口含义可能相反

实验环境基于 netlab + containerlab + VPP 26.06