为什么“禁止分段”标志没有被禁用?

0 投票
1 回答
3043 浏览
提问于 2025-04-28 06:44

我在尝试设置不同的 IP_MTU_DISCOVER 值,使用 setsockopt() 来看看对UDP数据包分片的影响。根据 ip(7) 的手册,我发现需要把这个值设置为 IP_PMTUDISC_WANTIP_PMTUDISC_DONT,这样才能关闭“不分片”这个标志。但是,当我尝试发送数据包时,出现了 socket.error: [Errno 90] Message too long 的错误。

import IN, socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

hostname = 'localhost'
s.connect((hostname, 1060))

s.setsockopt(socket.IPPROTO_IP, IN.IP_MTU_DISCOVER, IN.IP_PMTUDISC_DONT)
mtu = s.getsockopt(socket.IPPROTO_IP, IN.IP_MTU)

print 'MTU:', mtu
s.send('.' * (mtu + 1))
print 'Big packet sent'

谢谢

暂无标签

1 个回答

3

首先,我们来简单回顾一下PMTU是如何在UDP中工作的。

  1. 一个IP端点开始时会有一个指定的MTU,通常是它直接连接的网络的MTU。
  2. 在发送数据包时,端点总是会设置DF位。
  3. 如果中间的路由器发现无法发送这个数据包而不进行分片,它会返回一个ICMP Destination Unreachable的数据包,错误代码为4,并告诉你下一个跳转的MTU。
  4. IP端点收到这个不可达的消息后,会调整它的PMTU。
  5. IP端点决定如何处理原始数据包(重发、通知应用层等)。

这里非常重要的一点是,PMTU并不是自动发生的。没有内置的探测数据包会在你(应用程序)开始发送实际数据之前找到MTU。

在Linux中,控制这个(对于数据报)使用以下标志:

  • IP_PMTUDISC_WANT 按照上面描述的方式执行PMTU。如果应用程序发送一个超出已知MTU的数据包,套接字层会对其进行分片。所有出去的数据包(包括分片)都会设置DF位。
  • IP_PMTUDISC_DONT 不进行PMTU探测。
  • IP_PMTUDISC_DO 进行PMTU探测。如果应用程序发送一个超出已知MTU的数据包,会向应用程序发送错误信息。
  • IP_PMTUDISC_PROBE 设置DF位以进行PMTU探测,但忽略当前已知的MTU。所以如果应用程序发送一个过大的数据包,它仍然会尝试发送。这在偶尔发送探测包以查看PMTU是否增加时很有用。

需要注意的是,IP_PMTUDISC_DONT并没有说明如果数据包实际上大于当前的MTU(即直接连接的那个MTU)该怎么办。因此,很可能这个选择留给实际需要发送数据包的接口。大多数接口应该会对数据包进行分片,因为DF位没有被设置。然而,如果你使用的是链路本地接口,这通常是一个软件接口,功能稍微弱一些。可能在你的系统中,这个接口不支持分片,因此会发送错误信息。这是有道理的,看看我机器上的输出:

In [6]: s.getsockopt(socket.IPPROTO_IP, 14) #14 = IP_MTU, just wasn't in my python lib.
Out[6]: 16436

这显示链路本地接口的MTU在网络术语中相当大,那么为什么要支持分片呢。话虽如此,在我的系统上,这实际上运行得很好。

撰写回答