对 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 头后)。
- 文件封面:记录了大量元数据,包括数据包的来源、目的地、协议类型、接收接口等信息。
当一个广播帧到达网卡时,内核里发生了以下动作:
-
网卡驱动层接收: 驱动程序读取数据帧,此时它能看到完整的 MAC 帧头,发现目的 MAC 是
FF:FF:FF:FF:FF:FF(广播)。 -
打标签(关键步骤): 驱动程序在把数据包向上传递之前,做了两件事:
-
移动指针:把指向数据开头的指针向后移,跳过 MAC 头(逻辑上去除 MAC 头)。
-
填写元数据:在
sk_buff结构体的pkt_type字段里写上标记PACKET_BROADCAST。
-
-
网络层处理: IP 协议栈收到
sk_buff。虽然指针指向的是 IP 头,但它可以直接读取结构体上的pkt_type字段。
总结
学的越多,越感慨计算机世界的精妙设计。计算机网络的多层架构分工明确,又通过各种机制实现了层与层之间的高效信息传递与隔离。