一、IP 协议的核心职责

IP(Internet Protocol)工作在网络层(OSI 第三层),负责将数据包从源主机路由到目标主机。

职责 说明
寻址 为每台设备分配唯一地址
路由 决定数据包的传输路径
分片与重组 将大数据包拆分以适应不同网络的 MTU
无连接传输 不保证可靠性,由上层协议(如 TCP)负责

二、IPv4 报文头结构

固定头 20 字节,Options 可变。

字段 长度 说明
Version 4 bit IPv4 = 4
IHL(首部长度) 4 bit 单位 4 字节,最小 5(20 字节)
DSCP/ECN 8 bit QoS 优先级 + 拥塞通知
总长度 16 bit 整个 IP 包长度,最大 65535 字节
Identification 16 bit 分片重组用的唯一 ID
Flags 3 bit DF(禁止分片)、MF(更多分片)
分片偏移 13 bit 当前片在原始数据中的位置
TTL 8 bit 每经过一个路由器减 1,为 0 时丢弃
协议 8 bit TCP=6,UDP=17,ICMP=1
首部校验和 16 bit 仅校验头部
源 IP 地址 32 bit 发送方地址
目标 IP 地址 32 bit 接收方地址
Options 可变 可选,如时间戳、路由记录

三、关键字段详解

3.1 IHL(首部长度)

IHL 告诉接收方”IP 头到哪里结束,数据从哪里开始”。

固定头 = 20 字节(IHL=5,5×4=20)

带 Options 时:
┌──────────────────────┬──────────────┬──────────┐
│   固定头 20 字节       │ Options 12字节│  数据    │
└──────────────────────┴──────────────┴──────────┘
  IHL = 8(8×4 = 32 字节)

IHL 最小 = 5(20 字节),最大 = 15(60 字节)。

3.2 DSCP / ECN

  7   6   5   4   3   2   1   0
┌───┬───┬───┬───┬───┬───┬───┬───┐
│       DSCP(6位)      │ECN│ECN│
└───┴───┴───┴───┴───┴───┴───┴───┘

历史背景:为什么需要它

早期互联网(1970~1980年代):

互联网最初的设计哲学是”尽力而为(Best Effort)”,所有数据包完全平等,路由器按照先来先处理的原则(FIFO)转发。早期用途只有学术研究、文件传输、Email,流量对延迟不敏感,FIFO 完全够用。

1990年代:流量爆炸,问题出现

同一条链路上:
  ┌─────────────────────────────────────┐
  │  语音包  │  BT下载  │  BT下载  │  语音包  │
  └─────────────────────────────────────┘

  BT把带宽占满 → 语音包排队等待 → 通话卡顿
  路由器:我不知道哪个重要,先来先处理

第一次尝试:ToS 字段(1981年,RFC 791)

  7   6   5   4   3   2   1   0
┌───┬───┬───┬───┬───┬───┬───┬───┐
│  优先级(3位)  │D  │T  │R  │ 保留 │
└───┴───┴───┴───┴───┴───┴───┴───┘

优先级:0~7,7最高
D = Delay(低延迟)
T = Throughput(高吞吐)
R = Reliability(高可靠性)

ToS 彻底失败:语义模糊、没有执行机制(任何人都能把 BT 下载标成优先级 7)、粒度太粗,几乎所有路由器直接忽略。

第二次尝试:DiffServ(1998年,RFC 2474)

设计思路根本转变:

ToS的思路(失败):
  "我这个包很重要,请优先处理我"
  → 每个包自己声明,无法信任

DiffServ的思路(成功):
  "网络管理员定义规则,流量在入口被分类打标,
   核心路由器只看标记执行动作,不做复杂判断"
  → 分类在边缘,执行在核心
DiffServ架构:

用户网络          运营商网络(核心)         目标网络
           ┌─────────────────────────┐
  包进来 →  │边缘路由器               │核心路由器
           │1. 分类(看五元组)       │只看DSCP标记
           │2. 标记DSCP              │直接执行PHB
           │3. 限速/整形             │不做深度检测
           └─────────────────────────┘

核心概念 PHB(Per-Hop Behavior,逐跳行为):DSCP 不是直接定义”优先级”,而是定义这个包在每个路由器上应该得到什么处理行为。


DSCP 详解

命名体系:

CS(Class Selector):兼容旧ToS优先级
  CS0 = 000000 = 0    (最低)
  CS1 = 001000 = 8
  CS2 = 010000 = 16
  CS3 = 011000 = 24
  CS4 = 100000 = 32
  CS5 = 101000 = 40
  CS6 = 110000 = 48
  CS7 = 111000 = 56   (最高,网络控制流量用)

AF(Assured Forwarding,确保转发):
  AFxy = x类,y级丢弃优先级
  x = 1~4(4个类)
  y = 1~3(3个丢弃优先级)

EF(Expedited Forwarding,加速转发):
  = 46,最高优先级,专门给实时流量

AF 说明:

“类”决定带宽分配,”丢弃优先级”决定拥塞时先丢谁:

        丢弃优先级
        低(1)    中(2)    高(3)
        ──────────────────────
AF1x  │ AF11   AF12    AF13    ← 第1类(最低类)
AF2x  │ AF21   AF22    AF23    ← 第2类
AF3x  │ AF31   AF32    AF33    ← 第3类
AF4x  │ AF41   AF42    AF43    ← 第4类(最高类)

拥塞时:先丢 AF43 → 还拥塞丢 AF42 → 还拥塞丢 AF41

完整 DSCP 值对照表:

DSCP名 十进制值 典型用途
CS0/BE 0 默认,普通流量
CS1 8 低优先级,背景流量(批量备份)
AF11/12/13 10/12/14 低优先级数据
AF21/22/23 18/20/22 普通业务
AF31/32/33 26/28/30 视频流
AF41/42/43 34/36/38 视频会议
CS5 40 语音信令
EF 46 语音/实时流量(最严格保证)
CS6 48 网络控制协议(OSPF/BGP)
CS7 56 保留,最高优先

ECN 详解

为什么需要 ECN:

传统拥塞控制(丢包检测):
  路由器队列满了 → 丢包 → 发送方等超时 → 降速
  问题:包已经丢了,这段传输白费;实时流量无法重传

ECN 的思路:在还没丢包时就提前通知

ECN 两位的含义:

Not-ECT = 00   不支持ECN
ECT(0)  = 01  \
ECT(1)  = 10  /  两个都表示"我支持ECN"
CE      = 11   路由器标记"我快满了"

ECN 完整工作流程:

第一步:TCP握手时协商ECN支持
  发送方SYN:ECE=1, CWR=1  (我支持ECN)
  接收方SYN-ACK:ECE=1      (我也支持)

第二步:正常传输,包标记ECT
  发送方:IP包中ECN=01或10

第三步:路由器检测到拥塞(队列超过阈值,如75%)
  不丢包,把包的ECN字段改成11(CE),继续转发

第四步:接收方收到CE标记
  在ACK中设置ECE标志,告诉发送方"收到拥塞信号"

第五步:发送方收到ECE的ACK
  降低拥塞窗口,在下一个包里设置CWR
对比:
传统:路由器队列满 → 丢包 → 等RTT → 降速(有丢包)
ECN :路由器队列75%满 → 标记CE → 下一个ACK即降速(无丢包)

ECN 的局限:

必须两端都支持,中间任一不支持ECN的路由器会使其失效。
部分防火墙会把CE标记的包当异常流量丢掉(黑洞问题)。

# Linux查看ECN状态
sysctl net.ipv4.tcp_ecn
# 0=关闭,1=主动开启,2=被动接受(默认)

现实中的使用

企业网络入口路由器:
  VoIP(UDP 5060/RTP)  → 打EF(46)
  视频会议(Zoom/Teams)→ 打AF41
  业务系统(ERP/CRM)  → 打AF21
  普通上网              → 打CS0(默认)
  P2P/BT               → 打CS1(最低)

运营商网络:
  用户自己打的标不可信(可能全打EF)
  边缘路由器统一清零,按运营商自己的规则重新打标
  核心路由器只执行PHB,不做DPI → 转发速度极快

3.3 总长度

单个 IP 包的总长度,包含头部+数据,单位字节:

┌──────────────────────────────────────────┐
│  IP头(20~60字节)  │    数据(payload)    │
└──────────────────────────────────────────┘
│←──────────── 总长度(最大65535字节)────────│

分片后每个分片都有自己独立的总长度:

原始包 总长度=4020

分片1:总长度=1500(IP头20 + 数据1480)
分片2:总长度=1500
分片3:总长度=1060

3.4 Identification(标识)与 Flags

无分片时:ID 有值但接收方忽略,MF=0 且偏移=0 即判断为完整包。

有分片时:相同 ID 的分片属于同一原始包,接收方据此重组。

分片1: ID=12345, MF=1, 偏移=0
分片2: ID=12345, MF=1, 偏移=1480
分片3: ID=12345, MF=0, 偏移=2960  ← 最后一片

⚠️ 早期 ID 单调递增曾被用于空闲扫描攻击(Idle Scan),现代 OS 已改为随机生成。

DF(Don’t Fragment)— 路径 MTU 发现(PMTUD):

发送方                 路由器                 接收方
   │──── DF=1,包太大 ──→│                       │
   │←── ICMP: 该链路 MTU=1400                   │
   │──── 缩小包到 1400 ──→│──────────────────────→│

MF(More Fragments)— 重组判断:

收到分片时缓存所有分片
MF=0 的那片 → 知道这是最后一片
所有偏移连续且收到MF=0 → 重组完成,交给上层

3.5 首部校验和

为什么有 MAC FCS 还需要 IP 校验和?

FCS 只保护”一跳”内的链路传输,保护不了路由器内部处理过程:

发送方          路由器A                    路由器B
   │               │                          │
   │──── 帧 ──────→│                          │
   │          ①验FCS,通过                    │
   │          ②剥掉MAC帧                      │
   │          ③IP包赤裸地躺在路由器内存里      │
   │          ④读IP头,TTL-1                  │
   │          ⑤内存故障,目的IP悄悄翻转        │
   │          ⑥套上新MAC帧                    │
   │          ⑦计算新FCS(基于损坏的IP头)    │
   │               │──── 帧 ────────────────→│
   │               │                    验FCS通过(FCS是路由器A算的,当然对)

路由器内部数据流:

网卡收到帧
   ↓
DMA搬运到内存    ← 内存故障可在此翻转比特
   ↓
CPU读取IP头      ← CPU缓存故障可在此出错
   ↓
TTL - 1,写回内存
   ↓
DMA从内存搬到网卡
   ↓
网卡发出,计算FCS  ← FCS在这里才开始算(前面所有错误已被当成正确数据)

两者互补:

FCS      → 保证铜线/光纤上传输没出错
IP校验和  → 保证路由器处理过程没出错

IP 校验和也不能 100% 保证

IP 校验和只能发现”损坏发生在校验和计算之前”的错误。最危险的情况是损坏后恰好合法:

原始包:发给 10.0.0.1:80(HTTP服务器A)
目的IP翻转:10.0.0.3(恰好是合法的数据库服务器,也开了80端口)

结果:
→ FCS 通过、IP校验和通过、TCP校验和通过
→ 数据被写入错误的服务器,全程无任何报错
→ 问题可能几天后才被发现(完美犯罪)

没有任何单一机制能 100% 保证数据正确,网络安全靠的是多层独立校验的概率叠加

IP 校验和计算方法

算法使用反码求和(ones’ complement)

1. 把 IP 头按 16 位分组(校验和字段本身先填 0)
2. 全部相加
3. 进位加回低 16 位
4. 取反码(0变1,1变0)即得校验和

接收方验证:包括校验和在内所有 16 位组相加
结果应为 0xFFFF(全 1)→ 正确,否则丢弃

各层校验分工

┌─────────────────────────────────────────────────┐
│                    数据                          │ ← 应用层(如TLS)
├─────────────────────────────────────────────────┤
│              TCP/UDP 校验和                      │ ← 头+数据,端到端
├──────────────┬──────────────────────────────────┤
│   IP首部校验  │         数据(不管)              │ ← 只管头部,每跳重算
├──────────────┴──────────────────────────────────┤
│                 MAC FCS                         │ ← 管一跳内所有字节
└─────────────────────────────────────────────────┘
校验者 保护范围 生命周期
MAC FCS 当前链路所有字节 一跳,路由器验完即丢
IP 校验和 IP 头部 每跳重算,路由器负责
TCP/UDP 校验和 头+数据,端到端 源到目的,中间不动
TLS/应用层 应用数据 端到端,加密+完整性

3.6 Options(可选字段)

┌────────┬────────┬──────────────────────┐
│  类型   │  长度  │       数据            │
│ 1字节  │ 1字节  │    (长度-2)字节       │
└────────┴────────┴──────────────────────┘
选项 用途 现状
Record Route 记录经过的路由器 IP(ping -R 可用
Timestamp 记录每跳时间戳 可用
Loose Source Route 指定必须经过的路由器 基本废弃
Strict Source Route 指定完整路径 基本废弃

现代网络中大多数路由器/防火墙直接丢弃带 Options 的包(安全原因);Source Route 曾被用于 IP 欺骗攻击,RFC 1812 建议禁用。


四、分片机制

当数据包大于链路层 MTU(以太网默认 1500 字节)时触发分片:

原始包: 4000 字节数据

分片1: 偏移=0,    长度=1480, MF=1
分片2: 偏移=1480, 长度=1480, MF=1
分片3: 偏移=2960, 长度=1040, MF=0  ← 最后一片
  • 分片在目标主机重组,中间路由器不重组
  • 现代网络推荐使用路径 MTU 发现(PMTUD)主动避免分片

五、TTL(生存时间)

  • 防止数据包在网络中无限循环
  • 每经过一个路由器 TTL - 1,TTL = 0 时路由器丢包并发送 ICMP Time Exceeded
  • 常见默认值:Linux = 64,Windows = 128,网络设备 = 255

六、IPv4 地址

地址分类

类别 范围 用途
A 类 0.0.0.0 ~ 127.255.255.255 大型网络
B 类 128.0.0.0 ~ 191.255.255.255 中型网络
C 类 192.0.0.0 ~ 223.255.255.255 小型网络
D 类 224.0.0.0 ~ 239.255.255.255 组播
E 类 240.0.0.0 ~ 255.255.255.255 保留/实验

私有地址(RFC 1918)

10.0.0.0/8         →  A 类私网
172.16.0.0/12      →  B 类私网
192.168.0.0/16     →  C 类私网
127.0.0.0/8        →  本地回环(loopback)

七、路由原理

IP 路由基于最长前缀匹配

路由表示例:
目标网络           下一跳          接口
192.168.1.0/24    直连            eth0
10.0.0.0/8        192.168.1.1     eth0
0.0.0.0/0         192.168.1.254   eth0  ← 默认路由

数据包到达时,匹配前缀最长(最具体)的路由条目。


八、IPv6

主要改进

特性 IPv4 IPv6
地址长度 32 bit(约 43 亿) 128 bit(约 3.4×10³⁸)
头部 可变长,有校验和 固定 40 字节,无校验和
分片 路由器可分片 仅源主机分片
安全 可选 IPSec 内置 IPSec 支持
广播 支持 用组播替代
配置 DHCP SLAAC 自动配置

地址格式

完整格式:2001:0db8:85a3:0000:0000:8a2e:0370:7334
压缩格式:2001:db8:85a3::8a2e:370:7334

特殊地址:
::1          → 本地回环
fe80::/10    → 链路本地地址
::           → 未指定地址

为什么 IPv6 删掉了 IP 校验和

IPv4 时代:链路质量差(拨号、卫星),必须在 IP 层兜底

IPv6 时代:
  现代链路(光纤、以太网)误码率极低(10⁻¹² 量级)
  TCP/UDP 校验和强制开启(IPv4 的 UDP 校验和可以关)
  应用层普遍使用 TLS,自带完整性验证

结论:路由器每跳重算校验和的 CPU 开销
      > 现代网络中 IP 头损坏的实际风险
      → 删掉,换来转发性能提升

九、相关协议

传输层  ← TCP / UDP
  │
网络层  ← IP(核心)
          ├── ARP      (IP → MAC 地址解析)
          ├── ICMP     (差错报告与控制)
          ├── IGMP     (组播管理)
          └── OSPF/BGP (路由协议)
  │
链路层

十、关键特性总结

特性 说明
无连接 每包独立路由,无需建立连接
不可靠 不保证送达、顺序、重复检测
尽力而为 Best-effort delivery
可扩展 通过 Options 字段扩展功能

IP 协议设计极简,将复杂性推给端系统(TCP),这种端到端原则是互联网高度可扩展的根本原因。