Skip to content
Vincent's Notes
Go back

Tinkering with Home Network: N100 + PVE + iKuai + sing-box

by Vincent Yang

最近搬了新家,顺手开了一条 2000M 的宽带,家里的网络设备也正好趁这个机会重新折腾一遍。这篇就当是一次记录,也顺便给以后留个备忘。

为什么没用 BE7200 当主路由

一开始为了省事,我先买了一台 TP-LINK 的 Wi-Fi 7 路由器 BE7200,四个 2.5G 口加四个 1G 口。单看硬件规格其实挺能打,拨号、测速都没什么问题,2000M 的带宽基本也能跑满。

但系统功能就不太够了。我想要的是一个「能自己折腾」的主路由:能自动分流,能屏蔽一些广告和追踪域名,最好还能把代理直接放到网关上,LAN 里的设备不用单独配置就能自动分流。TP-LINK 的固件明显还是面向普通用户的,这些需求基本没法满足。

架构:N100 + PVE + iKuai + Debian

正好手头还有一台几年前买的 N100 小主机,四口 2.5G,拿来跑软路由绰绰有余。最后定下来的架构是这样:

这样拆开之后,职责会比较清楚:iKuai 只负责「稳定的基础网络」,Debian 负责「很可能被我反复改配置」的代理部分。就算哪天代理被我搞挂了,把 Debian 关掉,家里的基础网络也还能照常用。

网口分配与 PVE 管理口的小技巧

N100 的四个 2.5G 网口分别是 ETH0 到 ETH3。分配方案是:

这么做马上会遇到一个问题: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 侧还要做几件事才能让这套跑起来:

Happy Eyeballs 的奇怪现象

v6 通了之后,又遇到一个挺微妙的问题:打开国内一些 App(微信、抖音、小红书之类)的时候,经常要点好几次内容才加载出来。网页和国际 App 倒是完全没问题。

排查了一圈,原因是这样的:

TUN + radvd 让 LAN 客户端拿到了 v6 地址,所以客户端发起新连接时会做 Happy Eyeballs:同时试 v4 和 v6 两条路径,哪条先连上就用哪条。

国内大厂的 API 域名现在基本都有 AAAA 记录(腾讯、阿里、字节都在上 v6),但它们的 v6 CDN 通常只在国内 ISP 的 v6 链路上好用,也就是得从运营商的 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

配完之后,国内 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 就够用了。


Share this post:

Next Post
I Got My Lost Phone Back in Japan

Comments 0

Finished reading? Why not leave a word.