最近搬了新家,顺手开了一条 2000M 的宽带,家里的网络设备也正好趁这个机会重新折腾一遍。这篇就当是一次记录,也顺便给以后留个备忘。
为什么没用 BE7200 当主路由
一开始为了省事,我先买了一台 TP-LINK 的 Wi-Fi 7 路由器 BE7200,四个 2.5G 口加四个 1G 口。单看硬件规格其实挺能打,拨号、测速都没什么问题,2000M 的带宽基本也能跑满。
但系统功能就不太够了。我想要的是一个「能自己折腾」的主路由:能自动分流,能屏蔽一些广告和追踪域名,最好还能把代理直接放到网关上,LAN 里的设备不用单独配置就能自动分流。TP-LINK 的固件明显还是面向普通用户的,这些需求基本没法满足。
架构:N100 + PVE + iKuai + Debian
正好手头还有一台几年前买的 N100 小主机,四口 2.5G,拿来跑软路由绰绰有余。最后定下来的架构是这样:
- 底层装 PVE 9.0 作为虚拟化平台;
- 虚拟机 1:iKuai,负责 PPPoE 拨号、DHCP Server、NAT;
- 虚拟机 2:Debian 13,跑 sing-box 作为旁路由网关,负责透明代理和分流。
这样拆开之后,职责会比较清楚:iKuai 只负责「稳定的基础网络」,Debian 负责「很可能被我反复改配置」的代理部分。就算哪天代理被我搞挂了,把 Debian 关掉,家里的基础网络也还能照常用。
网口分配与 PVE 管理口的小技巧
N100 的四个 2.5G 网口分别是 ETH0 到 ETH3。分配方案是:
- ETH0:留给 PVE 管理;
- ETH1、ETH2、ETH3:全部通过 PCI passthrough 直通给 iKuai,其中 ETH1 作 WAN,ETH2/ETH3 作 LAN。
这么做马上会遇到一个问题:iKuai 独占这三个口之后,PVE 的管理口 ETH0 和 iKuai 的 LAN 在物理上就是隔开的。即使两边都配在 10.10.10.0/24 这个网段里,也还是互相访问不了。总不能再浪费一个口,专门把 ETH0 接回 LAN 吧。
解决办法其实也简单:在 PVE 里给 iKuai 虚拟机额外挂一张 虚拟网卡(virtio net),然后在 iKuai 里把这张虚拟网卡和 LAN 口桥接起来。这样相当于在 iKuai 内部搭了一座桥,PVE 的管理网络就能通过这张虚拟网卡接到 LAN 里。ETH0 这个物理口反而可以完全空着,数据只走虚拟网卡。
配完之后,所有端口协商都在 2.5G,基础网络这一层就算理顺了。
Debian 侧:从 Surge 到 dae 再到 sing-box
旁路由这边,我花的时间就多一些。
最想用的其实是 Surge VM Gateway。规则语法熟,日常 Mac 上也一直在用。但现在手头已经没有闲置的 Mac mini 可以拿来当网关了(一台在日本,另一台在另一个家里),所以只能看 Linux 方案。
第二选择是 dae。dae 的设计很简洁,基于 eBPF,功能不多但刚好够用,本来挺合我胃口。结果试了一下发现它不支持 Shadowsocks 2022,而我用的大部分都是 ss2022 和 snell,没办法,只能放弃。
最后还是落到了 sing-box 上。sing-box 的协议支持很全,但配置复杂度也确实有名,官方文档又分散在一堆子页面里,手写 JSON 会很费劲。我比较懒,直接把 ssh 交给 Claude Code(Opus 4.7),再把我 Surge 的 conf 文件丢过去,让它参考里面的分流规则,直接生成并部署 sing-box 配置。效率确实高了很多。
IPv6 踩坑:为什么要造一个 ULA 前缀
配完之后,v4 用起来没问题,但 v6 开始有点意思了。
先说下背景。我的代理节点是有 global v6 的,宿主机走代理 curl -6 ip.sb 也能正常返回 node 的 v6 地址。但是 LAN 里的客户端(手机、Mac)拿到的 v6 只有 link-local fe80::,基本等于没有可用的 v6 地址,自然也发不出 v6 流量。
原因是 iKuai 这边没有 v6 上行,也没有从上游拿到任何可以下发给 LAN 的 v6 前缀。那这种情况下,要怎么让客户端「用上 v6」?
答案是:自己在 LAN 里造一个 v6 前缀。具体做法是在 Debian 上用 radvd 广播一个 ULA(Unique Local Address) 前缀 fd00:dae::/64,让 LAN 客户端通过 SLAAC 自动配出一个 ULA 地址。
这里的关键是:ULA 在 v6 里有点像 v4 的 10.0.0.0/8 这种私有地址段,不能直接出公网。但我并不需要 ULA 真的能跨运营商路由出去。客户端发出的 v6 流量会被 sing-box 的 TUN 截获,最后通过 proxy node 的 global v6 出境。外面看到的源地址是 node 的 IP,不会看到 LAN 里的 ULA。对我这套环境来说,ULA 更像是一张 v6 世界里的「临时发车牌照」:先让客户端愿意生成 v6 流量,后面再交给 sing-box 送到真正有 v6 的出口。
除此之外 Debian 侧还要做几件事才能让这套跑起来:
net.ipv4.ip_forward=1、net.ipv6.conf.all.forwarding=1、net.ipv6.conf.ens18.forwarding=1(radvd 启动时会专门检查接口级别的 forwarding);send_redirects=0:不然 kernel 会发 ICMP Redirect,告诉客户端「直接找 10.10.10.1 更快」,客户端就会绕过旁路由;iptables FORWARD放行 tun0 的入和出:Docker 会把 FORWARD 默认策略改成 DROP,导致 UDP / ICMP 在ens18 → tun0这条路径上被丢掉,TCP 反倒没事(因为走的是 REDIRECT 转本地,不走 FORWARD);- 给 ens18 挂上
fd00:dae::1/64的静态地址:sing-box 的auto_redirect是从接口上读 v6 前缀加进 nftables 的inet6_local_address_set的,没有这个地址,LAN 前缀就不会进 set,v6 转发规则全部失效(参见 sing-box issue #3698)。
Happy Eyeballs 的奇怪现象
v6 通了之后,又遇到一个挺微妙的问题:打开国内一些 App(微信、抖音、小红书之类)的时候,经常要点好几次内容才加载出来。网页和国际 App 倒是完全没问题。
排查了一圈,原因是这样的:
TUN + radvd 让 LAN 客户端拿到了 v6 地址,所以客户端发起新连接时会做 Happy Eyeballs:同时试 v4 和 v6 两条路径,哪条先连上就用哪条。
国内大厂的 API 域名现在基本都有 AAAA 记录(腾讯、阿里、字节都在上 v6),但它们的 v6 CDN 通常只在国内 ISP 的 v6 链路上好用,也就是得从运营商的 v6 线路直连过去。
而我这边的出口情况是:
- 代理走 v6:proxy node 在海外,有 global v6,能连上国内站点的 v6,但路径绕了一大圈,延迟很高;
- 直连走 v6:Debian 的 direct outbound 走 ens18,ens18 只有 link-local v6,宿主机的 v6 主路由表里根本没有 default v6(default v6 被 sing-box TUN 接管了,放在 table 2022 给代理用),直连 v6 包发不出去,最后只能超时。
按照我的分流规则,国内 App 的 API 命中 geosite-cn → 走 direct → 客户端先试 v6 → 超时(默认 250ms 到 2s)→ Happy Eyeballs 回退 v4 → 成功。于是第一次连接会卡一下,再加上 App 自己的重试 / 缓存逻辑把这个体感放大了,看起来就像是「点一下没反应,再点一下才行」。
国际站不受影响,是因为它们命中 proxy 规则,代理节点有真 v6,直接就通了。
解法:direct outbound 强制 ipv4_only
解决也很小,只要给 sing-box 的 direct outbound 加一个 ipv4_only:
- 命中 direct 的流量只拿 A 记录,客户端只走 v4,不再先试一条注定会超时的 v6;
- 代理出口(proxy / ai-vless)继续保留 v6 能力,node 有 v6,外部 v6 站照样快;
- 国内 v6 原本就跑不通,这个限制等于把一个坏选项直接从菜单里删掉,不用再浪费 2 秒等它失败。
配完之后,国内 App 的点按体感马上就正常了。
小结
整套架构跑下来,主路由 iKuai 管稳定,旁路由 Debian 管分流,管理面板用 metacubexd,v4/v6 都能用,家里每台设备不用单独配置就能自动走分流。相比之前一台 BE7200 裸拨号的方案,可玩性和可控性确实高了不少。
唯一比较「耗心力」的是 v6 这一块,踩了好几个坑才理清:「LAN 要有 v6 地址,客户端才会发 v6」「ULA 不是为了出公网,而是为了让客户端愿意生成 v6 流量」「direct 出口没 v6,但客户端有 v6,就会触发 Happy Eyeballs 回退」。这些点想明白之后,以后再配类似环境应该会轻松很多。
接下来还有几件事想慢慢补上:先把分流规则再细一点,尤其是国内 App、AI 服务、流媒体这些容易走错出口的场景;再加上 DDNS,方便在外面稳定访问家里的入口;最后把 Tailscale 也配起来,之后人在外面排查网络或者访问家里服务会省事很多。
AP 也想换掉。现在 BE7200 在这套架构里只是当 AP 用,感觉有点大材小用。也许换成小一点的 Wi-Fi 7 AP 就够用了。