环境简介
学校两个校区,我们暂且称为A校区和B校区。服务器存放在B校区,但是B校区没有高速互联网连接,A校区有IPv6不限速的连接,且跨校区的IPv4是不限速的。因此我们考虑通过异地组网来实现高速的互联网连接。
需求分析
作为互联网服务基础设施,我们的连接应当能承载上层以太网帧,而非应用层数据,以此保证连接对上层透明。
此外,既然是为了实现“高速”互联网连接,高效的承载协议自然是必须的。
思路选取与实现
我们可以在OpenWrt的Wiki上找到隧道协议的相关内容, 其中GRE和VXLAN是符合我们上面的设想的承载方案。当然如果仅仅是为了高速传输,运行在应用层的Hysteria2也是可以的。
下面我们从简单的结构开始说起
七层代理
Hysteria和vmess此种代理协议,生来即是为流量转发而设计的,而且能够有效的利用带宽。如果只是简单的下行需求,可以采用这种方案
GRE
GRE是一种通用的封装形式。它可以封装IP包(不含以太网帧头),也可以封装完整的以太网帧。如果是后者,在Linux下我们称之为gretap。当然以太网帧头也是要占用数据包长度的,在计算MTU时需要注意计入。
在使用GRE封装流量时,应当注意MTU的设置。考虑MTU时,首先应当明确MTU的限制因素,即连接数据中心的线路对MTU的限制,和内层应用对MTU的需求。
就我们的场景来说,我们计划将上层通过SLAAC分配的IPv6通过隧道传输,而RA报文里存在MTU=1500的数据,而隧道结点又是作为二层连接,不能实现MTU钳制,所以GRE隧道内层所需要容纳的长度就来到了1518。而校方链路的MTU为1500,这不可避免的要求我们将GRE包分片。值得注意的是,只有IPv4支持分片,IPv6是不支持分片的。
不幸的是,校方防火墙拦截了GRE流量,我们需要在数据包外面动一些手脚。
GRE+WireGuard
通过在GRE数据包外面套一层WireGuard,我们既可以解决IPv6不支持分片的问题,又可以通过学校防火墙。搭配OpenWrt自带的ddns脚本,可以实现自动更新GRE对端。
VXLAN
VXLAN是基于UDP的组网协议,而且VXLAN不支持分片,因此这种方式并不适合我们使用
如何设置MTU
这是一个以太网包在网线中传输的样子。常规链路MTU为1500指的是除去以太网帧头和CRC部分的长度。我们使用gretap,那么GRE中需要封装一个完整的以太网帧
外层以太网帧头 | IP头 | UDP头 | WireGuard头 | IP头 | GRE头 | 内部以太网帧头 | 内部MTU | 内部以太网帧CRC | 外部以太网CRC |
---|---|---|---|---|---|---|---|---|---|
14 | 20/40 | 8 | 32 | 20 | 12 | 14 | 1500 | 4 | 4 |
这是不分片的理想情况,如果你的网络承载不了如此大的MTU,那么你需要缩减WireGuard的MTU,让系统将GRE包分片,才能完成传输。当然如果你的外层网络是IPv4,你也可以让系统将你的WireGuard数据包分片。
那么,有没有更优雅的方式呢
如果你有IPv6的前缀,你就可以自行下发RA报文,可以指定MTU(>=1280),这样系统就会使用你设置的路径最小MTU,而不会被夹。那IPv4也可以使用MSS clamping,将MTU缩减到恰当的值,优雅。
不过,关于MSS clamping,OpenWrt中默认只单向钳制出网的tcp syn请求。如果你使用端口映射将内部网络的端口映射出去,此时,syn请求为入方向,不会触发MSS clamping,就会出现MTU问题。此时只需要打开内网区域的MSS clamping即可解决。