如果你在服务器上看到过 fe80:: 开头的地址,或者云厂商给你分配了 2408:/2409: 开头的地址,说明你已经碰到 IPv6 了。很多人的第一反应是把它关掉——"我又不认识这个,关了省事"。这个做法在过去问题不大,但现在会越来越麻烦:越来越多的移动端网络(尤其是 4G/5G)已经默认分配 IPv6 地址,很多 App 上架审核要求支持 IPv6 网络,部分地区的家宽也已经在纯 IPv6 或者 IPv6 优先的环境下运行。
这篇文章讲清楚 IPv4 为什么不够用、IPv6 的地址格式和几个与 IPv4 完全不同的机制变化,然后重点给出双栈部署的实操清单:DNS 的 AAAA 记录、Nginx 的监听写法、防火墙必须单独配置、安全组要单独放行、数据库和 Redis 的绑定地址、以及 Docker 的 IPv6 坑。最后讲 IPv6 带来的暴露面扩大问题怎么防。
一、IPv4 为什么不够用
IPv4 地址是 32 位,理论上限约 43 亿个。这个数字在 1980 年代看起来绰绰有余,但三件事让它在 2010 年前后就基本耗尽了:
设备数量的爆炸。 手机、平板、物联网设备、车联网,联网设备数量早已远超当初的设想。
地址分配的历史浪费。 早期分配过于粗放,大量地址块被分配但没有实际使用,且难以回收。
地址不能按"个数"精细切分。 IPv4 的子网划分受限于二进制边界,一个只需要 10 个地址的网络往往也要分到 16 或 32 个。
NAT 是权宜之计,不是解决方案
NAT(网络地址转换)让多台设备共享一个公网地址,极大地延缓了地址耗尽,但它带来了一系列副作用:
- 端到端连接被破坏。外部无法主动发起连接,P2P、VoIP、游戏联机都需要额外的打洞机制
- 端口成为稀缺资源。一个公网 IP 的端口只有 65535 个,运营商级 NAT(CGNAT)下大量用户共享,端口耗尽会导致新连接失败
- 网络层变复杂。ALG(应用层网关)、NAT 穿越、会话表维护,都增加了设备的负担和故障点
- 溯源困难。多个用户共用一个地址,日志里的 IP 不能直接定位到人,需要额外记录端口和时间
IPv6 的设计目标就是回到端到端的互联网,让每个设备都能有全球唯一的地址,从而消除 NAT 的必要性。
二、IPv6 的地址格式与前缀
格式
IPv6 地址是 128 位,写成 8 组 4 位十六进制数,用冒号分隔:
2001:0db8:85a3:0000:0000:8a2e:0370:7334
有两条缩写规则:
- 每组前导零可以省略:
0db8→db8,0370→370 - 连续的全零组可以用
::代替,但整个地址中只能用一次
上面的地址可以写成:
2001:db8:85a3::8a2e:370:7334
几个必须认识的特殊地址:
| 地址 | 含义 |
|---|---|
:: |
未指定地址(等价 IPv4 的 0.0.0.0),常用于监听所有地址 |
::1 |
回环地址(等价 127.0.0.1) |
fe80::/10 |
链路本地地址,只在同一个二层链路有效,每张网卡都会自动生成一个 |
fc00::/7 |
唯一本地地址(ULA),类似 IPv4 的私网地址,但不应做 NAT |
2000::/3 |
全球单播地址,也就是公网 IPv6 地址 |
ff00::/8 |
组播地址 |
::ffff:192.0.2.1 |
IPv4 映射的 IPv6 地址,用于双栈套接受 |
注意 ULA(fc00::/7)不等于私网地址。 它在设计上允许在全球范围内唯一(随机生成的 40 位全局 ID 使得冲突概率极低),目的是用于内部通信而不做 NAT。很多人把它当成 IPv6 版本的 192.168.x.x 来用,再配合 NAT,这就走回了老路,不推荐。
前缀长度
IPv6 的子网前缀用 CIDR 表示,常见的分配惯例:
| 前缀 | 通常分配给 | 可用子网数 |
|---|---|---|
| /32 | 大型 LIR、运营商 | 极多 |
| /48 | 企业、机房、大客户 | 65536 个 /64 |
| /56 | 中小企业、部分家宽 | 256 个 /64 |
| /64 | 单个链路/子网的标准大小 | 一个子网 |
/64 是一个硬性约定,不要随手切分成 /96 或 /112。 因为 SLAAC(无状态地址自动配置)依赖 64 位的接口标识,前缀不是 /64 会导致 SLAAC 无法工作,某些操作系统也会拒绝配置。
三、与 IPv4 不同的几个机制
这部分是 IPv6 最容易让人困惑的地方,也是排查问题时最需要知道的差异。
| 机制 | IPv4 | IPv6 |
|---|---|---|
| 地址解析 | ARP(广播) | NDP 邻居发现(组播,ICMPv6) |
| 广播 | 有 | 没有广播,只有组播和任播 |
| 地址配置 | DHCP(主要)、手动 | SLAAC、DHCPv6、手动 |
| 分片 | 中间路由器可分片 | 只有端系统分片,依赖 PMTUD |
| 头部校验和 | 有 | 无(交给上层协议) |
| 包头 | 变长,带选项 | 固定 40 字节,扩展头链式 |
| 每接口地址数 | 通常一个 | 多个是常态(链路本地 + 全球 + 临时地址) |
| IPSec | 可选 | 协议规范要求支持(实际部署仍可选) |
SLAAC:不用 DHCP 也能拿到地址
SLAAC(Stateless Address Autoconfiguration,RFC 4862)的工作方式是:路由器周期性发送 RA(路由器通告)报文,报文中包含网络前缀;主机收到后用前缀加上自己的接口标识生成完整地址。
接口标识的生成有两种方式:
- EUI-64:由 MAC 地址转换而来。优点是可预测、便于管理;缺点是地址里暴露了 MAC,且换了网络也能被追踪,隐私性差
- 随机/稳定隐私地址:现代操作系统默认使用(RFC 4941 临时地址、RFC 7217 稳定隐私地址)
RA 报文里还有几个关键标志位:
| 标志 | 含义 |
|---|---|
| A(Autonomous) | 是否允许主机用 SLAAC 生成地址 |
| M(Managed) | 是否需要用 DHCPv6 获取地址 |
| O(Other) | 是否需要用 DHCPv6 获取其他配置(如 DNS) |
一个极其常见的坑:SLAAC 本身不告诉主机 DNS 服务器地址。 主机能拿到 IPv6 地址和默认网关,但没有 DNS,导致"有 IPv6 但解析不了域名"。解决方式有两种:路由器在 RA 里带上 RDNSS 选项(RFC 6106),或者用 DHCPv6 提供(M/O 标志置位)。
在 Linux 上查看是否收到 RA 及其内容:
ip -6 addr show
ip -6 route show
rdisc6 eth0 # 主动发送 RS 并查看回应(需安装 ndisc6 包)
DHCPv6
DHCPv6 分两种模式:
- 有状态(Stateful):像 IPv4 的 DHCP 一样分配地址,M 标志置位
- 无状态(Stateless):只分配 DNS 等配置信息,地址仍由 SLAAC 生成,O 标志置位
服务器场景通常不需要 DHCPv6,用静态配置或云平台的自动分配即可。
PMTUD 与必须放行 ICMPv6
IPv6 不允许中间路由器分片,超大包会被丢弃并返回 ICMPv6 "Packet Too Big" 消息,由发送端自行降低包大小。这个机制叫 PMTUD(路径 MTU 发现)。
直接后果:如果防火墙把 ICMPv6 全拦了,PMTUD 就会失败,表现为小包正常、大包卡住——能 ping 通、能建立连接,但打开网页或者传输文件时卡死。这是 IPv6 环境下最经典的故障。
所以配置 IPv6 防火墙时,必须放行 ICMPv6 的关键类型(至少包括目的不可达、包过大、超时、以及邻居发现的请求和通告)。不要照搬 IPv4 时代"禁掉 ICMP"的习惯。
四、双栈部署实操
双栈(Dual Stack)指同时运行 IPv4 和 IPv6,是当前最现实的过渡方案。
确认与配置
ip -6 addr show # 看 IPv6 地址
ip -6 route show # 看 IPv6 路由
cat /proc/net/if_inet6 # 另一种查看方式
ping -6 -c 4 2400:3200::1 # 测 IPv6 连通(或用 ping6)
curl -6 -sS https://ifconfig.co # 看自己的 IPv6 出网地址
Ubuntu 的 netplan 配置(/etc/netplan/*.yaml):
network:
version: 2
ethernets:
eth0:
addresses:
- 10.0.0.10/24
- "2001:db8:1::10/64"
routes:
- to: default
via: 10.0.0.1
- to: "::/0"
via: "2001:db8:1::1"
nameservers:
addresses:
- 223.5.5.5
- "2400:3200::1"
sudo netplan try
sudo netplan apply
风险提示:远程改网络配置有断连风险,务必用 netplan try(有自动回滚窗口),不要用 apply 直接生效。
RHEL 系用 nmcli:
sudo nmcli con mod eth0 ipv6.addresses 2001:db8:1::10/64
sudo nmcli con mod eth0 ipv6.gateway 2001:db8:1::1
sudo nmcli con mod eth0 ipv6.dns "2400:3200::1"
sudo nmcli con mod eth0 ipv6.method manual
sudo nmcli con up eth0
云平台一般会自动下发 IPv6 地址(DHCPv6 或 SLAAC),手动配置前先确认控制台是否已分配。
如何禁用 IPv6
有时确实需要禁用(比如环境没有 IPv6 连通性但应用会优先尝试 v6 导致延迟)。三种方式,影响范围不同:
# 方式一:sysctl,临时生效 + 可持久化(推荐,最容易回滚)
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
echo 'net.ipv6.conf.all.disable_ipv6 = 1' | sudo tee /etc/sysctl.d/99-disable-ipv6.conf
sudo sysctl -p /etc/sysctl.d/99-disable-ipv6.conf
# 方式二:grub 内核参数,彻底不加载 IPv6 模块(需要重启)
# 在 /etc/default/grub 的 GRUB_CMDLINE_LINUX 里加 ipv6.disable=1
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL 系 BIOS 启动
sudo update-grub # Debian、Ubuntu
方式三:只禁用单张网卡,把 all 换成网卡名:
sudo sysctl -w net.ipv6.conf.eth0.disable_ipv6=1
回滚方式:把对应参数改回 0 并 sysctl -p;grub 方式需要改回配置并重新生成 grub 配置后重启。
建议:优先让 IPv6 正常工作,而不是禁用它。 禁用的合理场景只有"确实没有 IPv6 连通性且排查暂缓"。
DNS:添加 AAAA 记录
在域名解析里添加 AAAA 记录指向服务器的 IPv6 地址:
dig AAAA www.example.com +short
dig AAAA www.example.com @223.5.5.5 +short
客户端行为要理解:现代系统使用 Happy Eyeballs(RFC 8305)算法,会同时请求 A 和 AAAA,优先尝试 IPv6,如果在很短时间内(通常 250 毫秒左右)没建立连接,就并行尝试 IPv4。
这带来一个重要结论:如果 AAAA 记录存在但 IPv6 实际不通,用户不会完全打不开,而是每次访问多出几百毫秒的延迟。 这种"慢一点但不报错"的问题最难被发现。所以要么把 IPv6 配通,要么干脆不要加 AAAA 记录,不要留一个半通不通的状态。
Nginx 监听
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name www.example.com;
# ...
}
要点:
listen [::]:443监听所有 IPv6 地址,与listen 443配合实现双栈- 老版本 Nginx 上可能需要显式加
ipv6only=on(默认已是 on,加了也无妨) - 不要只写
listen [::]:443就以为同时覆盖了 IPv4。在ipv6only=on时它只处理 IPv6;Linux 上绑定::是否同时接受 IPv4 取决于net.ipv6.bindv6only的值,依赖这个行为是不稳妥的。显式写两条 listen 最清晰。
验证:
sudo ss -lntp | grep -E ':443|:80'
# 应该看到 tcp 和 tcp6 两行,或者一行显示 :::443
curl -6 -sS -o /dev/null -w '%{http_code}\n' https://www.example.com/
防火墙必须单独配置
这是 IPv6 部署最大的坑:IPv4 和 IPv6 的防火墙是两套独立的规则。
sudo iptables -L -n -v # 只看 IPv4
sudo ip6tables -L -n -v # 看 IPv6,很可能是一条规则都没有!
如果只配了 iptables 而没配 ip6tables,你的 IPv6 服务就是完全裸奔状态——所有端口对所有 IPv6 用户开放。
ip6tables 放行:
sudo ip6tables -I INPUT -p tcp --dport 443 -j ACCEPT
sudo ip6tables -I INPUT -p tcp --dport 80 -j ACCEPT
# 必须放行 ICMPv6,否则 PMTUD 失败
sudo ip6tables -I INPUT -p ipv6-icmp -j ACCEPT
nftables 推荐用 inet 地址族,一份规则同时管 v4 和 v6:
sudo nft add table inet filter
sudo nft 'add chain inet filter input { type filter hook input priority 0 ; policy accept ; }'
sudo nft add rule inet filter input tcp dport { 80, 443 } accept
sudo nft add rule inet filter input meta l4proto ipv6-icmp accept
sudo nft list ruleset
firewalld 默认同时管理 IPv4 和 IPv6,用 firewall-cmd --add-port 加的端口对两者都生效,这是它比裸 iptables 方便的地方。
云安全组也要单独放行
云安全组的 IPv4 规则和 IPv6 规则通常是分开配置的。 在控制台添加规则时要注意选择协议版本或者地址段填 ::/0。很多人在安全组里只加了 0.0.0.0/0 的规则,结果 IPv6 完全不通,或者相反——某些平台默认对 IPv6 全放行,导致暴露面意外扩大。
配置完必须在外部用 IPv6 网络验证一次。
数据库与缓存的绑定地址
MySQL:
[mysqld]
bind-address = * # MySQL 8.0 支持 *,表示所有地址(含 IPv6)
# 老版本用 0.0.0.0;需要监听 IPv6 时可试 ::
Redis(6.0+):
bind * -::* # 同时监听所有 IPv4 和 IPv6
sudo ss -lntp | grep -E '3306|6379'
关键认知:在 Linux 上,绑定 :: 时是否同时接受 IPv4 连接,取决于内核参数:
sysctl net.ipv6.bindv6only # 0 = 监听 :: 时也接受 IPv4(IPv4 映射地址)
默认一般是 0,所以绑定 :: 通常能同时服务 v4 和 v6。但依赖这个默认值不安全(不同发行版和容器环境可能不同),最稳妥的还是显式确认实际监听情况。
安全提醒:数据库和 Redis 一旦监听 IPv6 公网地址,等于对全球开放。务必配合安全组限制来源、设置强密码、禁用默认账号。
Docker 的 IPv6 坑
Docker 默认不启用 IPv6,容器只有 IPv4。要启用需要改 /etc/docker/daemon.json:
{
"ipv6": true,
"fixed-cidr-v6": "2001:db8:1::/64",
"ip6tables": true,
"experimental": true
}
sudo systemctl restart docker
几个需要注意的坑:
坑一:默认桥接网络(docker0)不支持 IPv6。 即使用 daemon.json 开了 IPv6,也必须使用自定义 bridge 网络才能给容器分配 IPv6 地址:
docker network create --ipv6 --subnet 2001:db8:2::/64 mynet
docker run --network mynet -d nginx
坑二:前缀长度不够。 如果宿主机只拿到一个 /64,再切成更小的子网给 Docker 会导致 SLAAC 和某些实现出问题。理想情况是宿主机拿到 /56 或 /48,能给 Docker 分配独立的 /64。
坑三:转发要开。 容器访问外部 IPv6 需要开启转发:
sudo sysctl -w net.ipv6.conf.all.forwarding=1
坑四:ip6tables 规则会被 Docker 管理。 开了 ip6tables: true 后 Docker 会自己往 ip6tables 里加规则,手动加的规则可能被覆盖或顺序错乱。
风险提示:重启 Docker 会中断所有容器,务必在业务低峰操作。修改 daemon.json 前先备份原文件,配置错误导致 Docker 起不来时可以直接回滚。
五、暴露面扩大与防护
IPv6 最大的安全影响是:NAT 这道"天然防火墙"消失了。 IPv4 时代内网机器靠 NAT 隐藏,外部无法主动连接;IPv6 时代每台设备都有全球唯一地址,理论上端到端可达。
必须做的几件事:
第一,防火墙和安全组必须按 IPv6 完整配置一遍,不能只配 IPv4。 这是最重要的一条,前面已经强调过。
第二,不要把"地址空间大所以扫不到"当成安全。 确实,暴力扫描一个 /64 子网(2 的 64 次方个地址)不可行,但攻击者有其他手段:通过 DNS 记录、证书透明日志、邮件头、应用层日志拿到真实地址;在链路上通过组播和 NDP 枚举邻居;或者利用 EUI-64 地址里包含的 MAC 信息反推。
第三,服务器上建议关闭隐私扩展相关的临时地址(服务器需要稳定可访问的地址),同时避免对外服务使用 EUI-64 派生地址,减少信息泄露:
sudo sysctl -w net.ipv6.conf.eth0.use_tempaddr=0
第四,服务监听要精确。 数据库、Redis、管理后台只监听内网地址或 ::1,需要对外时才监听 :: 或 0.0.0.0,并配合来源限制。
第五,日志和监控系统要能处理 IPv6 地址。 检查你的 WAF、日志分析、封禁脚本是否支持解析和匹配 IPv6 格式(包括各种缩写形式),否则会出现"封禁规则对 IPv6 无效"的情况。
六、验证与排错
完整验证清单
# 1. 本机是否有 IPv6 地址
ip -6 addr show
# 2. 是否能 ping 通外部 IPv6
ping -6 -c 4 2400:3200::1
# 3. DNS 是否返回 AAAA
dig AAAA www.example.com +short
# 4. 服务是否监听 IPv6
sudo ss -lntp | grep 443
# 5. 防火墙是否放行
sudo ip6tables -L INPUT -n -v
# 6. 本机自测
curl -6 -sS -o /dev/null -w '%{http_code}\n' https://www.example.com/
# 7. 外部测试(用手机流量或另一台有 IPv6 的机器)
curl -6 -sS -o /dev/null -w '%{http_code}\n' https://www.example.com/
常见故障与原因
| 现象 | 原因 |
|---|---|
| 有 IPv6 地址但 ping 不通外部 | 安全组没放行 IPv6、路由缺失、或云平台未开启 IPv6 |
| 能 ping 通但网页打不开 | 防火墙拦了 ICMPv6 导致 PMTUD 失败,或 AAAA 记录指向错误地址 |
| 部分网站能开、部分卡住 | 典型的 PMTUD 问题,检查 ICMPv6 是否放行 |
| 网站变慢但能打开 | AAAA 记录存在但 IPv6 不通,Happy Eyeballs 退回到 IPv4 产生额外延迟 |
| 服务监听了但外部连不上 | 安全组或 ip6tables 只配了 IPv4 那一套 |
| 容器拿不到 IPv6 | Docker 未启用 IPv6,或使用了默认的 docker0 桥接 |
常见问题
我应该现在就启用 IPv6 吗?
如果是面向公众的网站或 App 后端,建议启用。移动端网络 IPv6 普及率已经很高,且部分审核要求支持 IPv6。如果是纯内部系统,可以暂缓,但要记着这件事。启用前确保能完整配置防火墙和安全组,否则不如不启用。
关掉 IPv6 有什么坏处?
短期内可能没感觉,但会造成:IPv6 用户访问时依赖 Happy Eyeballs 退回(多出延迟);错过 IPv6 用户;未来迁移成本更高。另外如果你的域名有 AAAA 记录而服务器禁用了 IPv6,会产生"能打开但慢"的隐性故障。
为什么有了 IPv6 还要保留 IPv4?
因为互联网上仍有大量纯 IPv4 的服务和用户。双栈是当前唯一现实的方案——同时支持两者,由客户端自己选择。纯 IPv6 环境需要 NAT64/DNS64 这类转换机制才能访问 IPv4 资源。
IPv6 还需要 NAT 吗?
设计上不需要,也不推荐。NAT66 存在但会重蹈 IPv4 的覆辙。正确的做法是用全球单播地址配合防火墙做访问控制——防火墙负责安全,地址转换不该承担安全职责。
为什么我配置了 IPv6 但访问变慢了?
大概率是 AAAA 记录指向的地址不通,或者防火墙拦了 ICMPv6 导致 PMTUD 失败。先用 curl -6 测试,再用 ping -6 -s 1400 测大包。如果短时间内无法修好,先把 AAAA 记录删掉,不要留半通不通的状态。
一个 /64 够用吗?
对单个子网来说,/64 是标准配置且绰绰有余(能容纳的地址数量是个天文数字)。要小心的是不要把它再切分成更小的前缀给 Docker 或其他用途,那样会破坏 SLAAC。需要多个子网时,向服务商申请 /56 或 /48。
总结
- IPv4 的 32 位地址早已耗尽,NAT 只是延缓手段且破坏了端到端连接、引入端口耗尽和溯源困难;IPv6 的 128 位地址目标是让每个设备拥有全球唯一地址,回归端到端。
- 记住几个关键地址:
::1回环、fe80::/10链路本地、fc00::/7唯一本地(不是私网,不该做 NAT)、2000::/3全球单播;子网前缀标准是 /64,不要切分成更小的前缀,否则 SLAAC 会失效。 - IPv6 的机制差异里最容易踩坑的三点:没有广播(用组播和 NDP 代替 ARP)、SLAAC 不下发 DNS(需要 RDNSS 或 DHCPv6 配合)、中间设备不分片(依赖 PMTUD,防火墙必须放行 ICMPv6,否则会出现"能 ping 通但网页打不开")。
- 双栈配置要成配对做:DNS 加 AAAA 记录、Nginx 显式写
listen 443和listen [::]:443两条、ip6tables 和云安全组都要单独配置。只配 IPv4 那一套等于 IPv6 端口裸奔。 - 服务绑定地址要注意
net.ipv6.bindv6only这个内核参数(默认为 0 时绑定::会同时接受 IPv4),不要依赖默认值,用ss -lntp确认实际监听;数据库和 Redis 一旦监听 IPv6 公网地址就是全球可达,必须限制来源并设强密码。 - Docker 的 IPv6 需要改
daemon.json启用、必须使用自定义 bridge 网络(默认 docker0 不支持)、宿主机最好有 /56 或 /48 前缀、并开启 IPv6 转发;重启 Docker 会中断容器,操作前备份配置。 - IPv6 让 NAT 这道天然屏障消失,暴露面实质扩大:"地址空间大扫不到"不是安全保障,要完整配置防火墙、避免使用 EUI-64 派生地址、并确认日志和封禁系统能处理 IPv6 格式。
企业QQ咨询




