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

时间:2014-11-15 05:15:44

标签: networking tcp scapy icmp

我正在向服务器注入ICMP“需要碎片,DF位设置”,理想情况下服务器应该开始发送ICMP中“next-hop 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?

2 个答案:

答案 0 :(得分:3)

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

ICMP直接通过IP运行,如RFC 792

中所述
  

使用基本IP标头发送ICMP消息。

这可能有点令人困惑,因为ICMP is classified as a network layer protocol rather than a transport layer protocol但考虑到它只是IP的一个附加物来携带错误,路由和控制消息和数据,这是有道理的。因此,由于TCP层依赖于ICMP帮助管理和排除故障的IP层,因此它无法依靠TCP层进行自身传输。


现在,让我们处理你的第二个问题(如果ICMP没有通过TCP发送,TCP如何了解MTU?)。我依靠官方规范尽力回答这个问题,但也许最好的方法是分析一些开源网络堆栈的实现,以便了解真正发生的事情。 ...

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

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

  

每个ICMP错误消息都包含Internet标头和至少触发错误的数据报的前8个数据八位字节;可以发送超过8个八位字节;此标头和数据必须与收到的数据报保持不变。

     

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

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

RFC 1122还规定,必须有一种机制,通过该机制,传输层可以学习可以为给定的{源,目的地,TOS}三元组发送的最大传输层消息大小。因此,我假设一旦收到ICMP Fragmentation needed and DF set错误消息,MTU值就会以某种方式提供给可以使用它来更新其MSS值的TCP层。

此外,我认为实例化TCP连接并利用它的应用层也可以处理此类消息,并在更高级别对数据包进行分段。应用程序可能会打开一个需要ICMP消息的套接字,并在收到ICMP消息时采取相应的措但是,在应用层对数据包进行分段对TCP和TCP完全透明。 IP层。请注意,大多数应用程序将允许TCP& IP层可以自行处理这种情况。

但是,一旦主机收到ICMP Fragmentation needed and DF set错误消息,其下层所指示的行为就不具有决定性。

RFC 5927, section 2.2指的是RFC 1122,第4.2.3.9节,其中指出TCP应该在从IP层传递ICMP Fragmentation needed and DF set错误消息时中止连接,因为它表示硬错误情况。 RFC声明主机应该实现此行为,但它不是必须的(第4.2.5节)。此RFC还在3.2.2.1节中声明必须将接收到的目标无法到达消息报告给TCP层。当在该连接上收到ICMP Fragmentation needed and DF set错误消息时,实现这两者将导致TCP连接的破坏,这没有任何意义,并且显然不是所期望的行为。

另一方面,RFC 1191说明了所需行为this

  

RFC 1191没有概述发送所需的特定行为   主机,因为不同的应用程序可能有不同的要求,并且   不同的实现架构可能有利于不同的策略[这   为这种方法留下了空间-OA]。

     

唯一需要的行为是主机必须尝试避免发送更多内容   在不久的将来具有相同PMTU值的消息。主持人也可以   停止在IP头中设置Don?t片段位(并允许   路由器碎片化的方式)或减少数据报大小。该   更好的策略是降低邮件大小,因为碎片化   将导致更多流量并消耗更多的互联网资源。

总而言之,我认为规范对于收到ICMP Fragmentation needed and DF set错误消息时主机所需的行为并不确定。我的猜测是,两个层(IP和TCP)都会收到消息通知,以便更新他们的MTU&分别是MSS值,其中一个负责以较小的块重新传输有问题的数据包。


最后,关于您的实现,我认为为了完全符合RFC 1122,您应该更新ICMP消息以包含有问题的数据包的IP头,以及其接下来的8个字节(尽管您可能包含的不仅仅是前8个字节)。此外,您应该验证在ICMP消息所引用的数据包的相应ACK之前是否收到ICMP消息。事实上,为了安全起见,我会彻底废除那个ACK。

Here是如何构建ICMP消息的示例实现。如果发送ICMP消息作为对其中一个TCP数据包的响应失败,我建议你先尝试在收到与之相关的TCP数据包之前发送ICMP消息,以确保在ACK之前收到它。只有在失败的情况下,尝试完全取消ACK。

答案 1 :(得分:0)

我理解它的方式,主机收到"所需的ICMP碎片和DF设置" ,但消息可能来自路径中的中间设备(路由器),因此主机不能直接将icmp响应与当前会话匹配,icmp只包含目标ip和mtu限制。

主机然后在路由表中添加一个条目,用于记录路由的目标ip,以及10分钟到期的mtu。

这可以通过在执行跟踪触发icmp响应的跟踪路径或ping之后通过 ip route get x.x.x.x 询问特定路由来在linux上观察到。

$ 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