Return

对 ICMP 差错报文的疑问解决

Table of Contents

在复习计网 ICMP 协议的知识点时,看到课本上的一句话:

“为了防止广播风暴,针对目的地址是广播或多播的 IP 数据包,接收方产生差错时,不会发送 ICMP 差错报文。”

对此我产生了一个疑问。

问题

ICMP 差错报文是由 网络层 发出的。但是按照 OSI 亦或是 TCP/IP 的分层封装原则,数据包从物理层向上传递时,数据链路层 的帧头(包含 MAC 地址)会被剥离,只把剩下的净荷(IP 包)交给网络层。

那么问题来了: 既然 MAC 头都已经去掉了,接收站点的网络层是怎么知道这段数据在链路层是广播发过来的?如果不知道,它又是依据什么来决定 不发 ICMP 的?

分析与解答

经过查阅资料,发现了之前的认知盲点。

实际上,分层间的数据传递是很复杂的:操作系统内核在各层之间传递数据时,传的不只是数据本身,还有一个包含各种 元数据(Metadata) 的结构体。

即使 MAC 头被物理剥离了,其源地址这个关键属性,也会被打成一个标签 ,贴在数据包上,一直传给网络层。

Linux 内核的 sk_buff

以 Linux 内核为例,网络数据包在内核中通常封装在一个叫做 sk_buff(socket buffer)的结构体对象中。

把这个结构体想象成一个 文件夹 的话:

  • 文件内容:真正的 IP 数据包(去掉 MAC 头后)。
  • 文件封面:记录了大量元数据,包括数据包的来源、目的地、协议类型、接收接口等信息。

当一个广播帧到达网卡时,内核里发生了以下动作:

  1. 网卡驱动层接收: 驱动程序读取数据帧,此时它能看到完整的 MAC 帧头,发现目的 MAC 是 FF:FF:FF:FF:FF:FF(广播)。

  2. 打标签(关键步骤): 驱动程序在把数据包向上传递之前,做了两件事:

    • 移动指针:把指向数据开头的指针向后移,跳过 MAC 头(逻辑上去除 MAC 头)。

    • 填写元数据:在 sk_buff 结构体的 pkt_type 字段里写上标记 PACKET_BROADCAST

  3. 网络层处理: IP 协议栈收到 sk_buff。虽然指针指向的是 IP 头,但它可以直接读取结构体上的 pkt_type 字段。

总结

学的越多,越感慨计算机世界的精妙设计。计算机网络的多层架构分工明确,又通过各种机制实现了层与层之间的高效信息传递与隔离。