症状
家里一台 iStoreOS 软路由,装 PassWall2(sing-box)做透明代理。某天发现一个问题:
软路由自己 curl https://www.google.com 超时,但手机、台式机走同一个代理却能正常翻墙。
# 路由器本机
curl -s -o /dev/null -w '%{http_code}' --connect-timeout 10 https://www.google.com
# => 000 (连接超时,等了 10 秒)
# 局域网设备(手机/台式机):完全正常
这看起来像"网络的锅",但仔细一想不对劲——局域网设备明明走的就是这台软路由的代理,它们能通,说明节点、规则、DNS 分流都没问题。那问题大概率出在"路由器自身流量"这条路上。
初判:先排除"网络断了"
按老规矩先确认基础设施,一次性把可排除的排掉:
ping -c 2 -w 3 223.5.5.5 # WAN 通
curl -s -o /dev/null -w '%{http_code}' https://www.baidu.com # 国内 200
nslookup www.google.com # DNS 解析正常,返回真实海外 IP
ps | grep -iE 'sing-box|chinadns' | grep -v grep # 核心进程都在跑
结果全正常:WAN 通、国内站 200、DNS 能解析到真实 IP、sing-box / chinadns 进程都在。排除断网,问题 100% 出在"本机流量"这条链上。
关键一步:用 sing-box 自己的 Socks 测一下
这是整个排查里最重要的一步。PassWall2 同时开了 socks 端口(socks_enabled=1,1070),而 socks 流量走的是同一套分流规则。所以让本机通过 socks 访问目标站:
curl -s -o /dev/null -w '%{http_code}' \
--socks5-hostname 127.0.0.1:1070 https://www.google.com
# => 200 !!! (直接访问是 000,走 socks 却 200)
结论瞬间锁定:sing-box 节点健康、分流规则正确、代理链畅通。目标站通过代理完全可达。那唯一的解释就是——路由器自己发起的外部流量没有进入 sing-box。既然流量没进代理,就被当作普通直连发去了 WAN,然后被墙。
深入 iptables:本机 TCP 和 UDP 走的是两条链
PassWall2 对本机流量的处理,TCP 和 UDP 是分开的,容易让人踩坑:
# mangle 表 OUTPUT 链 —— 本机 UDP 走这里(TPROXY)
iptables -t mangle -L OUTPUT -n -v
# RETURN all lo
# PSW2_OUTPUT udp <-- 只有 udp!
# RETURN all mark match 0x50535732
# nat 表 OUTPUT 链 —— 本机 TCP 走这里
iptables -t nat -L OUTPUT -n -v
# REDIRECT tcp lo dpt:53 -> 11400 (DNS)
# PSW2_OUTPUT tcp <-- TCP 在这条链!
关键就是:本机 TCP 走 nat OUTPUT -> PSW2_OUTPUT,本机 UDP 走 mangle OUTPUT -> PSW2_OUTPUT,是两条不同链路。很多人只查 mangle 那条,就漏了 TCP。接着看 nat 的 PSW2_OUTPUT 链末尾兜底规则:
iptables -t nat -L PSW2_OUTPUT -n -v | grep 'redir ports 1041'
# REDIRECT tcp multiport dports 22,25,53,...,80,443 redir ports 1041
这条兜底会把本机所有走代理端口的 TCP 连接重定向到 sing-box 的 1041(redirect_tcp inbound)。理论上 curl google:443 应该命中它。
决定性证据:包确实重定向了,但 sing-box 没收到
为了抓现行,我盯着这条兜底规则的计数器,同时跑一次 curl:
# 记下计数器 -> curl google -> 再看计数器
iptables -t nat -L PSW2_OUTPUT -n -v | grep 'redir ports 1041'
# 1 packets -> 2 packets <-- curl 的 SYN 真命中了 REDIRECT,被重定向了!
# 但 sing-box 日志却一片空白
tail /tmp/etc/passwall2/acl/default/global.log
# (空) <-- 没有任何连接记录!
这里就是破案点:包明明被 REDIRECT 到了 1041,计数器也涨了,但 sing-box 完全没收到这个连接。 流量在"被送往 1041"的半路上就消失了。
根因:route_localnet = 0
REDIRECT 本质是 DNAT,本机 OUTPUT 链把目的地址改写成了回环地址 127.0.0.1:1041。但 Linux 内核有个默认参数:
sysctl net.ipv4.conf.all.route_localnet
# => 0
route_localnet 控制能否把数据包路由到 127.0.0.0/8 回环网段。默认是 0,意思是:任何发往 127.0.0.0/8 的包视为非法目标,内核直接丢弃。
于是链路是:
- 本机 curl google:443 → 路由决策(走 main 表 pppoe-wan 直连)
- nat OUTPUT 兜底规则命中 →
REDIRECT 127.0.0.1:1041 - 内核发现目标是
127.0.0.1,而route_localnet=0→ 拒绝,包被丢,连 TCP 三次握手都建立不起来 - sing-box 收不到连接(日志空)→ 客户端干等 10 秒超时 →
000
那为什么手机、台式机没这个问题? 因为它们走的是 mangle/nat PREROUTING(进站流量),REDIRECT 的目标是局域网 IP(比如 192.168.100.1:1041),不是回环地址,所以根本不涉及 route_localnet。这就是"设备正常、唯独路由器自身连不上"的机械原理。
修复:打开 route_localnet 并持久化
一条命令临时生效:
echo 1 > /proc/sys/net/ipv4/conf/all/route_localnet
echo 1 > /proc/sys/net/ipv4/conf/lo/route_localnet
curl -s -o /dev/null -w '%{http_code}' --connect-timeout 10 https://www.google.com
# => 200 (0.3 秒,立刻通了!)
但 echo 1 > /proc 重启就没了,必须持久化。OpenWrt/iStoreOS 有两个配置位置,我都写上(防止被 /etc/sysctl.d/* 里的默认文件覆盖):
# 1) /etc/sysctl.conf 一份
echo 'net.ipv4.conf.all.route_localnet=1' >> /etc/sysctl.conf
echo 'net.ipv4.conf.lo.route_localnet=1' >> /etc/sysctl.conf
# 2) 高序号 sysctl.d 文件一份 —— 数字越大越后加载,保证最后生效
printf 'net.ipv4.conf.all.route_localnet=1\nnet.ipv4.conf.lo.route_localnet=1\n' \
> /etc/sysctl.d/99-route-localnet.conf
# 重新应用并验证
/etc/init.d/sysctl restart 2>/dev/null || sysctl -p
sysctl net.ipv4.conf.all.route_localnet # => 1
这样重启路由器也能保持。
一个容易误判的坑:别把"节点问题"当成"修好了"
修好 route_localnet 之后,我又多测了几次,发现 google 是稳定 200 但是偶发 000,而 wikipedia、bing、twitter、facebook 全都是稳定 200,一次超时都没有。注意:
# 用 socks 连续测多个境外站,避开本机链路的干扰
for s in https://www.google.com https://www.wikipedia.org https://www.bing.com; do
curl -s -o /dev/null -w "%{http_code}" --socks5-hostname 127.0.0.1:1070 $s; echo " <- $s"
done
# => google 偶发 000,wikipedia/bing 稳定 200
如果只是 google/youtube 这类 Google 系 IP 偶发超时,而其他境外站稳定,那说明本机代理链路已经完全通了,剩下的抖动是当前节点对 Google 系 IP 的访问不稳定(节点质量问题),跟 route_localnet 无关。这时候别再怀疑配置了,要么换个节点,要么等节点稳定。
复盘
- 设备正常 ≠ 路由没问题。凡是"某台机器走代理正常、路由器自身却不行"的,第一反应要想到本机出站走的是
OUTPUT链,不是PREROUTING链,两者是独立的代理通道。 - 用 socks 测 sing-box 是最快的二分法。socks 走同一套分流规则,能 200 就立刻把"节点/规则"和"本机流量通道"切开,直接把排查范围缩小 50%。
- REDIRECT 到回环需要
route_localnet=1。这是透明代理(尤其 sing-box/PassWall)里最容易忽略的内核参数,记它一次,省下次半天时间。 - 计数器涨但日志空,是典型的"流量死在路上"——重定向规则匹配了,但承接方没收到,优先查内核路由参数,而不是 rule 本身。
这次的经验就是一句话:透明代理的本机流量,先查 route_localnet,再查 iptables。
2026-09-08 · 写于深夜
💬 评论 (0)
💬 发表评论需要先登录
还没有评论,来抢沙发吧