写给 Geek 们的 IPv6 组网最佳实践系列:处理动态前缀造成的种种问题

303 天前
 raysonx

本文属于《写给 Geek 们的 IPv6 组网最佳实践》系列,旨在为需要小范围 IPv6 组网(如家庭组网)的爱好者们提供最佳实践方案和常见问题解答,部分概念性内容为方便理解可能会过度简化,部分方案可能不适用企业宽带接入、大规模组网等场景。首发于 V2EX ,转载请注明出处。关于此系列的介绍,见 https://v2ex.com/t/955078#reply36 。 勘误等内容将更新在附言区。

问题描述

IPv6 下 LAN 侧每一台机器都会获得独立的公网 IPv6 地址。

目前国内绝大多数地区的家庭带宽是通过 PPPoE 虚拟拨号接入的,下发的 IPv6 前缀会在每次重新拨号后发生变化。这就需要我们重新给 LAN 侧的设备分配 IPv6 地址,以及处理前缀变化造成的种种问题,如服务发现及防火墙配置。

前缀前化导致的断网问题

举例说明

假设光猫运行在桥接模式,路由器拨号获得 IPv6 PD 前缀 2001:db8:beef:1200::/56。路由器下联一个 LAN ,指定 PD 前缀的 SLA ID 为1(也就是从2001:db8:beef:1200::/56 挑选第 1 个(从 0 开始数)/64子网),路由器会将2001:db8:beef:1201::/64 这个子前缀划为给 LAN 。正确配置的情况下所有 LAN 下的设备都将获得2001:db8:beef:1201:xxxx:xxxx:xxxx:xxxx/64 格式的地址。

此时路由器上的 PPPoE 断开后重拨,ISP 下发新的 PD 前缀 2001:db8:dead:ab00::/56。此时路由器应该向 LAN 下的所有设备宣告原前缀2001:db8:beef:1201::/64失效,并重新下发 2001:db8:dead:ab01::/64 前缀,否则 LAN 下的设备可能会因为继续使用老前缀而导网络中断。

解决方法

已经使用纯无状态分配了,但重新拨号后老前缀没有失效?

前面说了,SLAAC 可以由服务器主动通知用户设备前缀失效。但这只是协议支持。如果重新拨号后老前缀没有失效或者新前缀没有下发,说明你的路由器固件存在问题。在条件允许的情况下,换一个其他的的固件吧。

新版 OpenWRT 是没有问题的。

如果使用 VyOS 1.4 ,可以设置 set service router-advert interface eth0 prefix ::/64 deprecate-prefix,其中 eth0 是 LAN 口网卡名。

如果是在 Linux 上跑 radvd ,修改radvd.conf,端号配置中加上 DeprecatePrefix on; ,并配置脚本使 pppoe 重拨后重启 radvd 。

服务发现问题

前缀变化后,所有 LAN 侧的 IPv6 地址都会变化。如何设定 DNS 以及避免服务中断?

防火墙设定问题

如果你想在网关路由器上为每台终端设备配置防火墙规则,但又无从下手(因为终端设备没有固定的 IPv6 地址),可以使用下面的方法。

如何给我的设备设定一个固定后缀?

首先需要明确的是,在使用 SLAAC 无状态地址分配时,路由器只宣告子网的前缀,而 64 位的后缀完全由终端设备自己决定。

目前大多数新的操作系统下,一个前缀会生成两个 IPv6 地址:永久地址和临时地址。

临时地址的后缀完全是随机生成的,用于发起访问,每隔一段时间会自动变化,在一定程度保护你的隐私(防追踪)。Linux 下使用ip a命令或者 mac 系统下 ifconfig 命令,如果一个地址后面有 temporary 标记,则表明这是个临时地址。iOS 系统下,第二个无状态地址是临时地址。中文 Windows 系统下 ipconfig 命令会直接标明哪个地址是临时地址。

永久地址的后缀是由特定的哈希算法生成的。这个哈希由网卡的 mac 地址和一个随机数共同决定。只要你的前缀不变并且 mac 地址不变,后缀就不会变。因此适合对外提供服务。但是如果前缀变了,则后缀也会跟着变化。(当你配置了 ULA 时,因为 ULA 的前缀是不变的,所以这个 ULA 地址是静态的)。iOS 系统连接 Wi-Fi 时,默认会使用随机 mac 地址,因此后缀会经常变化,这个功能可以在 Wi-Fi 设置中关闭。

部分老系统、老的网络管理软件的永久地址仅仅是 mac 地址的变形,这种生成方式叫做 Modified EUI-64 。判断方法为,如果永久地址的后 64 位形如:xxxx:xxff:fexx:xxxx ,即中间 16 位是 ff:fe ,则该地址是使用 Modified EUI-64 生成的。在这种配置下,只要 mac 地址不变,后缀就不会变,因此可以视作固定后缀。这种生成方式会对外暴露你的 mac 地址,看你介意不介意了。

SLAAC 下的后缀可以手动指定。

比如 Linux 下可以用 ip token set ::1234 dev eth0指定后缀固定为 1234 (其中 eth0 是网卡名)。这个命令是运行时命令,无法在系统重启后保持,实际使用时需要配置到网络管理软件(如 Network Manager, systemd-networkd, ifupdown 等)中,例如:

Network Manager

nmcli c mod eth0 ipv6.token ::1234
nmcli c mod eth0 ipv6.addr-gen-mode stable-privacy

ifupdown:

iface eth0 inet6 static
        autoconf 1
        accept_ra 2
        privext 1
        post-up /sbin/ip token set ::11 dev $IFACE

systemd-networkd

[Match]
Name=eth0

[Network]
IPv6AcceptRA=true

[IPv6AcceptRA]
Token=static:::1234

本文到此结束,如果各位有各种系统下的具体兼容情况和配置命令,可以在评论中补充,我会定期更新到附言中或者放到新帖中。

5215 次点击
所在节点    宽带症候群
49 条回复
jsq2627
303 天前
原来可以用负掩码匹配后缀,学到了!之前总是对着防火墙束手无策,最后干脆全放通了。。
basncy
303 天前
如果需要出站"相对"固定 ip, echo '0' > /proc/sys/net/ipv6/conf/wlan0/use_tempaddr
隐私换方便, 适量使用.
Jirajine
303 天前
还是无法解决实际的问题,比如 docker 容器必须使用固定前缀,使用 ula 要能访问 ipv6 网络必须用 nat ,要想容器同时可以公网可达(如 p2p 应用)只能用无状态 NAPT 或者端口转发,这就会涉及地址发现问题:应用不知道自己能通过哪个地址被访问。并且因为动态前缀这个地址还没法手动配置。

固定后缀只能客户端设备自己控制,有些系统( Android )还不好设置。再者使用隐私扩展的临时地址建立出站连接本身也是最佳实践,这时地址还是完全无法管理:前缀后缀全是动态的。
虽然可以通过 mac 地址在 4 层识别,但有些情况应用层防火墙、各种基于规则路由的应用层代理工具等,就不是那么容易访问到 4 层信息了。
1423
303 天前
很实用!
不知道 Mac 能不能固定 SLAAC 下的后缀
raysonx
303 天前
@Jirajine 本系列文章只讨论大多数家庭使用场景下的方案。至于某些情况下(比如 Docker ),可能 NPTv6 比较适合。
另外如果你必须要直接把公网地址分给 docker 容器,还可以有以下选择:
1. 专门开一台虚拟机或者 lxc 给 docker ,这台虚拟机直接从 LAN 上用 SLAAC 拿地址,docker 容器指定--network=host 使用宿主网络栈。
2. docker 容器的网络桥接到 LAN 上,然后跑 SLAAC 。
如果你非要用物理机做路由,下联一个公网的 docker 网络,这可能需要 PD propagation 。 然而现有的软件很少有支持这个的。
raysonx
303 天前
@1423 其实对我来说,我所有的服务都跑在 Linux 上,所以实际上我只为 Linux 设定了固定后缀。其他系统不一定支持手动指定。
实在没办法那就只能用上面说的次选方案了:DHCPv6+短租期。
Jirajine
303 天前
@raysonx 其实这都是正常家庭使用场景下的需求,用 docker 容器跑个 qbittorrent ,挺常见吧。至于第二点“基于规则路由的应用层代理工具” 你知道我指什么,也是非常常见的需求。

第一点写脚本通过 ssh 或 api 从网关拿到前缀并手动配置 ipv6 地址,然后定期重启服务勉强也能用。第二点暂时无解,只能不用 ipv6 。
neroxps
303 天前
非常棒的教学。
raysonx
303 天前
@Jirajine 我只能说环境是死的,人是活的。如果你发现你要解决这问题很麻烦时,可能意味着你这种方案不合适,也许换一种方案问题就迎刃而解。你说的这些问题产生的本质原因是因为你采用的方案引起的,比如我不用你的方案,所以我不会遇到你的问题。

关于 docker 获取动态前缀的问题,我已经提过可以用 host 网络模式解决,而第二问题,其他方案很多,因为不知道你具体的配置方式,无法作答。
Jirajine
303 天前
@raysonx 因为这已经是最容易的方案了,家庭内网下面不同设备通过不同策略路由、有些设备屏蔽互联网访问、有些设备限速等,实现这些基于 ip 地址比基于 mac 地址或 vlan 方便的多。
最基本的,内网设备能够互通,且通过设备向 dhcp 发送的主机名自动生成的域名直接访问。


至于 docker host 模式,很多文章这么写,实际上是 docker 正常配置 ipv6 太过困难的无奈之举。bt 下载要和其他服务走不同的路由出口,所以还是需要独立的 ip 地址以在网关上区分;如果用单独的虚拟机,那硬盘挂载到哪个设备上、存储如何共享,又会产生新的问题。

归根结底还是 ipv6 当前软件支持不完善,最基本的上网使用场景能跑,但 corner case 太多。最近有遇到一个桥接 wifi 和以太网接口的问题,通常情况下很难做到,VMware/VirtualBox 通过 spoof 源 mac 地址的 trick 来实现,需要软件自己干预地址管理/arp ,只支持 ipv4 。
docker 的 ipv6 支持也被骂了很多年了,没啥提升,官方根本不觉得动态前缀是设计中的使用场景。
neroxps
302 天前
@Jirajine 现在 docker 已经支持 ipv6 nat 了。直接和 ipv4 一样 port 映射到本机的 :: 地址监听。解决了动态 ipv6 前缀问题,其实一早有 github 方案第三方解决。
yuchenr
302 天前
我现在用的这个脚本
#!/bin/bash

PREVIOUS_IPV6_ADDRESS="YOUR_PREVIOUS_IPV6_ADDRESS" # 替换为之前记录的 IPv6 地址

while true; do
# 获取当前 IPv6 地址
CURRENT_IPV6_ADDRESS=$(ip -6 addr show dev pppoe0 | awk '/inet6/ {print $2}')

# 比较当前 IPv6 地址与之前记录的地址
if [ "$CURRENT_IPV6_ADDRESS" != "$PREVIOUS_IPV6_ADDRESS" ]; then
echo "IPv6 Changed : $CURRENT_IPV6_ADDRESS"
# 执行相应的操作或触发事件
echo "Restart radvd"
/etc/init.d/radvd restart
# 更新记录的 IPv6 地址
PREVIOUS_IPV6_ADDRESS=$CURRENT_IPV6_ADDRESS
fi

sleep 60 # 每隔 60 秒检测一次 IPv6 地址的变化
done
JoeSmith
302 天前
有一个疑问,本地机器同时有几个 IPv6 地址,访问 internet 的时候主机会选哪个作为源地址呢?只有一个公网 IP 的时候容易选,要是有多个公网 IP 呢?比如我 Lan 上接了俩运营商的路由器,就会广播两个公网前缀。
其实只有一个公网 IP 的时候我也不十分确定,因为我记得好像抓包看到过 internet 访问源 IP 用的 FE 开头的内网地址。
zent00
302 天前
贴一个 VyOS v1.4 的 IPv6 防火墙范例

假设我的 Home Server 地址后缀为 ::1234 ,这台服务器上有一个 WireGuard 监听在 UDP 65000 端口,我想要从公网通过 IPv6 连接家里的 WireGuard ,可以这样添加防火墙规则:

set firewall ipv6-name WAN-LAN-v6 rule 30 action accept
set firewall ipv6-name WAN-LAN-v6 rule 30 destination address ::1234
set firewall ipv6-name WAN-LAN-v6 rule 30 destination address-mask ::ffff:ffff:ffff:ffff
set firewall ipv6-name WAN-LAN-v6 rule 30 destination port 65000
set firewall ipv6-name WAN-LAN-v6 rule 30 protocol udp
raysonx
302 天前
@JoeSmith 地址选择是个很复杂的问题。RFC6724 中定义了源地址选择的流程 https://datatracker.ietf.org/doc/html/rfc6724#section-5

简单来说,如果你同时有联通和电信两家的公网地址,访问电信的站点时会优先选用电信的源地址,访问联通的站点时优先用联通的源地址(按照最长公共前缀匹配)。只有没有公网地址时才会选用内网地址。
LnTrx
302 天前
@Jirajine 没太看懂,Docker 用 macvlan 配 IPv6 公网访问能满足你的需求么?
LodonBoy10086
302 天前
光猫里,路由配置项目怎么设置呢。有三项:动态路由配置、静态路由配置、IPv6 静态路由配置
使用 ULA 后还需要配置 IPv6 静态路由配置?
动态路由配置:RIP V1 V2 这些,家庭用户需要开启么? UPNP 关掉后影响 LOL 这些网游延迟么?
raysonx
302 天前
@LodonBoy10086
除非你有多台设备运行在路由模式下,否则不需要手动配置路由表。

> UPNP 关掉后影响 LOL 这些网游延迟么?
不影响。
qwvy2g
302 天前
其实如果 ipv6 能像有些防火墙那样直接对 mac 地址做策略就好了。
raysonx
302 天前
@qwvy2g 这是不可能的,根本不是一个层级的事。mac 地址不能跨路由器,而且二层甚至不一定是以太网。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/955636

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX