前景提要
电信提供了公网IPv6地址, 一般情况下无需改动光猫配置. 有时需要通过管理员密码(找宽带安装师傅要)登录光猫后台启用IPv6功能.
根据观测, 大部分家庭宽带默认开启了较高等级的防火墙, 会禁止外部发起IPv6链接. 需要通过管理员密码登录光猫后台找到防火墙配置, 改为更低等级的防护或关闭防火墙.
注意, 对于家庭网络中光猫-路由器-其他设备的场景, 路由器IPv6配置方式可能需要改为 Native 才能使局域网设备也获得公网IPv6. 某些路由器(比如华硕)的 Stateless 配置似乎存在Bug导致只有路由器本身能获取到v6而下级设备无法正确获取地址.
另外根据观测, 不同ISP(例如联通和电信)之间的公网IPv6地址无法实现互通.
问题概述
两个小区之间都接入了电信的家庭宽带, 其中一端(下称A)开启了允许公网访问. 由另一端(下称B)向开启公网访问的这一端发起WireGuard连接.
电信提供的公网IPv6地址似乎在一段时间之后会失效, 但是在失效前一段时间, 会同时并存多个IPv6公网地址, 例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| # ip a ... 2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff altname enp0s18 inet 192.168.xx.xx/24 brd 192.168.xx.255 scope global ens18 valid_lft forever preferred_lft forever inet6 240e:xx:xx:xx::xx/128 scope global dynamic noprefixroute valid_lft 25044sec preferred_lft 25044sec inet6 240e:xx:321a:xx:xx:xx:xx:xx/64 scope global dynamic mngtmpaddr noprefixroute valid_lft 177778sec preferred_lft 91378sec inet6 240e:xx:3216:xx:xx:xx:xx:xx/64 scope global dynamic mngtmpaddr noprefixroute <--- 注意这个地址 valid_lft 91395sec preferred_lft 4995sec inet6 fe80::xx:xx:xx:xx/64 scope link valid_lft forever preferred_lft forever ...
|
大部分情况下这种情况不会持续太久, 但是有时候老的地址会在 valid_lft 之前实际上失效, 即无法通过该IPv6地址收到来自外部的回包, 但仍然能够通过该地址发送IPv6数据包.
由于WireGuard并不能设置ListenAddress这样的东西, 导致从tcpdump看到远端A能够正常收到来自 240e:xx:3216:xx:xx:xx:xx:xx 的握手包且发送了回包, 但是本端(B)无法正常收到回包导致握手失败.
进一步排查, Linux允许同一个接口有多个IPv6地址, 并且会根据 RFC 6724 来选择出网的IPv6地址的. 可以看到其中并不会根据 preferred_lft 或 valid_lft 进行选择. 而对比到目标地址 240e:xx:3211:... 的最长前缀, 可以发现两个地址到目标地址的最长前缀都是一样的 (0x3211=11001000010001, 0x3216=11001000010110, 0x321a=11001000011010) 因此似乎Linux内核的选择并没有问题, 但实际上会导致通讯一直中断, 直到 240e:xx:3216:xx:xx:xx:xx:xx 这条地址所指定的 valid_lft 归零才会恢复.
解决办法
解决办法有两种:
- 手动(或者通过定时任务)删除
valid_lft 更小的IPv6地址
1
| sudo ip addr del 240e:xx:3216:xx:xx:xx:xx:xx/64 dev ens18
|
- 强制该接口重新配置IPv6, 这会立刻删除掉该接口的所有IPv6地址, 内核会重新创建 link-local address, 并完成SLAAC配置.
1 2
| sudo sysctl -w net.ipv6.conf.ens18.disable_ipv6=1 sudo sysctl -w net.ipv6.conf.ens18.disable_ipv6=0
|
上述命令会使Linux内核创建 fe80::... 这样的 link-local 地址.
如果没有自动获得公网IPv6地址, 对于使用 systemd-networkd 的系统, 可通过 networkctl 触发配置:
1 2 3 4 5 6 7
| $ sudo networkctl IDX LINK TYPE OPERATIONAL SETUP 1 lo loopback carrier unmanaged 2 ens18 ether routable configured
$ sudo networkctl reload $ sudo networkctl reconfigure ens18
|
完成后接口将获取到新的IPv6地址. (不一定和之前的不一样, 但只会有一个)
除了使用 networkctl, 还可以使用 rdisc6 (sudo apt install ndisc6) 命令触发 SLAAC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| $ rdisc6 -1 ens18 Soliciting ff02::2 (ff02::2) on ens18...
Hop limit : 255 ( 0xff) Stateful address conf. : No Stateful other conf. : Yes Mobile home agent : No Router preference : medium Neighbor discovery proxy : Yes Router lifetime : 1800 (0x00000708) seconds Reachable time : unspecified (0x00000000) Retransmit time : unspecified (0x00000000) Prefix : 240e:xx:xx:xx::/64 On-link : Yes Autonomous address conf.: Yes Valid time : 2993 (0x00000bb1) seconds Pref. time : 2993 (0x00000bb1) seconds Recursive DNS server : 240e:xx:xx:xx:xx:xx:xx:xx DNS server lifetime : infinite (0xffffffff) MTU : 1500 bytes (valid) Source link-layer address: xx:xx:xx:xx:xx:xx from fe80::f22f:xx:xx:xx
|