分享一个在 NAT1 下将端口打开到公网上的方法

2022-08-09 20:38:13 +08:00
 delpo

前言

最近在机缘巧合之下入了几个 pt 大站。大站下载人数多,总的来说还是比较好混的。但是为了永久保留账号,需要刷一定的上传流量。在苦恼于硬盘空间不足无法大量保种的同时,还有一个很重要的问题就是没有公网 ip 。家里的宽带是电信内网 v4+公网 v6 ,虽然有 ipv6 地址,并且大部分 pt 都支持 v6 tracker ,但是总的来看 ipv6 无论是做种还是下载人数都是远远少于 ipv4 的。所以一直想要一个公网 ip 。给运营商要公网 ip 好几次,都被打太极给拒绝了。因为之前研究过 nat 以及打洞相关的知识,就想研究一下没有公网 ip 是否也可以把某个端口暴露在公网上。恰好家里宽带是 nat1 ,经过一番折腾,发现了一种可以在 nat1 下将某个端口暴露在公网的办法,在这里分享给大家。

准备工具

预备知识

具体步骤

1 第一步当然是打开 BT 客户端。这里以 qbittorrent 为例,假设 qbittorrent 的 BT 监听端口,即 local port 为 12345

2 将 local port 和 nat 防火墙对应的 external port 之间的映射打开。由于 qbittorrent 已经监听了 12345 端口。所以无法通过创建一个新 socket 进行发包。当然,你可以重新编译 qbittorrent ,加上 SO_REUSEPORT 标志,但是这里为了快速实验,使用基于 raw socket 的 sendip 工具进行网络包创建,命令如下:

watch -n 1 sendip -p ipv4 -is <LOCAL_ADDR> -id <YOUR_VPS_ADDR> -p tcp -ts 12345 -td <ARBITRARILY_PORT> <YOUR_VPS_ADDR>

这里使用 watch 命令每秒执行一次 sendip 命令,是因为我这里的 CGN nat 在未建立连接的情况下超时时间非常短,只有 5 秒。作为对比,linux 下 iptables 的 conntrack 模块在 NEW 状态下会持续 180 秒。所以需要每秒发一次包保持连接。注意,这个命令在之后需要一直运行以保持 nat 映射,即使已经端口映射成功。

命令中-td 表示的目标端口可以随意指定

3 在 vps 上抓包获取 external port 。使用 tcpdump 抓取上一步中发送的网络包,从而获取到 local port 12345 对应的 external port ,这里假设为 54321 。

4 本地部署 mitmproxy ,抓取 qbittorrent 的 tracker 请求。这里 qbittorrent 需要修改三个设置:

  1. Connection -> Peer connection protocol 改为 TCP 。因为 tcp 和 udp 在 nat 防火墙上映射得到的端口是不一样的,所以想同时映射 udp 的话需要再执行一次本教程。
  2. Connection -> Proxy Server 设置 http 代理为 mitmproxy 的监听端口。注意这里不要勾选 Use proxy for peer connections ,因为我们只需要中间人 tracker 请求。
  3. Advanced -> Validate HTTPS tracker certificate 取消勾选。因为解密 https 需要中间人证书,这里直接忽略校验证书,省去了配置 mitmproxy 自签名证书的步骤。这样就可以在 mitmproxy 中抓取到 tracker 请求了。

5 通过 mitmproxy 自定义脚本修改 tracker 请求中的 port 参数。官网有一个示例,将参数换为 port ,参数的值替换为第三步中得到的 external port 54321 即可。

6 大功告成

结果

我从以下三方面验证了内网的 12345 端口已经打开在了公网上:

  1. 通过另外设备(手机)或者站长工具里的tcp ping,可以 ping 通 54321 号端口。
  2. pt 站的个人信息页面里会显示客户端的 ip 以及端口,可以确认端口变为了 external port 54321 ,同时连接性为绿色。
  3. qbittorrent 中,在种子的 peers 选项卡里,可以看到很多 peers 的 Flags 一栏中有 I 标志,意味着是由其他 peers 主动连接的。

问题

  1. ipv6 。将 ipv4 的 external port 修改以后,在 pt 站的个人信息里,ipv6 的 external port 也改变了,导致 ipv6 无法接受入站连接。经过初步调研发现,似乎是 bt 标准对 ipv4&v6 双栈情况下的支持问题,即使 tracker 请求是 ipv4 发出的,qbittorrent 也会在参数上附加 ipv6=xxx 。尝试过将 v6 请求不经过 mitm 修改,也无法解决问题。其实个人也不推荐这么做,如果你的 v4 和 v6 端口不一样,可能会被管理员怀疑使用魔改客户端导致封号。一个变通的方法是,在本地使用 ip6tables 转发 ipv6 的 54321 端口到 12345 。
  2. nat 映射断开,事实上,虽然使用 watch 命令每秒发送一次数据包维持映射,但是在我实际测试中,还是有两次映射改变的情况,其中一次是因为宽带重拨,另一次我没有捕捉到原因。每次映射改变,都需要在 mitmproxy 脚本里更改 external port ,可以说是很不自动化。
  3. 这种方法只适用于通过 tracker 连接 peers 的情况,DHT 下似乎用不了?不过问题不大,毕竟是为了 pt 折腾的。

总结

可以看出,通过(很麻烦的)折腾,是可以达到将 BT 客户端的 nat1 端口映射到公网的目的的。这主要归功于 nat1 的特性,所以显然 nat234 都不能使用。这个方法应该只在 PT 的情况下比较实用,毕竟如果是打洞的话,有一台有公网 ip 的 vps 就已经可以完成打洞了。这次试验结果来看还是很成功的,发现了一种将 nat1 主机的某个端口打开到公网的方法,但是使用上述方法显然不能完成自动化,大部分操作都需要手动配置。所以如果可以开发一款 C/S 软件,自动化的打开 nat 映射,服务端抓包,获取外部端口,结果发送到客户端,可以极大地简化上述操作,并且在 nat 映射发生改变的情况下自动的更改外部端口。

5033 次点击
所在节点    宽带症候群
31 条回复
br2049
2022-08-09 21:05:59 +08:00
感谢 思路很清晰
terrancesiu
2022-08-09 22:13:33 +08:00
很厉害,学到了
LnTrx
2022-08-09 22:32:11 +08:00
很好的想法。过程中涉及很多方面技能的综合运用,或有用于教学的潜力。
ScotGu
2022-08-09 22:52:40 +08:00
谢谢,然后拿起电话继续拨打一万号要公网 IP 。
v2tudnew
2022-08-09 22:53:48 +08:00
如果有软件自动化还是不错的。
isad
2022-08-09 23:36:56 +08:00
我也这样设想过,但没有实施,因为我发现 DHT 直接就会用你的公网端口,只需要下载个热门种子“启动”一下就好了
hello365
2022-08-10 09:10:40 +08:00
请教一下,如果 qb 勾选了 Use proxy for peer connections 是不是 pt 站显示的就是 vps 的 ip 了?如果本地 debian 和 vps 通过 tinc 、wireguard 这类的组网软件组成一个局域网,然后 vps iptables 转发到 qb 是不是也可行?只是所有流量都是通过 vps 进来的链路更长了。
FrankAdler
2022-08-10 10:15:49 +08:00
牛逼啊,移动 nat1 有救了,虽然我有公网用不上
delpo
2022-08-10 10:21:12 +08:00
@isad DHT 我测试了一下,qbittorrent 会使用设置中的监听端口,通过 udp 发送 dht 查询消息. 但是具体到连接 peers 进行下载的时候,除非你把 peers 传输协议设置为仅μtp, 否则还是会优先使用 tcp 进行连接. 由于 tcp 和 udp 的 nat 映射不一样, 所以连接性依然不是很理想. 即使设置为仅μtp, 也同样只能在 nat1 下接受入站连接.
不过话说回来,既然使用 DHT,就意味着在下载公网的种子,其实只要有速度,连接性好不好也不是很重要了,毕竟公网种子不强制要求上传.
delpo
2022-08-10 10:25:58 +08:00
@hello365 我这个实践里,vps 仅用于获取本地客户端在 nat 防火墙外的 external port, 而不是用来架代理的. mitmproxy 是和客户端一样搭建在本地的,所以源 ip 依然是你本地的 ip.
至于你说的,相当于通过 vps 代理下载. 相关教程很多就不赘述了
FrankAdler
2022-08-10 10:30:43 +08:00
自动化的话,这套 cs 软件就是在提供打洞的基础上代理并修改 tracker 的流量,看起来是可行的🤔,可能 mitmproxy 需要在 c 端再实现一个才能自动化
delpo
2022-08-10 10:41:10 +08:00
@hello365 你勾选 Use proxy for peer connections 相当于是把 BT 协议和 μtp 协议代理到 mitmproxy 里, 大概 mitmproxy 处理不了这两个协议汇报错吧.
isad
2022-08-10 10:47:46 +08:00
@delpo 你搞错了吧,DHT 是别的主机交换信息后来找你连接,至于为什么都是 μtp ,是因为 qb 就只支持 udp 打洞。
我没有用过公网 ip ,分享率依然有一点几
delpo
2022-08-10 10:54:20 +08:00
@isad 不好意思,我写错了,应该是: 除非"你"把 peers 传输协议设置为仅μtp --> 除非"别人"把 peers 传输协议设置为仅μtp
因为大部份 BT 客户端默认情况都是基于 TCP 的 BT 协议优先,这个是没有问题的
ChangeTheWorld
2022-08-10 13:58:53 +08:00
这 NAT 都把人逼成啥样了,各种骚操作都出来了,IPv6 任重而道远啊
lanlandezei
2022-08-10 14:27:54 +08:00
收藏了 过程太复杂了操作不来,期待大佬做个自动化软件
mxuan
2022-08-11 01:40:21 +08:00
mark 一下,很有价值,很想尝试一下。
hello365
2022-08-11 08:52:45 +08:00
嗯,感谢解答,另外还有个疑问,sendip 里面指定-td 我理解的是将在 cgn nat 上申请的从 local 公网端口到 vps 的链接,是这样吗?如果是这样怎么保证这个-td 参数值是否可用呢?会不会已经在 cgn nat 上被占用了。
delpo
2022-08-11 08:59:26 +08:00
@hello365 -td 是你 tcp 包的目的端口,也就是你 vps 上的端口,你说的那个是 nat 防火墙上的 external port,这个是 nat 防火墙自动分配的
mxuan
2022-08-11 09:25:23 +08:00
cgnat 是 nat3 ,用 stun 试了下,没法提供对应端口。心酸。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/871773

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX