type
status
date
slug
summary
tags
category
icon
password
这一次,在我们的用户态 TCP/IP 协议栈中,我们将实现一个最小可用的 IP 层,并通过 ICMP Echo 请求(也就是我们熟知的
ping
)进行测试。本文目标
- 实现 IPv4 头部解析和构建
- 实现 ICMPv4 Echo 请求/响应 处理逻辑
- 验证协议数据完整性(校验和检查)
- 使用
ping
命令测试整个实现链路是否成功
为什么选用 IPv4?
我们选择实现 IPv4 而不是 IPv6,是因为:
- IPv4 目前仍是互联网上默认使用的网络协议
- 它的协议格式相对更简单,更适合入门实现
- 当然,随着 IPv6 的逐渐普及,我们的协议栈未来也可以扩展支持 IPv6
补充:IP 分片(Fragmentation)等高级特性在本实现中被省略,作为读者练习。
IPv4 - 互联网协议版本 4(Internet Protocol version 4)
在实现以太网帧之后,我们继续向上进入 第三层(L3):网络层的核心协议 —— IPv4。
IP 的作用
IP 协议的主要职责是:将数据从一个主机送达另一个主机。它是构建在以太网之上的“通用传输通道”,为更上层的协议如 TCP 和 UDP 提供基础服务。
IPv4 解决的是“如何把数据包送到目的地”,而不是“数据是否送达”或“是否按顺序送达”。
IP 的几个关键特性:
- 无连接(Connectionless)
- IP 不建立会话,每个数据包(datagram)都是独立处理的。
- 数据包可能乱序到达,甚至完全丢失,这完全是允许的。
- 不保证交付(Unreliable)
- IP 不负责保证数据一定送达,也不会重试。
- 可靠性由上层协议(如 TCP)负责,IP 专注于快速发送。
- 面向主机(Host-to-Host)
- IPv4 使用 32 位地址标识源地址和目的地址。
- 它不管“端口”或“会话”,只管将数据交付给目标主机。
IP 的设计理念
“尽量简单,把复杂性留给上层协议。”
这是 IP 协议诞生之初的设计哲学。UDP、ICMP 等协议接受 IP 的不可靠性,而 TCP 则基于 IP 增加了重传、确认、排序等机制。
实现中的思考
在我们用户态协议栈中,实现 IPv4 的关键在于:
- 正确解析和构建 IPv4 头部
- 校验 IP 校验和
- 将 IPv4 payload 正确传递给上层(如 ICMP)
IPv4 报文头格式(Header Format)
IPv4 报文头(Header)通常为 20 字节(octets),如果包含额外选项则可能更长。但在我们的简化实现中,我们将不处理 header options,只支持最小固定长度的头部(20 字节)。
C 语言结构体定义
字段说明
字段 | 大小 | 说明 |
version | 4bit | IP 协议版本号,IPv4 为 4 |
ihl | 4bit | Header 长度,以 32-bit 为单位(最小为 5 = 20 字节) |
tos | 8bit | 服务类型(已被细分为 DSCP + ECN,但我们用原始定义) |
len | 16bit | 整个数据包长度(包括头部 + 数据)最大为 65535 |
id | 16bit | 数据包 ID,用于分片重组 |
flags | 3bit | 控制位:是否可分片、是否是最后一个分片等 |
frag_offset | 13bit | 当前分片在原始包中的偏移量(单位为8字节) |
ttl | 8bit | 生存时间,每经过一个路由节点就减1,为0即丢弃 |
proto | 8bit | 指示上层协议类型,例如 ICMP=1、TCP=6、UDP=17 |
csum | 16bit | IP 头校验和,只校验头部 |
saddr | 32bit | 源 IPv4 地址 |
daddr | 32bit | 目标 IPv4 地址 |
设计注意事项
- 分片处理(Fragmentation)
- 我们当前不实现分片,因此:
flags
可设置为0b010
(即:不分片)frag_offset = 0
- 校验和
- IP 头校验和只计算头部,不包括 payload。我们稍后将实现其计算函数。
示例:固定 IPv4 报头初始化
Internet Checksum 校验和计算(适用于 IPv4 头、ICMP 等)
互联网校验和是一种简单但经典的完整性校验算法,用于检测 IP 报文头或 ICMP 数据是否被破坏。
计算规则(源自 RFC 1071):
Checksum = 16 位字段的反码(one’s complement)之和的反码
简而言之:
- 将 IP 头部分成若干个 16 位字段(big-endian);
- 把它们累加起来(使用 32 位中间变量);
- 如果有溢出(高 16 位),把溢出加回来;
- 最后对结果取反(~sum);
💡 注意:计算时将校验和字段设为 0
C 语言实现(兼容 IPv4 / ICMP)
示例校验和计算
示例 IP 报文头(去掉校验和字段):
- 将其视为十个 16-bit 字段累加:
- 进位叠加:
- 取反作为 checksum:
最终:
校验和验证
你可以在接收到一个 IP 报文头之后这样验证:
ICMPv4(Internet Control Message Protocol version 4)
由于 IP 协议本身 不保证可靠性(例如无法确认目的地是否可达),因此引入了 ICMP(互联网控制消息协议) 用于在网络中提供诊断与错误报告功能。
ICMP 的作用举例:
- 如果网关不可达,网络栈将发送一个 ICMP “目标不可达(Destination Unreachable)” 的消息回源主机。
- 如果一个主机收到 TTL 为 0 的数据包,它也会发送一个 ICMP “Time Exceeded” 错误。
- 我们熟悉的
ping
工具,就是通过 ICMP Echo Request(类型 8)与 Echo Reply(类型 0) 来测量主机是否可达。
ICMPv4 报文头结构
ICMP 报文位于 IP 数据包的 payload 中,因此在解析时,需先解析 IP,再将
ip->payload
类型转换为 ICMP:字段详解
字段名 | 长度 | 描述 |
type | 1B | 表示 ICMP 消息类型(0、3、8 是最常见的) |
code | 1B | 提供更具体的错误说明(例如 type=3 时说明不可达的原因) |
csum | 2B | 校验和(算法同 IPv4,但计算范围包括整个 ICMP 报文) |
data[] | 可变 | 包含请求 ID、序列号和附加数据(如 ping 请求中) |
常见类型与代码(Type & Code)
类型(Type) | 含义 | 常见 Code 值及含义 |
0 | Echo Reply(响应 ping) | 0(标准响应) |
3 | Destination Unreachable | 0 = Net unreachable 1 = Host unreachable |
8 | Echo Request(ping 请求) | 0(标准请求) |
校验和说明
- 与 IPv4 相同的 16-bit 反码累加算法(RFC 1071)
- 区别:ICMP 校验和包括 ICMP 报文的整个 payload
即 header + data 一起参与校验和计算
示例:Echo Request 数据结构
ICMP Echo 报文的数据通常包含:
完整的结构是:
测试 ICMP Echo Reply 的实现(Testing the Implementation)
一切就绪后,我们可以在终端中使用
ping
命令,验证我们的用户态网络协议栈是否成功回应了 ICMP 请求。示例测试输出:
结果解读:
icmp_seq=1,2,3
:说明我们正确地回应了 3 个 Echo Request 请求。
ttl=64
:我们构造的 IP 头部中的 TTL 字段被成功识别。
time
:表示往返时间(RTT),也是我们回复越快越低的指标。
0% packet loss
:说明我们的用户态协议栈表现完美,无任何丢包。
成功的标志:
检查点 | 是否成功? |
正确解析以太网帧? | ✅ |
正确解析 IP 头部? | ✅ |
校验 IP header 校验和? | ✅ |
识别 ICMP Echo Request? | ✅ |
回复 ICMP Echo Reply? | ✅ |
填写正确的 IP 与 MAC? | ✅ |
被 ping 命令识别? | ✅ |
总结(Conclusion)
我们成功构建了一个最小可用的用户态网络协议栈,支持以下协议:
- ✅ 以太网帧(Ethernet)
- ✅ ARP 地址解析协议
- ✅ IPv4 协议解析与校验
- ✅ ICMPv4 回显请求与应答(Ping)
尽管这些实现相对容易,但原始协议规范在过去几十年中早已被大量扩展。本篇中我们略过了:
- ✳️ IP 选项(Options)
- ✳️ 分片(Fragmentation)
- ✳️ DSCP / ECN 字段
这些功能虽未实现,但在生产环境中是不可或缺的。
IPv6:未来的网络协议
虽然目前 IPv6 还未在全球范围内完全普及,但它已经是互联网的未来方向。由于 IPv4 地址即将耗尽,实现 IPv6 支持将是我们协议栈的一个关键进阶目标。
源码地址
完整的代码实现可以在 GitHub 上找到:[链接]
下一步:进入传输层(L4)
在下一篇博客中,我们将开始实现网络中最复杂、也最知名的协议之一:
TCP - 传输控制协议
- 支持 连接状态(connection-oriented)
- 提供 可靠性保障
- 包括 重传、确认、流量控制、拥塞控制
- 需要实现 状态机(State Machine)
作为一个历史悠久且广泛使用的协议,TCP 拥有复杂的行为和众多边角特性。实现它,将是本项目中最具挑战也最有趣的一部分。