GENEVE 协议简介与 VNI 多租户隔离实验
· 1748 words · ~ 9 min read
云计算数据中心里,一台物理服务器上可能同时运行数百个虚拟机或容器,分属不同租户。这些租户需要各自独立的 L2 网络,互相不可见——但物理网络的 VLAN 标签只有 12 位,最多 4096 个网络,远远不够用。Overlay 网络因此而生:把虚拟的 L2 帧封装在物理网络的 UDP/IP 包里传输,让虚拟网络的数量不再受物理硬件限制。
GENEVE 是目前最新、最具扩展性的 Overlay 封装协议,被 OVN、AWS Nitro、Cilium 等主流项目采用。本文从它诞生的历史背景讲起,逐层剖析其协议细节,最后在单台机器上做完整实验。
1. 历史背景:封装协议的碎片化
在 GENEVE 之前,业界已经有多种 Overlay 封装协议,但各自为政,互不兼容:
| 协议 | 提出者 | RFC | 特点 |
|---|---|---|---|
| NVGRE | Microsoft | RFC 7637 | GRE 封装,Key 字段做租户 ID |
| STT | Nicira (VMware) | 草案 | TCP-like 封装,利用硬件 TSO |
| VXLAN | VMware/Cisco | RFC 7348 | UDP 封装,24 位 VNI,最流行 |
| GENEVE | VMware/MS/Red Hat/Intel | RFC 8926 | UDP 封装,可变长 Options |
VXLAN 解决了 VLAN 数量不足的问题,但它的 8 字节固定头部没有扩展空间——一旦需要携带额外的元数据(如安全策略 ID、服务链标签、QoS 标记),就只能在内层帧外再套一层封装,或者依赖带外信道。
不同 SDN 控制器(NSX、OpenStack OVN、AWS 等)为了传递各自需要的元数据,纷纷自定义私有格式,导致设备互通困难,硬件卸载芯片无法统一支持。
GENEVE(Generic Network Virtualization Encapsulation)正是为了终结这种碎片化而设计:提供一个统一的、可扩展的封装框架,让各方在同一个协议头部里携带任意元数据,同时对硬件解析友好。
2. GENEVE 协议详解
完整数据包结构
一个携带以太网内层帧的 GENEVE 数据包,从外到内的层次如下:
|
|
外层 UDP 使用目的端口 6081(VXLAN 是 4789),源端口由发送方基于内层五元组哈希生成,确保 ECMP 等价多路径负载均衡能正确分流。
GENEVE 固定头部(8 字节)
|
|
| 字段 | 位宽 | 说明 |
|---|---|---|
| Ver | 2 | 协议版本,当前固定为 0 |
| Opt Len | 6 | Options 长度,单位为 4 字节,取值 0–63,即最多 252 字节 Options |
| O | 1 | OAM 控制包标志,置 1 时内层帧为管理报文而非数据 |
| C | 1 | Critical Options 标志,置 1 时接收方必须理解所有 Critical 选项,否则丢弃 |
| Rsvd | 6 | 保留,发送方置 0 |
| Protocol Type | 16 | 内层协议类型:0x6558 = 透明以太网桥接(最常用),0x0800 = IPv4,0x86DD = IPv6 |
| VNI | 24 | Virtual Network Identifier,24 位 = 1677 万个虚拟网络 |
| Reserved | 8 | 保留,置 0 |
TLV Options(0–252 字节)
这是 GENEVE 相对 VXLAN 最核心的差异点。Options 区域由一到多个 TLV(Type-Length-Value)条目组成:
|
|
| 字段 | 位宽 | 说明 |
|---|---|---|
| Option Class | 16 | 定义选项的组织,类似以太网 OUI。0x0100 = Linux 内核,0x0101 = Open vSwitch,0xFFFE = 实验用 |
| Type | 8 | 在该 Class 内的选项编号,最高位为 1 时表示 Critical |
| R | 3 | 保留 |
| Length | 5 | Option Data 的长度,单位为 4 字节,最多 124 字节 |
| Option Data | 变长 | 实际携带的元数据内容 |
硬件友好性:不认识某个 Option Class/Type 的设备可以根据 Length 跳过该选项(除非 Critical 位为 1),而不需要解析选项内容。这使得新版本的选项格式不会导致旧设备彻底无法处理数据包,只是跳过未知部分。
OVN 如何使用 GENEVE Options
OVN(Open Virtual Network)是使用 GENEVE 元数据最典型的案例。它在 Options 中携带两个关键信息:
- Logical Datapath:标识数据包当前所在的逻辑网络(相当于租户 VPC)
- Logical Port:标识数据包的来源或目的逻辑端口
这让 Hypervisor 上的 OVS 在收到隧道包时,不需要查外部数据库,直接从 GENEVE Options 里读出包的逻辑身份,决定转发策略。这是 GENEVE Options 让 SDN 控制面简化的典型体现。
3. GENEVE 相对 VXLAN 的核心优势
VXLAN 的 8 字节固定头部里,除了 24 位 VNI,只有一个 I 标志位(表示 VNI 有效),没有任何扩展空间。需要携带元数据时只能走以下绕路方案:
- 在内层帧外再套一层封装(增加头部开销)
- 把元数据编码进 VNI 本身(牺牲 VNI 空间,且语义不清)
- 依赖带外信道(控制平面额外通信)
GENEVE 通过 TLV Options 机制,将这些需求内化到协议本身:
| 能力 | VXLAN | GENEVE |
|---|---|---|
| VNI 位宽 | 24 位 | 24 位 |
| 最大虚拟网络数 | 1677 万 | 1677 万 |
| 携带元数据 | 不支持 | 最多 252 字节 TLV |
| 头部长度 | 固定 8 字节 | 8 字节 + Options |
| 硬件跳过未知选项 | N/A | 支持 |
| Critical 选项标记 | 不支持 | 支持 |
| UDP 目的端口 | 4789 | 6081 |
| 主要使用者 | Kubernetes Flannel/Calico | OVN、AWS Nitro、Cilium |
4. 主流项目中的 GENEVE
OVN:默认隧道协议就是 GENEVE,所有 Hypervisor 之间的流量都走 GENEVE 封装,并在 Options 中携带逻辑数据路径和端口信息。
AWS Nitro:AWS 的 Nitro 卡(Smart NIC)使用 GENEVE 封装实例之间的网络流量,并通过 Options 传递安全组策略标签,实现硬件卸载时的策略执行。
Cilium:CNI 插件支持 GENEVE 模式(--tunnel=geneve),作为 VXLAN 的替代,在需要携带 eBPF 元数据时更有优势。
Open vSwitch(OVS):OVS 2.5+ 支持 GENEVE,可以通过 ovs-vsctl 创建 GENEVE 类型的 Port,并读写 Options。
5. 实验:单台物理机验证 VNI 隔离
用两个 network namespace 模拟两台物理主机,通过 veth pair + Linux bridge 构建 underlay,在上面配置两套 GENEVE 隧道(VNI 100 和 VNI 200)。两套隧道使用完全相同的 overlay IP,用 VRF 让内核路由表按 VNI 查各自的路由,演示不同租户 IP 地址空间完全隔离。
拓扑
flowchart TB
subgraph H1["ns-h1(模拟主机 1,underlay: 192.168.100.1)"]
direction TB
V100_H1["vrf100\n路由表 100"]
V200_H1["vrf200\n路由表 200"]
G0_H1["geneve0\nVNI=100\n10.0.0.1/24"]
G1_H1["geneve1\nVNI=200\n10.0.0.1/24"]
V100_H1 --> G0_H1
V200_H1 --> G1_H1
end
BR["br-underlay\nLinux bridge(模拟物理交换机)\nunderlay: 192.168.100.0/24"]
subgraph H2["ns-h2(模拟主机 2,underlay: 192.168.100.2)"]
direction TB
V100_H2["vrf100\n路由表 100"]
V200_H2["vrf200\n路由表 200"]
G0_H2["geneve0\nVNI=100\n10.0.0.2/24"]
G1_H2["geneve1\nVNI=200\n10.0.0.2/24"]
V100_H2 --> G0_H2
V200_H2 --> G1_H2
end
G0_H1 -- "GENEVE VNI=100\nUDP 6081" --- BR
G1_H1 -- "GENEVE VNI=200\nUDP 6081" --- BR
BR -- "GENEVE VNI=100\nUDP 6081" --- G0_H2
BR -- "GENEVE VNI=200\nUDP 6081" --- G1_H2
style G0_H1 fill:#4a90d9,color:#fff
style G0_H2 fill:#4a90d9,color:#fff
style G1_H1 fill:#e67e22,color:#fff
style G1_H2 fill:#e67e22,color:#fff
style BR fill:#27ae60,color:#fff
搭建 Underlay 网络
|
|
配置 VRF + GENEVE
在 ns-h1 和 ns-h2 上各建两个 VRF,每个 VRF 绑定一张独立路由表,再将对应 VNI 的 GENEVE 接口加入该 VRF:
|
|
验证隔离
ip vrf exec <vrf名> 在指定 VRF 的路由上下文中执行命令,相当于切换到对应租户视角:
|
|
同时在另一个终端抓 underlay 接口上的 GENEVE 包:
|
|
实际输出:
|
|
三点可以直接从输出读出:
vni 0x64(十进制 100)和vni 0xc8(十进制 200)交替出现,两条流量并行跑在各自的隧道里,互不干扰。- 两个 VNI 的内层 IP 完全相同(10.0.0.1 → 10.0.0.2),证明不同租户可以复用同一套地址空间。
id 28082和id 28083是两条 ping 各自的 ICMP 标识符,接收端按 VNI 分别解封装到geneve0(vrf100)或geneve1(vrf200),不会混淆。
清理实验环境
|
|
7. 总结
GENEVE 解决的核心问题是:如何在一个统一的 Overlay 封装协议里,既做到 24 位 VNI 的大规模租户隔离,又能携带任意扩展元数据,同时对硬件解析友好。
它没有在 VXLAN 上打补丁,而是从头设计了可变长 TLV Options 机制——定义了一个"元数据总线",让不同的 SDN 系统(OVN、AWS、Cilium 等)各自在 Option Class 命名空间下写入自己需要的元数据,而其他不认识这些选项的设备可以安全跳过。
从实验可以直观看到:两套 GENEVE 隧道使用完全相同的 overlay IP,各自走各自的 VNI 封装,在 underlay 上共用同一条 UDP 通道,在接收端由内核按 VNI 值分发到对应接口——这正是云计算多租户网络隔离的数据平面基础。