计算机网络-IP 协议详解
一、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),这种端到端原则是互联网高度可扩展的根本原因。
