一、IPv4 数据报格式

 0     4     8          16                    31
┌─────┬─────┬──────────┬─────────────────────┐
│版本 │头长 │  服务类型  │      总长度(字节)    │
├─────┴─────┴──────────┼──────┬──────────────┤
│      标识符 (16)       │标志(3)│ 片偏移 (13)  │  ← 分片用
├───────────┬──────────┼──────┴──────────────┤
│  TTL (8)  │ 协议 (8)  │    头部校验和 (16)    │
├───────────┴──────────┴─────────────────────┤
│               源 IP 地址 (32)                │
├────────────────────────────────────────────┤
│              目的 IP 地址 (32)               │
├────────────────────────────────────────────┤
│            选项(可变长,很少使用)             │
├────────────────────────────────────────────┤
│                   数据                      │
└────────────────────────────────────────────┘

关键字段

字段 说明
版本 4 或 6
头部长度 以 4 字节为单位;无选项时为 5(20 字节)
服务类型(ToS/DSCP) QoS 标记;低 2 位现用于 ECN(第 16 讲)
总长度 头部+数据,最大 65535 字节
标识/标志/片偏移 分片重组用
TTL 每经过一台路由器减 1,减到 0 就丢弃并回 ICMP 超时
协议 上层协议:6 = TCP,17 = UDP,1 = ICMP
头部校验和 ⚠️ 只覆盖头部,不覆盖数据;且每跳都要重算(因为 TTL 变了)

TTL 的两个作用:① 防止路由环路导致分组永远转圈;② traceroute 的实现基础(第 3 讲)。

头部开销

无选项时 20 字节。对 1500 字节的 MTU:
   IP 头 20 + TCP 头 20 = 40 字节,占 2.7%

二、IP 分片

2.1 为什么需要

不同链路的 MTU(最大传输单元)不同:

以太网      1500 字节
PPPoE       1492 字节
IEEE 802.11 2304 字节
一些隧道     1400 字节甚至更小

一个大数据报要经过 MTU 更小的链路时,必须被切开

2.2 机制

⭐ 分片由【路由器】完成,重组由【目的主机】完成

**为什么重组只在终点?**因为分片可能走不同路径,中间路由器不一定能收齐所有片;而且让路由器维护重组状态会破坏其无状态性。

三个字段协同工作

字段 作用
标识符 同一个原始数据报的所有片段共享同一个标识符
标志位 MF More Fragments:1 表示后面还有,0 表示这是最后一片
片偏移 ⭐ 本片数据在原始数据中的位置,以 8 字节为单位

2.3 计算示例

一个 4000 字节的数据报(20 字节头 + 3980 字节数据)要通过 MTU = 1500 的链路:

每片最多携带数据 = 1500 − 20 = 1480 字节
⚠️ 片偏移以 8 字节为单位,所以每片数据必须是 8 的倍数
   1480 ÷ 8 = 185 ✓ 正好

片 1: 数据 1480 字节,偏移 = 0,    MF=1   总长 1500
片 2: 数据 1480 字节,偏移 = 185,  MF=1   总长 1500
片 3: 数据 1020 字节,偏移 = 370,  MF=0   总长 1040
                                          (1480+1480+1020 = 3980 ✓)

偏移的计算片 n 的偏移 = 前面所有片的数据字节数 ÷ 8

2.4 分片的问题

⚠️ 分片在工程上被普遍认为是一个坏主意

  1. 一片丢失 = 整个数据报作废(TCP 要重传整个报文段)
  2. 重组消耗接收方资源,是 DoS 攻击面(teardrop 攻击)
  3. 中间设备(防火墙、NAT)难以处理非首片(没有 TCP 端口号)

IPv6 直接取消了路由器分片(第 19 讲)。现代做法是 Path MTU Discovery:发送方主动探测路径上的最小 MTU,自己保证不超过它。


三、IP 编址与子网

3.1 地址属于接口,不属于主机 ⭐

IP 地址标识的是一个「接口」,而不是一台主机或路由器。

一台路由器有多个接口,每个接口都有自己的 IP 地址。一台装了 WiFi 和有线网卡的笔记本,两个接口有两个地址。

3.2 点分十进制

11000000 10101000 00000001 00000001
   192   .   168  .    1   .    1

3.3 子网的精确定义 ⭐

子网是一组接口的集合,这些接口彼此之间可以【不经过路由器】直接通信。

判定方法:把所有路由器接口断开,剩下的每一个孤立的网络岛,就是一个子网。

   ┌──────┐  ┌──────┐        ┌──────┐  ┌──────┐
   │主机A  │  │主机B  │        │主机D  │  │主机E  │
   └──┬───┘  └──┬───┘        └──┬───┘  └──┬───┘
      │         │                │         │
   ═══╧═════════╧═══[R1]═════════╧═════════╧═══
     子网 1: 223.1.1.0/24    子网 2: 223.1.2.0/24

3.4 CIDR 表示法

a.b.c.d / x

  x = 前缀长度(网络部分的位数)
  32 − x = 主机部分的位数

200.23.16.0/23

11001000 00010111 00010000 00000000
└──────── 23 位网络前缀 ────┘└─9 位主机─┘

地址范围:200.23.16.0 ~ 200.23.17.255
可用主机数:2⁹ − 2 = 510

⚠️ 为什么要 −2:全 0 的是网络地址,全 1 的是广播地址,都不能分配给主机。

3.5 子网掩码

/24  →  255.255.255.0     →  11111111.11111111.11111111.00000000
/26  →  255.255.255.192   →  11111111.11111111.11111111.11000000
/20  →  255.255.240.0

📌 速查表(背下来能省很多时间)

前缀 掩码末字节 每子网地址数 可用主机数
/24 0 256 254
/25 128 128 126
/26 192 64 62
/27 224 32 30
/28 240 16 14
/29 248 8 6
/30 252 4 2(点对点链路专用)

3.6 从分类编址到 CIDR

历史(有类别编址)

A 类: /8   —— 1600 万个地址   (只有 126 个这样的块)
B 类: /16  —— 65534 个地址
C 类: /24  —— 254 个地址

⚠️ 问题:一个需要 500 台主机的组织,C 类不够(254),B 类浪费 99.2%(65534)。这种粗粒度导致了 1990 年代初的地址快速枯竭。

CIDR(1993, RFC 1519) 的解法:前缀长度可以是任意值,按需分配。

3.7 地址聚合(Route Aggregation)⭐

CIDR 的第二个收益:减小全球路由表

ISP 拥有:  200.23.16.0/20

分配给客户:
   组织 0:  200.23.16.0/23
   组织 1:  200.23.18.0/23
   组织 2:  200.23.20.0/23
   ...
   组织 7:  200.23.30.0/23

⭐ ISP 向互联网只通告【一条】路由:200.23.16.0/20
   而不是 8 条

**这是层次化编址的核心价值。**没有它,全球路由表会膨胀到无法承受。

例外情形:更具体路由

若组织 1 换了 ISP,但想保留原地址:
   新 ISP 必须单独通告 200.23.18.0/23
   ⭐ 最长前缀匹配保证了流量会去新 ISP
   代价:全球路由表多了一条无法被聚合的条目

📌 这就是全球 BGP 路由表持续膨胀的主要原因之一(第 21 讲)。

3.8 特殊地址

地址块 用途
10.0.0.0/8 ⭐ 私有地址(RFC 1918)
172.16.0.0/12 私有地址
192.168.0.0/16 私有地址
127.0.0.0/8 环回(localhost)
169.254.0.0/16 链路本地(DHCP 失败时自动配置,APIPA)
224.0.0.0/4 多播
100.64.0.0/10 运营商级 NAT(CGNAT)
255.255.255.255 受限广播

四、DHCP:即插即用地址分配

主机怎么得到 IP 地址?两种方式:管理员手工配置,或 DHCP(动态主机配置协议)。

4.1 DHCP 提供的四样东西

不只是 IP 地址:

① 客户端的 IP 地址
② 子网掩码
③ ⭐ 默认网关(第一跳路由器)的地址
④ ⭐ 本地 DNS 服务器的地址

⚠️ 没有 ③ 和 ④,你有 IP 也上不了网。这就是"网络配置"的全部内容。

4.2 四步交互(DORA)

DHCP 运行在 UDP 上(客户端 68,服务器 67)。

客户端(还没有 IP!)                        DHCP 服务器
   │
   │ ① DHCP DISCOVER
   │    源 IP: 0.0.0.0    ⭐ 我还没有地址
   │    目的: 255.255.255.255  ⭐ 广播,我不知道服务器在哪
   ├──────────────────────────────────────────▶
   │
   │ ② DHCP OFFER                (可能有多个服务器响应)
   │◀──────────────────────────────────────────┤
   │    「我可以给你 192.168.1.100,租期 12 小时」
   │
   │ ③ DHCP REQUEST              ⭐ 仍然是广播
   ├──────────────────────────────────────────▶
   │    「我接受这个 offer」(广播是为了让其他服务器知道自己没被选中)
   │
   │ ④ DHCP ACK
   │◀──────────────────────────────────────────┤
   │    「确认,地址是你的了」

为什么用广播?因为客户端此时既没有自己的 IP,也不知道服务器的 IP。这是一个「先有鸡还是先有蛋」问题,只能用广播打破。

⭐ **为什么用 UDP 而不是 TCP?**TCP 需要三次握手,而握手需要有 IP 地址——同样的循环依赖(第 10 讲的 UDP 使用场景)。

4.3 租期与中继

租期(lease):地址是的,不是给的。租期过半时客户端会尝试续租。这允许地址在设备离开后被回收。

DHCP 中继代理:广播不能跨越路由器。若 DHCP 服务器不在本子网,路由器可配置为中继代理,把广播转成单播转发给服务器。


五、NAT:网络地址转换

5.1 动机

IPv4 只有 2³² ≈ 43 亿个地址,而全球有上百亿台联网设备。

NAT 的思路:一个家庭/组织内部用私有地址,对外只用一个公网地址

5.2 机制

        内网                    NAT 路由器                外网
                          138.76.29.7 (公网)
  10.0.0.1:3345 ──────────────┤
                              │  NAT 转换表
  10.0.0.2:3345 ──────────────┤  ┌──────────────┬──────────────┐
                              │  │ 公网侧        │ 内网侧        │
  10.0.0.3:5001 ──────────────┤  ├──────────────┼──────────────┤
                              │  │138.76.29.7:5001│10.0.0.1:3345│
                              │  │138.76.29.7:5002│10.0.0.2:3345│
                              │  │138.76.29.7:5003│10.0.0.3:5001│
                              │  └──────────────┴──────────────┘

出站

① 收到 (10.0.0.1:3345 → 服务器:80)
② 分配一个未用的公网端口,如 5001
③ 记入转换表
④ ⭐ 改写源 IP 和源端口为 (138.76.29.7:5001)
⑤ ⭐ 重算 IP 头校验和【和】TCP/UDP 校验和(因为伪首部,第 10 讲)
⑥ 转发

入站

① 收到 (服务器:80 → 138.76.29.7:5001)
② 查表 → 5001 对应 10.0.0.1:3345
③ 改写目的 IP 和端口
④ 转发到内网

⭐ **本质:NAT 用「端口号」作为额外的地址位。**16 位端口理论上可支持 65535 个并发连接共享一个 IP。

5.3 收益

  1. 节省地址:ISP 只需给一个家庭分配一个 IP
  2. 内网地址可自由改变,不影响外部
  3. 更换 ISP 不需要重新编址内网
  4. ⚠️ 副作用式的"安全":外部无法主动发起到内网主机的连接

5.4 NAT 的争议 ⭐

反对者的论点(都很有力)

1️⃣ 它违反了端到端原则(第 4 讲)

网络中间的设备改写了传输层信息。这是分层的严重破坏。

2️⃣ 路由器不应该处理第 4 层

按分层,路由器只该到第 3 层。NAT 让它读写端口号,这是一个层次侵犯。

3️⃣ 它破坏了 IP 的可寻址性

内网主机无法被主动连接。这直接杀死了 P2P 应用、IP 电话、在家自建服务器的能力。

4️⃣ 它把「地址短缺」的压力转成了「一切都要打补丁」

有人认为:NAT 让 IPv4 苟延残喘,反而拖延了 IPv6 的部署

支持者的回应

**NAT 是让互联网活到今天的东西。**没有它,IPv4 地址在 2000 年代初就会彻底耗尽,而 IPv6 当时远未准备好。理论上的纯洁性不能当地址用。

📌 这是网络课上最好的一场辩论,因为双方都对。它展示了一件事:工程决策经常在"正确"和"可行"之间,而可行的往往赢。

5.5 NAT 穿越

问题:外网的 A 想主动连接 NAT 后的 B,怎么办?

方案 原理
端口转发(静态映射) 手动在 NAT 上配置「外部 8080 → 内部 192.168.1.10:80」
UPnP / NAT-PMP 内网应用自动请求 NAT 建立映射
STUN 让 B 询问外部服务器「我在公网上看起来是什么地址:端口?」
打洞(Hole Punching) 双方都先向外发包建立映射,再借助中间服务器交换地址,然后直连
TURN 打洞失败时的兜底:通过中继服务器转发全部流量(昂贵)
ICE 上述方案的统一框架,WebRTC 使用

📌 打洞的巧妙之处:NAT 只阻止「未经请求的入站」。如果双方同时向对方发包,各自的 NAT 都会认为这是自己发起的连接的回包,从而放行。它利用了 NAT 规则的对称性。

5.6 CGNAT

ISP 地址不够时,会做运营商级 NAT——用户拿到的是 100.64.0.0/10 里的地址,再经过 ISP 的 NAT 出去。

⚠️ 后果双层 NAT,打洞成功率大幅下降,很多 P2P 应用退化到必须用 TURN 中继。这是移动网络上 P2P 体验差的主要原因。


六、例题(Worked Example)

题目:某公司获得地址块 203.0.113.0/24。需要划分为四个子网:

  • 工程部:100 台主机
  • 销售部:50 台主机
  • 管理部:25 台主机
  • 一条连接两台路由器的点对点链路

请给出各子网的 CIDR 前缀、地址范围与可用主机范围。

解答

方法:VLSM(可变长子网掩码),从需求最大的开始分配。

第 1 步:确定每个子网需要的前缀长度

工程部 100 台 → 需 100+2 = 102 个地址 → 2⁷=128 ≥ 102 → /25
销售部 50 台  → 需 52 个地址          → 2⁶=64  ≥ 52  → /26
管理部 25 台  → 需 27 个地址          → 2⁵=32  ≥ 27  → /27
点对点链路    → 需 2 个地址           → 2²=4         → /30

第 2 步:从大到小依次分配

① 工程部 /25:
   203.0.113.0/25
   地址范围: 203.0.113.0   – 203.0.113.127
   网络地址: 203.0.113.0
   广播地址: 203.0.113.127
   ⭐ 可用主机: 203.0.113.1 – 203.0.113.126  (126 个 ≥ 100 ✓)

② 销售部 /26(从 128 开始):
   203.0.113.128/26
   地址范围: 203.0.113.128 – 203.0.113.191
   ⭐ 可用主机: 203.0.113.129 – 203.0.113.190 (62 个 ≥ 50 ✓)

③ 管理部 /27(从 192 开始):
   203.0.113.192/27
   地址范围: 203.0.113.192 – 203.0.113.223
   ⭐ 可用主机: 203.0.113.193 – 203.0.113.222 (30 个 ≥ 25 ✓)

④ 点对点链路 /30(从 224 开始):
   203.0.113.224/30
   地址范围: 203.0.113.224 – 203.0.113.227
   ⭐ 可用主机: 203.0.113.225 – 203.0.113.226 (2 个 ✓)

剩余未分配: 203.0.113.228 – 203.0.113.255(28 个地址,留作扩展)

验证掩码

/25 → 255.255.255.128
/26 → 255.255.255.192
/27 → 255.255.255.224
/30 → 255.255.255.252

📌 两个易错点

  1. 必须从大到小分配,否则会产生无法使用的地址碎片
  2. 每个子网都要减去网络地址和广播地址——「100 台主机」需要的是 /25 而不是 /26(62 < 100),也不是 /24(浪费)

七、随堂自测

  1. IP 头部校验和覆盖数据吗?为什么每跳都要重算?
  2. 分片由谁做?重组由谁做?为什么不对称?
  3. 一个 5000 字节的数据报经过 MTU=1500 的链路,会分成几片?写出每片的偏移和 MF。
  4. 用一句话精确定义「子网」。给出判定方法。
  5. 192.168.10.0/26 有多少可用主机地址?范围是什么?
  6. 为什么 CIDR 取代了分类编址?地址聚合带来了什么?
  7. DHCP 为什么必须用广播和 UDP?
  8. NAT 违反了哪条设计原则?给出支持和反对 NAT 的各一条最有力的论证。
  9. 打洞(hole punching)为什么能成功?

八、本讲要点回顾

  • TTL 防环路,且是 traceroute 的基础;IP 头校验和只覆盖头部每跳重算
  • 分片由路由器做,重组只由目的主机做;片偏移以 8 字节为单位。IPv6 取消了路由器分片。
  • IP 地址属于接口,不属于主机。
  • 子网 = 一组不经过路由器就能互相通信的接口。
  • CIDR 让前缀长度任意,带来按需分配与⭐ 地址聚合(减小全球路由表)。
  • 主机数 = 2^(32−x) − 2(去掉网络地址和广播地址)。
  • DHCP 四步 DORA,给出 IP + 掩码 + 网关 + DNS;用广播和 UDP 是因为循环依赖。
  • NAT 用端口号当地址位,救了 IPv4,也破坏了端到端原则和可寻址性
  • NAT 穿越:STUN / 打洞 / TURN / ICE;CGNAT 让情况更糟

九、自测答案

1. 不覆盖数据,只覆盖头部。(数据的完整性交给传输层的校验和与链路层的 CRC——这是分层的分工。)每跳必须重算,是因为 TTL 每跳减 1,头部内容发生了变化,原校验和失效。(这也是 IPv6 干脆取消头部校验和的原因之一:每跳重算是纯粹的开销。)

2. 分片由路由器做,重组只由目的主机做。不对称的原因:① 各片段可能沿不同路径传输,中间路由器无法保证收齐所有片;② 让路由器为重组维护状态会破坏其无状态转发的特性、消耗内存并构成 DoS 攻击面;③ 端到端原则——重组只有终点能正确完成。

3. 数据部分 = 5000 − 20 = 4980 字节。每片最多携带 1480 字节数据(1500 − 20,且 1480 是 8 的倍数)。

片 1: 数据 1480, 偏移 = 0,   MF = 1
片 2: 数据 1480, 偏移 = 185, MF = 1
片 3: 数据 1480, 偏移 = 370, MF = 1
片 4: 数据  540, 偏移 = 555, MF = 0
共 4 片(1480×3 + 540 = 4980 ✓)

4. **子网是一组接口的集合,这些接口彼此之间不经过路由器就能直接通信。**判定方法:把每台路由器的所有接口从图上"剪断",剩下的每一个相互连通的孤岛就是一个子网。

5. /26 → 主机位 32−26 = 6 位 → 2⁶ = 64 个地址 → 62 个可用主机地址。范围:网络地址 192.168.10.0,广播地址 192.168.10.63可用主机 192.168.10.1192.168.10.62

6. 分类编址的粒度太粗(/8、/16、/24 三档),导致严重浪费:需要 500 台主机的组织,C 类(254)不够而 B 类(65534)浪费 99%。这加速了 1990 年代初的地址枯竭。CIDR 允许任意前缀长度,可按实际需求分配。地址聚合让一个 ISP 能用一条前缀通告其下所有客户的地址块,从而把全球路由表规模从「每个组织一条」压缩了几个数量级——没有聚合,BGP 路由表规模会失控

7. 广播:客户端在获得地址之前既没有自己的 IP,也不知道 DHCP 服务器的 IP,无法进行任何单播通信,只能向 255.255.255.255 广播。UDP:TCP 需要三次握手,而握手本身要求已有可用的 IP 地址——这是同一个循环依赖。UDP 无连接,可以直接发送。

8. 违反了端到端原则(第 4 讲)——网络中间的设备改写了传输层的端口信息,且路由器越层处理了第 4 层内容。反对最有力的一条:它破坏了 IP 的可寻址性,使内网主机无法被主动连接,从而系统性地扼杀了 P2P、IP 电话、个人自建服务等一整类应用,把互联网从「对等网络」推向了「客户机-服务器」的不对称结构。支持最有力的一条:没有 NAT,IPv4 地址会在 2000 年代初彻底耗尽,而当时 IPv6 远未具备大规模部署条件——NAT 用架构上的妥协,为互联网换来了二十年的时间

9. 因为 NAT 的规则是「阻止未经请求的入站分组」,而不是「阻止所有入站分组」。当内网主机 B 主动向外发出一个分组时,NAT 会为它建立一条映射并在一段时间内放行该映射对应的入站分组。打洞的做法是:让 A 和 B 同时向对方(的公网地址:端口,通过中间信令服务器交换得到)发包——各自的 NAT 都会把对方的回包视为「我方主动发起的会话的响应」而放行,双向映射同时建立,直连成功。⚠️ 这依赖 NAT 的映射行为可预测;对称型 NAT(Symmetric NAT) 为每个目的地分配不同的外部端口,因而打洞失败,只能退回 TURN 中继。