前言
本文梳理常见报文格式、联网流程及 Linux 网络协议栈的收发路径。
网络分层与报文格式
OSI 七层模型用于理解分层;TCP/IP 模型通常将 L5~L7 合为应用层。学习 Linux 网络时,重点掌握 L2、L3、L4、L7,以及以太网、IP、TCP/UDP 的报文格式。
| 层级 | 名称 | 典型协议或功能 |
|---|---|---|
| L7 | 应用层 | HTTP、DNS、FTP、SMTP、SSH |
| L6 | 表示层 | 编码、加密、压缩 |
| L5 | 会话层 | 会话建立、管理与终止 |
| L4 | 传输层 | TCP、UDP |
| L3 | 网络层 | IPv4、IPv6、ICMP、IPsec |
| L2 | 数据链路层 | Ethernet、PPP、PPPoE、802.1Q |
| L1 | 物理层 | 电气、光学信号及其传输介质 |
L2 数据链路层
Ethernet II 以太网帧
普通、未带 VLAN 标签的帧格式如下:
| 目的 MAC | 源 MAC | EtherType | 数据及填充 | FCS(帧校验序列) |
|---|---|---|---|---|
| 6 字节 | 6 字节 | 2 字节 | 46~1500 字节 | 4 字节 |
- 帧长与 MTU:帧长为 64~1518 字节(含 FCS);IP MTU 通常为 1500 字节,不含以太网首部、填充和 FCS。数据不足 46 字节时需填充,填充不属于 IP 报文。RFC 894
- 前导码与帧间隙:帧前为 7 字节前导码和 1 字节帧开始定界符(SFD)。帧间隙是空闲时间,通常按 96 bit times(12 字节的发送时间)计算,不属于帧内容。IEEE 802.3 工作组说明
- EtherType 与 VLAN:
0x0800为 IPv4,0x0806为 ARP,0x86DD为 IPv6。802.1Q 在源 MAC 后插入 4 字节标签(TPID + TCI),常用 TPID 为0x8100,标签后仍有实际 EtherType。IANA 注册表
ARP 用于解析 IPv4 地址对应的 MAC 地址,在以太网中直接封装,不经过 IP。
L3 网络层
IPv4
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL | DSCP |ECN| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options + Padding (optional) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- IHL × 4 为首部长度,范围为 20~60 字节;Total Length 为整个 IPv4 报文长度,含首部。RFC 791
- 原 Type of Service 字段现分为 6 位 DSCP 和 2 位 ECN。RFC 3168
- Protocol 标识上层协议,如 TCP 为 6、UDP 为 17、ICMP 为 1;Header Checksum 只校验 IPv4 首部。
- 分片偏移以 8 字节为单位;DF 表示禁止分片,MF 表示后面还有分片。
IPv6
以下为 40 字节固定首部,后面可接扩展首部:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| Traffic Class | Flow Label |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Payload Length | Next Header | Hop Limit |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Source Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Destination Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- 常规报文的 Payload Length 不含固定首部,但包含扩展首部;Next Header 标识下一个扩展首部或上层协议。RFC 8200
- IPv6 没有首部校验和;路由器不对 IPv6 报文分片,必要时由源主机分片。
L4 传输层
TCP 提供可靠、有序的字节流,不保留消息边界;UDP 保留数据报边界,不保证可靠或有序交付。
UDP 数据报
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-------------------------------+-------------------------------+
| Source Port | Destination Port |
+-------------------------------+-------------------------------+
| Length | Checksum |
+-------------------------------+-------------------------------+
| Data |
+---------------------------------------------------------------+
- 首部固定为 8 字节;常规数据报的 Length 为首部与数据的总长度,最小为 8。RFC 768
- Checksum 覆盖 IP 伪首部、UDP 首部和数据。IPv4 中可为 0,表示未启用校验;IPv6 中通常必须启用。
TCP 报文段
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-------------------------------+-------------------------------+
| Source Port | Destination Port |
+-------------------------------+-------------------------------+
| Sequence Number |
+---------------------------------------------------------------+
| Acknowledgment Number |
+-------+-----+-+-+-+-+-+-+-+-+-+-------------------------------+
| Data | Rsvd|A|C|E|U|A|P|R|S|F| |
| Offset| |E|W|C|R|C|S|S|Y|I| Window |
| | | |R|E|G|K|H|T|N|N| |
+-------+-----+-+-+-+-+-+-+-+-+-+-------------------------------+
| Checksum | Urgent Pointer |
+-------------------------------+-------------------------------+
| Options + Padding (optional) |
+---------------------------------------------------------------+
| Data |
+---------------------------------------------------------------+
- Data Offset × 4 为首部长度,范围为 20~60 字节,含选项和填充。RFC 9293
- 序号按字节计数,SYN、FIN 各占一个序号;ACK 置 1 时,Acknowledgment Number 表示期望收到的下一个序号。
- 图中 AE 为 AccECN 标志;Rsvd 为 3 位保留位。较早的格式图可能将 AE 位标为 NS 或保留位。RFC 9768
从联网到访问网站
接入网络
以以太网接入、DHCPv4 首次获取地址并执行地址冲突检测为例:
- Link UP:网卡检测到物理链路建立。
- 获取配置:通过 DHCP Discover → Offer → Request → ACK,获取 IPv4 地址、子网掩码及网关、DNS 等配置。RFC 2131
- 检测冲突:广播 ARP Probe,ARP 发送方 IP 为
0.0.0.0,目标 IP 为候选地址;发现冲突则发送 DHCPDECLINE,重新申请。 - 宣告地址并启用:未发现冲突后广播 ARP Announcement,ARP 发送方和目标 IP 均为本机地址,随后使用该配置通信。RFC 5227
静态 IPv4 配置跳过 DHCP;续租不一定经过上述四次交互。IPv6 使用 NDP 发现邻居和路由器,可结合 SLAAC 或 DHCPv6 配置地址,并通过 DAD 检测地址冲突。RFC 4861、RFC 4862
通过 HTTPS 访问域名
访问流程概览
以直接访问 https://www.example.com、新建 HTTP/1.1 或 HTTP/2 连接为例:
- 解析域名:查询本地缓存与配置,必要时查询 DNS 服务器,获得 IPv4/IPv6 地址。
- 确定下一跳:路由表确定出口和下一跳;邻居缓存未命中时,通过 ARP(IPv4)或 NDP(IPv6)解析其 MAC。下一跳可能是目标主机,也可能是网关。
- 建立 TCP 连接:通过三次握手连接服务器,默认目标端口为 443。
- 完成 TLS 握手:验证服务器身份,协商加密参数和密钥。
- 交换 HTTP 消息:通过加密连接发送请求并接收 HTML 等响应。
- 渲染页面:解析 HTML,按需加载 CSS、JS、图片等资源,逐步渲染页面。
缓存和连接复用可省略部分步骤;DNS 查询也可能先触发邻居解析。HTTP/3 改用基于 UDP 的 QUIC,将 TLS 1.3 握手集成到连接建立中,无需 TCP 握手。RFC 9114
公网中的逐跳转发与跨域路由
AS 与 BGP
互联网由多个 AS 互联,BGP 是它们交换路由信息的基础协议:
- AS(Autonomous System,自治系统):对外采用统一路由策略的一组 IP 网络和路由器,以 ASN(自治系统号)标识。一个运营商或组织可运营多个 AS。RFC 1930
- BGP(Border Gateway Protocol,边界网关协议):交换 IP 前缀的可达性和路径属性,支撑跨 AS 选路。eBGP 用于不同 AS 之间交换路由,iBGP 用于同一 AS 内部传播 BGP 路由。RFC 4271
- AS_PATH 与选路策略:AS_PATH 携带路由通告的 AS 路径信息,用于环路检测和选路。选路还受 LOCAL_PREF(本地优先级)等属性及本地策略影响,不保证 AS 跳数最少或时延最低。
AS 内部通常结合直连、静态路由及 OSPF、IS-IS 等内部网关协议(IGP)到达下一跳。根据选定路由生成转发表,报文按转发表逐跳转发。
报文的逐跳转发
路由转发发生在每个报文的传输过程中。以上述 TCP 连接的首个 SYN 报文为例:
- 发送到网关:访问公网通常命中默认路由。主机按需解析网关 MAC;IP 目的地址仍是网站 IP,以太网目的 MAC 则是网关 MAC。
- 出口转发与 NAT:网关查转发表,选择出口和下一跳。IPv4 私网出网通常通过 NAPT,将源 IP 和端口映射为出口 IP 和端口,并保存映射;使用公网 IPv6 地址时通常直接路由。RFC 3022
- 跨网络逐跳转发:报文可能跨越多个 AS,每个 AS 内也可能经过多台路由器。各路由器独立查转发表,按目的 IP 最长前缀匹配选择下一跳,并按出口链路重新封装。RFC 1812
- 到达网站入口:报文进入目标 IP 所在网络,继续经内部路由交给目标节点。该节点可能是服务器、CDN 节点或负载均衡器,不一定是源站。
- 返回客户端:响应按目的 IP 逐跳返回,往返路径不必相同。使用 NAT 时,回包须经持有对应映射的设备,还原内网目的 IP 和端口,再送达主机。后续 TCP、TLS、HTTP 报文也按上述机制转发。
以太网链路上,源 MAC 为出接口 MAC,目的 MAC 为下一跳 MAC,后者按需通过 ARP/NDP 解析。
普通转发保持源、目的 IP 不变,NAT 等处理除外;IPv4 TTL / IPv6 Hop Limit 逐跳递减,耗尽则丢包。IPv4 还需更新首部校验和。RFC 1812、RFC 8200
Linux 网络协议栈收发包过程
以下为普通以太网卡收发本机 TCP/UDP 数据的典型路径;桥接、转发、虚拟设备及卸载可能改变具体路径。
核心对象
| 对象 | 作用 |
|---|---|
| DMA | 网卡直接读写内存,CPU 无需逐字节搬运;驱动负责映射和同步。 |
| RX/TX Ring | 接收/发送描述符环形队列;描述符记录缓冲区的 DMA 地址、长度、状态等,数据存放在关联缓冲区中。 |
sk_buff(skb) | 描述数据包的内核结构体,保存长度、协议和数据位置等元信息;数据存放在关联缓冲区中。 |
收包
- 接收帧:网卡接收并检查以太网帧,FCS 通常由硬件校验并剥除。
- DMA 写入:网卡将数据写入驱动预先准备的 RX Buffer,更新接收描述符。
- IRQ / NAPI:网卡通常通过硬中断调度 NAPI,批量处理收包及发送完成事件。收包轮询受预算限制,通常运行在
NET_RX软中断上下文,轮询期间屏蔽相关中断。NAPI 文档 - 构建 skb:驱动创建
sk_buff,关联或复制接收数据,设置协议等元信息,并补充 RX Buffer。 - 协议处理:按需经 GRO 聚合,进入 IP、TCP/UDP 等协议处理及相关 Netfilter 钩子。
- 交付应用:数据进入对应 socket 接收队列,应用通过
recv()/read()等读取。
发包
- 应用提交数据:通过
send()/write()等进入内核 socket 层。 - 协议处理:构建 skb,处理 TCP/UDP、IP 首部,选择路由,并经过相关 Netfilter 钩子。
- 邻居解析与调度:按需通过 ARP/NDP 解析下一跳 MAC,封装以太网首部,再经 qdisc(队列规则)排队、调度或直接发送。
- 提交描述符:驱动映射数据缓冲区,填写 TX 描述符并通知网卡。
- DMA 读取并发送:网卡读取数据,完成相应硬件卸载处理后发送以太网帧。
- 回收资源:驱动处理发送完成事件,回收 TX 描述符、DMA 映射及本次发送的 skb 引用。
易混淆点
- Netfilter:钩子分布在协议栈多处。典型本机 IP 收包经过
PREROUTING → 路由判断 → INPUT,发包经过OUTPUT → POSTROUTING;初始路由选择在OUTPUT前,规则可能触发重新选路。Netfilter 架构 - XDP:原生 XDP 在构建 skb 前执行,可丢弃、回送或重定向数据包;generic XDP 在构建 skb 后执行。被提前处理的数据包可绕过后续协议栈。XDP 开发者说明
- Ring 耗尽:没有可用 RX Buffer 时可能丢包;TX 描述符不足时通常先暂停对应发送队列,回收资源后恢复,并不必然立即丢包。驱动文档
- 发送完成 ≠ 对端收到:
send()成功通常只表示数据已交给内核;TX Completion 只表示网卡完成本次发送,不能代替 TCP ACK。TCP 可能仍需保留数据用于重传。
聚合与分段卸载
| 机制 | 作用 |
|---|---|
| GRO(通用接收卸载) | 收包时聚合符合条件的数据包,减少后续协议栈处理次数。 |
| GSO(通用分段卸载) | 发包时让协议栈使用较大的 skb,延后分段;硬件不支持时可由软件完成。 |
| TSO(TCP 分段卸载) | 由网卡将较大的 TCP 数据分成多个报文段。 |
因此,一个 skb 不一定对应线上一个报文;抓包也可能看到聚合或尚未分段的数据。内核卸载文档