HTTP 代理跨子网不通问题排查报告
HTTP 代理跨子网不通问题排查报告
IP 说明
文中 IP 地址最后一组已脱敏替换,对照关系如下:
172.20.2.A1— 远程机器(出问题的客户端)172.20.2.A2— 远程子网网关172.20.6.B1— Windows SS 代理服务器172.20.6.B2— 排查用的对比本机172.20.6.B3— OpenWrt 路由器(nginx stream 转发节点)
问题概述
远程机器 172.20.2.A1 配置 HTTP 代理指向 172.20.6.B1:1080,TCP 连接成功但代理不返回任何 HTTP 响应。
网络拓扑
1 | |
- 172.20.6.B1: Windows 机器,运行 Shadowsocks(或类似代理软件),PID 9036,监听
0.0.0.0:1080 - 172.20.6.B3: OpenWrt 路由器,运行 nginx stream 四层 TCP 转发 (
172.20.6.B3:1080 → 172.20.6.B1:1080) - 172.20.2.A1: 远程 CentOS 机器,默认网关
172.20.2.A2 - 172.20.6.B2: 排查过程使用的对比本机
排查过程
第一步:确认基础现象
远程机器代理配置:
1 | |
curl 测试结果:TCP 握手成功,HTTP GET 请求发出后超时,0 字节收到。
1 | |
第二步:检查 TCP 层连通性(远程机器 → 代理)
TCP 端口连通性:端口 1080 可达。
1 | |
ICMP 连通性:ping 正常,无丢包。
1 | |
路由分析:路由表正常,默认网关 172.20.2.A2 可达。
traceroute:第一跳到达网关 172.20.2.A2(~4ms),之后全部无响应(中间节点不响应 ICMP,属正常现象)。
结论:IP 层路由正常,TCP 三次握手完成,ICMP 通。问题不在网络层。
第三步:tcpdump 抓包(远程机器侧)
在远程机器上对代理端口抓包,限制 3 个包以观察握手:
1 | |
抓到的三个包:
- 包 1:
172.20.2.A1:35464 → 172.20.6.B1:1080— SYN(发起连接) - 包 2:
172.20.6.B1:1080 → 172.20.2.A1:35464— SYN-ACK(代理应答) - 包 3:
172.20.2.A1:35464 → 172.20.6.B1:1080— ACK(握手完成)
三次握手完全正常。HTTP 请求发出后,代理端没有任何数据返回,curl 最终超时。
第四步:本机对比测试
本机 (172.20.6.B2) 与代理 (172.20.6.B1) 同属 172.20.6.0/24 子网,用同样方式测试:
1 | |
本机 172.20.6.B2(同子网):
- ping 代理: ✅ <1ms
- TCP 握手: ✅
- HTTP GET 到达代理: ✅
- HTTP 响应: ✅ 200 OK
远程 172.20.2.A1(跨子网):
- ping 代理: ✅ <1ms
- TCP 握手: ✅
- HTTP GET 到达代理: ✅(抓包证实)
- HTTP 响应: ❌ 完全静默
结论:代理服务本身工作正常,但只服务于同子网客户端。
第五步:代理侧抓包确认(关键证据)
在代理机器 172.20.6.B1 上用抓包工具过滤 ip.addr==172.20.2.A1 && tcp.port==1080。
捕获到的 TCP payload(hex):
1 | |
解码后的 HTTP 请求:
1 | |
stream 中仅有此一条请求数据,代理侧无任何回复包。 HTTP 请求完整到达了代理进程,但代理选择静默丢弃,不做任何响应。这是应用层访问控制行为,排除了网络层问题。
第六步:发现 OpenWrt 中转方案
发现网络中有一台 OpenWrt 设备 (172.20.6.B3),配置了 nginx stream 四层 TCP 转发:
1 | |
远程机器改用 OpenWrt 作为代理入口:
1 | |
结果:HTTP 200 OK,通信正常。
根因分析
数据流对比
直连(失败):
1 | |
经 OpenWrt 中转(成功):
1 | |
根本原因
Windows 上的 Shadowsocks 客户端(或其内置的 HTTP 代理组件)配置了 ACL(访问控制),只允许同子网或特定来源 IP 的客户端使用代理。来自 172.20.2.0/24 的请求虽然 TCP 可达,但被应用层 ACL 静默丢弃。
nginx stream 四层 TCP 转发之所以能解决,是因为 SS 看到的连接来源变成了 172.20.6.B3(同子网),不会触发 ACL 拒绝。
解决方案
方案一:使用 OpenWrt nginx stream 中转(已验证可用)
在远程机器上:
1 | |
- 优点:无需修改 Windows SS 配置,立即可用
- 缺点:增加一跳转发,依赖 OpenWrt 设备可用性
方案二:修改 Windows SS 的 ACL 配置
-
确认 PID 9036 对应的确切进程名:
1
tasklist /fi "PID eq 9036" -
根据具体软件修改 ACL:
- Shadowsocks-libev / Shadowsocks-windows: 检查 ACL 配置文件(JSON 中的
acl或allowed_clients字段) - Clash / Clash for Windows: 检查
allow-lan和ipv4配置 - v2ray / Xray: 检查
inbounds[].streamSettings.tcpSettings.acceptProxyProtocol或路由规则 - SSTap: 检查是否启用了 IP 过滤
- Shadowsocks-libev / Shadowsocks-windows: 检查 ACL 配置文件(JSON 中的
-
将
172.20.2.0/24或0.0.0.0/0加入允许列表即可
方案三:在远程机器上运行本地代理客户端
在 172.20.2.A1 上直接安装并运行 SS/v2ray 客户端,不走远程 HTTP 代理,改为本地 SOCKS5/HTTP 代理。
关键排查经验
- TCP 握手成功 ≠ 应用层可达:这次问题中 ping 通、端口通、三次握手正常,但应用层 ACL 拦截了请求
- 代理侧抓包是定位关键:在代理机器上抓包证实了 HTTP 请求已到达但无回复,排除了所有网络层问题
- 对比测试快速缩小范围:本机(同子网)vs 远程(跨子网)的对比直接锁定了子网差异
- 四层转发可以绕过应用层 ACL:nginx stream 这类纯 TCP 转发不改变 payload,但改变了源 IP,从而绕过了基于来源 IP 的访问控制