Networking 关于ICMP“;需要分段,DF位设置“;或ICMP数据包太大消息

Networking 关于ICMP“;需要分段,DF位设置“;或ICMP数据包太大消息,networking,tcp,scapy,icmp,Networking,Tcp,Scapy,Icmp,我正在将ICMP“需要碎片,DF位集”注入服务器,理想情况下,服务器应该开始发送ICMP中“下一跳MTU”字段中所述大小的数据包。但这是行不通的 以下是服务器代码: #!/usr/bin/env python import socket # Import socket module import time import os range= [1,2,3,4,5,6,7,8,9] s = socket.socket() # Create a sock

我正在将ICMP“需要碎片,DF位集”注入服务器,理想情况下,服务器应该开始发送ICMP中“下一跳MTU”字段中所述大小的数据包。但这是行不通的

以下是服务器代码:

#!/usr/bin/env python 
import socket               # Import socket module
import time
import os

range= [1,2,3,4,5,6,7,8,9]
s = socket.socket()         # Create a socket object
host = '192.168.0.17'                   # Get local machine name
port = 12349               # Reserve a port for your service.
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))        # Bind to the port
rand_string = os.urandom(1600)

s.listen(5)                 # Now wait for client connection.
while True:
   c, addr = s.accept()     # Establish connection with client.
   print 'Got connection from', addr
   for i in range:
    c.sendall(rand_string)
        time.sleep(5)
   c.close()
以下是客户端代码:

#!/usr/bin/python           # This is client.py file

import socket               # Import socket module

s = socket.socket()         # Create a socket object
host = '192.168.0.17' # Get local machine name
port = 12348              # Reserve a port for your service.
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.connect((host, port))
while 1:
    print s.recv(1024)
s.close()
Scapy注入ICMP:

###[ IP ]###
  version= 4
  ihl= None
  tos= 0x0
  len= None
  id= 1
  flags= DF
  frag= 0
  ttl= 64
  proto= ip
  chksum= None
  src= 192.168.0.45
  dst= 192.168.0.17
  \options\
###[ ICMP ]###
  type= dest-unreach
  code= fragmentation-needed
  chksum= None
  unused= 1300

Send(ip/icmp)

未使用字段显示为wireshark中的下一跳MTU。服务器是否足够智能,可以在与客户端通信时检查DF位是否未设置,并且仍然接收ICMP“需要碎片,DF位设置”消息?如果不是,那么为什么服务器不将其数据包大小从1500减少到1300?

首先,让我们回答您的第一个问题(ICMP是通过TCP发送的吗?)

ICMP直接在IP上运行,如中所述:

ICMP消息使用基本IP报头发送

这可能有点让人困惑,但考虑到它仅仅是IP的一个补充,用于传输错误、路由和控制消息和数据,这是有道理的。因此,它不能依赖TCP层来传输自己,因为TCP层依赖于ICMP帮助管理和排除故障的IP层


现在,让我们来讨论第二个问题(如果ICMP不是通过TCP发送的,TCP是如何知道MTU的?)。我试图根据我的理解来回答这个问题,并依赖于官方规范,但也许最好的方法是分析一些开源网络堆栈实现,以了解到底发生了什么

TCP层可能会知道路径的MTU值,即使ICMP消息不在TCP上分层。由操作系统网络堆栈的实现来通知MTU的TCP层,以便它可以使用此值来更新其MSS值

要求ICMP消息包括IP标头以及触发该ICMP消息的问题数据报的前8个字节:

每个ICMP错误消息包括互联网报头和触发错误的数据报的至少前8个数据八位字节;可以发送8个以上的八位字节;此标头和数据必须与接收到的数据报保持一致

在要求Internet层向传输层传递ICMP错误消息的情况下,必须从原始报头中提取IP协议号,并用于选择适当的传输协议实体来处理错误

这说明了操作系统如何确定应该更新其MSS的TCP连接,因为这8个字节包括源端口和目标端口

RFC 1122还指出,必须有一种机制,通过该机制,传输层可以了解给定{source,destination,TOS}三元组可发送的最大传输层消息大小。因此,我假设一旦收到所需的
ICMP碎片和DF set
错误消息,MTU值就会以某种方式提供给TCP层,TCP层可以使用MTU值更新其MSS值

此外,我认为实例化TCP连接并使用它的应用程序层也可以处理此类消息,并在更高级别上对数据包进行分段。应用程序可以打开一个期望ICMP消息的套接字,并在收到ICMP消息时采取相应的行动。然而,在应用层对数据包进行分段对于TCP和IP层来说是完全透明的。请注意,大多数应用程序都允许TCP和IP层自行处理这种情况

但是,一旦主机接收到所需的
ICMP碎片和DF set
错误消息,其由较低层指示的行为就不是决定性的

参考RFC 1122第4.2.3.9节,该节规定,当需要
ICMP碎片且DF set
错误消息从IP层向上传递时,TCP应中止连接,因为它表示硬错误条件。RFC声明主机应该实现此行为,但这不是必须的(第4.2.5节)。该RFC还在第3.2.2.1节中规定,必须向TCP层报告接收到的目标不可到达消息。当连接上接收到
ICMP碎片需要和DF set
错误消息时,实现这两种方法将导致TCP连接的破坏,这毫无意义,而且显然不是期望的行为

另一方面,关于所需行为的国家:

RFC 1191未概述发送过程中预期的特定行为 主机,因为不同的应用程序可能有不同的要求,以及 不同的实现架构可能有利于不同的策略[这一点] 为该方法留出空间[OA]

唯一需要的行为是主机必须尝试避免发送更多 在不久的将来具有相同PMTU值的消息。主机可以是 停止设置IP头中的“不分段”位(并允许 通过路由器碎片化)或减少数据报大小。这个 更好的策略是降低消息大小,因为存在碎片 将导致更多的流量和消耗更多的互联网资源

作为结论,我认为在收到
ICMP碎片需要和DF set
错误消息时,该规范对于主机的所需行为不是确定的。我猜这两层(IP和TCP)都会收到消息通知,以便分别更新它们的MTU和MSS值,其中一层负责在较小的数据块中重新传输有问题的数据包


最后,关于您的实现,我认为为了完全符合RFC 1122,您应该更新ICMP消息,以包括有问题数据包的IP头,以及它的下一个8字节(尽管您可能不只是包括前8个字节)。此外,您应该验证
$ ip route get 10.x.y.z
10.z.y.z via 10.a.b.1 dev eth0  src 10.a.b.100 
cache  expires 598sec mtu 1300