MTU值所导致的VPN链路故障

MTU即Maximum Transmission Unit(最大传输单元), 它设定了TCP/IP协议所能通过的最大数据包大小, 一般来说, 如果传输的数据包超过该值, 就会被进行分片传输, 最近遇到这么一个故障.

故障环境

机器A通过走VPN访问机器B

A --> VPN --> B

发现A ping得通 B, 从A能ssh登录B, ssh登录后能正常操作常用命令, 但无法从A上scp文件到B, 表现为卡住, 同时使用rsync传文件也会出现卡住的现象

故障定位及分析

从现象上归结为小数据量时可以通, 大数据时过不去, 通过使用ping指定数据包大小来测试, 最后可以定位为数据包大小超过1400后, 就无法到达机器B

#-s 设置包大小
#-M do 指定DF标记, 让数据包不分片传输
ping -s 1400 -M do xxx.xxx.xxx.xxx

一直以为所有超过MTU大小的包都应该是被分片传输, 但查了文档才知道, openvpn所使用的协议是要求数据包不能分片传输的(即使用了DF标记), 这就导致了超过vpn网关MTU大小的包被直接丢掉了, 虽然看不到VPN网关的配置, 不过我们不难猜出来.

通过ifconfig, 找到vpn虚拟接口的MTU值, 确定为1500

ifconfig utun0

utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
    inet 10.0.0.11 --> 10.0.0.11 netmask 0xfffffc00

那么故障时发生了什么?

A由于接口MTU为1500, 所以在大数据传输时, 发送的数据包为1500(实际不完全是这个数值,这里不深入讨论), 当这个数据包到达VPN网关时, 由于网关的MTU小于1500, 那么只有对这个数据包进行分片才能传输, 但VPN协议设定了DF标记, 表明不能分片, 所以这个数据包就被丢掉了

故障解决

为了验证自己的猜想, 我通过ifconfig来修改对应接口的MTU值, 改小MTU, 看看能否解决问题

sh-3.2# ifconfig utun0 mtu 1300
sh-3.2# ifconfig utun0
utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1300
    inet 10.0.0.11 --> 10.0.0.11 netmask 0xfffffc00

修改后scp和rsync恢复正常, 可以确定就是A机的MTU大于VPN网关导致的(也有可能是大于中间某个路由节点的MTU). 如何确定要设置为1300而不是设置其它值, 这个可以之前使用的ping命令来确认哪个值能正常传输数据包, 设置为能正常传输的最大值, MTU过小会导致网络性能下降, 因为需要在分片和重组上消耗一定时间.

每次都ifconfig来修改MTU? 这显然是不需要的, 对于openvpn的配置, 它本身就提供了设置方法, 只需要修改openvpn配置, 加入tun-mtu配置, 这样拨vpn后, 接口的MTU就是指定的值

<connection>
remote xxx.xxx.xxx.xxx
proto udp
port xxx
tun-mtu 1300
</connection>
2015-03-20 17:252433