在校园网之上用 WireGuard 承载 VXLAN 二层网络
坑边闲话:办公室隔壁本来已经有一个路由器,后来工位上多了一台台式机,再从现场拉一根线到隔壁就显得很不优雅。校园网链路已经在桌面上了,那就借它做 underlay,把真正想要的二层网络带回来。
1. 背景和目标·
1.1 物理问题不是核心问题·
这台机器是一台 Debian 13 台式机,主网口 eno1 接入学校校园网。校园网给它分配了 124.16.71.177/23,网关是 124.16.71.254。隔壁办公室有原来的路由器和内网环境,新的台式机希望像插在那个路由器后面一样工作。
直接拉线当然能解决问题,但它把一个网络问题变成了布线问题。更重要的是,工位变化、设备移动、临时机器接入都会让物理线缆变成维护成本。既然校园网已经提供了 IP underlay,更好的办法是把二层 Ethernet frame 封装起来,让台式机在逻辑上接回原来的内网。
这里的目标不是普通意义上的 VPN。普通三层 VPN 解决的是 IP reachability,本文需要的是二层语义:ARP、DHCP、默认网关、DNS domain,以及内网里依赖广播或同网段发现的东西,都应该像本地一样工作。
1.2 为什么是 VXLAN·
VXLAN 的价值在于把 Ethernet frame 放进 UDP packet 里。Linux 里 vxlan100 是一个标准的 net_device,上层协议栈看到的是一块 Ethernet 设备,下层实际发出去的是 UDP 封装包。
这台机器上的 VXLAN 不是大规模数据中心里的 EVPN 控制平面,而是一个 point-to-point 的 VTEP 连接:
1 | campus underlay |
vxlan100 的 VNI 是 100,本端 VTEP 是 10.100.21.1,远端 VTEP 是 10.100.10.2。这两个 VTEP 地址不暴露在校园网里,它们在 WireGuard 的点到点隧道内部。
1.3 为什么还要套 WireGuard·
VXLAN 本身不提供加密、认证和对端身份确认。把 VXLAN 直接跑在校园网之上,校园网就能看到裸的 overlay 二层流量,攻击面也会变大。
WireGuard 在这里承担安全边界。校园网只看到本机和远端 124.16.111.94:7980 之间的 UDP 流量,看不到 VXLAN 里的 ARP、DHCP 和内网 IP 流量。换句话说,校园网是 transport, WireGuard 是 cryptographic underlay,VXLAN 才是业务网络的二层 overlay。
2. 当前机器上的网络分层·
2.1 观测到的接口·
当前活跃接口里,和本文相关的有三个:
1 | eno1 124.16.71.177/23 campus network |
eno1 是真实物理出口,MTU 为 1500。wg0 是 WireGuard net_device,MTU 为 1280。vxlan100 是 VXLAN net_device,MTU 为 1230。
这个 MTU 组合是非常严密的:vxlan100 的 IP MTU 设为 1230,这意味着内层 IP 包最大为 1230 字节。加上内层以太网帧头(14 字节),内层以太网帧为 1244 字节;再封装 VXLAN 头部(8 字节)、UDP 头部(8 字节)和 VTEP 外层 IP 头部(20 字节),总包长正好是 1244 + 8 + 8 + 20 = 1280 字节。这恰好等于 wg0 的 MTU 1280,是无分片封装下的理论最大极限值。
2.2 数据平面封装·
从应用的角度看,它只是把 packet 发给 10.2.2.10 这个默认网关。但实际出线时,packet 会经历多层封装:
1 | +---------------------------------------------------------------+ |
这里有一个容易混淆的点:vxlan100 的 parent 是 wg0,所以 VXLAN 的 underlay 不是 eno1,而是 WireGuard 内部的 10.100.21.1 到 10.100.10.2。校园网只负责把 WireGuard 的外层 UDP packet 送到远端。
2.3 控制面配置·
控制面主要由 NetworkManager profile 和内核 routing table 共同完成。
2.3.1 物理出口 profile·
Ethernet_UCAS 是物理校园网 profile。它通过 DHCP 获取 124.16.71.177/23,默认路由 metric 为 200,并额外配置一条到 WireGuard endpoint 的 host route。
2.3.2 WireGuard profile·
wg0 是 WireGuard 设备。当前机器上它被 NetworkManager 识别为外部连接,配置来源是 /etc/wireguard/wg0.conf 风格的配置,核心参数是:
1 | Address = 10.100.21.1/32 |
2.3.3 VXLAN profile·
vxlan100 是 NetworkManager 管理的 VXLAN profile:
1 | VNI = 100 |
3. Underlay 设计·
3.1 物理网口只负责可达性·
eno1 连接学校校园网。当前 DHCP 状态是:
1 | address: 124.16.71.177/23 |
从设计视角看,eno1 不承载业务内网语义。它只要保证一件事:本机可以稳定访问远端 WireGuard endpoint,也就是 lab.littlenewton.cn:7980。这个域名当前解析到 124.16.111.94。
3.2 Endpoint host route 是锚点·
当前 Ethernet_UCAS profile 里有一条非常关键的静态路由:
1 | 124.16.111.94/32 via 124.16.71.254 dev eno1 |
对应的 NetworkManager keyfile 内容是:
1 | [ipv4] |
这条 /32 host route 的作用不是为了普通访问远端机器,而是为了固定 WireGuard 外层 packet 的路径。即使系统默认路由后来被 vxlan100 抢走,目的地址为 124.16.111.94 的 packet 仍然从 eno1 走校园网。
可以用下面的命令验证:
1 | ip route get 124.16.111.94 |
当前结果是:
1 | 124.16.111.94 via 124.16.71.254 dev eno1 src 124.16.71.177 |
4. WireGuard 层·
4.1 只暴露点到点 VTEP reachability·
WireGuard 的 AllowedIPs 当前只有 10.100.10.2/32。这非常重要,因为它让 wg0 的职责保持单一:只提供到远端 VTEP 的点到点可达性,而不是承担默认路由。
当前主 routing table 里有这条路由:
1 | 10.100.10.2 dev wg0 scope link |
所以当 vxlan100 需要把 VXLAN UDP packet 发到 10.100.10.2:4789 时,内核会把它交给 wg0。这时 WireGuard 再把它加密成外层 UDP packet,发给 124.16.111.94:7980。
4.2 不把 WireGuard 做成 full tunnel·
这套设计里,不应该把 WireGuard peer 的 AllowedIPs 写成 0.0.0.0/0。如果那样做,WireGuard 自己会参与默认路由竞争,NetworkManager 或 wg-quick 还可能引入 fwmark 和 policy routing。那是另一种 full tunnel 设计,不是本文的目标。
本文的默认路由应该来自 VXLAN 里的 DHCP,也就是远端内网网关 10.2.2.10。WireGuard 只负责给 VXLAN 提供安全的 underlay。
4.3 MTU 的分工·
当前 MTU 如下:
1 | eno1 1500 |
可以把它理解为一层一层给封装头部留预算,各层开销计算如下:
1 | application packet (IP MTU: 1230) |
可以看到,1230 并非随意设置的保守值,而是通过严格的以太网帧及封装协议头部尺寸逆推得到的理论上限。如果将 vxlan100 设为 1231,封装后的帧(1281 字节)在进入 wg0 时就会因超过 MTU 而发生分片(Fragmentation)或丢包,引发吞吐急剧抖动、大包 TCP 连接卡死等玄学问题。
5. VXLAN 层·
5.1 vxlan100 是业务网络入口·
vxlan100 的内核 link 信息是:
1 | vxlan id 100 remote 10.100.10.2 local 10.100.21.1 dev wg0 dstport 4789 |
这句话已经完整描述了 VXLAN 数据平面:
- VNI 是
100 - remote VTEP 是
10.100.10.2 - local VTEP 是
10.100.21.1 - parent device 是
wg0 - UDP destination port 是
4789
注意这里没有 Linux bridge。对这台客户端来说,直接把 IP 配在 vxlan100 上就足够了。它需要的是加入远端二层广播域,不一定要在本机再桥接其他物理端口。
5.2 DHCP 在 overlay 里完成·
vxlan100 的 IPv4 method 是 DHCP。当前它从远端内网拿到:
1 | address: 10.2.8.21/16 |
这说明 DHCP Discover、Offer、Request、Ack 都是在 VXLAN 二层 overlay 里完成的。上层系统看到的是标准 DHCP,底层实际是广播 Ethernet frame 被 VXLAN 封装后,通过 WireGuard 发到远端路由器。
5.3 FDB learning 的意义·
当前 vxlan100 开启了 learning。由于这是 point-to-point VXLAN,学到的远端 MAC 都会映射到同一个 VTEP:
1 | MAC address VTEP |
可以用下面的命令看 FDB:
1 | /sbin/bridge fdb show dev vxlan100 |
这里不需要 multicast VXLAN,也不需要 EVPN。所有远端二层对象都在同一个远端 VTEP 后面,静态 remote 加 learning 就够了。
6. 默认路由和防嵌套·
6.1 当前默认路由·
当前主 routing table 里有两条 IPv4 默认路由:
1 | default via 10.2.2.10 dev vxlan100 metric 100 |
Linux 选择 metric 更低的路由,所以普通 IPv4 流量默认走 vxlan100,也就是从隔壁路由器那边出网。校园网默认路由仍然保留,作为 vxlan100 没起来时的 fallback,也作为 endpoint host route 的 next hop 所在链路。
验证普通公网目的地址:
1 | ip route get 8.8.8.8 |
当前结果是:
1 | 8.8.8.8 via 10.2.2.10 dev vxlan100 src 10.2.8.21 |
这正是预期行为。业务默认出口是远端内网网关,不是学校校园网网关。
6.2 什么是路由嵌套·
隧道系统最容易出问题的地方是 outer packet 被 inner default route 吃掉。没有 endpoint host route 时,系统可能会这样决策:
6.2.1 递归路径·
1 | need to send WireGuard outer packet to 124.16.111.94 |
这就是 tunnel recursion。表现可能是握手时好时坏、默认路由一切换就断、VXLAN 刚拿到 DHCP 后 WireGuard 立刻失联。
当前设计用一条更具体的 /32 route 打断这个递归:
6.2.2 打断路径·
1 | dst 8.8.8.8 --> default metric 100 --> vxlan100 |
路由查找遵循 longest prefix match。124.16.111.94/32 比 0.0.0.0/0 更具体,所以即使 vxlan100 是默认路由,WireGuard endpoint 也永远从校园网物理口出去。
6.3 IPv6 和 Tailscale 的旁路·
当前 vxlan100 禁用了 IPv6,但 eno1 通过 RA 拿到了 IPv6 默认路由:
1 | default via fe80::7625:8aff:fe0e:b340 dev eno1 |
在现代网络环境下,这会导致极高的流量旁路泄漏安全隐患。由于操作系统和浏览器在 Happy Eyeballs 算法下会默认优先选择 IPv6 进行传输,当你访问支持双栈的网站(如 GitHub、Google 等)时,大部分实际业务流量会直接走 eno1 通过校园网外发,完全绕过了 WireGuard 和 VXLAN 隧道!
如果希望所有业务流量都安全、完整地经过隔壁路由器,需要给 overlay 同样配置 IPv6,或者在物理校园网的 NetworkManager profile 中明确关闭 IPv6 默认路由(Default Route),迫使所有互联网流量回退至 IPv4 从而百分之百走 VXLAN 回传。
机器上还有 tailscale0,并且 ip rule 里有 table 52。这不影响本文的主路径,但它说明系统里并不是只有 main table。排障时不要只看 ip route,也要看 ip rule show 和 ip route show table all。
7. NetworkManager 配置命令·
7.1 校园网 profile·
物理网口 profile 的核心是 DHCP、较高的 route metric,以及 endpoint host route:
1 | nmcli connection add type ethernet ifname eno1 con-name Ethernet_UCAS autoconnect yes |
注意:这里添加静态路由时省略了下一跳网关地址(124.16.71.254)。这样做能让 NetworkManager 在激活连接时,自动将 DHCP 实际获取到的网关填充为该静态路由的下一跳。如果物理网络拓扑或 DHCP 分配的网关发生变化(如变更为其他网段),此静态路由依然能够自适应生效,避免了硬编码引起的路由故障。
如果 endpoint 的 IP(DDNS 域名解析结果)会发生动态变化,建议在 /etc/NetworkManager/dispatcher.d/ 放置一个轻量级的触发脚本,或者用 Cron 定时监测并更新 /32 主机路由,防止 IP 漂移时流量漏入 vxlan100 导致路由嵌套死锁。
7.2 WireGuard profile·
当前机器上 wg0 看起来是由 /etc/wireguard/wg0.conf 风格配置创建的,NetworkManager 把它识别为 externally connected。若继续沿用这种方式,最小配置结构如下,密钥不要写进博客和日志:
7.2.1 沿用 wg-quick·
1 | [Interface] |
启动可以是:
1 | sudo wg-quick up wg0 |
如果希望全交给 NetworkManager 管理,可以从 WireGuard 配置导入:
7.2.2 导入到 NetworkManager·
1 | sudo nmcli connection import type wireguard file /etc/wireguard/wg0.conf |
这里仍然要保持 AllowedIPs = 10.100.10.2/32。不要让 WireGuard 先变成 full tunnel,否则默认路由的所有权会变复杂。
7.3 VXLAN profile·
VXLAN profile 是这套系统里最适合用 NetworkManager 管的部分:
1 | nmcli connection add type vxlan ifname vxlan100 con-name vxlan100 \ |
这会生成类似下面的 keyfile:
1 | [connection] |
7.4 激活顺序和检查·
激活顺序应该先有 underlay,再有 WireGuard,最后起 VXLAN:
1 | nmcli connection up Ethernet_UCAS |
如果 wg0 仍由 wg-quick 创建,则中间一步换成:
1 | sudo wg-quick up wg0 |
检查命令建议固定成一组:
1 | ip -br addr |
预期结果有三条:
ip -d link show vxlan100里能看到dev wg0ip route get 124.16.111.94走eno1ip route get 8.8.8.8走vxlan100
8. 运行时排障思路·
8.1 分层定位·
这类网络故障不要一上来抓业务包,先按层次确认 invariant:
1 | Layer 1: campus underlay |
只要某一层的 invariant 不成立,就先停在那一层排障。比如 ip route get 124.16.111.94 如果走了 vxlan100,那就不要继续查 DHCP 或 DNS,先修 endpoint host route。
8.2 最容易踩的坑·
第一,忘记 endpoint host route。vxlan100 一旦拿到默认路由,WireGuard 外层 packet 被送进内层 overlay,形成 tunnel recursion。
第二,把 WireGuard AllowedIPs 配成 0.0.0.0/0。这会把 WireGuard 从安全 underlay 变成默认路由参与者,和本文的 VXLAN DHCP 默认路由冲突。
第三,MTU 过大。小包 ping 正常不代表 TCP 正常,尤其是 VXLAN over WireGuard 这种双层封装。
第四,忽略 IPv6。当前 IPv4 默认路由走 VXLAN,但 IPv6 默认路由仍在 eno1 上。是否接受这种 split behavior,要根据安全边界决定。
9. 总结·
这套网络的关键不是「能不能连通」,而是每一层都只做自己的事:
eno1只负责校园网 underlay 和 WireGuard endpoint reachabilitywg0只负责加密点到点 VTEP reachabilityvxlan100负责二层 overlay,并通过 DHCP 接入远端内网/32endpoint host route 负责打断 tunnel recursion- route metric 让 IPv4 默认出口稳定落在远端内网网关
10.2.2.10
这样做以后,新台式机不需要物理上插到隔壁路由器,逻辑上仍然像在那个二层网络里。校园网只承载加密后的 WireGuard UDP packet,真正的内网广播域被 VXLAN 搬到了桌面上。









