Appearance
IPv6 与组播
概念
IPv6(Internet Protocol version 6)用 128 位地址取代 IPv4 的 32 位,并顺手把 IPv4 那些"历史包袱"——首部检验和、路由器分片、可变长首部——一并砍掉;组播(multicast)指"一份数据发给一组特定的接收者",IPv4 用 D 类地址 224.0.0.0/4 表达,IPv6 用 ff00::/8。
一句话说清两者的关系:IPv6 把"地址不够"这个催生 NAT 的问题从根上解决;而"取消广播、全面改为组播"是它顺手做的一件更大的事——因为广播会打断全网每一台主机,组播只打扰订阅者。
★ 本章要解决的三件事:
| 问题 | 对应内容 |
|---|---|
| 128 位地址怎么写、怎么压缩 | 冒号十六进制 + :: 零压缩(只能用一次) |
| 首部长什么样、砍掉了什么 | 固定 40 B、8 个字段;取消检验和与路由器分片 |
| 组播怎么编址、怎么管理 | 224.0.0.0/4 与常用地址清单、IGMP |
与上一章的衔接:上一章(net/33-routing.md)说"OSPF 用 IP 协议号 89"——IPv6 版是 OSPFv3,它不再用协议号,改用 IPv6 的"下一个首部"字段表达;而上一章讲的路由协议为 IPv6 重写的版本(OSPFv3、RIPng)也都在本章末尾的过渡技术里出现。
原理
一、地址表示:冒号十六进制与 :: 零压缩
IPv6 地址 128 位,写成 8 组、每组 4 位十六进制、用冒号隔开:
text
完整形式 (8 组 x 4 位十六进制 = 32 位十六进制 = 128 位):
2001:0db8:0000:0000:0000:ff00:0042:8329
└──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘
16 位 x 8 = 128 位
★ 两条简化规则:
① 每组的前导 0 可以省略: 0db8 -> db8, 0042 -> 42, 0000 -> 0
② 连续的全 0 组可以用 "::" 代替 (★ 整个地址里只能出现一次!)★ 同一地址的四种写法(按压缩程度递增):
| 写法 | 结果 |
|---|---|
| 完整 | 2001:0db8:0000:0000:0000:ff00:0042:8329 |
| 省前导 0 | 2001:db8:0:0:0:ff00:42:8329 |
:: 压缩 | 2001:db8::ff00:42:8329 |
| 再省一组内的 0 | 同上(这一组里没有可再省的了) |
⚠️
::只能用一次,这是硬约束(最常考的细节):因为::的语义是"这里省略了若干个全 0 组",而"若干个"是几,只能由"总组数必须是 8"倒推出来。 若出现两次,就变成两个未知数、无法还原。 所以2001::1::2是非法地址——解析器根本不知道该在第一个::和第二个::各补几组 0。⚠️ 另一条约束:
::压缩的必须是"长度 ≥ 2 的连续全 0 段"——2001:db8:0:1:0:0:0:1里的中间 3 个 0 组可以压成2001:db8:0:1::1,但单个0组不能写成::(2001:db8::1:8329表示省略了 5 组,等价于2001:db8:0:0:0:0:0:1:8329——组数已经超了)。 考试里只要数一数"压缩后还剩几组、::应补几组"就能判对错。
★ 前缀表示法与 IPv4 嵌入:
| 写法 | 含义 |
|---|---|
2001:db8::/32 | CIDR 前缀,与 IPv4 的 /n 完全同义(见 net/31-subnet.md) |
::ffff:192.168.1.1 | IPv4 映射地址(前 80 位为 0、接着 16 位全 1、再嵌 32 位 IPv4)——用于 IPv4/IPv6 共存 |
::192.168.1.1 | IPv4 兼容地址(已废弃,历史写法) |
::1/128 | 环回地址(相当于 IPv4 的 127.0.0.1) |
::/128 | 未指定地址(相当于 IPv4 的 0.0.0.0,只能作源地址) |
二、首部:固定 40 B,只留 8 个字段
★ 首部结构(必背,8 个字段恰好 40 B):
text
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
┌───────┬───────────────────────────┬───────────────────────────┐
│ 版本 │ 通信量类 │ 流标号 │ <- 4 B
│ 4 位 │ 8 位 │ 20 位 │
├───────┴───────────┬───────────────┼───────────────────────────┤
│ 有效载荷长度 │ 下一个首部 │ 跳数限制 │ <- 4 B
│ 16 位 │ 8 位 │ 8 位 │
├───────────────────┴───────────────┴───────────────────────────┤
│ 源地址 │ <- 16 B
│ 128 位 │
├───────────────────────────────────────────────────────────────┤
│ 目的地址 │ <- 16 B
│ 128 位 │
└───────────────────────────────────────────────────────────────┘
★ 8 个字段, 位数 4 + 8 + 20 + 16 + 8 + 8 + 128 + 128 = 320 位 = 40 B
★ 固定 40 B —— 没有"首部长度"字段, 也永远没有选项挤在首部里★ 与 IPv4 首部的对照(这张表是本节的落点):
| 项 | IPv4 | IPv6 | 变化的意义 |
|---|---|---|---|
| 地址长度 | 32 位 | 128 位 | 地址空间从 |
| 首部长度 | 固定 20 B + 可变选项(最长 60 B) | 固定 40 B | 取消"首部长度"字段,路由器解析更快 |
| 字段数 | 12 个 + 选项 | 8 个 | 少了 4 个字段 |
| 首部检验和 | 有(每跳重算) | ★ 取消 | 路由器少算一次,提速 |
| 分片 | 路由器可以分片 | ★ 路由器不分片,只在源端分 | 中间路由器负担下降 |
| 标识 / 标志 / 片偏移 | 三个字段都在首部 | 取消,改由扩展首部承载 | 首部瘦身 |
| TTL | 8 位 | 改名"跳数限制",仍 8 位 | 只是换了个更准确的名字 |
| 协议 | 8 位,值如 6 = TCP | 改名"下一个首部",仍 8 位 | 可指向扩展首部,串成链 |
| 地址类型 | 单播 / 广播 / 组播 | 单播 / 组播 / 任播(★ 无广播) | 见本节第三部分 |
| 选项 | 在首部里(导致首部变长) | 移出到扩展首部 | 首部恒定 40 B |
★ 取消首部检验和的三条理由(常考简答):
| 理由 | 说明 |
|---|---|
| ① 链路层已经查过了 | 以太网的 FCS 已经做了 CRC 校验,上层再查一遍收益有限(见 net/21-error.md 与 net/24-ethernet.md) |
| ② 传输层也有校验和 | TCP / UDP 的伪首部覆盖了地址与端口,端到端那一层兜得住 |
| ③ 路由器每跳都要重算,代价太高 | IPv4 的检验和每跳都变(因为 TTL 要减 1)——把它砍掉,路由器只需看几个字段就能转发 |
⚠️ "取消路由器分片"要理解成"改由源端做"而不是"不能分片":IPv6 要求源主机先做"路径 MTU 发现",按整条路径上最小的 MTU 决定报文大小;若确实需要分片,则用"分片扩展首部",由源主机自己切——中间路由器遇到太大的报文不是分片,而是回一个 ICMPv6 "Packet Too Big" 差错报文(这恰好对应 IPv4 的类型 3 代码 4"需要分片但 DF 置 1")。 所以 IPv4 的"路由器分片 + 目的主机重组"在 IPv6 里变成了"源主机分片 + 目的主机重组"。
三、地址类型:单播、组播、任播(没有广播)
★ 三类地址:
| 类型 | 含义 | 对应 IPv4 的什么 |
|---|---|---|
| 单播(unicast) | 一对一 | 就是 IPv4 的单播 |
| 组播(multicast) | 一对"一组"(只有订阅者收) | IPv4 的组播(D 类) |
| 任播(anycast) | 一对"最近的一个"(同地址配在多台机器上,路由把包交给拓扑最近的那台) | ★ IPv4 里没有的正式类型 |
| (无广播) | — | IPv4 的广播在 IPv6 里被取消 |
★ "取消广播"的动因(一句话):广播会强制中断本地链路上所有主机的 CPU 去处理这个包——在 IPv4 里,一个 ARP 广播就是一次"全网打扰";IPv6 换成"组播"后,没订阅的主机连包都不收,网卡层面就过滤掉了。 这也是"IPv6 全面用组播替代广播"的根本理由(工程收益远大于地址空间本身)。
★ 常用前缀(必背清单):
| 前缀 | 名称 | 说明 |
|---|---|---|
2000::/3 | 全球单播地址(GUA) | 占整个地址空间的 1/8,公网可路由,相当于 IPv4 的公网地址 |
fe80::/10 | 链路本地地址 | 每个接口自动生成,只在本地链路有效,路由器不转发(邻居发现、OSPFv3 的 HELLO 都用它) |
fc00::/7 | 唯一本地地址(ULA) | 不路由到公网,相当于 IPv4 的私有地址 |
ff00::/8 | 组播地址 | 相当于 IPv4 的 224.0.0.0/4 |
::1/128 | 环回 | 相当于 127.0.0.1 |
::/128 | 未指定 | 相当于 0.0.0.0,只能作源地址 |
⚠️ "链路本地地址"是 IPv6 最有特色的设计:任何接口只要一启用 IPv6,就会自动有一个
fe80::/10的地址(由接口标识符推出,不需要 DHCP、不需要手动配)——所以"两台直连的主机不用配任何地址就能互通"。 这也是 IPv6 里"邻居发现(ND)取代 ARP"的基础。⚠️ 一个常考的细节:
/64是 IPv6 子网的标准长度——一个子网里能放个地址,所以 IPv6 基本不需要"借位划分子网"那套(对比 net/31-subnet.md的"精打细算",这是两个时代的气质差异)。
四、组播:IPv4 的地址与 IGMP
★ 组播地址的范围:IPv4 的 D 类地址 224.0.0.0 ~ 239.255.255.255(首位 1110)——它就是"组播地址",没有"网络号/主机号"之分,整个 28 位都用来标识"组"。
★ 常用组播地址清单(必背几个):
| 地址 | 用途 |
|---|---|
224.0.0.1 | 本网段所有主机("所有主机组") |
224.0.0.2 | 本网段所有路由器 |
224.0.0.5 / 224.0.0.6 | OSPF 的 AllSPFRouters / AllDRouters |
224.0.0.9 | RIPv2 |
224.0.0.251 | mDNS(局域网设备名解析,Bonjour 用的就是它) |
224.0.0.252 | LLMNR(微软的本地名字解析) |
239.0.0.0/8 | 管理范围组播(类似私有地址,可在内网自由划分) |
⚠️
224.0.0.0/24这一段是"本地链路范围":它只在本地网段有效,路由器一概不转发——这也是为什么 OSPF、RIP 的组播地址都落在这段(协议本身就只在本地链路跑)。 若要做跨网段的组播,需要239.x.x.x这类"可路由组播地址" + 组播路由协议(PIM 等)。
★ IGMP(Internet Group Management Protocol,网际组管理协议)管的是"主机与本地路由器之间"的组成员关系(注意:它只管本地,不管跨网段的组播路由):
| 版本 | 增补的能力 |
|---|---|
| IGMPv1 | 最基本的"查询—响应" |
| IGMPv2 | 增加"离开组"报文与"特定组查询"(缩短离开延迟) |
| IGMPv3 | 支持"源过滤"(指定只接收(或不接收)某些源发来的组播) |
★ 组播里"谁做什么"的分工:
text
主机 -> 路由器: "我要加入组 G" / "我要离开组 G" <- IGMP 管这一段
路由器 <-> 路由器: "我这里有人要组 G 的流量" <- 组播路由协议 (PIM 等) 管这一段
主机收包: 网卡按组播 MAC 过滤, 没订阅的包根本不交给 CPU★ IPv6 的组播对应物:
| IPv6 组播地址 | 用途 |
|---|---|
ff02::1 | 所有节点(相当于 224.0.0.1) |
ff02::2 | 所有路由器 |
ff02::5 / ff02::6 | OSPFv3 的 AllSPFRouters / AllDRouters |
ff02::9 | RIPng |
ff02::1:ff00:0/104 | 被请求节点组播地址(邻居发现用它,取代 IPv4 的 ARP 广播) |
⚠️ IPv6 里"组成员管理"由 MLD 承担:MLD(Multicast Listener Discovery)是 ICMPv6 的一个子协议——也就是说,在 IPv4 里分属 ICMP 与 IGMP 的职责,在 IPv6 里被合并进 ICMPv6。 这条"IPv6 把 ICMP + ARP + IGMP 三者归并到 ICMPv6"的说法,是常考的一句话总结。
五、过渡技术:三条路
IPv6 不可能一夜替换 IPv4(全球几十亿设备),所以必须有共存方案:
| 技术 | 做法 | 局限 |
|---|---|---|
| 双协议栈(dual stack) | 主机/路由器同时装 IPv4 与 IPv6 两套协议栈,按目的地址选 | 要两套地址与两套路由,运维成本高(但最干净) |
| 隧道(tunneling) | 把 IPv6 报文整个封进 IPv4 数据报里穿过 IPv4 网络(6to4、ISATAP 等) | MTU 变小、故障定位难 |
| 翻译(translation) | 在边界做协议转换(NAT64 / SIIT / DNS64) | 把 IPv4 里 NAT 的毛病又带进来了(见 net/32-nat.md) |
⚠️ 记住一句话就够:"双栈是正路、隧道是过渡、翻译是妥协"——三种方案在考试里只需知道"名字 + 一句话做法 + 主要缺点"。
示例
例 1:C 实现——IPv6 地址格式化与零压缩
参数:5 个 IPv6 地址(含"全 0"边界情形)。
#include <stdio.h>
/* 未压缩形式: 8 组 x 4 位十六进制 */
void fmt_full(const unsigned short *g, char *out) {
int i, k = 0;
for (i = 0; i < 8; i++) {
if (i > 0) out[k++] = ':';
k += sprintf(out + k, "%04x", g[i]);
}
out[k] = '\0';
}
/* 零压缩形式: 找最长的连续全 0 段 (长度 >= 2), 用 "::" 代替 */
void fmt_short(const unsigned short *g, char *out) {
int i, best = -1, bestlen = 0, k = 0;
for (i = 0; i < 8; ) {
if (g[i] == 0) {
int j = i;
while (j < 8 && g[j] == 0) j++;
if (j - i > bestlen) { bestlen = j - i; best = i; }
i = j;
} else {
i++;
}
}
if (bestlen < 2) { bestlen = 0; best = -1; } /* 单组 0 不压缩 */
for (i = 0; i < 8; ) {
if (best >= 0 && i == best) {
out[k++] = ':'; /* "::" 的两个冒号: 前一个兼作分隔符 */
out[k++] = ':';
i += bestlen;
} else {
if (k > 0 && out[k - 1] != ':') out[k++] = ':';
k += sprintf(out + k, "%x", g[i]);
i++;
}
}
out[k] = '\0';
}
int main(void) {
unsigned short a[5][8] = {
{0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0xff00, 0x0042, 0x8329},
{0xfe80, 0x0000, 0x0000, 0x0000, 0x0202, 0xb3ff, 0xfe1e, 0x8329},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x2001, 0x0db8, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001}
};
char full[64], shortf[64];
int i;
printf("① 完整形式 -> 零压缩形式\n");
for (i = 0; i < 5; i++) {
fmt_full(a[i], full);
fmt_short(a[i], shortf);
printf(" %s\n -> %s\n", full, shortf);
}
printf("\n");
printf("② 首部 8 个字段的位数账\n");
printf(" 版本 4 + 通信量类 8 + 流标号 20 + 有效载荷长度 16\n");
printf(" + 下一个首部 8 + 跳数限制 8 + 源地址 128 + 目的地址 128\n");
printf(" = %d 位 = %d B\n", 4 + 8 + 20 + 16 + 8 + 8 + 128 + 128,
(4 + 8 + 20 + 16 + 8 + 8 + 128 + 128) / 8);
printf(" ★ 固定 40 B, 没有\"首部长度\"字段\n");
printf("\n");
printf("③ IPv4 与 IPv6 首部对照\n");
printf(" %-22s %-18s %s\n", "ITEM", "IPv4", "IPv6");
printf(" %-22s %-18s %s\n", "address bits", "32", "128");
printf(" %-22s %-18s %s\n", "header(no option)", "20 B", "40 B");
printf(" %-22s %-18s %s\n", "fields", "12 + options", "8");
printf(" %-22s %-18s %s\n", "header checksum", "yes", "no");
printf(" %-22s %-18s %s\n", "router fragmentation", "yes", "no");
printf(" %-22s %-18s %s\n", "address kinds", "uni/broad/multi", "uni/multi/any");
return 0;
}
c 本站为静态站,不提供在线运行;可复制到本地用 gcc / python 执行
预期输出:
text
① 完整形式 -> 零压缩形式
2001:0db8:0000:0000:0000:ff00:0042:8329
-> 2001:db8::ff00:42:8329
fe80:0000:0000:0000:0202:b3ff:fe1e:8329
-> fe80::202:b3ff:fe1e:8329
0000:0000:0000:0000:0000:0000:0000:0001
-> ::1
0000:0000:0000:0000:0000:0000:0000:0000
-> ::
2001:0db8:0000:0001:0000:0000:0000:0001
-> 2001:db8:0:1::1
② 首部 8 个字段的位数账
版本 4 + 通信量类 8 + 流标号 20 + 有效载荷长度 16
+ 下一个首部 8 + 跳数限制 8 + 源地址 128 + 目的地址 128
= 320 位 = 40 B
★ 固定 40 B, 没有"首部长度"字段
③ IPv4 与 IPv6 首部对照
ITEM IPv4 IPv6
address bits 32 128
header(no option) 20 B 40 B
fields 12 + options 8
header checksum yes no
router fragmentation yes no
address kinds uni/broad/multi uni/multi/any⚠️ 三点说明:
- 本机无 C 编译器:此段代码逐行人工审查,并用等价的 Python 实现实跑核对,输出逐字一致;C 段表头一律用 ASCII(
printf按字节补齐,中文表头必错位)。 if (bestlen < 2) { bestlen = 0; best = -1; }这一行不能省:单个全 0 组压缩成::会让"组数"信息丢失——2001:db8:0:1:0:0:0:1中间那 3 组才值得压;若把单独的0也写成::,还原时会补错组数。0x2001:0db8:0:1::1这个例子的价值:它说明"::压缩的是最长的连续全 0 段"——该地址有两段全 0(第 3 组单独一个 0、第 5~7 组三个 0),程序选了较长的那段(标准也规定"优先压最长的一段")。
例 2:Python——地址规范化、类型判别与地址空间账
import ipaddress
def pad(s, w):
"""按"显示宽度"补空格: 汉字算 2 列, 否则终端里对不齐"""
return s + ' ' * max(0, w - sum(2 if ord(c) > 0x2000 else 1 for c in s))
print('=== ① 地址压缩规范化 (Python 标准库自动择最长零段) ===')
samples = ['2001:0db8:0000:0000:0000:ff00:0042:8329',
'fe80:0000:0000:0000:0202:b3ff:fe1e:8329',
'0000:0000:0000:0000:0000:0000:0000:0001',
'0000:0000:0000:0000:0000:0000:0000:0000',
'2001:0db8:0000:0001:0000:0000:0000:0001']
for s in samples:
a = ipaddress.IPv6Address(s)
print(' ' + pad(s, 44) + '-> ' + a.compressed)
print(' ★ "::" 全地址只能出现一次, 因为"省略了几组"只能由总组数 = 8 倒推')
print(' ★ 非法示例: 2001::1::2 —— 两个 :: 无法还原')
print()
print('=== ② 地址类型判别 ===')
kinds = [('2000::/3', '全球单播 GUA', '公网可路由, 占整个空间 1/8'),
('fe80::/10', '链路本地', '每接口自动生成, 路由器不转发'),
('fc00::/7', '唯一本地 ULA', '不路由到公网, 相当于私有地址'),
('ff00::/8', '组播', '相当于 IPv4 的 224.0.0.0/4'),
('::1/128', '环回', '相当于 127.0.0.1'),
('::/128', '未指定', '只能作源地址, 相当于 0.0.0.0')]
print(' ' + pad('地址', 30) + pad('匹配前缀', 16) + pad('类型', 16) + '说明')
for ip_s in ['2001:db8::1', 'fe80::202:b3ff:fe1e:8329', 'fc00::1', 'ff02::2',
'::1', '::']:
a = ipaddress.IPv6Address(ip_s)
best = None
for p, n, d in kinds:
net = ipaddress.IPv6Network(p)
hit = (a == net.network_address) if net.num_addresses == 1 else (a in net)
if hit and (best is None or net.prefixlen > best[0].prefixlen):
best = (net, n, d)
if best is None:
print(' ' + pad(ip_s, 30) + pad('—', 16) + pad('无匹配', 16) + '—')
else:
print(' ' + pad(ip_s, 30) + pad(str(best[0]), 16) + pad(best[1], 16) + best[2])
print(' ★ 一个地址可能落在多个前缀里 -> 按最长前缀判')
print()
print('=== ③ 地址空间账 ===')
v6 = 2 ** 128
v4 = 2 ** 32
earth_m2 = 5.10072e14 # 地球表面积 (平方千米 x 1e6)
people = 8.0e9
print(' IPv6 地址总数 = 2^128 = %d' % v6)
print(' ≈ %.4f x 10^38' % (v6 / 1e38))
print(' IPv4 地址总数 = 2^32 = %d (≈ %.2f 亿)' % (v4, v4 / 1e8))
print(' 倍数 = 2^128 / 2^32 = 2^96 ≈ %.4f x 10^28' % (v6 / v4 / 1e28))
print(' 地球表面积 %.2f x 10^14 平方米 -> 每平方米 %.4f x 10^23 个地址'
% (earth_m2 / 1e14, v6 / earth_m2 / 1e23))
print(' 全球 %.0f 亿人 -> 每人 %.4f x 10^28 个地址' % (people / 1e9, v6 / people / 1e28))
print(' 一个 /64 子网 = 2^64 = %d 个地址 (IPv6 的标准子网长度)' % (2 ** 64))
print(' 可划分的 /64 子网数 = 2^64 = %.4f x 10^19 个' % (2 ** 64 / 1e19))
print(' ★ 对比 net/31-subnet.md 里的"精打细算借位": IPv6 完全不必省')
print()
print('=== ④ 组播地址常用清单 ===')
v4m = [('224.0.0.1', '本网段所有主机'), ('224.0.0.2', '本网段所有路由器'),
('224.0.0.5', 'OSPF AllSPFRouters'), ('224.0.0.6', 'OSPF AllDRouters'),
('224.0.0.9', 'RIPv2'), ('224.0.0.251', 'mDNS'), ('224.0.0.252', 'LLMNR'),
('239.0.0.0/8', '管理范围组播 (类私有, 可路由)')]
v6m = [('ff02::1', '所有节点'), ('ff02::2', '所有路由器'),
('ff02::5', 'OSPFv3 AllSPFRouters'), ('ff02::6', 'OSPFv3 AllDRouters'),
('ff02::9', 'RIPng'), ('ff02::1:ff00:0/104', '被请求节点组播 (取代 ARP)')]
print(' ' + pad('IPv4 地址', 20) + pad('用途', 34) + 'IPv6 地址')
for i in range(max(len(v4m), len(v6m))):
l = v4m[i] if i < len(v4m) else ('', '')
r = v6m[i] if i < len(v6m) else ('', '')
print(' ' + pad(l[0], 20) + pad(l[1], 34) + r[0])
print(' ★ 224.0.0.0/24 只在本地链路有效, 路由器不转发 -> 正好适合 OSPF/RIP')
print()
print('=== ⑤ 过渡技术三条路 ===')
for a, b, c in [('双协议栈', '同时装 IPv4 与 IPv6 两套栈', '最干净, 但两套地址与路由'),
('隧道', '把 IPv6 整体封进 IPv4 里穿过', 'MTU 变小, 故障定位难'),
('翻译 (NAT64)', '边界做协议转换', '又把 NAT 的毛病带回来了')]:
print(' ' + pad(a, 16) + pad(b, 34) + c)
print()
print('=== ⑥ IPv6 取消检验和与路由器分片的三条理由 ===')
for i, s in enumerate(['链路层已有 CRC (以太网 FCS), 上层再查一遍收益有限',
'传输层伪首部覆盖地址与端口, 端到端兜得住',
'IPv4 的检验和每跳都要重算 (TTL 变了), 砍掉能提速'], 1):
print(' %d. %s' % (i, s))
print(' ★ 取消路由器分片 = 改由源主机做"路径 MTU 发现" + 分片扩展首部')
print(' ★ 路由器遇到过大报文 -> 回 ICMPv6 "Packet Too Big" (对应 IPv4 的 3/4)')
python 本站为静态站,不提供在线运行;可复制到本地用 gcc / python 执行
输出对照(真实运行结果):
=== ① 地址压缩规范化 (Python 标准库自动择最长零段) ===
2001:0db8:0000:0000:0000:ff00:0042:8329 -> 2001:db8::ff00:42:8329
fe80:0000:0000:0000:0202:b3ff:fe1e:8329 -> fe80::202:b3ff:fe1e:8329
0000:0000:0000:0000:0000:0000:0000:0001 -> ::1
0000:0000:0000:0000:0000:0000:0000:0000 -> ::
2001:0db8:0000:0001:0000:0000:0000:0001 -> 2001:db8:0:1::1
★ "::" 全地址只能出现一次, 因为"省略了几组"只能由总组数 = 8 倒推
★ 非法示例: 2001::1::2 —— 两个 :: 无法还原
=== ② 地址类型判别 ===
地址 匹配前缀 类型 说明
2001:db8::1 2000::/3 全球单播 GUA 公网可路由, 占整个空间 1/8
fe80::202:b3ff:fe1e:8329 fe80::/10 链路本地 每接口自动生成, 路由器不转发
fc00::1 fc00::/7 唯一本地 ULA 不路由到公网, 相当于私有地址
ff02::2 ff00::/8 组播 相当于 IPv4 的 224.0.0.0/4
::1 ::1/128 环回 相当于 127.0.0.1
:: ::/128 未指定 只能作源地址, 相当于 0.0.0.0
★ 一个地址可能落在多个前缀里 -> 按最长前缀判
=== ③ 地址空间账 ===
IPv6 地址总数 = 2^128 = 340282366920938463463374607431768211456
≈ 3.4028 x 10^38
IPv4 地址总数 = 2^32 = 4294967296 (≈ 42.95 亿)
倍数 = 2^128 / 2^32 = 2^96 ≈ 7.9228 x 10^28
地球表面积 5.10 x 10^14 平方米 -> 每平方米 6.6713 x 10^23 个地址
全球 8 亿人 -> 每人 4.2535 x 10^28 个地址
一个 /64 子网 = 2^64 = 18446744073709551616 个地址 (IPv6 的标准子网长度)
可划分的 /64 子网数 = 2^64 = 1.8447 x 10^19 个
★ 对比 net/31-subnet.md 里的"精打细算借位": IPv6 完全不必省
=== ④ 组播地址常用清单 ===
IPv4 地址 用途 IPv6 地址
224.0.0.1 本网段所有主机 ff02::1
224.0.0.2 本网段所有路由器 ff02::2
224.0.0.5 OSPF AllSPFRouters ff02::5
224.0.0.6 OSPF AllDRouters ff02::6
224.0.0.9 RIPv2 ff02::9
224.0.0.251 mDNS ff02::1:ff00:0/104
224.0.0.252 LLMNR
239.0.0.0/8 管理范围组播 (类私有, 可路由)
★ 224.0.0.0/24 只在本地链路有效, 路由器不转发 -> 正好适合 OSPF/RIP
=== ⑤ 过渡技术三条路 ===
双协议栈 同时装 IPv4 与 IPv6 两套栈 最干净, 但两套地址与路由
隧道 把 IPv6 整体封进 IPv4 里穿过 MTU 变小, 故障定位难
翻译 (NAT64) 边界做协议转换 又把 NAT 的毛病带回来了
=== ⑥ IPv6 取消检验和与路由器分片的三条理由 ===
1. 链路层已有 CRC (以太网 FCS), 上层再查一遍收益有限
2. 传输层伪首部覆盖地址与端口, 端到端兜得住
3. IPv4 的检验和每跳都要重算 (TTL 变了), 砍掉能提速
★ 取消路由器分片 = 改由源主机做"路径 MTU 发现" + 分片扩展首部
★ 路由器遇到过大报文 -> 回 ICMPv6 "Packet Too Big" (对应 IPv4 的 3/4)六条结论:
- ★ 零压缩只压"最长的一段连续全 0",且全地址只能出现一次
::——单组0不压(压了会让组数信息丢失)。 - ★ 首部固定 40 B、只有 8 个字段:
位 B。 - ★ 砍掉的两样东西最有考试价值:首部检验和(链路层与传输层已兜)与路由器分片(改由源主机做)。
- ★ 地址类型只有单播 / 组播 / 任播,没有广播——广播强制打断所有主机,组播只打扰订阅者。
- ★
fe80::/10链路本地地址是"每接口自动生成、路由器不转发"——它让"直连两台主机不配地址就能通",也是邻居发现的基础。 - ★ 地址空间账:
,是 IPv4 的 倍,每平方米约 个地址——与上一章的"精打细算借位"形成鲜明对照。
考点
考点
1. 必背结论
- ★ IPv6 地址 128 位,8 组 × 4 位十六进制,冒号分隔;前导 0 可省;连续全 0 组可用
::代替,且整个地址里只能出现一次。 - ★
::只能一次的原因:"省略了几组"必须由"总组数 = 8"唯一倒推——2001::1::2非法。 - ★
::只压长度 ≥ 2 的连续全 0 段;多段全 0 时压最长的一段。 - ★★ IPv6 首部固定 40 B、8 个字段:版本 4 + 通信量类 8 + 流标号 20 + 有效载荷长度 16 + 下一个首部 8 + 跳数限制 8 + 源地址 128 + 目的地址 128。
- ★★ IPv6 取消了三样(必背):首部检验和 / 首部长度字段 / 路由器分片(及标识、标志、片偏移)。
- ★ 取消检验和的三条理由:链路层已有 CRC / 传输层伪首部已覆盖地址与端口 / 路由器每跳重算代价高。
- ★ 取消路由器分片 ≠ 不能分片:改由源主机做"路径 MTU 发现" + 分片扩展首部;路由器超 MTU 时回 ICMPv6 "Packet Too Big"。
- ★ 地址类型:单播 / 组播 / 任播(IPv6 无广播);任播 = 一组地址中交给拓扑最近的一个。
- ★ 常用前缀:
2000::/3全球单播、fe80::/10链路本地、fc00::/7唯一本地、ff00::/8组播、::1/128环回、::/128未指定。 - ★ IPv6 的标准子网长度是
/64,一个子网 个地址——基本不需要"借位划分子网"。 - ★ IPv4 组播范围
224.0.0.0/4(D 类);常用:224.0.0.1所有主机、224.0.0.2所有路由器、224.0.0.5/6OSPF、224.0.0.9RIPv2。 - ★
224.0.0.0/24只在本地链路有效,路由器不转发;跨网段组播要用239.0.0.0/8之类的可路由地址 + 组播路由协议。 - ★ IGMP 管"主机 ↔ 本地路由器"的组成员关系(v1 基本查询响应、v2 加离开、v3 加源过滤);跨网段的组播路由由 PIM 等协议负责。
- ★ IPv6 里 ICMPv6 合并了 IPv4 的 ICMP + ARP + IGMP 三者职责;组成员管理叫 MLD;邻居发现用被请求节点组播
ff02::1:ff00:0/104取代 ARP 广播。 - ★ 三种过渡技术:双协议栈(最干净)/ 隧道(把 IPv6 封进 IPv4)/ 翻译(NAT64,把 NAT 的毛病带回来)。
- ★ 地址空间账:
,是 IPv4 的 倍。
2. 高频陷阱
- 把 IPv6 地址写成 6 组或 10 组:错。固定 8 组——每组 4 位十六进制 = 16 位,
。 - 在一个地址里写两次
:::错。只能一次——2001::1::2非法。 - 把单个全 0 组也写成
:::错。必须连续 ≥ 2 组才用::——否则还原时组数会多出来。 - 认为 IPv6 首部是"20 B + 选项,最长 60 B":错,那是 IPv4。 IPv6 首部固定 40 B,没有"首部长度"字段,选项移到扩展首部。
- 把 IPv6 首部字段数写成 12:错。是 8 个(少了首部长度、标识、标志、片偏移、首部检验和等)。
- 认为 IPv6 还有"TTL"字段:错。改名为"跳数限制",作用一样、位宽仍 8 位。
- 认为 IPv6 还有"协议"字段:错。改名为"下一个首部"(Next Header)——仍 8 位,但可以指向扩展首部,串成一条链。
- 认为 IPv6 保留了广播地址:错。取消广播,用组播替代——广播会打断链路上所有主机的 CPU。
- 把"任播"说成"一对多":错。任播是"一对最近的一个"(一对多的是组播)。
- 认为"IPv6 不再分片":错。是"路由器不分片",源主机可用分片扩展首部自己分。
- 认为"IPv6 有首部检验和":错。取消了——靠链路层 CRC 与传输层校验和兜住。
- 把
fe80::/10说成"可以在公网路由":错。链路本地地址只在本地链路有效,路由器不转发——它是每接口自动生成的。 - 把
fc00::/7与fe80::/10混为一谈:要分清。fc00::/7是唯一本地(类私有、可用于内网路由),fe80::/10是链路本地(不出链路)。 - 把 IPv4 的组播地址记成
239.0.0.0/8的范围:要分清。整个 D 类是224.0.0.0/4;239.0.0.0/8只是其中的"管理范围组播"。 - 认为 IGMP 负责跨网段的组播路由:错。IGMP 只管"主机与本地路由器"之间的组成员关系——跨网段靠组播路由协议(PIM 等)。
- 把 IPv6 的组成员管理说成 IGMP:错。叫 MLD,属于 ICMPv6。
3. 解题模板("IPv6 与组播题")
① 地址合法性判断题:
数一数 "::" 出现几次 -> 超过 1 次直接判非法
数一数 "::" 之外还有几组 -> 必须 <= 7 组 (留位置给被省略的)
每组必须是 1~4 位十六进制字符
② 地址压缩题:
省略每组的前导 0
找最长的连续全 0 段 (长度 >= 2) -> 换成 "::"
若有并列最长, 压靠前的那一段 (标准惯例)
③ 首部对照题 (记住三个数):
40 B 固定 / 8 个字段 / 128 位地址
取消项: 首部检验和 + 首部长度 + 路由器分片
④ 地址类型判断题:
2000::/3 -> 全球单播 (公网)
fe80::/10 -> 链路本地 (只在本地链路)
fc00::/7 -> 唯一本地 (类私有)
ff00::/8 -> 组播
::1 / :: -> 环回 / 未指定
★ 一个地址可能落在多个前缀里 -> 取最长前缀
⑤ 组播题:
IPv4 组播范围 224.0.0.0/4 (D 类); 本地链路用 224.0.0.0/24
224.0.0.1 所有主机 / 224.0.0.2 所有路由器 / 224.0.0.5,6 OSPF / 224.0.0.9 RIPv2
IPv6 对应: ff02::1 / ff02::2 / ff02::5,6 / ff02::9
⑥ 地址空间题:
2^128 ≈ 3.4028 x 10^38 ; 是 2^32 的 2^96 ≈ 7.9228 x 10^28 倍
一个 /64 子网有 2^64 ≈ 1.8447 x 10^19 个地址4. 与相邻章节的接口
net/30-ip.md(IP 数据报与 IPv4 地址):上一章的"IPv4 首部 12 字段、首部长度单位 4 B、分片与片偏移"是本章的对照系;本章的"取消首部检验和、取消路由器分片"正是对上一章那套字段的逐项回应。net/31-subnet.md(子网划分与 CIDR):上一章是"在 32 位里精打细算借位",本章2000::/3、/64的用法说明 IPv6 时代不再需要那套精算——但"前缀长度 /n"这个记法被完整继承下来。net/32-nat.md(NAT 与 ICMP):NAT 是"地址不够"的权宜之计,IPv6 是终局;本章的"路由器超 MTU 回 ICMPv6 Packet Too Big"对应上一章 ICMP 类型 3 代码 4"需要分片但 DF = 1";过渡技术里的 NAT64 就是把上一章的 NAT 思路搬到 v4/v6 边界。net/33-routing.md(路由协议):OSPFv3 与 RIPng 是为 IPv6 重写的版本;上一章的 RIP 组播地址224.0.0.9与本章 IPv6 对应物ff02::9成对出现;OSPFv3 的 HELLO 跑在链路本地地址上,正是本章fe80::/10的典型用途。net/21-error.md(差错控制)与net/24-ethernet.md(以太网):"取消 IPv6 首部检验和"的第一条理由就是"以太网 FCS 已经做了 CRC"——这条跨章因果把网络层与链路层的分工一次讲通。net/40-udp.md(UDP):UDP 在 IPv6 上必须计算校验和(IPv4 里可以不算、填 0 表示不算;IPv6 取消了首部检验和,传输层就成了唯一一层校验,所以强制计算)——这是"取消首部检验和"的直接连锁后果。
小结
- IPv6 地址 128 位 = 8 组 × 4 位十六进制;前导 0 可省,
::压缩连续全 0 段且全地址只能一次。 - ★
::只压长度 ≥ 2 的全 0 段,多段时压最长的一段;2001::1::2非法。 - ★ 首部固定 40 B、8 个字段:
位。 - ★ 取消三样:首部检验和 / 首部长度字段 / 路由器分片(含标识、标志、片偏移)。
- ★ 取消检验和三条理由:链路层 CRC 已查 / 传输层伪首部已覆盖 / 路由器每跳重算太贵。
- ★ 三类地址:单播 / 组播 / 任播——没有广播。
- 常用前缀:
2000::/3GUA、fe80::/10链路本地、fc00::/7唯一本地、ff00::/8组播、::1环回、::未指定。 - ★ 标准子网
/64,一网 个地址——IPv6 不必借位。 - IPv4 组播
224.0.0.0/4:224.0.0.1所有主机、224.0.0.2所有路由器、224.0.0.5/6OSPF、224.0.0.9RIPv2;224.0.0.0/24不出本地链路。 - IGMP 只管本地组成员关系;IPv6 用 MLD(属 ICMPv6);ICMPv6 合并了 ICMP + ARP + IGMP 三者职责。
- 过渡三条路:双协议栈 / 隧道 / 翻译(NAT64)。
- ★ 地址空间:
,是 IPv4 的 倍。
下一篇:UDP:报文结构与校验
评论(0)
当前浏览器不允许本地存储,评论无法保存。
还没有评论,来说两句。