本文介绍通过 VXLAN 在局域网中创建一条高性能二层隧道的方法。
在学校里有两间实验室,其中一间 (A) 中放置了软路由和服务器,而另一间 (B) 则只有一个 50 元钱买的斐讯 k2t 路由器;两个实验室都有连接了校园网,但没有单独的网线连通。由于学校的网络登录比较麻烦,而且单个用户有设备数量限制,所以不想在 B 房间登录校园网。我希望通过校园网,在两个实验室之间创建一个二层隧道:在 B 房间中,直接进入 A 实验室的内网,所有设备从 A 实验室出口访问网络。同时,还希望在两个实验室间实现 Wi-Fi 的漫游。
要想连接两个内网,我们首先会想到使用 OpenVPN 或 WireGuard 来实现。但是,OpenVPN 体量太重、配置麻烦、性能差(尤其是 B 房间的路由器性能也不太好);WireGuard 轻量、性能好,却是 3 层协议,对 2 层不太友好。经过比较,我决定选择 VXLAN 来实现目标。
VXLAN 是一个简单的协议,将以太网封装到 UDP 中,在 3 层网络中传输 2 层帧。此协议不带加密,因此不适合需要高安全性的环境;考虑到实验室内网几乎人人可访问,不存在什么安全性可言,为了方便和高性能,选择 VXLAN 作为桥接的协议。
两边的路由器都使用 OpenWrt 系统,OpenWrt 的 uci 配置框架对 VXLAN 提供了支持(虽然必须用图形界面配置)。vxlan 的配置比较简单,只需使用命令 opkg install vxlan
安装包,然后按照官方 wiki 的教程填写好 peer 配置,两边就可以通信了。我的配置如下:
config interface 'vxlan0' option proto 'vxlan' option peeraddr '10.134.xxx.xxx' option port '4789' option vid '8' option tunlink 'wan'
然后,在 luci 图形界面中,将 vxlan0 接口添加到路由器的 br-lan 桥接接口上:
桥接接口后,B 房间的电脑就可以顺利地通过 DHCP 获得 IP、通过 A 房间的路由器访问外网(实际性能也很不错),A、B 房间的机器也能互相 ping。
此桥接并非完美无缺。由于设备对 VXLAN 桥没有感知,认为自己连接到了标准的以太网链路(MTU 是 1500),TCP 协议的 MSS 字段会被设为 1460。VXLAN 会带来 50 个字节的数据包头额外开销,因此 MSS 实际应设为 1410,而非 1460,否则大的数据包会被丢弃。在 A 机器上,OpenWrt 的路由 firewall 里默认开启了 MSS Clamping 功能,从 VXLAN 来、传向外网的 TCP SYN 包,经过 firewall iptables 后,MSS 值会被自动调整为 1410,使得出口到外网的 TCP 连接一切正常。但是,A、B 两个房间的机器桥接时,数据包不会经过 iptables,导致 MSS 值依旧为 1460,AB 之间的数据包在内容长度超过 1410 时,被直接丢弃。这个问题,如果不知道其中的原因,看起来会比较玄学:机器能正常 ping 通,ssh 能够开始握手,但握手到一半时会卡住,无法连上。
为了解决这个问题,我们需要开启 Linux 内核中的一个功能:bridge-nf-call-iptables。这个功能会使桥接的数据包也经过 iptables,我们就可以使用 iptables 来进行 MSS 值的调整。我在 B 房间的路由器上运行以下语句,打开这个功能:
opkg install kmod-br-netfilter echo net.bridge.bridge-nf-call-iptables=1 >> /etc/sysctl.conf
然后,在 /etc/firewall.user
中,加入以下语句,使 OpenWrt 防火墙启动时,插入一条 iptables 规则。该 iptables 规则会自动根据接口的 MTU,调整 TCP 包中的 MSS 值。
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
最后,重启 B 路由器,就可以使两边机器的 TCP 连接正常工作了。
本作品使用基于以下许可授权:Creative Commons Attribution-ShareAlike 4.0 International License.