我们在客户处安装了数十台嵌入式设备,所有设备都通过我们的 OpenVPN 服务进行呼叫。这通常运行良好,但我们的一些客户存在严重的路径 MTU 问题。我们对客户修复其网络的影响有限,因此我们需要 OpenVPN 来处理这个问题。简而言之,我的问题是:
如何才能缓解每个客户端的低路径 MTU 问题,即不使用全局设置来适应所有客户端的最坏情况
请注意,我们的最坏情况相当糟糕:路径 MTU 576,丢弃所有碎片,不自行碎片化,不遵守 DF 位。你明白为什么我不想全局解决这个问题。
这OpenVPN 手册页提供了许多与 MTU 相关的选项,最值得注意的是--link-mtu, --tun-mtu, --fragment and --mssfix
。但它也说
--link-mtu [...] 最好不要设置此参数,除非您知道自己在做什么。
--tun-mtu [...] 最好使用--fragment 和/或--mssfix 选项来处理 MTU 大小问题。
因此我开始尝试--fragment
,--mssfix
但很快就意识到,至少前者不仅必须在客户端设置,而且也包括服务器端。然后我查看了服务器端每个客户端的配置,--client-config-dir
但它说
以下选项在客户端特定上下文中是合法的:--push、--push-reset、--iroute、--ifconfig-push 和 --config。
没有提到 MTU 选项!
以下是我更具体的问题:
- 为什么不鼓励使用
link-mtu
和tun-mtu
不鼓励使用?这些选项可能存在哪些问题?请注意,我对低级 IP 标头处理非常熟悉。 - 哪些选项
link-mtu tun-mtu fragment mssfix
必须在服务器端镜像才能起作用? link-mtu tun-mtu fragment mssfix
可以使用哪些选项client-config-dir
?- 如果所有四个选项都必须在服务器端镜像,并且不能在内部使用
client-config-dir
:是否有其他方法可以解决每个客户端的低路径 MTU 问题?
笔记:
- 我的部分问题 5 年前已经有人问过了这里,但当时还没有真正回答过这些问题,因此我才敢重复它们。
- OpenVPN 服务器目前在 Ubuntu 12.04 上为 2.2.1。我们正准备在 Ubuntu 14.04 上升级到 2.3.2
- Debian 7.6 上的 OpenVPN 客户端是 2.2.1
- 我很乐意自己手动确定客户的路径 MTU
- 目前我们无法进行太多服务器端测试。但我们正在构建一个完全独立的测试平台,应该很快就会准备好。
我感谢任何有用的建议。
答案1
mssfix 1300
我通过在配置文件中添加选项解决了客户端的问题。
来自 openvpn 手册页:
--mssfix max
Announce to TCP sessions running over the tunnel that they should limit their send packet sizes such that after OpenVPN has encapsulated them, the resulting UDP packet size that OpenVPN sends to its peer will not exceed max bytes.
我的解决方案的原始想法来自personalvpn.org
答案2
不确定这是否可以帮助嵌入式设备,但我愿意分享我的经验,希望它可以帮助其他人。
TL;DR:跳到最后一个问题/答案寻找可能的解决方案
- 为什么不建议使用 link-mtu 和 tun-mtu?这些选项可能存在哪些问题?请注意,我对低级 IP 标头处理非常熟悉。
根据我作为 OpenVPN 用户的经验,对于大多数常见的在某些情况下,实际上fragment
和/或mssfix
值更好不是被摆弄,即与文档所述相反。
link-mtu
和tun-mtu
的值是根据隧道使用的密码(如果有)相互关联的。默认情况下,tun-mtu
是经典的 1500 字节, 则link-mtu
基于此得出,但您可以设置其中一个,从而相应地重新计算另一个。在这方面,一个相当确定的经验法则是绝不放两个都:只需设置其中一个,因为另一个也会随之改变。
我在修改它们时遇到问题的唯一情况是当我将端点设置为具有非常不同的值时和与服务器是值非常低的那个,比如它上面是 576,而客户端上是 1500。这些设置根本无法握手,我从未进一步调查过,因为坦率地说,我只在人工实验室测试中遇到过这样的设置。相反的情况,即客户端上的值非常低,而服务器上的值正常/高,则工作正常。
事实上,我发现设置link-mtu
或tun-mtu
(我更喜欢设置link-mtu
)实际上可以巧妙地解决大多数常见情况,即隧道可以正常工作,包括任何非 TCP 流量。
当然,设置fragment
和/或mssfix
值(如果这些也明确设定了)必须考虑明确的 link-mtu/tun-mtu 值,但我发现最好不要管它们(即甚至在配置中不提及),因为它们只是根据 link-mtu/tun-mtu 值自动调整。
尽管如此,仅有的 mssfix
(因此保留 link-mtu/tun-mtu 以及fragment
未指定)确实解决了与 TCP 相关的所有问题,但是所有其他协议必然会遇到问题,它们通常不会遇到问题的唯一原因仅仅是因为大多数非 TCP 流量要么是 UDP 上的 DNS,通常尺寸较小,要么是 ICMP 回显请求/回复,默认情况下它们也是小数据包。
但请注意,我指的是 Linux 上的 OpenVPN;在其他操作系统上,特别是 Windows 系列和 iOS 设备,情况可能会有所不同,即更改 link-mtu/tun-mtu 可能无关紧要甚至造成破坏,您可能不得不按照文档的建议更改 fragment/mssfix。特别敌对的情况可能是当服务器或客户端(或两者)不支持 IP 碎片/重组时,可能会通过人为降低 link-mtu/tun-mtu 来触发,因此在这种情况下您只能fragment
在 OpenVPN 中启用。
- 为了工作,哪些选项 link-mtu tun-mtu fragment mssfix 必须在服务器端镜像?
根据 OpenVPN 在设置 link-mtu/tun-mtu 但不将其镜像到终端时向其日志发出的警告消息,link-mtu 和 tun-mtu 值应该是确切地镜像,无论是显式还是隐式。然而,在实际使用案例中,我从未遇到过改变任何一个值的问题,即使我最终得到的隧道端点之间的值非常不同。
fragment
必须任何一个是双方都有或者双方缺席,因为它的存在使得 OpenVPN 可以使用其自己的内部算法(而不是 IP 分段)进行分段,因此必须由两个端点都同意。但是,当它存在时,它可能具有不对称值。
mssfix
不需要镜像,并且端点之间也可能具有不对称值。
- 哪些选项 link-mtu tun-mtu fragment mssfix 可用于 client-config-dir?
没有任何
可能的解决方案
- 如果所有四个选项都必须在服务器端镜像,并且不能在客户端配置目录中使用:是否有其他方法可以解决每个客户端的低路径 MTU 问题?
一种方法是使用 Linux 的每路由设置,它允许任意设置 MTU 值,最高可达实际接口的 MTU。具体方法如下:
首先,手动执行路径 MTU 发现(例如,使用ping -M do -s xxxx <client's-*public*-address>
从服务器到远程客户端的命令),以便找出服务器和每个特定远程客户端之间的正确 MTU 值。然后,
在客户端,假设它只是一个客户端,而不是其 LAN 中其他主机的路由器,只需将 OpenVPN 的link-mtu
值设置为实际 MTU 减 28。例如,在实际 MTU 为 576 的远程客户端上link-mtu 548
就可以了。
请注意,它将全部来自客户端的流量(不仅仅是 TCP),因为link-mtu
OpenVPN 使用该值作为发送到远程端点的其自身(UDP 端口 1194)有效负载的上限大小,因此远程客户端的实际 MTU(手动发现)减去 20(没有选项的外部 IP 头大小)减去 8(外部 UDP 头大小)。
OpenVPN 将自动限制在隧道内 TCP 流量的 MSS 值将link-mtu
减去 OpenVPN 自身的开销(根据所使用的密码而变化),减去 20(没有选项的内部 IP 头大小)减去 20(没有选项的内部 TCP 头大小)。
在服务器端,为每个“低 MTU”客户端安装一个路由,以便为每个客户端设置正确的 MTU。每个路由设置上要设置的每个正确值必须是您在该远程客户端上设置的link-mtu
值(如前所述)减去 OpenVPN 自己的开销。后者可以从 link-mtu 值减去 OpenVPN 服务器为该特定隧道报告的 tun-mtu 值得出。例如,假设 OpenVPN 服务器报告的 link-mtu 为 1541,tun-mtu 为 1500,那么在托管 OpenVPN 服务器的机器上,您将执行以下操作:
ip route add <client's-*vpn*-address> via <your-openvpn-server's-*vpn*-address> mtu 507
此类操作可以通过脚本方便地按需完成client-connect
,该脚本接收由 OpenVPN 动态设置的环境变量中的这些值(以及许多其他值)。用 shell 脚本的说法就是:
ip route replace "$ifconfig_pool_remote_ip" via "$ifconfig_local" mtu "$(( 576 - 28 - (link_mtu - tun_mtu) ))"
从那时起,流向该特定客户的出站流量VPN地址将尊重该MTU而不是tun接口的MTU。
此外,如果你的 OpenVPN 服务器还充当sysctl net.ipv4.ip_forward=1
其 LAN 和远程客户端之间的路由器( ),那么当这些机器向远程客户端发送 DF 流量时,应用于 OpenVPN 服务器计算机(其 Linux 内核)的每个路由设置也将触发正确的 ICMP 消息(类型 3 代码 4,需要分段)到 LAN 中的机器,在这种情况下,LAN 中的这些其他机器必须遵守这些 ICMP 消息,并且还会执行 IP 分段隧道内当 LAN 中的机器发送非 DF 流量时,在这种情况下您的远程客户端必须支持对从隧道出来的 IP 片段进行 IP 重组。
还要注意,在 Ubuntu 14.04 HWE 和更新版本(或最高 v5.7.x 的等效内核)上,你还必须进行设置,sysctl net.ipv4.ip_forward_use_pmtu=1
以便 Linux 在以下情况下执行上述 ICMP/碎片处理:转发其他机器的流量,而出sysctl
站流量则不需要额外的起源由服务器本身。
最后请注意,为了获得完全正确的配置,您还应该设置link-mtu 1472
(假设底层接口的 MTU 为 1500)服务器侧。实际上,我随时随地都会将其作为基本配置,除非特殊情况需要特定的解决方法。这是因为 OpenVPN 不将底层接口的 MTU 视为其 link-mtu/tun-mtu 的起始值,即使在支持它的操作系统上将其mtu-disc
选项设置为,它也不会执行 PMTU 发现。因此,明确设置为反映底层接口 MTU 的值(使用我上面描述的公式)对我来说是真正的最佳严重默认值,至少在 Linux 上是这样。yes
link-mtu
高血压
答案3
由于缺乏答案,我现在发布了一个不太优雅但简单的解决方案:在 TCP 上为“坏客户端”运行另一个 OpenVPN 实例
proto tcp
并降低客户端的 TCP MSS,例如
iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -o ${OUT_DEV} -j TCPMSS --set-mss ${PATH-MTU-MINUS-40}
该解决方案的优点在于每个客户端都可以设置其单独的 MSS。
这无疑是 TCP-over-TCP,但是在许多情况下应该表现得足够好。
请注意,我仍然对不需要的解决方案非常感兴趣proto tcp
,如果它们或多或少满足我所概述的要求,我会将它们标记为有效答案。