找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4668

积分

0

好友

641

主题
发表于 3 天前 | 查看: 11| 回复: 0

作为一名网络爱好者,工作之余最大的乐趣就是折腾家里那套网络环境。对我来说,打通网络就像是建立了一条条隐秘的通道,充满了成就感。

通常在家折腾网络,主要面临两大挑战:“走出去”(访问外部资源)和“引进来”(从外部访问家庭内部服务)。今天要聊的,就是当“引进来”这条路被运营商堵上时,我是怎么另辟蹊径的。

八月初,我突然发现再也无法从外部连回家里部署的任何服务了。登录路由器管理界面一看,真相大白:运营商给我分配了一个100.64.x.x的私有地址,这意味着我的所有出站流量都要经过运营商的一层 NAT 转换。

PPPoE连接状态截图,显示已获取内网IP地址 100.64.251.126

后来经过一番调查才弄明白原因。原来我用的电信宽带在两年前被免费提速到了300Mbps,而今年8月刚好过了优惠期,套餐被降回原本签约的100Mbps。一同消失的,还有当初费了不少口舌才申请到的动态公网IPv4地址。

我尝试了各种渠道反馈,甚至联系了工信部,但得到的答复基本一致:电信方面收紧了面向个人宽带用户的公网IPv4地址分配政策。现在用户访问互联网IPv4资源时,统一通过运营商的 NAT 设备进行转换。在多次沟通无果后,我也懒得再浪费口舌去扯皮了。

我的套餐是每月79元,带宽100Mbps。相比之下,联通同城套餐58元就能享受1000Mbps。当初选择电信,很大程度上就是看中其相对容易申请公网IP。不过,由于一些其他因素,我暂时不方便更换运营商。既然如此,那就只能靠技术手段来解决问题了。

技术方案的选择

利用 frp、ngrok 这类反向代理工具,是最容易想到的方案。但我个人不太希望让流量经过第三方服务器中转。原因很简单:国内大多数云服务器的特点是“价格高、带宽低”。如果内网有文件共享这类对带宽需求较高的服务,通过服务器中转后,访问速度会非常感人。

其实早在今年3月份,我就在一篇文章里探讨过家庭宽带部署服务的可能性,其中就提到了 TCP 打洞 这种技术。它的原理大致是这样的:一个打洞客户端会主动去连接一些常见的公网服务(比如访问某个网页),并保持这个 TCP 连接(开启keep-alive)。这个操作会在运营商的 NAT 设备上“凿”出一个洞,即建立一个从公网IP和端口到内网IP和端口的映射关系。接着,利用 TCP 端口复用的特性,将这个公网端口转发给内网的特定服务。同时,通过查询 STUN 服务器,可以获知这个映射对应的公网IP和端口号。这样一来,外部用户就可以通过这个“公网IP:端口”直接访问到你内网的服务了。

TCP 打洞对网络环境的要求比较苛刻,需要 NAT 设备支持“端点无关映射”等特性。幸运的是,我当前的宽带环境满足这些条件。经过一番折腾,打洞最终成功了。

新的挑战:动态端口

虽然通过 TCP 打洞成功获得了从外部直连内网的能力,但相比过去拥有固定端口范围公网IP的体验,还是有不小落差。

以前有动态公网IP时,我可以通过 DDNS 服务将变动的IP绑定到一个固定的域名上。各个服务(比如Web服务器、远程桌面)配置在固定的端口,访问时只需要记住“域名:端口”即可。

但在现在的运营商级 NAT 环境下,不仅IP是内网的,连打洞成功后分配的公网端口也是动态的。可能今天成功映射到 12345 端口,明天重启或连接超时后,就变成 54321 端口了。端口不固定,让访问变得异常麻烦。

因此,我梳理了一下家里正在运行的服务,看看如何针对性地进行优化。家里的服务主要分为以下几类:

  • 支持在线点播、可通过 HTTPS 网页访问的媒体服务器。
  • 基于 OpenVPN 技术搭建的虚拟专用网,连接后可以直接访问内网所有设备。
  • 私有的 Gitea 代码托管服务器,需要支持 HTTPS 网页访问和 SSH 克隆/推送。

总结起来,主要的应用场景就是三种:HTTP/HTTPS 网页访问、SSH 连接以及虚拟专用网连接。

场景一:HTTP/HTTPS 网页访问

这类资源的访问体验,是优化后最接近以往的。

某家以“C”字母开头的知名DNS服务商(后文简称C厂)提供了一项“URL重定向”功能,可以利用 HTTP 302 状态码进行跳转。

具体操作分为两步:

  1. 创建一个不经过C厂代理的 DNS A 记录(例如 direct.example.com),用于记录你当前通过打洞获取到的公网 IP 地址。
  2. 再创建一个经过C厂代理的 DNS A 记录(例如 service.example.com),并为这个域名配置重定向规则。

DNS服务商后台的URL重定向配置界面,使用concat函数动态拼接目标URL

如上图所示,在重定向规则中,使用 concat 函数将基础URL(包含IP)和动态端口拼接起来。其中,端口号可以在每次打洞成功后,通过服务商提供的 API 动态更新到这个规则中。

这样,当用户访问 service.example.com 时,请求会先到达C厂的代理服务器,代理服务器查询规则后,返回一个 302 重定向响应,将用户浏览器引导至 direct.example.com:动态端口,最终成功访问到内网服务。整个过程对访问者基本透明。

场景二:SSH 连接

SSH 访问主要用于操作私有 Git 仓库。在端口动态变化的情况下,传统的访问方式显然行不通。好在 natmap 这个打洞项目提出了一种 IP4P 格式,它巧妙地将 IPv4 地址和端口号编码成一个类似 IPv6 的地址,并存储在 DNS 的 AAAA 记录中。使用时,通过脚本从这个“伪IPv6”地址中解码出真实的IP和端口即可。

但我个人觉得,IP4P 本质上每条 AAAA 记录只能对应一个端口。如果你有多个服务(SSH、RDP等),就需要配置多条记录,有些冗余。于是,我“融会贯通”,自己设计了一种基于文本的格式,称之为 POT 格式(Port Over TXT)。

假设你有 SSH、远程桌面和 HTTP 三个服务,可以按 服务名称:端口 的格式记录,每行一个服务:

ssh:12032
rdp:22389
http:34567

然后,将这段文本用 Base64 编码:

c3NoOjEyMDMyCnJkcDoyMjM4OQpodHRwOjM0NTY3

最后,将编码后的字符串作为你某个域名(例如 ports.example.com)的 TXT 记录 保存起来。

使用时,脚本会先通过一个固定的 A 记录(如 direct.example.com)获取当前的公网 IP,再查询 ports.example.com 的 TXT 记录,解码后就能获得各项服务的动态端口,从而建立连接。配合 SSH 客户端的 ProxyCommand 配置,可以实现无缝访问。相关的实现代码,我曾向 natter 项目提交过 Pull Request。

场景三:虚拟专用网连接

虚拟专用网的访问需求,本质上和 SSH 类似,都是需要知道 IP 和 端口。但区别在于,SSH 客户端支持通过 ProxyCommand 执行自定义脚本,我们可以在连接建立前动态解析信息。而大多数虚拟专用网客户端是封装好的,不方便额外集成脚本。况且,有些协议的服务端口甚至是固定的,无法适配动态端口环境。

最终,我选择基于 OpenVPN 来搭建。原因很简单——我的主路由(MikroTik RouterOS)原生支持配置 OpenVPN 服务端,修改监听端口非常方便。

服务端配置好之后,关键问题来了:如何让客户端自动适应服务端的动态端口呢?

调研中我发现,DNS 系统里有一种专门记录服务位置的 SRV 记录 类型。它的格式类似于 _service._proto.name TTL IN SRV priority weight port target,本就是为记录服务端口和优先级而设计的,常用于负载均衡等场景。对于需要应对动态端口的家庭用户来说,这简直是个完美的解决方案。

但遗憾的是,现有的主流 OpenVPN 客户端并不支持读取 SRV 记录来解析端口。

要让虚拟专用网连接也用上动态端口,就必须自己动手集成 SRV 记录查询功能。思路有两种:

  1. 修改上层的图形化客户端,在发起连接前查询 SRV 记录,然后将端口信息传递给底层的核心库。
  2. 直接修改 OpenVPN 的核心库,这样无论用户使用哪个平台的客户端,只要核心库被替换,再配合特定的配置文件,就能实现功能。

我选择了第二种方案。我 Fork 了 OpenVPN 3 核心库的代码,为其增加了 SRV 记录解析功能。当配置文件中指定了 remote 指令指向一个域名,且该域名存在 SRV 记录时,核心库会在连接前自动查询该记录,并用查到的端口号覆盖配置文件中指定的端口。

OpenVPN 3 命令行客户端连接成功截图,显示自动获取到内网IP 10.31.0.16

如上图所示,这就是使用我修改过的 OpenVPN 3 命令行客户端成功连接回家的效果。整个过程自动完成了 SRV 记录查询和端口替换,对于使用者而言,配置好一次之后,每次连接就只是点一下“连接”按钮那么简单,完全无需关心IP和端口是否变化。

总结与展望

经过这一番折腾,原本运行在固定端口的服务,基本都成功迁移并适配到了新的“动态端口+TCP打洞”环境。更令人欣慰的是,像 BT 下载这类 P2P 应用,也因为成功的入站连接而恢复了活力,其他节点可以正常连接到我的客户端下载资源。

qBittorrent下载客户端界面截图,显示多个任务正在活跃下载和做种

当然,这一切折腾都是基于当前 IPv4 资源枯竭、运营商实施深度NAT的背景。最终的希望,还是寄托于 IPv6 的全面普及。如果公共场所、酒店网络、公司网络都能提供稳定可靠的 IPv6 接入,每个家庭设备都能获得一个全球唯一的公网地址,那今天讨论的所有端口转发、打洞技巧都将成为历史。

技术的本质就是为了解决问题。当标准道路受阻时,灵活运用 DNS 这类基础服务,结合一些“野路子”,往往能开辟出新路径。希望我的这次“赛博回家”经历,能给遇到类似网络困境的朋友们提供一些思路。如果你也有类似的家庭网络部署经验,欢迎到 云栈社区网络/系统板块一起交流探讨。


引用与项目链接

  1. natter 项目提交的,关于通过TXT记录解析动态端口的 Pull Request:https://github.com/MikeWang000000/Natter/pull/106
  2. 支持 SRV 记录解析的 OpenVPN 3 核心库修改版发布地址:https://github.com/jdjingdian/openvpn3_srv/releases



上一篇:DNS协议请求报文格式详解:从报文结构到代码实现
下一篇:星华辰U40R2真4K 120Hz带鱼屏使用体验分享:Mac外接与色彩校准心得
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-7 18:37 , Processed in 0.874187 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表