Android 热点端口转发:三个入口
手机、运营商路径和 VPN 出口暴露的是不同的边界。在打开任何端口之前,先弄清监听套接字属于哪台机器。
你打开热点、连上 VPN、在应用里开了端口,另一台设备却依然连不上。缺失的一环几乎从来不是端口本身,而是监听套接字归哪台机器、另一台设备应该访问哪个地址。我从手机往外梳理,因为你所在的界面会告诉你,你实际在测三个入口中的哪一个。
从服务真正运行的地方开始
打开应当接受连接的应用,读它自己的状态页。如果它说在监听,记下它绑定的地址,因为这一个细节决定了下面的大多数情况。绑定到 127.0.0.1 的应用只能从手机本身访问,任何热点客户端、运营商映射或 VPN 出口都不会改变这一点。
然后确认哪台设备在运行服务。要么是手机,此时套接字属于该应用;要么是热点后面的笔记本电脑,此时监听器和它的防火墙属于笔记本,手机只负责转发数据包。这是两种不同归属的设计,把它们混在一起,正是在一处打开的端口在另一处毫无作用的原因。
在可调试的版本上,adb shell 可以通过 /proc/net/tcp 列出套接字,但对大多数人来说,应用自己的状态才是诚实来源。它还能避开一种陷阱:去测试应用在没预料到的地址上打开的套接字。
第一个入口:热点上的另一台设备
这是本地场景,也最容易做对。另一台设备通过它看到的网关地址、访问应用监听的端口,热点之外什么都不需要参与。既不需要运营商映射,也不需要提供商的任何功能,因为流量从不离开本地网段。
有两种情况会破坏它。应用绑定到了回环地址,或者手机上的客户端策略不允许本地连接。如果应用要做认证,就用应用自己的协议测试,而不是裸 TCP 连接,因为一个先接受再拒绝的监听器,从远处看与关闭的端口毫无差别。
如果服务其实跑在与手机相连的笔记本上,手机的 hotspot 地址根本不是目标。你需要热点接口上笔记本的地址,以及笔记本上的监听器,此时手机除了转发不再拥有任何东西。
第二个入口:运营商路径
从移动网络访问手机是另一个问题,多数时候对 IPv4 根本不成立。运营商会把手机放在共享转换之后,RFC 6598 正是为这种安排预留了共享地址段。未经请求的入站连接没有可用的映射到达,本地配置再怎么做也造不出来,因为转换器并不是你的设备。
root 权限也改变不了这一点。它能让你控制手机的协议栈,而不是运营商的转换器;声称相反的教程通常假设了一个运营商从未分配的公有地址。在运营商提供公有 IPv6 前缀的地方,入站路径可能存在,但监听器和手机的防火墙仍需要明确的授权,才会有响应。
第三个入口:VPN 出口
隧道开启时网站看到的地址,是被覆盖的出站流量在出口的地址。它并不等于一个代替你接受入站连接的地址。要用它做入站,需要提供商真正提供的功能,例如端口转发或绑定到你账户的入站映射,还需要一条从出口到客户端的可用路径。
本地放行规则造不出那种映射。它能做的是改变应用回复走哪条路径,这在应用被排除在隧道之外时很关键:请求和回复可能落在不同的网络上下文里,连接会以一种看起来像防火墙故障的方式失败。
先端到端测一条路径,再给归属方命名
测试前把元组写下来:预期源、目的地址、传输协议、端口,以及拥有监听套接字的那台机器。任何一项靠猜,测试都不会告诉你有用的事,最后你只会为了解释第一层而去改第二层。
然后带着应用自身认证完整地测那一条已授权的路径。不要为了一个含糊的公有地址检查能得到响应,就发布宽泛的监听器或关掉无关的控制项,因为这两处改动都会比测试活得更久,也都不证明设计成立。
Android 的 tethering 子系统有自己独立的转发路径,与应用常规的 VPN 抓取分开,所以连着热点笔记本上的浏览器能用,并不能说明手机上的服务可以被入站访问。在你称其可用之前:
- 监听套接字位于预期对端可以到达的地址上,而不是回环。
- 对端使用的地址属于那个入口,而不是状态页上的出口地址。
- 运营商或提供商一侧的任何映射都是受支持的功能,而不是你本地添加的规则。
- 请求与回复走同一个网络上下文,应用的认证依然通过。