GitHub: https://github.com/MikeWang000000/Natter
目前公网 IP 越来越稀缺,有些地区已经无法申请公网 IP 了。
不过,在 NAT 1 网络下,我们可以通过一种 “打洞” 的方式,将本地的 TCP 端口暴露至公网上,运行 HTTP 服务等。
经过一番研究,我使用 Python 写了一个工具,起名叫 Natter 。
简洁地说,Natter 的原理就是端口重用:
Natter 应当运行于路由器上,因为这样只经过一层 NAT 。不过,正确设置 DMZ 主机,经过多层 NAT 也是 OK 的。
好消息是国内运营商级 NAT 多数已经转为 NAT 1 了,因此开放公网 TCP 端口应该是比较容易的。
目前我在中国移动的家庭宽带上已经成功使用 Natter 开放公网 TCP 端口。
1
Tink 2022-09-13 00:26:40 +08:00 via Android
star 送上~
|
2
twl007 2022-09-13 00:29:51 +08:00 via iPhone
slack 有个类似的项目 Nebula
https://github.com/slackhq/nebula |
3
mikeluckybiy 2022-09-13 00:37:22 +08:00 via Android
大佬牛皮,是不是之前发过贴是准备研究这个的
|
4
ihipop 2022-09-13 00:47:34 +08:00 via Android
但是这个开放的端口号不是你能控制的吧而且每次发起映射会变,这个很烦
|
5
mikewang OP @Tink 谢谢~
@twl007 学习学习 @mikeluckybiy 之前在 /t/878703 评论区讨论过 hhhh @ihipop 所以 Natter 采用的是长连接,这个程序需要保持在后台运行状态,重拨号前端口就不会变了。 另外考虑到重新拨号后,端口变化的解决方法...其实有一种暴力的方式,就是不停并发创建新连接,直到选到自己想要的端口号上。后续会评估这个方案的可行性。 |
6
Zy143L 2022-09-13 03:23:59 +08:00 via Android
在路由器上已经开启 FullCone 电脑测试为 NAT1 模式
可否先监听再使用 Natter 进行打洞呢 |
7
swiftg 2022-09-13 07:56:52 +08:00 via iPhone
openwrt 拨号,防火墙提前打开了需要的端口 tcp 和 udp 入站,运行报如下错误
[INFO] - Getting STUN server IP... [INFO] - Checking NAT Type for UDP... Traceback (most recent call last): File "natter.py", line 583, in <module> main() File "natter.py", line 576, in main natter.tcp_punch() File "natter.py", line 471, in tcp_punch nat_type = self.stun_client.check_nat_type() File "natter.py", line 332, in check_nat_type raise Exception("UDP Blocked or not enough STUN servers available.") Exception: UDP Blocked or not enough STUN servers available. |
8
mikewang OP @Zy143L 简单的说:可能会出现端口号冲突问题(如果您的系统不报错,那就一切 OK )
因为端口监听默认是“独占”模式。程序没有设置 SO_REUSEADDR 、SO_REUSEPORT 的时候,这个端口只能被先监听占用的程序使用,因此就无法在上面继续打洞了。(会出现经典的 [Errno 48] Address already in use ) 因此一般情况下,是 Natter 打洞+防火墙的转发规则,将打洞端口转发到局域网的 NAS 服务器上(或者本机的其他端口)。 防火墙转发规则不会导致端口号冲突,因此先设好转发再在此端口打洞,这种“监听”是 OK 的。 |
9
acbot 2022-09-13 08:22:47 +08:00
如果以后 DNS 能扩展一个端口解析都好了,比如:在解析记录里面增加一个特定规律的 TXT 记录,里面包含特定域名的端口,或者是增加一个记录类型 XXXX ,里面直接由地址+端口组成,并且主流浏览器都支持那该多好啊!
|
11
Marionic0723 2022-09-13 08:45:16 +08:00
坐等 openwrt 软件包大佬编译个 luci 版集成到软件源里
|
12
ragnaroks 2022-09-13 08:52:31 +08:00
有一个单词,以 N 开头以 er 结尾
|
13
ronman 2022-09-13 09:19:53 +08:00
有个疑问请教下
本地端口 A 向外发起 TCP 长连接 是连接到哪里呢? |
15
gyorou 2022-09-13 09:50:56 +08:00
是不是理论上运营商在 gateway 的路由器上直接把外部发起的 tcp sync 丢了就行了。
为啥运营商不这么干? |
16
swiftg 2022-09-13 09:53:18 +08:00
@mikewang 用-v 运行后可以看到是我的问题,我这 stun.miwifi.com 的 dns 结果返回是 0.0.0.0 ,换了个 dns 服务器解决了,可惜我这不是 nat1 ,还是用不了
|
17
mikewang OP #15 @gyorou 是的 :doge: 我猜是没有这个必要
运营商那边只要搞好 NAT 转换就 OK 了,加一道防火墙反而有可能引入新的问题。 有防火墙一般检测也是 NAT 2/NAT 3 ,而不是 NAT 1 。 |
18
wym0823 2022-09-13 10:30:04 +08:00
可以的话麻烦有空编译成.exe 单文件,谢谢!
|
19
shikkoku 2022-09-13 10:35:18 +08:00
两台小米路由由于是高通 5018 ,没法开 telnet ,没法折腾 NAT1 了。
|
20
Peven 2022-09-13 10:45:43 +08:00 via Android
显示打洞成功,但 3456 端口关闭了。。。
|
21
mikewang OP |
22
1041412569 2022-09-13 11:01:24 +08:00
@mikewang 问问 windows 有类似 iptables 的工具吗
|
23
JoeoooLAI 2022-09-13 11:08:19 +08:00
Star 了。。。牛
|
24
mikewang OP @1041412569 Windows 下可能比较尴尬,后续考虑直接在 Natter 上集成转发功能。
|
25
Archeb 2022-09-13 11:09:42 +08:00
挺有意思的,star 了,希望继续完善。
|
28
mikewang OP @qingmuhy0 任意一个能保持很长很长 TCP 连接的地址都行。目前 Natter 是向 www.qq.com:80 发起 HTTP Keep-Alive
|
29
xixiv5 2022-09-13 11:54:24 +08:00
长链接的话,我 0757 的佛山移动会过段时间 5 分钟左右释放掉 UDP 端口,我经常 L2TP 和远程桌面连接会被断开,tcp 目前还没察觉到
|
32
XiLingHost 2022-09-13 12:14:00 +08:00
@acbot 你说的是 SRV 记录吗
|
34
mikewang OP #29 @xixiv5 一般情况下运营商是不会随意断开 TCP 的,否则这个网就几乎没法用了。
Natter 对 TCP 的保活做得算是比较激进的,默认间隔 10 秒就向外发一次数据。 |
35
Zy143L 2022-09-13 13:15:12 +08:00
star 奉上
|
36
missdeer 2022-09-13 13:23:34 +08:00
家宽 NAT 对称型,残念。。。
|
37
Zy143L 2022-09-13 13:31:34 +08:00
联通打洞没问题 但是 http 访问会直接 RESET
发现一个问题就是 NAT 测试不准?有时候测出来是 NAT4 |
38
mikeluckybiy 2022-09-13 14:46:01 +08:00
@missdeer 对称型 NAT 和锥形 NAT 有什么区别么?
|
39
mikeluckybiy 2022-09-13 14:50:05 +08:00
@Peven 同样问题,-t 测试正常,端口转发不行
|
40
acbot 2022-09-13 14:55:32 +08:00
|
41
lanlandezei 2022-09-13 15:38:33 +08:00
star 了,明天用家里的移动宽带试下
|
42
shikkoku 2022-09-13 16:03:17 +08:00
显示 It works!,但是我怎么让 BT 软件打通?公网 IP 的 IP 端口不一样。
|
44
jousca 2022-09-13 18:58:53 +08:00
测试了,功能挺好玩的。可用~
|
46
mikewang OP @Zy143L UDP 网络环境不稳定的时候可能会影响类型判断,不过仅作参考,不影响 Natter 运行。
@mikeluckybiy 可以简单理解为一个时间段内,锥形的端口映射关系是固定的,因此有打洞的可能性。而对称型的映射关系不固定,因此只能做“被动”的一方,不能对外提供服务。 @shikkoku 理论上,打洞端口 A ,得到了公网端口 B 。可以设置 BT 软件端口号也设为 B ,然后端口 A 数据转发至 B ,从而得到一致端口。 |
47
mikewang OP @shikkoku 实际上,NAT 1 对于 BT 应该已经足够了,毕竟用的 UDP ,不需要对 TCP 打洞。
|
49
tediorelee 2022-09-13 19:33:52 +08:00
|
50
Tink 2022-09-13 19:37:18 +08:00 via Android
启用 shellclash 的情况下,tcp nat 会变成对称的,不启用就是 nat1
|
51
star7th 2022-09-13 19:45:25 +08:00
也可以试下 内网穿透搞一下 https://www.showdoc.com.cn/gaoyixia
|
53
mikewang OP #49 @tediorelee 对的,看到这个就是成功,恭喜你。
#51 @star7th 这个穿透是服务器中转的么? Natter 注重于直连打洞,无限制访问 #52 @shikkoku 哈哈 有点尴尬,这个转发事情应该是由 Natter 做的,我画了个饼,还没写,等我更新吧。 |
54
1423 2022-09-13 20:36:47 +08:00
有联通用户成功的吗
|
55
wym0823 2022-09-13 20:45:07 +08:00
OSError: [WinError 10048] 通常每个套接字地址(协议 /网络地址 /端口)只允许使用一次。
有时候会有这个报错,原因不明~好像是端口被占用? |
58
mikewang OP |
59
eijnix 2022-09-13 23:33:18 +08:00
需要转发的用 类似于这样的命令就行:
ssh -L 192.168.0.103:3456:127.0.0.1:8000 -N 127.0.0.1 需要注意前面一定要用自己的局域网地址 而不是 127.0.0.1 |
60
Zy143L 2022-09-14 01:14:39 +08:00 via Android
问题都解决了
只是发现 TCP NAT 测试不是很准...时不时的测出 NAT4 模式 更换了一部分 nat 服务器列表 现在稳定一些了 |
61
mikewang OP @Zy143L 对的,因为 TCP STUN 公共服务器都是境外的,我没有找到国内可用的服务器 : (
而且如果运营商对某些境外地址,改变了出口 IP ,那么也会影响判断结果 emmm 可能暂时不太好解决 |
62
1423 2022-09-14 03:08:31 +08:00
不太明白,为什么 tcp 连接 stun server 用到的公网端口会恰好与后续 keepalive 连接的公网端口相同?
|
63
1423 2022-09-14 03:51:25 +08:00
@1423 随便写了几行代码抓包试了下果真如此。。这么多年,没想到 tcp 穿透如此简单。。
https://gist.github.com/wen-long/0bda5c5eeaf837f6e485269aee65bb26 |
64
jasonliu769 2022-09-14 09:16:50 +08:00
@1041412569 Windows 其实有 netsh interface portproxy 实现端口转发功能
|
65
mikewang OP @1423 有意为之的,这样才能打洞。原理十分简单,重点在于维持 TCP 连接的思路,以及外部地址的获取( Natter 相当于把 STUN 协议的客户端重新实现了一次)
@jasonliu769 我不在用 Windows ,不过印象中 netsh 的端口转发会独占端口。 |
66
shikkoku 2022-09-14 11:08:51 +08:00
|
68
Verizon 2022-09-14 13:11:14 +08:00
跟 frp 有什么区别吗
|
70
matzoh 2022-09-14 13:56:49 +08:00
和 tailscale/zerotier 这类的打洞原理上有区别吗
|
71
mikewang OP @matzoh tailscale/zerotier 打洞时一般都是 UDP ,Natter 使用 TCP 。
另外,Natter 的“打洞”其实只做了一半:只有服务端打洞,向外暴露端口;客户端则是没有感知的,不借助任何软件,手输外部 IP:端口号即可访问。 |
72
mikewang OP |
73
YAFEIML 2022-09-14 15:11:27 +08:00
比较叼,联通、移动都轻松打洞成功,都改的桥接,路由都是 openwrt ,rdp 协议复制了下文件,速度不错。
https://img.gejiba.com/images/c0669216d535c1458a45ad3b72702cc9.png |
74
mrzx 2022-09-14 17:04:55 +08:00
@mikewang 请教下,没看懂原理,尤其是 www.qq.com:80 和 STUN server 之间发生了什么?
|
75
mrzx 2022-09-14 17:13:47 +08:00
@mikewang 我不可以这么理解,虽然不需要像 FRP 那种数据包全部由公网服务器中转了。但对对外连接的端口的端口信息,需要告知 STUN server 服务器,然后外面的客户端通过从 STUN server 的这些信息,直接连过来?
至于为什么要连 qq:80 ,是故意想将 TCP session 时间延长,避免默认的 300 秒超时,导致 session 丢失和端口产生变化? 这些都是我猜测的。原理是这样吗? |
76
1423 2022-09-14 19:17:40 +08:00 1
@mrzx 看我上面的代码,自己跑一下抓个包就能看明白怎么回事
另外我试了下,其实 tcp 穿透不需要任何工具,路由器配置端口 10000-50000 tcp 全部转发到指定服务,外部端口直接用 masscan 扫描,扫出来的都可以用。如下图扫到的自己宽带的公网开放端口,都可以使用 |
77
mikewang OP @mrzx
详细地说,原理是这样的。 假设本机获得了运营商分配的局域网 IP (100.64.32.10): 当本地 100.64.32.10 向 www.qq.com (109.244.211.100) 发起 HTTP 请求时,NAT 会做转换。 1. 本地发起请求,来源端口为 100.64.32.10:3456 ,想建立 (100.64.32.10:3456 至 109.244.211.100:80) 的连接。 2. ISP NAT Server 察觉到本地想要请求,在公网 IP 空闲端口中挑一个,在映射表中新增 (100.64.32.10:3456 ~ 203.0.113.10:14500) 映射关系。 3. 100.64.32.10:3456 转发 203.0.113.10:14500 至 109.244.211.100:80 ,本机和 www.qq.com 成功建立连接。 4. 本机断开与 www.qq.com 的连接,NAT Server 释放端口,清除 (100.64.32.10:3456 ~ 203.0.113.10:14500) 映射关系 因此,在第 4 步之前,第 3 步的时候 (100.64.32.10:3456 ~ 203.0.113.10:14500) 已经成立了映射关系,NAT1 下,两者是等同关系。此时,端口被“临时地”暴露至公网。 当该端口还提供服务时,相当于开了一个“洞”,外部的连接就能“趁虚而入”。 一旦到达第 4 步,NAT Server 回收 14500 端口,就无法继续“趁虚而入”了。 然而,这是上帝视角看整个连接的过程。实际操作中,我们不知道出口的 IP 和端口号,www.qq.com 也不会告诉我。 因此,我们需要问一个路人:“打扰一下,您看看我外面 IP 和端口号是多少”。这个路人就是 STUN 服务器。 STUN 服务器没那么多闲工夫,告诉你 IP 端口之后就立即关闭连接了。而 www.qq.com 比较客气,只要你提出我想要保持连接不关闭,它就不会关闭,外部端口也就不会被回收。 Natter 会每 10 秒说一次:“求求你别关连接”。 如果没有 STUN 服务器,但您知道自己的出口 IP ,就可以做全端口扫描,不过这就很费事了。 |
78
mikewang OP @1423
您的做法符合 Natter 最基础的思路,原理是一致的。 1. Natter 使用 STUN 服务器,避免端口扫描(全端口转发猜端口)。 2. Natter 自己维护对外连接,因此公网的端口有效期理论无限长。 批量转发只能对单个端口有效果,同时运行多个 Natter 可以向公网暴露多个端口。 |
79
mikeluckybiy 2022-09-14 21:28:39 +08:00
@1423 宽带的公网开放端口有很多,怎么判断哪个端口是对应自己路由器的某个端口转发的?
|
80
1423 2022-09-15 02:28:43 +08:00
@mikeluckybiy 宽带的公网端口有服务在监听的并不多,除非你的网上邻居都这么玩
syn scan 显示握手成功的端口有若干,是因为家里的设备总是有 tcp 对外连接 你的网上邻居一般不会去 listen 端口,他们不会 tcp 握手成功,所以握手成功的都是你的(端口都映射到同一服务了) |
82
acbot 2022-09-15 08:59:40 +08:00
@optional https://www.jianshu.com/p/dd9719c4c2c1 " ...首先要建立好 HTTP2 连接,然后发送 HTTP2 扩展帧,这个帧包含 IP 和端口,浏览器收到扩展帧,使用该 IP 和端口,使用 QUIC 建立连接,如果成功,断开 HTTP2 ,升级为 HTTP3 。..." http3 或许可以但是他得先建立 http2
|
86
berry10086 2022-09-15 11:28:37 +08:00
每次重启或者换 ip ,外部端口应该会变吧?
|
87
mikewang OP @mrzx 不用客气。
@berry10086 是的,因为外部端口选择取决于 NAT 服务器。 如果一定想要固定端口,可以暴力并发连接,直到 NAT 服务器给出自己想要的端口,然后关闭其他外部端口号不对的连接。(很暴力,因此未加入此功能) |
88
mikeluckybiy 2022-09-15 12:38:13 +08:00
@1423 那意思是指,我把路由器开 dmz ,电脑通过软件全端口映射,然后对外网 ip 全端口扫描,扫描到的端口都是我的是么?这个意思是指,只要我本地开了端口,外网会自动把对应的 tcp 端口映射出去?比如我本地开了 8888 的 tcp 端口,并且在路由器端进行映射,其他操作什么都不动,外网 1.2.3.4 的随机端口 17801 会自动与我这边的 8888 形成对应的映射关系是么?
另外,外网 1.2.3.4 会最多给我几个端口,极端情况,比如我本地开了 100 个端口映射,外网 1.2.3.4 也会随机映射 100 个端口给我么? 1.2.3.4 探测到本地开放的映射端口,就会自动形成映射关系么? |
89
sangleft 2022-09-15 15:47:34 +08:00
好用,这能给 bt 打洞吗?
|
90
mikewang OP @mikeluckybiy 通俗的解释吧,端口转发就相当于留了条道,NAT 服务器就相当于小区门闸机。
有人出小区,闸机就会打开。这个人有编号,是端口号。路由器转发这个端口号,其他人就能“蹭”这个闸机进去。转发所有端口,相当于只要有人出,就能“蹭”这个人的闸机。 所以并不是自动形成的映射关系,而是 10000-50000 tcp 端口肯定有人要出去,有人出去了闸机就开了。 |
92
mikeluckybiy 2022-09-15 17:01:02 +08:00
@mikewang 这个让人出去的原理是什么,为什么 BT 会自动打洞
|
93
xzcfz123 2022-09-15 17:28:07 +08:00
我是联通的。使用之后显示 UDP 是 NAT1 但是 TCP 是 NAT3 ,打洞失败了,我在 openwrt 里面也是这样,已经是桥接拨号了。这个是运营商的问题吗?我不太懂。
[INFO] - Getting STUN server IP... [INFO] - Checking NAT Type for UDP... [INFO] - NAT Type for UDP: [ Full cone (NAT 1) ] [INFO] - Checking NAT Type for TCP... [INFO] - NAT Type for TCP: [ Symmetric ] [ERROR] - You cannot perform TCP hole punching in a symmetric NAT network. |
94
mikewang OP @mikeluckybiy 只要你的设备要访问网络(微信 /百度 /看 V2EX/…)就会有传出连接,NAT 肯定会转换并放行。
BT 会自动打洞因为他的协议就是这么设计的。 就如同为什么 Natter 会打洞,因为就是这么设计的。 |
95
mikeluckybiy 2022-09-15 20:31:22 +08:00 via Android
@mikewang 又有个新问题,为什么 nat2 、3 就不行,也是可以访问网络的,应该也是存在 nat 转换的么?
|
96
mikewang OP @xzcfz123 运营商的问题。
UDP 是 NAT 1 ,那么一定可以向公网开放 UDP 端口(这个功能我还没加上); TCP 的条件比较苛刻,运营商支持与否看运气。 @mikeluckybiy NAT 2 是防火墙限制 IP 一致(比如 qq 的 IP 是 109.244.211.100 ,只允许 109.244.211.100 进入); NAT 3 是防火墙限制 IP 和端口一致(更严格),因此其他来源的 IP 地址就不能趁虚而入了。NAT 4 则是打一枪换一炮,地址映射不固定,能猜对的几率几乎为零。 |
97
mikeluckybiy 2022-09-15 21:43:31 +08:00 via Android
@mikewang 那有没有可能以后运营商关闭 nat1 ,只保留 nat2 、3 、4 ,防止打洞?
|
98
mikewang OP @mikeluckybiy 参考#15 的做法,直接使得打洞失效。然而设置额外的防火墙会使得网络质量下降,使用 P2P 的游戏玩不了等,引来客户投诉。
Natter 缓解了一部分原本一定要公网 IP 用户的需求,运营商应该高兴才对。 |
99
wslzy007 2022-09-16 11:51:31 +08:00 1
既然想要 TCP 打洞了,何不直接用 smargate 呢? nat1-3 都能是想 tcp 打洞,还有 tls 加密隧道功能,内网到内网穿透,内网到外网发布神器,拿走不谢
github.com/lazy-luo/smarGate |