分享一个在 NAT1 下将端口打开到公网上的方法 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
delpo
V2EX    宽带症候群

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

  •  2
     
  •   delpo 2022-08-09 20:38:13 +08:00 6028 次点击
    这是一个创建于 1211 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

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

    准备工具

    • OS:Debian
    • BT 客户端: 这里以 qbittorrent-nox 为例,其他客户端可以自行参考
    • https mitm 、rewrite 工具: mitmproxy,选择它是因为可以兼顾中间人以及修改 http 请求
    • 网络包构造工具: sendip
    • 抓包工具:tcpdump
    • 另一台具有公网 ip 的 vps

    预备知识

    • nat1: 即完全锥形 nat ,意味着某一个 local port 对应的 external port 可以被任意地址的 peer 访问。其中 local port 和 external port 一般是不一样的。
    • tracker: 一般 pt 站使用的均为 http(s) tracker ,根据bep_0003,对 tracker 的 http(s)请求参数中包含客户端 ip 和 port 信息,其中 ip 信息是可选的。通过对 qbittorrent 的 http 请求抓包可以证实上述过程。
    • nexusphp:大部分国内 pt 站都用的这个框架。通过大致研究源码,可以发现客户端的 ip 地址实际是通过 tracker 请求对应的 ip 得到的,而 port 则是直接取 tracker 请求中的 port 参数。这就导致了服务器获取到的端口实际上是客户端的 local port 。

    具体步骤

    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 映射发生改变的情况下自动的更改外部端口。

    第 1 条附言    2022-08-10 19:11:15 +08:00

    更新

    为了简化流程,方便自动化,我改进了之前的流程

    1. 客户端/服务端不再使用sendip/tcpdump进行抓包,而是直接在服务端架设一个简单的http服务器,客户端每秒发出一次http请求,服务端返回值为客户端的external port。http客户端使用一个额外的固定的本地端口发出请求,记为extra port。
    2. 客户端得到external port后保存为文件。即使nat映射改变,客户端也可以及时更新保存端口的文件。mitmproxy读取该文件中的端口进行tracker请求修改。
    3. 经过上述步骤,extra port已经暴露在公网上。为了使local port也暴露在公网上,使用iptables的DNAT模块进行本地端口转发,从而将extra port的入站流量全部转发到local port上。

    相比原流程,新流程使用的工具更简单,更方便自动化布署。如果之后有时间我会写成脚本发布。

    第 2 条附言    2022-08-10 19:22:11 +08:00
    话说,要是有什么在线的服务 /工具,可以给客户端返回 external port ,那好像连 vps 都可以不用了
    第 3 条附言    2022-08-10 19:47:13 +08:00

    刚看了一下STUN协议标准,发现STUN竟然支持使用TCP连接,而公共的STUN服务器可是有很多的。感觉此贴可以终结了。

    第 4 条附言    2022-08-13 14:35:12 +08:00

    用go写了一个小工具,可以保持nat映射,以及将外部端口输出到文件,包装成docker镜像了,欢迎大家使用

    https://hub.docker.com/r/alozxy/trav

    31 条回复    2024-06-10 21:29:12 +08:00
    br2049
        1
    br2049  
       2022-08-09 21:05:59 +08:00
    感谢 思路很清晰
    terrancesiu
        2
    terrancesiu  
       2022-08-09 22:13:33 +08:00 via iPhone
    很厉害,学到了
    LnTrx
        3
    LnTrx  
       2022-08-09 22:32:11 +08:00
    很好的想法。过程中涉及很多方面技能的综合运用,或有用于教学的潜力。
    ScotGu
        4
    ScotGu  
       2022-08-09 22:52:40 +08:00
    谢谢,然后拿起电话继续拨打一万号要公网 IP 。
    Overfill3641
        5
    Overfill3641  
       2022-08-09 22:53:48 +08:00
    如果有软件自动化还是不错的。
    isad
        6
    isad  
       2022-08-09 23:36:56 +08:00   1
    我也这样设想过,但没有实施,因为我发现 DHT 直接就会用你的公网端口,只需要下载个热门种子“启动”一下就好了
    hello365
        7
    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
        8
    FrankAdler  
       2022-08-10 10:15:49 +08:00 via iPhone
    牛逼啊,移动 nat1 有救了,虽然我有公网用不上
    delpo
        9
    delpo  
    OP
       2022-08-10 10:21:12 +08:00
    @isad DHT 我测试了一下,qbittorrent 会使用设置中的监听端口,通过 udp 发送 dht 查询消息. 但是具体到连接 peers 进行下载的时候,除非你把 peers 传输协议设置为仅μtp, 否则还是会优先使用 tcp 进行连接. 由于 tcp 和 udp 的 nat 映射不一样, 所以连接性依然不是很理想. 即使设置为仅μtp, 也同样只能在 nat1 下接受入站连接.
    不过话说回来,既然使用 DHT,就意味着在下载公网的种子,其实只要有速度,连接性好不好也不是很重要了,毕竟公网种子不强制要求上传.
    delpo
        10
    delpo  
    OP
       2022-08-10 10:25:58 +08:00   1
    @hello365 我这个实践里,vps 仅用于获取本地客户端在 nat 防火墙外的 external port, 而不是用来架代理的. mitmproxy 是和客户端一样搭建在本地的,所以源 ip 依然是你本地的 ip.
    至于你说的,相当于通过 vps 代理下载. 相关教程很多就不赘述了
    FrankAdler
        11
    FrankAdler  
       2022-08-10 10:30:43 +08:00 via iPhone
    自动化的话,这套 cs 软件就是在提供打洞的基础上代理并修改 tracker 的流量,看起来是可行的,可能 mitmproxy 需要在 c 端再实现一个才能自动化
    delpo
        12
    delpo  
    OP
       2022-08-10 10:41:10 +08:00   1
    @hello365 你勾选 Use proxy for peer connections 相当于是把 BT 协议和 μtp 协议代理到 mitmproxy 里, 大概 mitmproxy 处理不了这两个协议汇报错吧.
    isad
        13
    isad  
       2022-08-10 10:47:46 +08:00
    @delpo 你搞错了吧,DHT 是别的主机交换信息后来找你连接,至于为什么都是 μtp ,是因为 qb 就只支持 udp 打洞。
    我没有用过公网 ip ,分享率依然有一点几
    delpo
        14
    delpo  
    OP
       2022-08-10 10:54:20 +08:00
    @isad 不好意思,我写错了,应该是: 除非"你"把 peers 传输协议设置为仅μtp --> 除非"别人"把 peers 传输协议设置为仅μtp
    因为大部份 BT 客户端默认情况都是基于 TCP 的 BT 协议优先,这个是没有问题的
    ChangeTheWorld
        15
    ChangeTheWorld  
       2022-08-10 13:58:53 +08:00
    这 NAT 都把人逼成啥样了,各种骚操作都出来了,IPv6 任重而道远啊
    lanlandezei
        16
    lanlandezei  
       2022-08-10 14:27:54 +08:00
    收藏了 过程太复杂了操作不来,期待大佬做个自动化软件
    mxuan
        17
    mxuan  
       2022-08-11 01:40:21 +08:00
    mark 一下,很有价值,很想尝试一下。
    hello365
        18
    hello365  
       2022-08-11 08:52:45 +08:00
    嗯,感谢解答,另外还有个疑问,sendip 里面指定-td 我理解的是将在 cgn nat 上申请的从 local 公网端口到 vps 的链接,是这样吗?如果是这样怎么保证这个-td 参数值是否可用呢?会不会已经在 cgn nat 上被占用了。
    delpo
        19
    delpo  
    OP
       2022-08-11 08:59:26 +08:00   1
    @hello365 -td 是你 tcp 包的目的端口,也就是你 vps 上的端口,你说的那个是 nat 防火墙上的 external port,这个是 nat 防火墙自动分配的
    mxuan
        20
    mxuan  
       2022-08-11 09:25:23 +08:00
    cgnat 是 nat3 ,用 stun 试了下,没法提供对应端口。心酸。。
    q197
        21
    q197  
       2022-08-11 20:55:31 +08:00 via Android
    @ChangeTheWorld 应该是 tracker 程序的问题,正常 nat1 无公网 ip 完全不影响 bt 互联
    delpo
        22
    delpo  
    OP
       2022-08-11 22:02:04 +08:00 via Android
    @q197 其实即便 tacker 记录的是 external port ,两个 nat1 的 peers 也很难连上,主要就是因为 nat 的映射时间太短,而 tracker 汇报的间隔很长,所以只有两个 peers 同时在 nat 映射打开的时候连接对方才行,这个概率真的很随缘。不过如果真是这种情况,只需要一个 peer 主动一直打开映射就一定能被其他 peer 连上,可以说能简化很多。
    delpo
        23
    delpo  
    OP
       2022-08-12 09:51:58 +08:00
    @q197 更进一步研究抓包发现,在 TCP 协议下,无论是连接 peers 还是连接 tracker ,使用的本地端口都是 socket 随机指定的,而不是设置里的监听端口,在这种情况下 nat 外的端口也是不固定的,所以两个 peers 自动打洞成功的概率可以忽略不计。
    但是作为对比,UDP 协议下,无论是μtp 还是 udp tracker ,使用的本地端口都是设置里的监听端口,这种情况下才有可能实现上一楼中说的情况,但是这要求必须是μtp + udp tracker ,实际使用中 udp tracker 其实是很少的。
    但是如果下载的不是私有种子,那么就和之前说的一样,可以使用 DHT 或者 PEX 协议接受μtp 入站连接,[BEP_0055]( https://www.bittorrent.org/beps/bep_0055.html)也规定了一部分打洞扩展。
    以上实验皆基于 qbittorrent
    lanlandezei
        24
    lanlandezei  
       2022-08-13 21:08:25 +08:00
    大佬有没有能直接运行的二进制执行文件,DOCKR 感觉比较麻烦
    delpo
        25
    delpo  
    OP
       2022-08-13 22:47:03 +08:00
    @lanlandezei 有需要可以自行编译

    https://github.com/Alozxy/trav
    lanlandezei
        26
    lanlandezei  
       2022-08-15 08:27:13 +08:00
    我应该已经执行到最后一步了,PT 站个人信息那里,BT 客户端有显示 trav 执行的端口了。现在就是差最后一步 DNAT 不知道怎么写。(用 iptables 的 DNAT 模块进行本地端口转发,从而将 extra port 的入站流量全部转发到 local port 上)。
    我的运行环境
    移动宽带 NAT1
    debian11
    apt 直接安装的 qbittorrent
    mitmdump -p 8888 -s http-modify-query-string.py
    trav -i 8 -l 12345
    qb 连接 mitmdump 的代理。
    delpo
        27
    delpo  
    OP
       2022-08-15 10:37:34 +08:00
    @lanlandezei 程序里有自动创建 iptables 规则的,你可以在 nat 表里找到
    还有,interval 不用设置太高,半个小时更新一次都没问题,毕竟用的公共服务器
    subing
        28
    subing  
       2022-08-15 18:09:51 +08:00
    老哥要是长期玩 pt 还是建议有公网方便,电信 /联通总有一家会给的
    james19820515
        30
    james19820515  
       2024-06-08 19:21:32 +08:00
    nat3 才是全锥吧?
    delpo
        31
    delpo  
    OP
       2024-06-10 21:29:12 +08:00
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2844 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 22ms UTC 00:28 PVG 08:28 LAX 16:28 JFK 19:28
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86