一、故障现象与初步定位

前阵子我折腾家里的FreeBSD软路由,开了IPv6之后,突然出现网络时断时续的情况:网页刷到一半卡住,视频缓冲停住,隔几秒或者几分钟又突然好了,反复无常。我先排查外部问题:手机连同一个WiFi的IPv6完全正常,能刷外网、看视频,排除运营商或者路由器的问题。那问题肯定出在FreeBSD软路由的IPv6配置上。

1.1 基础故障验证

我在FreeBSD终端敲了ping命令测IPv6公网地址,选了个国内稳定的公网IPv6地址,敲完回车,发现大概有30%的包超时,有时候连着3次都通,有时候连续丢包,完全不是正常连接的状态。这种“间歇性断”的情况,基本说明协议栈本身是好的,但某个小环节出了问题,比如某个规则拦截了关键数据包。

二、深入排查:邻居发现与路由广告异常

IPv6和IPv4最大的区别之一,就是IPv6不需要手动配地址和网关,靠的是两个核心东西:邻居发现协议(就是设备之间认对方的“串门登记”,简称NDP),和路由广告(路由器给子网里设备发的“导航信息”,简称RA)。这俩要是出问题,IPv6就没法正常工作。

2.1 检查邻居发现协议的状态

FreeBSD里有个专门看NDP的命令,叫ndp,我敲下面的命令看IPv6的邻居表:

# 查看IPv6邻居表,确认网关和其他设备的可达性
ndp -a -6

出来的列表里,我一眼看到网关的IPv6地址(一般是fe80开头的链路本地地址,因为是子网里识别用的),它的状态是STALE,过了几秒钟再敲一次,状态变成了INVALID。STALE的意思是“邻居状态存疑,不知道还能不能通信”,INVALID直接就是“这个邻居不认了,相当于找不到人了”。这说明FreeBSD根本没确认到网关的状态,自然没法走IPv6流量。

2.2 检查路由广告的接收情况

网关状态不对,肯定是没收到路由器发的RA包。RA是路由器告诉子网设备“我是网关,你的IPv6地址要怎么配”的数据包,属于ICMPv6类型的一种。FreeBSD里控制收不收RA的参数是net.inet6.ip6.accept_ra,我敲sysctl看这个参数:

# 查看IPv6路由广告接收开关的当前状态
sysctl net.inet6.ip6.accept_ra

结果显示是0,这就坑了!本来应该是1(允许接收),我之前改系统参数的时候不小心关了,FreeBSD根本不搭理路由器发的RA,自然不知道网关的存在,NDP就没法正常工作,邻居状态就一直出错。

接下来还要看防火墙有没有拦RA,FreeBSD默认用pf防火墙,要是规则里没放RA的数据包,肯定会丢。我查pf的配置文件,在/etc/pf.conf里找ICMPv6的规则,发现之前为了安全,只加了允许ping的规则,没开RA和邻居发现的包:

# 错误的ICMPv6规则,只允许ping,漏掉了核心的RA和邻居发现包
pass inet6 proto icmp6 all icmp6-type echoreq

pf防火墙要是拦了这些包,RA就到不了FreeBSD,自然出问题。

三、具体故障原因与修复方法

折腾下来,我遇到的这个故障有两个主要原因,都是新手改配置容易踩的坑,我把修复方法写得特别细,大家照着做就行。

3.1 第一种原因:防火墙拦截路由广告包

刚才说的pf规则漏了RA,这是最常见的。修复的时候,只要把ICMPv6的规则补全,允许RA、邻居发现这些必须的包就行:

# 正确的ICMPv6规则,包含IPv6邻居发现和路由广告必要类型
pass inet6 proto icmp6 all icmp6-type { router-advert, neighbor-solicit, neighbor-advert, echoreq }

改完配置,还要重载pf规则让它生效:

# 重载pf防火墙规则,不用重启服务,生效快
pfctl -f /etc/pf.conf

改完之后,再去看ndp的状态,网关的状态会变成REACHABLE(可达),IPv6的ping包就不会超时了。

3.2 第二种原因:MTU不匹配导致RA包丢失

还有一个容易忽略的点是MTU,就是网卡一次能发的最大数据包大小。我之前改PPPoE拨号的时候把MTU设成了1492,后来改IPv6的时候没改回来,路由器发的RA包是1500字节,超过了FreeBSD网卡的MTU,数据包被截断了,FreeBSD解析不了,就以为没收到RA。

解决方法很简单,先临时改MTU测试:

# 临时修改网卡vtnet0的MTU,改成和路由器一致的1500(重启后失效)
ifconfig vtnet0 mtu 1500

如果改完之后ping包正常,那就是MTU的问题,要永久生效的话,把配置写到/etc/rc.conf里:

# 永久设置网卡vtnet0的MTU,重启后也不会变
ifconfig_vtnet0="inet 192.168.1.100 netmask 255.255.255.0 mtu 1500"

这样MTU就固定了,不会再因为数据包太大丢包。

四、该场景的技术优缺点与注意事项

4.1 应用场景

这个排查流程适合的场景是:FreeBSD软路由、服务器开启IPv6后,网络间歇性断连,同网段其他设备的IPv6连接正常,基本可以确定是FreeBSD本身的配置问题,尤其是NDP和RA相关的配置。

4.2 技术优缺点

优点:IPv6用RA自动配地址和网关,不用手动敲一堆参数,对新手友好;NDP协议能自动确认设备可达性,减少人工配置的错误。缺点:对防火墙规则的要求高,漏开ICMPv6的类型就会出问题;MTU不匹配很容易被忽略,是隐形的故障点,新手排查起来比较绕。

4.3 注意事项

  1. FreeBSD的pf防火墙,必须放行ICMPv6的router-advert、neighbor-solicit、neighbor-advert这三个类型,不然IPv6肯定会断;
  2. MTU参数一定要和路由器、网关一致,一般用1500,如果是PPPoE拨号的话改成1492,不然会有丢包;
  3. 不要随便改net.inet6.ip6.accept_ra这个参数,除非你打算手动配置IPv6地址,不然一定要保持开启;
  4. 平时可以定期用ndp -a -6看邻居状态,要是看到STALE或者INVALID,赶紧排查防火墙或者MTU,别等到断网才处理。

五、故障总结

这次排查下来,我最大的感受是IPv6的核心就是两个东西:NDP和RA,FreeBSD上的防火墙规则和MTU是最容易踩的坑。很多新手改配置的时候,要么漏开必要的ICMP规则,要么没注意MTU,结果导致IPv6的邻居发现和路由广告异常,网络就时断时续。只要按照我上面说的步骤,先查ndp状态,再查防火墙规则和MTU,基本都能搞定。这次的故障排查记录,希望能帮到同样遇到FreeBSD IPv6断网的朋友。