OpenWrt中的MSS钳制是干什么用的

  前记——首先还是挺遗憾的,正式入职的时候被调到了别的组,关于网络的知识不得不说dog250赵亚大佬的博客,从高中开始就一直帮助过我解决过很多问题,甚至后来才发现hostloc论坛十分流行的魔改版BBR就是出自大佬之手。实习的时候一直没有和赵亚大佬仔细聊聊,本想着正式入职再简单介绍自己,但是万万没想到调去了别的部门,不过有幸实习的时候组会有过一些讨论,也算是共事过了。
  老早之前就看到OpenWrt防火墙中的MSS钳制选项,一直没有过多思考其作用,可以在防火墙->wan->常规设置中找到,wan区域中该选项默认是开启的,其他区域默认没有。这里以发现问题->解决问题的思路来探讨这个选项的意义及作用。

MTU问题

  这个问题很常见,当Don't Fragment标志被设置在IPv4头中,且IP数据包大小超过接口MTU时,就会因为无法分片而被丢弃。典型的状况就是,DNS解析正常、可以ping通目标、HTTP访问正常、部分网站HTTPS异常。这是由于TLS握手时服务端会响应一个包含证书的远超MTU大小的数据包,该包很大程度上(当服务端发出时就被设置了DF标志)会被丢弃,导致HTTP网站无法打开,Wireshark可以观察到TCP previous segment not captured消息。

  这次碰到的情况的简易网络拓扑可以如下显示:

1
2
3
4
PC <---> (LAN) OpenWrt (Wireguard) <---> (Wireguard) Server (WAN)
LAN MTU = PC MTU = 1500
Wireguard MTU = 1400
WAN MTU = 9000

  可以确信各自的MTU都合法,网络通信均正常,但是仍旧发生了MTU问题。在PC和Server处进行抓包,可以观察到PC->Server的TCP第一次握手的MSS值为1460,Server->PC的TCP第二次握手的MSS值为1436,显然TCP数据段在经过Wireguard隧道的时候,PMTU(路径MTU发现)机制没有正常工作,导致过大的数据包被丢弃。接下来是思考和解决的过程:

  1. 再次检查Wireguard两端的MTU配置,均正常
  2. 怀疑OpenWrt路由表的配置,该路由条目为手动添加,怀疑MTU错误,发现路由默认MTU即为接口MTU,正常
  3. 怀疑nftable出现了问题,因为该路由是通过规则来决定使用的,即对于某些数据包设置标记0x8888,然后添加一个规则,有0x8888标记的数据包使用0x8888号路由表
  4. 尝试在设置0x8888标记时,强制缩小TCP的MSS值,这个专业术语叫做TCP MSS Clamping,看到Clamping这个单词还有点陌生,一查夹紧的意思,顿时恍然大悟,这不就是TCP钳制的意思
  5. 所以只要将Wireguard接口对应区域的TCP钳制选项打上勾,这个问题便迎刃而解,握手时MSS值将会被缩小到1360(1400-IP头-TCP头),从而可以顺利通过Wireguard隧道

TCP钳制

  用白话文概括一下:这个选项就是在TCP三次握手的时候,将MSS值匹配至接口的MTU值。这个选项理应默认就打开,不知为何默认是关闭的状态,因为会产生性能消耗?或者说会使硬件NAT机制失效?话说回来一般家庭网络情况也较为简单,WAN区域的TCP钳制选项打开即可应对大多数情况。