🌐 第三章 网络间的通用语言TCP/IP
📄 一句话概括

IP 是"尽力而为"的(3.2)——包丢了它不出声。可网络总得有人报信:送不到要说一声、路上超时要说一声。干这活的就是 ICMP(网际控制报文协议)——它不传你的业务数据,专门传"关于数据的信息",是网络自己的系统消息。你天天用的 ping 和 tracert,都是它在干活。

📝 本节是几笔旧账的总出处

前面几节陆续露过几面,当时都没展开:3.4 Demo18 那句「需要分片但 DF 置位」、3.7 的「TTL 传输中过期」、2.4 起就在用的 ping——它们全是 ICMP。本节把这条线一次收干净。

一、为什么需要 ICMP

问题:IP 只管送,不管汇报

IP 的工作方式是"尽力而为"(best effort): - 它不确认(不问你收到没有) - 它不重传(丢了就算了) - 它不出声(送不到也不告诉你)

这对路由器很省事,但对发送方是个麻烦:不知道出了什么事,就没法做反应——是对方不在?是路走不通?还是我的包太大?分不清就只能瞎猜。

答案:专门派一个"报信的"

ICMP(Internet Control Message Protocol,网际控制报文协议)就是干这个的。它的定位要记准:

ICMP 不传业务数据。它传的是"关于数据的信息"。

打个比方:快递员不负责给你送包裹,但包裹派不出去时,他会给你发一条系统短信——"收件地址不存在,已退回"。这条短信不是你的包裹,但它在告诉你包裹的下场。ICMP 就是那条短信。

它住在哪一层

ICMP 报文也装在 IP 包里——看 IP 首部的"协议"字段就能认出来(3.7 那张表里的 1):

协议号 里面装的是
6 TCP
17 UDP
1 ICMP

所以 ICMP 归网际层(跟 IP 是同一个办公室的同事),不是传输层——虽然它看起来"像"在传消息。

二、两大类报文

(对应上面第一张图)ICMP 报文按用途分两大类:

第一种:差错报告——"出事了,回禀发件人"

类型 名字 什么时候发
类型 3 终点不可达 找不到收件人(还会用"代码"进一步细分,见下)
类型 11 超时 包在网里转太久(TTL 减到 0,收 3.7)
类型 12 参数问题 包头里有字段写错了
类型 5 重定向 "有更近的路,下次改走那边"

类型 3 的"代码"细分很实用——同样是"不可达",原因完全不同:

代码 含义 你怎么会遇到
0 网络不可达 整条路没通
1 主机不可达 路通了,但目标机器不在
3 端口不可达 机器在,但那个端口上没程序在听(UDP 的唯一反馈,见第六部分)
4 需要分片但 DF 置位 3.4 Demo18 里那句话的正式出处
📝 报文的"类型 + 代码"是两个字段

ICMP 报文最前面是两个字节:类型(1 字节,决定"是什么大事")+ 代码(1 字节,决定"细分成哪种小情况")。上面「类型 3 / 代码 4」的写法就是这么来的——先认大事,再看细分,这个读法在抓包和排障时很常用。

第二种:询问——"主动问一句,看看对方怎么样"

类型 名字 干什么
类型 8 / 0 回送请求 / 回送应答 这就是 ping
类型 13 / 14 时间戳请求 / 应答 问对方"现在几点",用来测时间

两类报文的根本区别: - 差错报告:出了事才发,只有去程(不需要对方回答) - 询问:主动发出,成对往返(有问必有答)

另外有个容易漏的对比:IP 首部校验和只管头部(3.7),而 ICMP 的校验和覆盖整个 ICMP 报文——两种校验和的范围不一样,别混。

三、差错报文的一条关键规矩

(对应上面第二张图)这是 ICMP 设计里最聪明的一手:

差错报文总是回给"原包的源地址",而且报文里装着原包的"身份证复印件":原 IP 包的首部 + 前 8 字节数据。

为什么要带复印件?

因为发送方可能同时开了几十条连接。如果只告诉它"出错了",它根本不知道该重传哪个、该放弃哪个。而 TCP/UDP 头最前面的 8 个字节里,正好就是源端口和目的端口——有了这 8 字节,发送方一看就知道:

"原来是我访问那个网站的那条连接,端口 50000 那条。"

这一手让 ICMP 从"模糊的通知"变成了"精确的定位"。没有这 8 字节,差错报告基本没用。

顺带一个反直觉的点:差错报文不转发给目标。因为目标根本没收到那个包(或收到了也帮不上忙),真正需要知道的是发的人。

四、ICMP 只报告,不修复

这一点必须拎清:

ICMP 做了 ICMP 不做
告诉发送方"出了什么事" 不重传、不换路、不改变什么

真正"处理"的是两头: - 重传是 TCP 的活(3.5 的超时重传) - 调小包是发送方自己的活(收到"需要分片但 DF 置位"后,主动把包改小) - 报错给用户是应用程序的活

又是"分层各管一段":ICMP 当信使,TCP 当管家。

💡 这也是为什么 ICMP 很"轻"

它只负责传一句"出什么事了",不做任何决策。所以它实现简单、开销极小——一台路由器每转发几个包才可能发一条 ICMP,绝大多数包根本不触发。

五、四条"不发"的规矩

ICMP 差错报文不是想发就发,标准规定了四种情况不发:

情况 为什么
对 ICMP 差错报文本身,不再发差错报告 否则你报我、我报你,无限循环
对分片后的非第一片不发 第一片才带完整的端口信息,后面的片说不清
对广播 / 组播地址不发 一个包发给全段,回了就会炸一片
对 127.0.0.1、0.0.0.0 这类特殊地址不发 没意义

这几条都是为了别把网络搞得更乱——报信是为了帮忙,不是为了添乱。

六、ICMP 的实战:ping 与 UDP 的命运

ping = ICMP 回送请求 / 应答

ping 做的就一件事:发一个类型 8,对方回一个类型 0。

因此它同时验证了三件事:对方在不在、路通不通、来回要多久。而且应答里会把请求带的数据原样带回,所以还能顺便确认数据没被改坏。

tracert = ICMP 超时

tracert 靠的是不断制造"TTL 用完"的现场,让沿途每台路由器回一条类型 11(3.7 Demo21 详细讲过)。它本质是在收集 ICMP 差错报告。

UDP 唯一的"反馈"来自 ICMP

这一条很关键。UDP 是没有确认机制的(3.3)——包发出去,有没有人接它完全不知道。

但如果目标主机发现"这个 UDP 包的端口上没程序在听",它会回一条 ICMP 类型 3 / 代码 3(端口不可达)。

⚠️ 所以 UDP 程序排障,要看 ICMP

UDP 自己不会告诉你"没人收",ICMP 才会。这也是为什么一些基于 UDP 的服务(DNS、游戏、音视频)出问题时,抓包里能看到"端口不可达"——这是它唯一能拿到的线索。

但要注意:很多防火墙会连 ICMP 一起拦,那就连这条线索都没了,表现成"一直没回音"。

七、ICMPv6:不只是"加个 v6"

IPv6 里 ICMP 升成了 ICMPv6,而且职责重得多——它不只报错,还承担了这些正经活:

用途 原来谁干 现在谁干
找 MAC 地址 ARP(3.16) ICMPv6 的邻居发现 NDP(抓包看到的是 NS / NA)
找路由器 / 自动配地址 DHCP、路由器通告 ICMPv6
报错、ping ICMP ICMPv6(ping6)
🚨 所以 IPv6 下"禁 ICMP"是个大坑

在 IPv4 世界里,"禁 ping"(拦 ICMP)是常见的安全策略,问题不大。 但 IPv6 下绝不能一刀切拦掉 ICMPv6——地址解析、地址自动配置全靠它,拦掉的后果是连邻居都认不出来,网络直接瘫痪。

正确做法是按类型放行:邻居发现、差错报告必须放,回送请求可酌情。

八、动手 Demo29:亲眼看看 ICMP 在说什么

  1. 看最基本的成对往返 ping www.baidu.com 每一行回复 = 一次"类型 8 出去、类型 0 回来"。顺便看看"时间"(往返毫秒)和 TTL=(收 3.7 的跳数估算)。

  2. 对照两种"不通"的说法(本节最实用的一条) ping -n 1 -w 1000 8.8.8.8 ping -n 1 -w 1000 1.2.3.4 回来多半是两种完全不同的措辞:

  3. 「请求超时」——没人回音,很可能被中途丢掉了或对方不理你
  4. 「无法访问目标主机」——收到了 ICMP 差错报文,是网络明确告诉你"送不到"

这两种不是一回事:一个是"没消息",一个是"有坏消息"。排障时方向完全不同。

  1. 亲手触发一条 ICMP 超时差错报文 ping -i 1 www.baidu.com 报「TTL 传输中过期」——这就是类型 11 的原文(收 3.7 Demo21)。

  2. 看 tracert 里的 * * * tracert -4 www.baidu.com 某几跳显示 * * * = 那台设备不回 ICMP(很多运营商设备为了省事或安全,直接忽略)。不代表它坏了、也不代表路断了——后面的跳照样能通。

  3. 30 秒实验:三行排障,看清每一层报什么错

    依次 ping 三个目标,注意每次报错文字的差别: 1. ping 192.168.1.1 —— 自家路由器,多半通(自家人一定理你) 2. ping 8.8.8.8 —— 外网 IP,多半也通(说明出门没问题) 3. ping 一个不存在的域名(比如 ping nosite-abc123.com)—— 报「找不到主机」

    第 3 条的错不是 ICMP 发的,是 DNS 层面的错(3.12 的 DNS 那件套)。

    分清这一点很值钱:「找不到主机」= DNS 的事,「请求超时」/「无法访问」= 网络的事,处理方向完全不同。

❓ 自测一下
  1. IP 既然是"尽力而为"的,为什么还需要 ICMP?
  2. ICMP 报文装在哪里?从 IP 首部的哪个字段能认出来?它算哪一层?
  3. ICMP 两大类报文分别是什么?各自的特点是什么?
  4. ICMP 差错报文发给谁?里面装着什么?为什么一定要装着那个东西?
  5. ICMP 会替你重传丢掉的包吗?
  6. 「类型 3 / 代码 4」是什么意思?它对应 3.4 里的哪句话?
  7. UDP 收不到回复时,能从哪里得到"没人在听"的线索?
  8. IPv6 下为什么不能一刀切禁掉 ICMP?
  9. tracert 某跳显示 * * *,说明那台设备坏了吗?
  10. 「找不到主机」和「请求超时」这两种报错,分别指向哪一层的问题?

答案: ① 因为 IP 不确认、不重传、不出声,发送方不知道出了什么事,就无法做任何反应。ICMP 专门承担"报信"这个职责。 ② 装在 IP 包里,从 IP 首部的协议字段 = 1 能认出(6 是 TCP、17 是 UDP)。所以它归网际层。 ③ 差错报告(类型 3 不可达 / 11 超时 / 12 参数问题 / 5 重定向),出了事才发、只有去程;询问(类型 8/0 回送 = ping、13/14 时间戳),主动发出、成对往返。 ④ 回给原包的源地址(发件人)。里面装着原 IP 包的首部 + 前 8 字节数据。因为这 8 字节里含源端口和目的端口,发送方才能认出是哪条连接出了事——不带回去,它只知道"出错",不知道"哪个包错"。 ⑤ 不会。ICMP 只报告不修复,重传是 TCP 的活(3.5),调整包大小是发送方自己的活。 ⑥ 类型 3 = 终点不可达,代码 4 = 需要分片但 DF 置位——正是 3.4 Demo18 里 ping -f -l 1473 收到的那句话。 ⑦ 来自 ICMP 类型 3 / 代码 3(端口不可达)。这是 UDP 唯一能拿到的反馈,但防火墙若连 ICMP 一起拦,这条线索也会断。 ⑧ 因为 ICMPv6 承担了地址解析(邻居发现 NDP,取代 ARP)、路由器发现、地址自动配置等核心功能。一刀切拦掉会让网络直接瘫痪,正确做法是按类型放行。 ⑨ 不是。* * * 只表示那台设备不回 ICMP,它照样在正常转发——后面的跳通就说明路是好的。 ⑩ 「找不到主机」是 DNS 层面的错(名字没查成地址);「请求超时」/「无法访问目标主机」是网络层面的(前者=没回音,后者=收到 ICMP 差错报文)。

常见误区:

  1. ❌ "ping 不通就是网络断了" → 很多服务器和安全设备主动禁 ICMP(收 2.4)。ping 不通只能说明"它不理你",不等于"它不在"
  2. ❌ "ICMP 和 ping 是一回事" → ping 只是 ICMP 的一种用法(类型 8/0);ICMP 还有差错报告等一大家子
  3. ❌ "ICMP 能帮忙修复" → 只报告不修复。重传归 TCP、改包大小归发送方
  4. ❌ "ICMP 属于传输层" → 它装在 IP 包里(协议号 1),是网际层的成员
  5. ❌ "ICMP 只是拿来测试的" → 它是网络的基础设施:tracert、MTU 发现、UDP 端口不可达通知,全靠它
  6. ❌ "IPv6 下也应该禁 ICMP" → 绝对不能,ICMPv6 承担地址解析和自动配置,拦了网络就跑不起来
  7. ❌ "tracert 里的 * * * 说明那台设备坏了" → 只是不回 ICMP,转发照做
  8. ❌ "差错报文会发给目标主机" → 回给源地址,因为目标根本没收到(收到了也帮不上忙)

九、下一步学什么

相关笔记