我以编程方式创建了一个绑定到虚拟接口的套接字,并根据默认路由表规则设置接收所有传出流量。一旦传出数据包到达,它将被封装一些 VPN 标头并通过物理接口发送到远程隧道网关。
我想知道是否可以实现到隧道网关的任何类型的路径 MTU 发现算法,以避免数据包碎片并提高性能。
我想到的一种解决方案是在 IP 标头中为一些随机传出数据包设置 DF 位,并捕获具有以下标头的任何 icmp 响应:
Type: 3 (Destination Unreachable)
Code: 4 (Fragmentation Needed and Don't Fragment was Set)
比我保持响应长度并发送尺寸减小的 icmp 数据包,直到达到 DF=1 的最大数据包大小并获得成功响应。
我想知道是否有更好的方法来做到这一点..也许使用一些内置机制?
也许以下设置会自动执行此操作(net.inet.tcp.path_mtu_discovery)?如果没有,有没有办法从路由表缓存中读取它?
谢谢 !
对所有数据包设置 DF ,而不仅仅是随机数据包。(请记住,“路径 MTU”意味着每条路径可能有自己的 MTU – 到节点 B 的 MTU 可能为 1500,但到节点 C 的 MTU 为 1280。)
这是通常内置的机制。对于 TCP,它内置于操作系统的 TCP 实现中;当您在 UDP 之上进行构建时,您基本上是在实现自己的传输协议,因此处理 MTU 发现也成为您的工作。
当设置 IP_MTU_DISCOVER 套接字选项(用于设置 DF 位的选项)时,Linux 本身将读取 ICMP“太大”响应并更新其路由缓存。正如 ip(7) 手册中 IP_MTU_DISCOVER 下所述,您可以使用 getsockopt(IP_MTU) 检索 MTU,而无需自己解析 ICMP。
创建一个 UDP 套接字,将其 connect() 到端点的地址,然后使用 getsockopt(IP_MTU) 检索路径 MTU – 也在 ip(7) 手册中的 IP_MTU_DISCOVER 下提到。
最后,请记住,并非所有路由器都能正确生成“太大”的 ICMP,并且并非所有防火墙都允许此类数据包通过,因此不幸的是,纯粹依赖 IP 级 PMTUD 是不够的 - 您很可能想要实现还有更高层的机制。例如,TCP(至少在Linux上)在
net.ipv4.tcp_mtu_probing
sysctl下实现了自己的MTU发现;每当大型 TCP 段无法传送时就会使用此方法。