·约 10 分钟阅读·DeepSeek

IPv6 初学者实战教程:一篇给一直躲着它的人、能动手做完的入门指南

一篇写给一直躲着 IPv6 的人的实战教程:地址计算其实比看上去简单(前缀、/64、SLAAC、没有 NAT),运维上真正变了什么(nftables、ip -6、AAAA 记录、监听 [::]),为什么双栈才是务实的路线,那些会咬人的坑(链路本地地址、MTU、ICMPv6 依赖),以及如何在一台机器上把它测通。

networkinglinuxself-hosting

大多数人躲着 IPv6,并不是因为它难。他们看到的是一个 128 位的十六进制地址、一段讲 “NAT 为什么是邪恶的”的文字,再加一个子网计算器——然后就关掉标签页了。我懂。所以这篇 IPv6 教程,是我希望当年有人能塞给我的那篇:不讲意识形态,不做“IPv4 已死”的演讲, 只讲对一个在跑真实机器的人来说到底变了什么,以及如何在动手改生产环境之前,先在一台机器上 把它验证通。如果你是 IPv6 初学者、心里有点发怵,这个姿势是对的。

让人安心的话先说在前头:对日常真正要做的事——托管一个服务、给服务器配防火墙、让一条 DNS 记录指过来——IPv6 的心智负担其实比 IPv4 更小。NAT 没了,掩码变成了一个你几乎不用改的数字, 主机自己就能配置好。恐惧主要来自记号法;学会几行 nftables 加一个 sysctl,你就能跑起来了。

IPv6 初学者:地址计算和 SLAAC 其实比看上去简单

一个 IPv6 地址是 128 位,写成八组四位十六进制,有两条简写规则:组内开头的零可以省略,连续 一段全零的组可以折叠成 ::。所以 2001:0db8:0000:0000:0000:0000:0000:0001 就是 2001:db8::1。记号比 IPv4 的四个十进制八位组难看,但你实际要做的算术简单得多。

前缀就是 “前多少位是固定的”

2001:db8:abcd:12::/64 的意思是“前 64 位是网络”。这就是地址与掩码之间的全部关系:没有 点分掩码要你在脑子里换算,也没有 255.255.255.0 ↔ /24 ↔ “借位”那一套仪式。/64 就是 掩码。

忘掉子网计算器:几乎永远是 /64

这个数字能解放你:RFC 6177 和普遍实践都说,给每个局域网一个 /64,剩下的让主机自己选。 一个 /64 能装 2^64 个地址——你永远用不完。运营商一般会给你委派一个 /56 或 /48;每个 局域网从里面拿走一个 /64。因为边界正好落在十六进制位上,划分子网就是在数十六进制,而不是 二进制:

# 运营商委派了 2001:db8:1000::/56 —— 也就是 256 个 /64。
# 每个局域网分一个,只需改第 4 组。不用算:
#   2001:db8:1000:00::/64    主局域网
#   2001:db8:1000:01::/64    实验室
#   2001:db8:1000:02::/64    访客
# 主机位就是末尾那个 /64。子网划分到此结束。

在 IPv4 里同样的活需要一份 VLSM 规划来避免浪费;在 IPv6 里,每个局域网都拿 /64,哪怕只有 两台机器,因为没有稀缺要省。

SLAAC:主机自己给自己配地址

SLAAC(无状态地址自动配置)听起来很玄,其实是 IPv6 最好的部分。你的路由器会周期性发送 路由器通告(RA),宣告本地前缀;每台主机收到前缀,自己生成一个 64 位接口标识符,两者 拼起来。没有 DHCP 地址池、没有租约数据库、没有“地址已被占用”。它由 RFC 4862(SLAAC)和 RFC 4861(邻居发现)标准化,在 Linux、macOS、Windows、Android 和一切现代路由器上开箱即用。 DHCPv6 依然存在,用于需要集中记账的环境——RA 里的 M 和 O 标志会告诉主机该去取地址 还是只取其它选项——但对家用实验环境来说,光靠 SLAAC 就够了。

没有 NAT,而这正是重点

IPv4 路由器改写地址,好让大量主机藏在同一个公网 IP 后面。IPv6 没有地址短缺,所以每台主机 都拿到一个全局可路由地址,路由器只负责路由。那个恐惧——“每台主机都公网可达,是不是全 暴露了?”——忽略了一点:现在防火墙才是安全边界。NAT 从来不是安全特性,它只是顺带把东西 藏起来的意外产物。你用一份显式的放行清单,换掉了隐式的隐藏:前期多花点功夫,但诚实得多。 想知道 NAT 为什么不是一堵墙,可以看《NAT 穿透到底是怎么工作的》。

运维上真正改变的是什么

模型是新的,但工具大多是同一批工具前面加了个 6。

DNS:加 AAAA 记录,而不是删掉 A

AAAA 记录就是 IPv6 版的 A 记录。整个双栈的故事都在这里:保留你的 A,在旁边加一条 AAAA,客户端会按自己的偏好去选。Happy Eyeballs 会让 IPv4 和 IPv6 赛跑,谁先应答就用谁。

; 同一个名字下两条记录。客户端自己选。
blog.example.com.   3600  IN  A     203.0.113.7
blog.example.com.   3600  IN  AAAA  2001:db8:1000:1::7

有一条规矩:绝不要给一条走不通 IPv6 的主机发布 AAAA。客户端会去试、会超时,于是你的服务 对那些有 IPv6 的用户反而显得很慢——正好与目的相反。

监听地址与 [::]

应用不用改,改的只是绑定地址。0.0.0.0:8080 是“所有 IPv4 地址”;IPv6 的写法是 [::]:8080。在 URL 里,IPv6 字面量要放进方括号——http://[2001:db8::1]:8080/——因为冒号 已经用来分隔主机和端口了。

ss -6 -lntp          # IPv6 的 TCP 监听
ss -4 -lntp          # IPv4 的 TCP 监听
server {
    listen      [::]:80;
    listen      80;          # 同时保留 IPv4 监听:这就是双栈
    server_name blog.example.com;
}

在 Linux 上,除非 net.ipv6.bindv6only=1,绑定到 [::] 的套接字也会接收 IPv4 流量。所以 “v4 能用”并不能证明你有在监听 v6。用 ss -6 确认。

用 nftables 和 ip6tables 配防火墙

真正要下功夫的地方在这里,而且比你担心的小。nftables 同时处理两个协议族,所以一份规则就能 覆盖 v4 和 v6。如果你还在用旧工具,iptables 的 v6 孪生兄弟是 ip6tables,规则不会自动 迁移。

# /etc/nftables.conf —— 一张 `inet` 表同时覆盖 IPv4 和 IPv6。
table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        ct state established,related accept
        iif lo accept

        # ICMPv6 不是可选项(见后文“坑”)—— 放行必要的部分。
        ip6 nexthdr ipv6-icmp accept

        tcp dport { 22, 80, 443 } accept
    }
}
# 旧工具的等价写法,仅 IPv6 —— 与 iptables 逐条对应:
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
ip6tables -P INPUT DROP

无论用哪个工具,绝不要忘的就是那条 ICMPv6。把 ICMPv6 全部丢掉——这是从 IPv4 “封 ping”建议 里带过来的习惯——会直接把 IPv6 弄瘫。

为什么双栈才是务实的路线

你不该做选择。双栈就是在同一台主机上同时跑 IPv4 和 IPv6:同一个服务、两个监听、两条 DNS 记录。它是业界默认,因为它是唯一不需要“切换日”的迁移方式。纯 IPv4 客户端照常可用, IPv6 客户端拿到更短的路径,而你可以一段网络一段网络地升级。

其它路线都有锋利的边。纯 IPv6 配 NAT64/DNS64 很优雅,但需要一套能用的 DNS64 基础设施,否则 半个互联网对你就消失了;挂一条到 broker 的隧道,拿来学习没问题,作为长期生产路径则很痛苦。 双栈每个服务多一行、每台主机多一条记录;作为交换,不会有什么东西因为 IPv6 而坏掉。所以: 到处打开 IPv6,只在你确实能应答的地方发布 AAAA,并且把“v6 时好时坏”当成 bug 去修,而不是 一种可以接受的常态。

那些真的会咬人的坑

有几件事,从 IPv4 过来会觉得不对劲。

链路本地地址与 fe80::

每个接口都会自动拿到一个 fe80::/10 里的链路本地(link-local)地址,哪怕没有路由器。 它只在那一小段链路上有效、永远不会被路由,所以 fe80::1 在每条线上指的是不同的机器。两个 后果:ping 和 SSH 的目标需要一个区域标识(fe80::1%eth0),而 ip -6 addr 里那些 fe80:: 条目是正常的,不是错误。它们也是邻居发现和 RA 底层的工作方式——别想着禁掉它们。

隐私扩展会改掉你的源地址

很多系统会从 MAC 地址推导接口标识符(EUI-64),这会泄露硬件身份,让一台主机在不同网络间被 追踪。隐私扩展(RFC 8981)则改用随机、轮换的标识符。多数桌面发行版默认开启,多数服务器 没有。

# 优先用稳定地址,同时保留轮换的临时地址:
echo 'net.ipv6.conf.all.use_tempaddr = 2' | sudo tee /etc/sysctl.d/40-ipv6.conf
sudo sysctl --system
ip -6 addr show dev eth0     # 应该同时看到一个稳定地址和一个临时地址

要注意的是:如果你按源地址做防火墙或 ACL,现在就得决定你信任哪个——稳定地址(像服务器)还是 临时地址(像客户端),否则你的放行清单会慢慢烂掉。

MTU 与对 ICMPv6 的依赖

IPv6 路由器不分片——只有发送方分片,靠的是路径 MTU 发现,而它依赖 ICMPv6 的 “Packet Too Big” 报文。一旦 ICMPv6 被挡,PMTUD 会静默失败:小请求能通,大请求永远卡住。

# 1500 字节链路减去 IPv6+TCP 头 —— 先试 1452 字节载荷:
ping6 -M do -s 1452 -c 3 2001:4860:4860::8888
# “-M do” = 不分片。如果回 “Message too long”,就往下降。

ICMPv6 是必需项,不是可选项

这是最常见的自伤式 IPv6 故障:一份把所有 ICMP 都丢掉的防火墙,会连邻居发现、RA 和 PMTUD 一起丢掉,网络就半死不活。放行 ICMPv6——至少类型 1、2、128、129。

IPv6 实战教程:在一台机器上把它测通

你不需要第二台机器,更不需要换运营商。

# 唯一本地地址块(fd00::/8)是 IPv6 的“私有网段”。
sudo ip link add dummy0 type dummy
sudo ip link set dummy0 up
sudo ip -6 addr add fd00:dead:beef::1/64 dev dummy0

ip -6 addr show dev dummy0     # 地址
ip -6 route show               # 路由
ip -6 neigh show dev dummy0    # 邻居发现
ping6 -c 3 ::1                 # 走 IPv6 的环回:最简单的证明

至于真正可路由的 IPv6,大多数运营商和 VPS 供应商会给你一段前缀;如果没有,隧道代理( Hurricane Electric 的 tunnelbroker.net 是长期存在的那家)能在你现有的 IPv4 上给你一段可 路由的 /64——拿来学习没问题,拿来长期依赖则不行。有了真实地址之后:

ping6 -c 3 2001:4860:4860::8888          # 走 IPv6 的 Google DNS
# 强制用 IPv6 发 HTTP —— 一次验证地址、路由、DNS 和远端服务:
curl -6 -sS -o /dev/null -w '%{http_code} %{remote_ip}\n' https://example.com
dig +short AAAA example.com

如果 curl -6 打印出一个地址和一个 200,那这台机器上的 IPv6 就是端到端真实可用的。如果你 想在不等运营商委派前缀的前提下拿到同样的可达性,一张私有 overlay 是条捷径——我用 EasyTier 搭过一张——不过它只解决“连到我自己的机器”, 不解决“被公网访问”。

什么情况下你其实不必折腾

有些场景里,IPv6 确实可以放到一边。

  • 一台纯客户端的机器,待在纯 IPv4 网络里。 如果你的笔记本只是消费互联网,IPv6 今天 不给你带来任何东西。
  • 藏在别人网络后面的封闭设备。 一台打印机,或者访客网里一台你从不暴露的树莓派:双栈是 没有回报的中性工作。
  • 整个实验环境都活在 overlay 里。 如果你在意的每台机器都已经能通过一张 mesh 访问,你 已经用另一个工具解决了寻址问题;公网 IPv6 是另一个项目,不是前提。
  • 完全不服务 v6 流量的团队。 给一个 100% IPv4 的受众加 AAAA,只增加一种失败模式,不 增加用户。

但注意这些情形的形状:它们全都是“我只消费”,没有一个是“我在服务”。一旦你要托管点什么,IPv6 就不再是可选项了。我的建议没变:打开它,给你服务的一切上双栈,只在你确实能应答的地方发布 AAAA,剩下的交给 IPv4 扛着,直到它不必再扛。