一、IPv6 的动机
直接原因:IPv4 地址耗尽。IANA 的最后一批 /8 在 2011 年 2 月分配完毕,各区域注册机构随后陆续宣告枯竭。
IPv6 的地址空间:
2¹²⁸ ≈ 3.4 × 10³⁸
**这个数字有多大?**地球表面每平方毫米可以分到约 6.6×10¹⁷ 个地址。地址短缺在 IPv6 里不是一个需要考虑的问题。
⚠️ **但地址不是唯一动机。**IPv6 还借机做了一次协议清理:
- 简化头部,加快路由器处理
- 去掉分片、去掉校验和
- 内建对流标签、扩展首部的支持
二、IPv6 数据报格式
0 4 12 31
┌─────┬────────┬──────────────────────────────┐
│版本 │流量类别 │ 流标签 (20) │
├─────┴────────┼───────────┬──────────────────┤
│ 有效载荷长度 │ 下一个首部 │ 跳限制 (8) │
├──────────────┴───────────┴──────────────────┤
│ │
│ 源地址 (128 位) │
│ │
├──────────────────────────────────────────────┤
│ │
│ 目的地址 (128 位) │
│ │
└──────────────────────────────────────────────┘
⭐ 固定 40 字节,无选项字段
2.1 三处关键删减 ⭐
| 删掉了什么 | 为什么 |
|---|---|
| ⭐ 头部校验和 | 传输层(TCP/UDP 强制校验和)和链路层(CRC)都在做,每跳重算是纯浪费 |
| ⭐ 分片/重组字段 | 路由器不再分片;改由源主机做 Path MTU Discovery,包太大就回 ICMPv6 “Packet Too Big” |
| 可变长选项 | 移到「扩展首部」链中,基本首部固定 40 字节 → 硬件处理更快 |
📌 一个反直觉的事实:IPv6 地址长度是 IPv4 的 4 倍,但头部字段数从 13 个减到 8 个。**更大的地址,更简单的头部。**这是因为 IPv4 头部里有一半的字段是历史包袱。
2.2 新增字段
- 流标签(Flow Label, 20 位):标识属于同一个「流」的数据报,供路由器做一致性哈希、负载均衡或 QoS 处理,无需解析传输层头部
- 下一个首部(Next Header):既表示上层协议(6=TCP、17=UDP),也用于串联扩展首部
- 跳限制(Hop Limit):就是 IPv4 的 TTL,改了个更诚实的名字(它从来就不是「时间」)
三、IPv6 地址
3.1 表示法
完整: 2001:0db8:0000:0000:0000:ff00:0042:8329
压缩规则:
① 每组去掉前导零: 2001:db8:0:0:0:ff00:42:8329
② ⭐ 连续的全零组用 :: 代替(【全地址中只能用一次】)
2001:db8::ff00:42:8329
常见地址:
::1 环回(相当于 127.0.0.1)
:: 未指定地址(相当于 0.0.0.0)
URL 中的写法:因为地址里有冒号,必须用方括号:
http://[2001:db8::1]:8080/index.html
3.2 地址类型 ⭐
| 类型 | 前缀 | 说明 |
|---|---|---|
| 全球单播 | 2000::/3 |
公网地址 |
| 链路本地 | fe80::/10 |
⭐ 每个接口自动生成,仅在本链路有效 |
| 唯一本地(ULA) | fc00::/7 |
相当于 IPv4 的私有地址 |
| 多播 | ff00::/8 |
一对多 |
| 任播(anycast) | 与单播共享格式 | ⭐ 多个节点共享同一地址,路由到最近的一个 |
⚠️ IPv6 没有广播!广播的功能由多播取代(如 ff02::1 = 本链路所有节点)。
为什么取消广播?因为广播会打扰链路上的每一台设备(哪怕它不关心),在无线网络和大型局域网上开销巨大。多播只打扰订阅了该组的设备。
📌 任播是一个重要概念:第 7 讲讲的 13 个根 DNS 服务器实际有 1500+ 个实例,靠的就是任播——多台服务器通告同一个地址,BGP 自然把你路由到最近的那个。CDN 也大量使用它。
3.3 SLAAC:无状态地址自动配置
IPv6 主机可以不需要 DHCP 服务器就配好地址:
① 用 fe80:: + 接口标识符 生成【链路本地地址】
② 做重复地址检测(DAD)
③ 发送「路由器请求」(RS)
④ 路由器回「路由器通告」(RA),里面有网络前缀
⑤ ⭐ 前缀 + 接口标识符 = 全球单播地址
⚠️ 隐私问题:早期用 MAC 地址生成接口标识符(EUI-64),导致设备可以被跨网络追踪。现代实现使用隐私扩展(RFC 8981)随机生成并定期轮换。
3.4 NDP 取代 ARP
IPv6 用 邻居发现协议(NDP) 替代了 IPv4 的 ARP(第 24 讲),并且它跑在 ICMPv6 之上而不是直接在链路层。
⚠️ 这带来一个实际后果:**过滤 ICMPv6 会让 IPv6 网络彻底不工作。**很多管理员按 IPv4 的习惯「屏蔽所有 ICMP」,在 IPv6 上这是灾难性的配置错误。
四、从 IPv4 过渡到 IPv6
⭐ 核心难题:不可能在某个时刻让全世界同时切换(“flag day”)。
4.1 双栈(Dual Stack)
主机同时拥有 IPv4 和 IPv6 地址,两套协议栈并行运行
DNS 同时返回 A 记录和 AAAA 记录
应用按可用性选择
- ✅ 最干净的方案
- ❌ 需要同时维护两套网络(两套路由、两套防火墙规则、两套监控),成本翻倍
- ❌ 仍然需要 IPv4 地址——没有解决地址短缺
⚠️ 双栈的性能陷阱:若 IPv6 路径不通,应用要等 IPv6 连接超时才回落到 IPv4,用户感受到几秒的卡顿。解法是 Happy Eyeballs(RFC 8305):同时发起两种连接,谁先成功用谁。
4.2 隧道(Tunneling)
当两个 IPv6 网络之间隔着 IPv4 网络时:
IPv6 网络 A IPv4 网络 IPv6 网络 B
│ │
│ ⭐ 把整个 IPv6 数据报当作 IPv4 的【载荷】 │
├────[IPv4头][ IPv6头 | 数据 ]─────────────▶│
│ │
隧道入口 隧道出口
(封装) (解封装)
具体协议:6in4、6rd、Teredo、GRE。
📌 隧道是一个通用得多的概念:VPN(第 28 讲)、MPLS(第 22 讲)、VXLAN(数据中心)本质上都是隧道。「把一个协议的分组装进另一个协议里」是网络工程中最常用的一招。
4.3 NAT64 / DNS64
让纯 IPv6 的客户端访问纯 IPv4 的服务器:
① 客户端查询 example.com 的 AAAA 记录
② ⭐ DNS64 服务器发现没有 AAAA,只有 A 记录 93.184.216.34
③ 它【合成】一个 IPv6 地址:64:ff9b::5db8:d822
④ 客户端向这个地址发起连接
⑤ NAT64 网关识别 64:ff9b::/96 前缀,转换成 IPv4 转发出去
这是今天很多移动运营商采用的方案——手机侧完全是 IPv6,出口做 NAT64。
4.4 为什么部署这么慢 ⭐
IPv6 标准化于 1998 年,至今(近三十年)全球部署率仍未过半。原因值得深思:
1️⃣ 没有向后兼容
⚠️ 这是最根本的错误。IPv6 主机不能直接和 IPv4 主机通信。如果 IPv6 被设计成 IPv4 的超集,部署会容易得多。
2️⃣ 网络效应的鸡蛋问题
ISP: 「没有内容用 IPv6,我为什么要投入?」
内容商: 「没有用户用 IPv6,我为什么要支持?」
3️⃣ NAT 缓解了痛点(第 18 讲)
NAT 让地址短缺不再是紧急问题。没有紧急性,就没有动力。
4️⃣ 迁移成本落在网络运营者,收益归于整个生态
典型的正外部性问题:投入的人不是主要受益的人。
📌 这是一个关于「协议僵化」(第 6 讲)最深刻的案例:沙漏的窄腰(第 4 讲)带来了可扩展性,代价是窄腰本身几乎无法更换。HTTP 从 1.1 到 3 只用了几年,IP 从 4 到 6 用了三十年还没完成——层次越低,改变越难。
五、泛化转发与 SDN 数据平面
5.1 传统转发的局限
传统 IP 路由器:
匹配:只看【目的 IP 地址】
动作:只有【转发到某个接口】或【丢弃】
但现实中的网络设备做的远不止这些:
| 设备 | 实际在做什么 |
|---|---|
| 交换机 | 按目的 MAC 转发 |
| 防火墙 | 按 IP + 端口 + 协议决定放行或丢弃 |
| NAT | 改写 IP 和端口 |
| 负载均衡器 | 按某种策略改写目的地址 |
这些都是"匹配某些字段,然后做某件事"。能不能统一?
5.2 「匹配 + 动作」抽象 ⭐
OpenFlow 给出的答案:
流表(Flow Table)中每一条规则:
┌──────────────┬────────────┬──────────┐
│ 匹配(Match) │ 动作(Action)│ 计数器 │
└──────────────┴────────────┴──────────┘
可匹配的字段跨越了多个层:
链路层: 入端口、源 MAC、目的 MAC、以太网类型、VLAN ID、VLAN 优先级
网络层: 源 IP、目的 IP、协议号、ToS
传输层: 源端口、目的端口
⭐ 注意这个抽象打破了分层——一条规则可以同时匹配第 2、3、4 层的字段。这是有意为之:网络设备本来就在跨层工作,抽象应当承认这个现实。
可执行的动作:
FORWARD 转发到指定端口(可以是多个 = 多播)
DROP 丢弃
MODIFY ⭐ 改写头部字段(源/目的 IP、MAC、端口、VLAN、TTL)
ENCAP 封装并发送给控制器
METER 限速
匹配字段支持通配符,多条规则匹配时按优先级选择。
5.3 一张流表实现四种设备 ⭐
这是本讲最重要的例题,考试常考。
① 实现一台 IP 路由器
匹配: 目的 IP = 51.6.0.8/16
动作: forward(2)
② 实现一台二层交换机
匹配: 目的 MAC = 22:A7:23:11:E1:02
动作: forward(3)
③ 实现一个防火墙
匹配: 源 IP = 128.119.1.1, 目的端口 = 22
动作: drop ← 禁止某主机 SSH
匹配: 目的 IP = 10.0.0.5, 传输协议 = TCP, 目的端口 = 80
动作: forward(1) ← 只放行 Web
④ 实现一个负载均衡器
匹配: 目的 IP = 10.0.0.100(虚拟 IP), 源 IP = 10.1.*.*
动作: 改写目的 IP → 10.0.0.11, forward(2)
匹配: 目的 IP = 10.0.0.100, 源 IP = 10.2.*.*
动作: 改写目的 IP → 10.0.0.12, forward(3)
🎯 同一个抽象、同一张表、同一套硬件,实现了四种传统上完全不同的设备。 这就是 SDN 的核心洞察:网络设备的差异不在硬件,而在配置。
5.4 这带来了什么
- 可编程性:新的网络功能不需要新硬件,只需下发新流表
- 集中控制:全局视角的路由/策略决策(第 22 讲)
- 设备解耦:白盒交换机 + 开源控制器,打破厂商锁定
- ⚠️ 新的风险:控制器成为单点(实际部署需要控制器集群);流表容量受 TCAM 限制(第 17 讲)
5.5 中间盒(Middlebox)
⚠️ 一个必须承认的现实:今天的互联网上,中间盒的数量与路由器相当。
NAT、防火墙、IDS/IPS、负载均衡器、WAN 优化器、
TLS 终结器、缓存代理、DPI 设备……
它们全都在违反端到端原则(第 4 讲)。
📌 这是理论与现实的鸿沟:教科书上的互联网是「哑网络 + 智能端点」,真实的互联网是「布满了各种中间盒的网络」。协议僵化正是中间盒造成的——它们对协议做了各种假设,任何偏离这些假设的新协议都会被丢弃。QUIC 之所以要加密几乎所有头部(第 6 讲),一部分原因就是不让中间盒有机会做假设。
六、例题(Worked Example)
题目:某数据中心用一台 OpenFlow 交换机同时承担多项功能。请为以下需求各写一条流表规则(按优先级从高到低排列),并说明为什么顺序重要。
(a) 丢弃所有来自 192.0.2.66 的流量(该主机被判定为攻击源)
(b) 把发往虚拟 IP 10.0.0.100:80 的 HTTP 流量均分到 10.0.1.10 和 10.0.1.11
(c) 把发往 10.0.2.0/24 的流量从端口 4 转发出去
(d) 其余所有流量交给默认网关(端口 1)
解答:
优先级 100 匹配: 源IP = 192.0.2.66
动作: DROP
优先级 80 匹配: 目的IP = 10.0.0.100, 协议 = TCP, 目的端口 = 80,
源IP 末位为偶数
动作: 改写目的IP → 10.0.1.10, FORWARD(2)
优先级 80 匹配: 目的IP = 10.0.0.100, 协议 = TCP, 目的端口 = 80,
源IP 末位为奇数
动作: 改写目的IP → 10.0.1.11, FORWARD(3)
优先级 50 匹配: 目的IP = 10.0.2.0/24
动作: FORWARD(4)
优先级 0 匹配: *(全通配)
动作: FORWARD(1)
为什么顺序(优先级)重要:
⭐ **多条规则可能同时匹配同一个分组。**例如一个来自 192.0.2.66、发往 10.0.0.100:80 的分组,同时匹配 (a) 和 (b)。
若 (b) 优先级更高,攻击流量会被负载均衡到后端服务器——安全策略完全失效。
📌 **通用原则:安全/丢弃规则的优先级必须高于业务转发规则。**这与防火墙规则表的编写规则完全一致,也与第 17 讲的最长前缀匹配是同一类问题——当多条规则匹配时,必须有明确且正确的仲裁规则。
关于负载均衡的实现说明:按「源 IP 的奇偶」分流是一种一致性哈希的简化形式。它保证同一个客户端的所有连接都落到同一台后端(会话保持),这对有状态的服务是必需的。若用轮转(round-robin),同一用户的不同请求会落到不同后端,登录态会丢失。
七、随堂自测
- IPv6 头部去掉了哪三样东西?各自的理由是什么?
- 压缩这个地址:
2001:0db8:0000:0000:0001:0000:0000:0001。为什么::只能用一次? - IPv6 为什么取消了广播?用什么替代?
- 什么是任播?它在本课程中出现在哪两个场景?
- 双栈和隧道各自解决什么问题?各自的代价是什么?
- 说出 IPv6 部署缓慢的四条原因,并指出哪一条是最根本的设计失误。
- 泛化转发的抽象是什么?它为什么要打破分层?
- 用一条 OpenFlow 规则实现「禁止 10.0.0.0/8 访问外网 25 端口(禁止发垃圾邮件)」。
八、本讲要点回顾
- IPv6:128 位地址,固定 40 字节头部,去掉了校验和、分片、可变选项。
- ⭐ 更大的地址,更简单的头部——因为 IPv4 的一半字段是历史包袱。
- 没有广播,用多播替代;任播让多个节点共享一个地址(根 DNS、CDN 用它)。
- SLAAC 让主机无需 DHCP 自动配址;NDP 跑在 ICMPv6 上,所以不能屏蔽 ICMPv6。
- 过渡:双栈(成本翻倍)、隧道(通用技巧)、NAT64/DNS64(移动网主流)。
- ⭐ IPv6 最根本的设计失误是不向后兼容;加上 NAT 缓解了紧迫性,导致三十年仍未完成迁移。
- 泛化转发 = 匹配(跨层字段)+ 动作。一张流表可实现路由器、交换机、防火墙、负载均衡器。
- 中间盒无处不在,它们是协议僵化的直接原因。
九、自测答案
1. ① 头部校验和——传输层和链路层都在做差错检测,而 IPv4 因 TTL 变化需要每跳重算,纯属浪费。② 分片与重组字段——改由源主机通过 Path MTU Discovery 保证不超限,路由器遇到过大分组直接回 ICMPv6 “Packet Too Big”,避免了分片带来的性能与安全问题。③ 可变长选项字段——移入「扩展首部」链,使基本首部固定为 40 字节,便于硬件流水线处理。
2. 2001:db8::1:0:0:1。(去前导零后为 2001:db8:0:0:1:0:0:1,只能压缩其中一处连续零组。):: 只能用一次,是因为它表示「这里有若干组全零」——如果出现两次,解析器无法确定每一处各代表几组零,地址将产生歧义。
3. 因为广播会中断链路上每一台设备的处理(包括完全不关心该报文的设备),在无线网络中还会消耗所有设备的电量、占用共享信道。多播只送达显式加入该组的节点,例如 ff02::1 表示本链路所有节点、ff02::2 表示本链路所有路由器——需要"广播"效果时用前者即可,但默认情况下不打扰无关设备。
4. 任播是让多个物理节点通告同一个 IP 地址,路由协议(BGP)自然会把每个客户端的分组送到拓扑上"最近"的那个节点。本课程中它出现在:① 根 DNS 服务器——13 个逻辑标识背后是 1500+ 个任播实例(第 7 讲);② CDN 节点选择——除 DNS 方案外,部分 CDN 直接用任播 IP 吸引流量到最近边缘(第 6 讲)。
5. 双栈解决「同一台设备如何同时与 IPv4 和 IPv6 对端通信」,代价是要维护两套完整的网络配置与运维,且仍然消耗 IPv4 地址。隧道解决「两个 IPv6 孤岛之间隔着 IPv4 网络」,代价是封装带来的额外头部开销、MTU 减小(容易触发分片问题)、以及排障困难(问题可能出在隧道内层或外层)。
6. ① 不向后兼容——IPv6 主机不能直接与 IPv4 主机通信;② 网络效应的鸡蛋问题——ISP 与内容商互相等待;③ NAT 缓解了地址短缺的紧迫性,消除了迁移动力;④ 成本与收益错配——迁移成本由网络运营者承担,收益扩散到整个生态(正外部性)。最根本的设计失误是第 ①条:如果 IPv6 被设计成 IPv4 的超集(让 IPv6 节点能直接与 IPv4 节点互通),部署会是渐进的而非全有全无的。
7. 抽象是 「匹配(Match)+ 动作(Action)」:一条规则指定要匹配哪些头部字段的值(支持通配符),以及匹配后执行什么动作(转发、丢弃、改写、送控制器、限速),多条规则按优先级仲裁。它打破分层,是因为真实的网络设备本来就在跨层工作——防火墙要同时看 IP 和端口,负载均衡器要改写 IP 又要看 TCP 状态。与其假装分层完整再让每种设备各自违反它,不如给出一个诚实地承认跨层需求的统一抽象。
8.
优先级 100 匹配: 源IP = 10.0.0.0/8, 协议 = TCP, 目的端口 = 25
动作: DROP
(实践中还应有一条更高优先级的例外规则,放行组织自己的邮件服务器;这再次说明了规则顺序的重要性。)