为什么“禁止分段”标志没有被禁用?
我在尝试设置不同的 IP_MTU_DISCOVER
值,使用 setsockopt()
来看看对UDP数据包分片的影响。根据 ip(7)
的手册,我发现需要把这个值设置为 IP_PMTUDISC_WANT
或 IP_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中工作的。
- 一个IP端点开始时会有一个指定的MTU,通常是它直接连接的网络的MTU。
- 在发送数据包时,端点总是会设置DF位。
- 如果中间的路由器发现无法发送这个数据包而不进行分片,它会返回一个
ICMP Destination Unreachable
的数据包,错误代码为4,并告诉你下一个跳转的MTU。 - IP端点收到这个不可达的消息后,会调整它的PMTU。
- 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在网络术语中相当大,那么为什么要支持分片呢。话虽如此,在我的系统上,这实际上运行得很好。