为什么改了disable_ipv6,IPv6却还是没有被真正关掉

某些场景下想彻底关掉IPv6(比如某个服务对IPv6支持有问题,干脆关掉省心),常见做法是改/etc/sysctl.conf加一行net.ipv6.conf.all.disable_ipv6=1,跑一下sysctl -p,结果ip -6 addr一查,IPv6地址还在,跟没改过一样。要么就是反过来——真关掉了,过阵子发现SSH的某个功能用不了了,或者邮件服务死活启动不起来,两头都能踩坑。

“all"不等于"所有”

第一个坑出在这个参数的名字容易让人误会。net.ipv6.conf.all.disable_ipv6=1看着像是"关闭所有接口的IPv6",实际上它只是给新建立的接口设置默认值,加上作用到已经存在的常规网卡上;但回环接口(lo)不受它管,得单独再加一行:

1
2
3
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

社区里翻车的人不少都是只写了第一行,觉得all听着应该管得够全了,结果lo接口上的IPv6死活关不掉。

NetworkManager会在背后跟你唱反调

如果系统用的是NetworkManager管理网络(常见于RHEL/CentOS这类发行版),就算sysctl这边配置全部写对了,NetworkManager有自己的一套逻辑,可能会在网卡重新连接的时候,把IPv6又悄悄打开,跟sysctl的设置对着干。这种情况下光改sysctl没用,还得额外用nmcli单独告诉NetworkManager本身也别管IPv6:

1
nmcli connection modify "连接名" ipv6.method "disabled"

两边都设置了,才算真正锁死。

真想彻底关掉,得从内核启动参数下手

如果sysctl这条路来回折腾还是不干净,更彻底的办法是在GRUB里加内核启动参数ipv6.disable=1,这样重启之后IPv6模块压根不会被内核加载。这里有个有意思的连锁反应——如果之前sysctl.conf里还留着net.ipv6.conf.all.disable_ipv6=1这一行没清理,下次sysctl -p反而会报错,提示cannot stat /proc/sys/net/ipv6/conf/all/disable_ipv6: No such file or directory,因为内核层面IPv6压根不存在了,这个参数自然也没了,两种关闭方式叠加,变成新的报错,得把旧配置一并清掉。

关掉之后,别的服务可能跟着遭殃

IPv6被真正关掉之后,一些服务默认监听::1这种IPv6回环地址,会直接启动失败——比较典型的是Postfix邮件服务,配置文件里inet_interfaces默认写法可能依赖IPv6回环,得手动改成指向IPv4的127.0.0.1才能正常跑起来;SSH的X11转发功能同样可能受影响,需要在sshd_config里加上AddressFamily inet明确指定只用IPv4。这些副作用平时不会显现,只有真把IPv6关掉那一刻才会冒出来,排查起来容易让人摸不着头脑,表面上看跟"关IPv6"这个操作完全不沾边。

顺带一提

这种"配置文件明明改了,实际却没在预期的地方生效"的情况,跟之前写的ulimit设置了却不生效是同一类问题的不同变种——不是配置写错了,是改的那个地方,压根管不到你以为它管的那个东西。如果反过来是VPS本身IPv6 only、根本没有IPv4可以选择性关闭,遇到的会是完全不同方向的连接问题,可以看IPv6 VPS服务无法访问的常见原因与排查方法那篇,一个是"关不掉",一个是"只有它、还连不上",别搞混排查方向。