家庭网络改造(三)——IPv6
IPv6
家庭网络改造(二)——旁路由openWRT | polar-bear~Blog
距前文已经过去了一坤年(两年半),运营商的公网IPv4不断收紧,超管密码动态刷新,IPv6已经全面普及大势所趋的情况下,这一天终究是到来了吗。
美味新玩具
全屋FTTR套餐,运营商新套餐赠送了中兴ZXHN G7611V2和从机G1612。
装维师傅会按照千兆有线,配置主从机mesh组网。因为电脑的房间里没有预留墙内网线接口,于是网上购入成品隐形光纤,连接主从机PON接入,即可享受极简的走线,1250Mbps的上行,2500Mbps的下行,<1ms的延迟,无感的FTTR Roaming切换。。。(其实这些体验和1000M有线没有区别,正常基本碰不到瓶颈隐形光纤更好看而已,可以贴着墙缝和门缝)
网络拓扑
| 设备或接口 | IPv4 地址 | 用途 |
|---|---|---|
| 光猫 / 主路由 | 192.168.1.1 | 普通设备的默认网关 |
| OneCloud 的 eth0 | 192.168.1.45 | 宿主机、应用和公网服务入口 |
| OpenWrt 容器 | 192.168.1.30 | 手动旁路由及 LuCI |
| 宿主机 macvlan0 | 192.168.1.46 | 宿主机访问 macvlan 容器 |
1 | 光纤入户 |
消失的公网IPv4
路由器获取的ip地址与公网ip不同,并且 属于专为运营商级 NAT 部署而设计的专用空间100.64.0.0/10 ,RFC 6598,是CGNAT。
询问客服,明确现在不再提供公网IPv4 。
IPv6
任意设备打开Test your IPv6 connectivity,测试通过即可。
在 OneCloud 上查看:
1 | ip -6 addr show dev eth0 |
一张网卡可能同时有几个 IPv6 地址:
fe80::开头的链路本地地址,常用于邻居和网关通信。- ULA 地址,家庭网络中常见
fd开头的地址。 - 可用于公网路由的全局单播地址。
- 标有
temporary的临时地址。
将防火墙设置为低
关闭IPv6Spi
挑选一台设备启动监听服务,并且使用手机流量访问其ipv6地址,若能正确访问即配置正常。
直接在浏览器填写 IPv6 时,需要加方括号:
1 | http://[设备的全局IPv6]:8123/ |
OpenWrt
IPv4 部分继续保持:
1 | OpenWrt:192.168.1.30/24 |
配置 wan6
从 OneCloud 进入容器:
1 | docker exec -it openwrt sh |
先备份:
1 | backup_tag=$(date +%Y%m%d-%H%M%S) |
这台 OpenWrt 的 LAN 逻辑接口使用 eth0。wan6 复用 LAN 所在设备,作为 DHCPv6 客户端:
1 | uci set network.wan6='interface' |
这里使用上游所在的同一局域网,不向下游划分新子网,所以没有向上游请求新的前缀委派。需要另外划分 IPv6 子网时,再按照 PD 的拓扑配置。
接着关闭 LAN 的服务发布:
1 | uci set dhcp.lan.ignore='1' |
这里关闭的是 LAN 上的服务器 / 发布功能,wan6 的 DHCPv6 客户端仍然工作。RA、DHCPv6、NDP 可以分别设置,选项含义见 OpenWrt odhcpd 说明。
容器里 IPv6 被禁用的情况
Docker 的网络配置与容器内部接口状态,要分别看。
1 | sysctl net.ipv6.conf.all.disable_ipv6 |
这台设备上三个值都应该是 0。如果为 1,需要启用对应接口。当前容器通过 sysctl 文件持久化这些设置,下面给出同样用途的配置示例:
1 | mkdir -p /etc/sysctl.d |
接口名不同的话,文件里的 eth0 也要修改。修改后直接查路由和连通性,不要只凭某一个 sysctl 值判断 wan6 成功与否,OpenWrt 还有 netifd / odhcp6c 在管理网络。
1 | ifstatus wan6 |
已经有 IPv6 地址并能出站后,再检查主机上的 Docker 网络:
1 | docker network inspect macnet |
本次 macnet 的 EnableIPv6 为 false,但容器内部已经通过上游取得 IPv6。Docker IPAM 管理 IPv6 子网,与容器接口启用后从上游获取 RA/SLAAC 地址,是不同的配置方式;接口能否接收 IPv6 仍要检查。Docker macvlan 文档
手动终端还要看 IPv6 路由
Linux 终端:
1 | ip route |
Windows 可以查看:
1 | Get-NetRoute -AddressFamily IPv6 -DestinationPrefix "::/0" |
如果 IPv6 的下一跳还是光猫,这个终端的 IPv6 流量仍会使用光猫。
先在容器里查 OpenWrt 的链路本地地址:
1 | ip -6 addr show dev eth0 scope link |
需要手动经过 OpenWrt 的 Linux 终端,可用下面的方法临时验证,变量均替换为该终端的实际值:
1 | CLIENT_IFACE='替换为终端网卡名' |
同时确认没有更优先的默认路由。临时路由验证成功后,通过终端的网络管理工具保存配置;IPv6 DNS 的选择也单独检查。
同网卡收发的旁路由还可能产生 IPv6 Redirect。当前 OpenWrt 使用下面的规则,避免手动终端收到重定向后改变路径:
1 | uci set firewall.onecloud_no_v6_redirect='rule' |
普通设备保持原来的自动配置即可。完成容器中的配置后,用 exit 回到 OneCloud 宿主机;前面修改终端默认路由的命令,应在对应终端执行。
1 | exit |
DDNS
现在需要把域名指向承载公网入口的设备全局 IPv6,统一入口使用 OneCloud的IPv6 。
1 | ip.example.com 的 AAAA → OneCloud 全局 IPv6 |
它会从指定网卡选择地址、排除 temporary 等不可用状态、做 IPv6 出站确认、更新 Cloudflare 并读回记录。地址没变时也确认当前 DNS 内容,结果保存在本地状态文件中。
在 OneCloud 宿主机执行:
1 | command -v python3 curl ip |
配置 Cloudflare 记录
先在 Cloudflare 建好当前入口的 AAAA,内容填经过验证的 OneCloud IPv6,关闭代理。
Zone ID 从域名概览取得。脚本支持 API Token,也兼容旧的 API Key + email 配置。下面给 Token 形式的示例,权限需要覆盖对应 zone 的 DNS 读取与编辑。
Record ID 按准确的 hostname 和类型查询:
1 | CF_ZONE_ID='替换为Zone ID' |
编辑配置:
1 | sudo nano /etc/onecloud-ddns/config.json |
1 | { |
修改成自己的内容并保存:
1 | sudo chmod 600 /etc/onecloud-ddns/config.json |
已有 API Key 配置的设备可以继续使用脚本支持的 email 和 api_key 字段。配置文件与脚本分开保存,文章中的值都是占位值。
第一次还没有启用定时器时,先运行一次:
1 | sudo /usr/local/libexec/onecloud-ddns |
能看到 updated、unchanged 或检查通过的结果,再配置定时器。若出现多地址或多记录不明确的错误,先解决选址或记录配置,不随意取列表里的第一个。
用 systemd 定时运行
创建 /etc/systemd/system/onecloud-ddns.service:
1 | [Unit] |
创建 /etc/systemd/system/onecloud-ddns.timer:
1 | [Unit] |
启动:
1 | sudo systemctl daemon-reload |
启用定时器后,手动刷新使用:
1 | sudo systemctl start onecloud-ddns.service |
这样由 systemd 处理同一个服务的并发启动。不要在定时任务还在执行时直接另起一个脚本进程,脚本的锁会阻止重叠执行。
这是一次性任务,执行完出现 inactive 属于正常情况。可以查看:
1 | systemctl show onecloud-ddns.service \ |
判断最近一次是否成功,再看定时器是否仍在等待下一轮。
配置IPv6服务
Home Assistant 8123
Home Assistant 容器使用 host 网络,服务共享 OneCloud 的网络空间。
当前有:
1 | 0.0.0.0:8123 |
它已经双协议监听。IPv6 入站路径通了以后,8123 就能访问,不需要为了它重建一套 Docker IPv6 网络。
1Panel 3000
1Panel 原先只有:
1 | 0.0.0.0:3000 |
0.0.0.0 是 IPv4 的通配监听地址。域名只有 AAAA 时,客户端尝试的是 IPv6,服务没有对应 socket
我这里是 1Panel v1.10.26-lts,使用其原生命令:
1 | sudo 1pctl listen-ip ipv6 |
命令会切换监听并重启 1Panel。CLI 的说明见 1Panel v1 文档。
切换后分别测试:
1 | sudo ss -lntp 'sport = :3000' |
这台主机切换后,IPv4 与 IPv6 均能返回 HTTP 200,局域网电脑的双协议测试也通过。
OpenList 5244
OpenList 使用 bridge 网络,宿主机发布 5244:
1 | 0.0.0.0:5244 -> 容器5244/tcp |
检查时本机和局域网 IPv4 访问正常,IPv6 连接超时,UFW 中没有 5244 的放行规则。
恢复 OpenWrt 的 8000 入口
第二篇曾把 OpenWrt 页面改到 8000,再通过公网 IPv4 的 DMZ 或端口映射进入旁路由。
这次查询后发现,当前 OpenWrt 由 nginx 提供 LuCI,实际监听的是:
1 | 192.168.1.30:80 |
OneCloud 没有监听 8000,容器也没有监听 8000。
AAAA 又指向 OneCloud,所以浏览器连接 ip.example.com:8000 时,先找到的是宿主机。必须在宿主机上安排一个真正的入口。
最终使用下面的路径:
1 | 外部客户端 |
宿主机先能访问后端
macvlan 容器与宿主机不能直接通过父接口互相通信。第二篇创建的 macvlan0 就是用来处理这个问题的,Docker 文档也说明了这一限制和宿主机添加同父接口 macvlan 的方法。Docker macvlan 文档
先检查现有接口和服务:
1 | ip addr show macvlan0 |
这里要能得到后端响应,再继续设置 8000。
如果是刚按第二篇部署的新设备,可以用以下命令建立接口;已经有 macvlan0 的设备不要重复创建:
1 | sudo ip link add link eth0 name macvlan0 type macvlan mode bridge |
.46 要使用未被占用的地址。接口持久化继续使用第二篇的 macvlan-setup.service。如果接口已存在但后端仍不可达,还要检查到 .30 的路由是否确实使用 macvlan0:
1 | ip route get 192.168.1.30 |
如果查询结果没有经过 macvlan0,可先为这个后端增加定向路由:
1 | sudo ip route replace 192.168.1.30/32 dev macvlan0 src 192.168.1.46 |
验证后把相应路由保存进网络初始化服务,避免仅有临时配置。
用 systemd-socket-proxyd 转发
OneCloud 上已经有这个程序:
1 | ls -l /lib/systemd/systemd-socket-proxyd |
它接收 systemd 传入的监听 socket,再建立到后端的连接,双向转发 TCP 数据。Debian 11 对应的说明
创建 /etc/systemd/system/onecloud-openwrt.socket:
1 | [Unit] |
ListenStream指定 TCP 监听地址和端口。BindIPv6Only=both让这个 IPv6 socket 同时接受 IPv4 映射连接。Accept=no把监听 socket 交给一个常驻代理服务处理。
创建 /etc/systemd/system/onecloud-openwrt.service:
1 | [Unit] |
语法检查并启动:
1 | sudo systemd-analyze verify \ |
socket 设置为开机自启,service 会在有连接时被激活。service 不需要另外 enable。
最后放行主机入口:
1 | sudo ufw allow 8000/tcp |
再检查:
1 | systemctl status onecloud-openwrt.socket onecloud-openwrt.service |
本次这两个 unit 已经部署,socket 为 enabled / active,代理服务正常运行。














