在家里架设软路由踩坑记录
我不是从事跨境电商的。架软路由的主要动力是不想在每台电脑上单独装代理软件——尤其是 Windows,Docker、NPM、PNPM 有时候需要各自单独配置代理,TUN 增强模式也时不时出问题,烦不胜烦。现在的架构还需要美国静态 IP,防止跨境电商账号被封。最后的方案是买了一台 FriendlyElec R3S,刷上 ImmortalWrt,让它接管整栋房子的出口流量。
文章分两部分:第一部分是操作记录,第二部分是这个过程里我搞清楚的一些网络概念。
第一部分:怎么搭
材料准备
硬件
- FriendlyElec R3S:软路由主机,运行 ImmortalWrt
- 小米 BE3600:WiFi 7 路由器,约 150 元,设成有线中继(纯 AP,只管 WiFi)
- 交换机:把光猫、R3S、BE3600 都接进来
- TF 卡 32GB + 读卡器:ImmortalWrt 系统装在 TF 卡里
- 网线 ×3
软件/服务
- 代理节点:用自己的梯子服务商即可。获取 config 的方式有两种:在服务商后台直接复制订阅链接(格式选 Clash),或者在 Clash / Clash Verge 里导出现有节点信息。订阅链接格式通常是
https://xxx.com/link/xxx?clash=1,直接填进 OpenClash 的订阅管理里就能自动拉取。 - 美国静态住宅 IP(可选,跨境账号隔离用):我用的是 9981proxy,按账号购买独立的静态 SOCKS5 端点,每个账号分配一个固定的美国 IP。
网络拓扑
最终跑起来之后的接线是这样的:
R3S 作为网关,所有设备流量经过它,OpenClash 在这里做分流和代理。BE3600 只负责 WiFi 覆盖,不做任何路由。
用交换机而不是把 R3S 串在光猫和路由器之间,是为了容灾:R3S 挂了可以直接把设备接回光猫,不影响基本上网。代价是双重 NAT(光猫一层 192.168.0.x,R3S 一层 192.168.10.x),对游戏和 P2P 有影响,普通上网没问题。
调试顺序
最终拓扑和调试顺序是两回事,不能按最终状态直接接线,否则管理界面进不去。正确顺序:
- 用 Rufus 把 ImmortalWrt 镜像刷进 TF 卡,断电状态下把卡插入 R3S,然后上电。
- 用网线把 R3S 的 LAN 口直连电脑,访问
192.168.1.1进入管理界面,把 LAN IP 改成192.168.10.1/24。操作路径:侧边栏「网络」→「接口」→ 找到 LAN 接口点「编辑」→ 修改 IPv4 地址 → 「保存并应用」。如果应用失败(比如 IP 填错导致路由表坏掉),系统会强制回滚到上一个可用配置。 - 在 R3S 还直连电脑的状态下,通过 opkg 安装插件:
luci-app-openclash:代理核心luci-theme-argon:界面主题,比默认主题好看很多luci-app-ttyd:浏览器内 Terminal,方便不用 SSH 也能执行命令
- 把 R3S 的 WAN 口插到 BE3600 的 LAN 口,电脑接 BE3600 的另一个 LAN 口。
- 在电脑上进入 BE3600 管理界面,把它调成有线中继(AP 模式)。
- BE3600 断电重启,等它以 AP 模式上线。
- 把 R3S 的 WAN 口改接到交换机,完成最终接线。
- 进入 OpenClash 配置页面,把准备好的 config 文件上传进去,启动 OpenClash。
关键约束:
- 第 2、3 步必须在 R3S 直连电脑时做完——改完 LAN IP 之后管理地址就变了,断开直连就再也进不去了
- 第 5 步把 BE3600 调成 AP 模式之前,R3S 必须已经能提供网络——BE3600 切成 AP 后会关掉自己的 DHCP,这时电脑的 IP 必须由 R3S 来分配,否则电脑失去 IP,管理界面也进不去
OpenClash 配置
安装:在 ImmortalWrt 的软件包管理器(opkg)里搜索安装 luci-app-openclash。
我的配置选项:
| 选项 | 我的设置 | 原因 | |------|---------|------| | 运行模式 | Fake-IP | 延迟低,兼容性好 | | 代理模式 | 规则 | 按 config 分流 | | 区域绕过 | 停用 | 分流逻辑已在 config rules 里,不需要再叠一层 | | 域名嗅探 | 启用(config 里已配置) | Fake-IP 模式下还原真实域名必需 | | DNS 代理 | 启用 | 防止 DNS 被 ISP 截获 | | 流媒体解锁 | 停用 | 无需求 |
Config 文件用订阅链接从机场服务商获取,本地做了修改:加入链式住宅 SOCKS5 节点,以及 WebRTC 防泄露的 REJECT 规则。
目前状态
R3S 稳定跑了几周,没有断过。OpenClash 节点偶尔因为服务商端问题掉线,自动切换到备用节点。整体比在每台电脑上管代理省心很多。
后续想做的事:
- 把光猫改成桥接模式,消掉双重 NAT,顺便解决 NAT 类型严格的问题
- 探索 DNS-over-HTTPS 在 ImmortalWrt 上的配置
- 给不同账号做更精细的流量隔离(可以做到按设备)
第二部分:我学到了什么
基础网络
网关和 DNS
- 网关:设备发出的数据包,如果目标地址不在本地子网内,就先发给网关,由网关决定下一跳。在我的网络里,下游设备的网关是 R3S(192.168.10.1),R3S 自己的网关是光猫(192.168.0.1)。
- DNS:把域名翻译成 IP 的服务。浏览器访问
google.com之前,先向 DNS 服务器查这个域名对应的 IP,查询本身也是一个数据包,同样经过网关。 - OpenClash 在两层都介入:
- DNS 层:对特定域名返回"干净"的解析结果,绕开 DNS 污染
- 路由层:根据目标 IP 或来源设备 IP,决定流量走直连还是代理
- 只做 DNS 不够,IP 层的流量不走 DNS
DHCP
设备接入网络时不会自己选 IP,通过 DHCP(Dynamic Host Configuration Protocol)向路由器申请。过程分四步(DORA):
- Discover:设备广播"我需要 IP",发给局域网所有人
- Offer:DHCP 服务器(这里是 R3S)回复"我可以给你 192.168.10.x"
- Request:设备确认"要这个"
- Acknowledge:服务器确认,同时下发网关地址和 DNS 服务器地址
R3S 在 Acknowledge 这步把自己(192.168.10.1)同时作为网关和 DNS 服务器下发——这是 OpenClash 能接管所有设备流量的根本原因。设备拿到 IP 的同时,就被告知"你的一切流量和 DNS 查询都得经过我"。
子网掩码和 /24
- ImmortalWrt 里填 IP 地址必须写
192.168.10.1/24,不能只填192.168.10.1。我一开始只填了 IP,点保存并应用,管理界面直接断开——配置生效但路由表坏掉了。 /24是 CIDR 表示法,对应子网掩码255.255.255.0:这个子网里的所有设备共享前 24 位(192.168.10),最后 8 位是各自的主机编号(0–255)- 没有这个信息,路由器不知道哪些 IP 是"本地直达"、哪些需要转发出去,路由表无法建立
小贴士:IPv4 和 IPv6
- IPv4:192.168.x.x 这种格式,32 位,理论约 43 亿个地址,早就不够用了- IPv6:fd48:e8c1:f89d::1 这种格式,128 位,地址空间几乎不可能耗尽- 我的 R3S LAN 接口同时有 IPv4(192.168.10.1/24)和 IPv6(fd48:e8c1:f89d::1/60)地址
- 当我 IPv4 配置写错(缺 /24)导致 IPv4 路由坏掉时,设备还能上网——因为 IPv6 走了另一条路径。这个现象让我定位到问题是 IPv4 配置,而不是硬件或网线OpenClash 和 Mihomo
OpenClash 是什么,Mihomo 是什么
- OpenClash = 运行在 OpenWrt/ImmortalWrt 上的 LuCI 插件,负责管理界面、规则更新、配置热重载
- Mihomo = 底层真正干活的代理内核(Clash 原作者删库后的社区衍生版)
- Clash = 一套规范:YAML 格式的 config 文件标准 + 代理/分流 API。只要按这个格式写 config,底层是哪个实现都行——这就是为什么机场订阅链接带
?clash=1,为什么网上的 Clash 教程基本通用 - 截图里的
v0.47.097 alpha-smart-g9a3a608是 Mihomo 内核版本号,"alpha-smart"是带智能策略组功能的开发版
Mihomo 的工作原理
Mihomo 是一个普通的用户空间进程,不修改内核,不是驱动。它做三件事:
1. 流量劫持
- Mihomo 自己只是监听端口(7890/7891/7892)
- OpenClash 启动时向 iptables 写入规则,把设备流量重定向到 Mihomo 的端口:
`` iptables -t nat -A PREROUTING -p tcp -j REDIRECT --to-port 7892 ``
- 这条规则在路由决策之前生效:所有进入 R3S 的 TCP 流量,先被转到 7892,Mihomo 在那里接收
- 设备完全感知不到被拦截,以为在正常发包——这叫透明代理
2. 连接识别
- Fake-IP 模式下,设备请求的目标 IP 是假的(
198.18.x.x),Mihomo 需要还原真实域名 - Mihomo 内置 DNS 服务器,每次给设备返回假 IP 时,在内存里记一条映射:
198.18.0.5 → google.com - 后来同一假 IP 的 TCP 连接进来,查映射表拿到
google.com,规则匹配就能按域名进行 - 对于直接用 IP 访问的流量(没走 DNS),Mihomo 用 Sniffer 检查 TLS 握手的 SNI 字段把域名挖出来
3. 规则引擎 + 出站
- 顺序匹配 config 里的 rules,第一条命中就执行
GEOIP查本地 MaxMind GeoLite2 数据库,GEOSITE查 v2fly 域名分类列表,RULE-SET加载外部规则文件——全部本地查询,不走网络- 匹配到出口后,按协议(Hysteria2、VLESS、Trojan、SOCKS5……)建加密隧道,转发流量,回包原路返回
Fake-IP 模式
- 普通 DNS 的问题:设备查 DNS → 拿到真实 IP → 发起连接 → OpenClash 再决定走直连还是代理。DNS 查询已经出去了,即使最终走代理,查询本身已经暴露意图
- Fake-IP 的改变:收到 DNS 查询后立刻返回假 IP(不做真实解析),等设备用假 IP 发起连接,OpenClash 在连接层拿到域名,这时再决定走直连(做真实解析)还是走代理(让代理端解析)
- 效果:DNS 查询和路由决策合并在一起,消掉了"DNS 已出去但还没决定走哪"这个窗口期
配置文件结构
config 是一个 YAML 文件,从上到下分五部分:
全局设置
DNS 配置
nameserver-policy让国内/境外域名各走各的 DNS,互不干扰
坑:DNS 循环死锁
OpenClash 的分流规则依赖 DNS 解析来匹配域名,但境外的 DoH 服务器(如 dns.cloudflare.com)本身需要代理才能访问——而代理还没建立起来,因为 DNS 还没解析成功。这就形成了死锁:代理等 DNS,DNS 等代理。具体表现是:OpenClash 启动后节点测速一直失败,无法切换到可用节点,整个代理功能卡死。
解法有两个:
1. `fallback` 里一定要加国内可直连的 DNS(如119.29.29.29、223.5.5.5)。当境外 DoH 无法访问时,fallback 的国内 DNS 可以直连兜底,让 OpenClash 至少能解析到足够的地址来建立初始连接。
2. DNS 基础设施规则要放在代理规则之前直连(DOMAIN,dns.cloudflare.com,DIRECT),让 Mihomo 在做节点测速时能直接查 DNS,不依赖代理。同时要注意 DNS 污染问题:国内 DNS 对境外域名的解析结果可能被污染(返回错误 IP 或被劫持),所以境外域名必须走境外的 DoH 解析,不能全部用国内 DNS 兜底。这就是 nameserver-policy 分域名走不同 DNS 的原因,而不是直接把所有 fallback 都换成国内 DNS。节点列表(proxies)
- REALITY 借用真实网站的 TLS 证书伪装流量(如
www.microsoft.com),让流量看起来是正常 HTTPS dialer-proxy实现链式代理:连住宅 SOCKS5 时,底层连接由机场节点承载,不从国内直连
策略组(proxy-groups)
分流规则(rules)
- 规则从上往下匹配,第一条命中就执行
RULE-SET引用社区维护的外部规则文件,包含几万条域名/IP,比手写全面no-resolve= 匹配时不做 DNS 解析,直接用 IP 匹配,避免触发额外查询MATCH相当于default,兜底确保境外流量不会直连漏出
代理和隐私
链式代理
- 直接从国内连住宅 SOCKS5(服务器在美国)延迟高、稳定性差
- 解决方案:住宅 IP 的流量先走机场节点(线路稳定),再由机场节点连住宅 SOCKS5
- 实现方式:在住宅 SOCKS5 节点配置里加
dialer-proxy: 住宅中继组,Mihomo 建立到 SOCKS5 服务器的连接时,底层走指定策略组而不是系统默认路由 - 结果:目标网站看到的是美国住宅 IP,连接质量由机场节点保障
住宅 IP vs 机房 IP
- 机房 IP(datacenter IP):ASN 属于 AWS、Cloudflare 等数据中心,平台维护了这些 ASN 的黑名单,很容易被识别和封禁
- 住宅 IP:ASN 属于 Comcast、AT&T 等家庭宽带运营商,看起来是正常用户
- 静态住宅 IP:每次连接同一个 IP,账号历史不会因 IP 变化断裂
- IP 质量评估:住宅 IP 也有好坏之分,部分 IP 段被滥用。可用
ipqualityscore.com或scamalytics.com查欺诈分,低于 50 分比较安全
WebRTC 泄露
- 问题:WebRTC 的 ICE 机制会主动探测本机所有网络接口,通过 STUN 服务器探测公网 IP,这个过程绕过操作系统路由表,系统代理拦不住
- 后果:即使设了代理,浏览器里的 JavaScript 仍能拿到真实公网 IP,暴露给网页
- 我的修复:在 config rules 层直接 REJECT STUN 相关域名:
- DOMAIN-SUFFIX,stun.l.google.com,REJECT
- DOMAIN-KEYWORD,stun,REJECT
- DOMAIN-KEYWORD,turn,REJECT
- DOMAIN-KEYWORD,rtc,REJECT
- 效果:路由器层丢弃 STUN 请求,WebRTC 拿不到公网 IP,所有设备都覆盖到,不依赖浏览器扩展
- 验证:访问
https://browserleaks.com/webrtc,公网 IP 栏为空说明屏蔽成功
平台是怎么检测代理的
平台的检测是多维度叠加打分,不是单点判断:
IP 层
- 查询 IP 的 ASN,机房 IP 直接进黑名单
- 住宅 IP 也有质量问题,被滥用的 IP 段同样有风险
浏览器指纹
- 采集 User-Agent、屏幕分辨率、Canvas 渲染、WebGL 参数、字体列表、时区、语言
- IP 显示在美国,但时区 UTC+8、语言 zh-CN、字体全是中文——这个组合本身就是异常信号
- 解法:Multilogin、AdsPower 等指纹浏览器,每个账号独立 Profile,指纹锁定成对应地区
行为指纹
- 点击速度、操作间隔、滚动行为,异常规律的操作会被识别为机器
- 没有硬件层面的解法,只能靠操作习惯:间隔自然、不跨时区同时登多账号
账号关联
- 同一张信用卡、同一手机号、同一设备 ID、同一 IP 登录过的账号会被关联分析
- 被封账号如果和正常账号有关联,可能连带
我的架构风险点
- 链式代理:出口 IP 是住宅,但路径中间经过机房 IP 的机场节点。深度流量分析可能发现 TTL、延迟特征异常,暴露中间有跳转
- 所有设备共用同一个光猫公网 IP,如果 DNS 代理没有完全接管,DNS 层会留下关联痕迹
- WebRTC 已通过 STUN REJECT 处理,不用担心
NAT 类型
Full Cone vs Symmetric
NAT(Network Address Translation)是路由器把内网 IP 映射到公网 IP 的机制,不同的映射策略是不同的 NAT 类型,直接影响游戏联机和 P2P。
Full Cone NAT(全锥形)
- 每个内网 IP:端口对应一个固定的公网 IP:端口
- 映射建立后,任何外部 IP 都可以主动发数据到这个公网端口,路由器会转进来
- 对 P2P 和游戏最友好:对方可以直接连进来
Symmetric NAT(对称型)
- 每个不同的目标地址,分配不同的公网端口,即使来源 IP:端口相同
- 只接受内网设备主动连过的那个目标发来的回包
- 对 P2P 最不友好:两台都是 Symmetric NAT 的设备,没有中继几乎无法直连
我的情况
- 双重 NAT(光猫 + R3S 各一层)通常会推成 Symmetric
- 游戏联机遇到"NAT 类型严格"的提示,大概率是这个原因
- 解法:光猫改桥接,让 R3S 直接 PPPoE 拨号,消掉一层 NAT,R3S 就能跑 Full Cone
附:一个数据包从我家到美国的路径
几个容易误解的地方:
- VPS 不在国内:机场节点服务器在香港或美国,国内部分只是家到 ISP 出口这一段
- 海底光缆是物理极限:光在光纤里的速度约为真空光速的 2/3,从上海到洛杉矶物理极限约 130ms——这就是为什么西海岸节点比东海岸快
- VPN 和透明代理的区别:VPN 在网络层建隧道,所有流量(UDP、ICMP)都走;透明代理只处理 TCP,由路由器层拦截,设备无感知。我用的是透明代理,Mihomo 在 R3S 上完成所有工作
参考:https://redman.blog/index.php/2026/01/17/router01/
