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 端口。
101
wslzy007 2022-09-16 22:31:16 +08:00
@qingmuhy0 客户端 app 可以配置服务端间映射,配置好后就不需要走客户端了,实现 A 机器(局域网 1 )-》 B 机器(局域网 2 )的直接 tcp 穿透,还能做转发
|
102
qingmuhy0 2022-09-17 07:46:40 +08:00 via iPhone
@wslzy007 没太看懂,类似这个需求可以满足嘛。
https://qingmuhy.com/index.php/archives/71/ 你这个局域网间穿透? subnet 的方式?如果是也不行啊,很多软件都可以,还是不能解决我博客中说到的场景啊。 |
103
heiher 2022-09-28 17:03:48 +08:00
@mikewang 借 OP 的思路已经在自己环境中用起来了,我的需求是将访问内网的代理服务端口映射在移动宽带的局端公网 IPv4 上,从而利用移动较大的上行带宽。
关于端口转发: 我没有采用转发方案,而是在内网代理服务侧开启了 Reuseport ,允许 TCP 打洞也绑定在相同的端口上发起对外连接。这样 TCP 打洞成功后,入站连接只会被内网代理服务处理,避免配置转发规则。 关于动态端口: 我的场景里内网代理服务的客户端是专用的,所以有增加地址模式的可能性,所以我利用 DNS AAAA 记录的 16 字节空间编码了 4 字节的 IPv4 和 2 字节的端口。这样打洞成功后只需要把获得的外网地址、端口更新到域名的 AAAA 记录上,客户端只通过一个常量的域名就能访问到。 感谢~ |
104
mikewang OP @heiher AAAA 记录不错的思路~ 我选择 TCP 的原因有一点就是一些地区的 UDP 环境可能不太好,TCP 可以最大限度利用带宽。我自建的服务也是直接使用 Reuseport ,转发则是因为大多软件没做这个 hhh
|
105
heiher 2022-10-01 15:11:22 +08:00
@mikewang #104
我发现如果打洞程序的 socket 开启 reuseport 且先于目标服务做端口 bind ,那么则不影响后续服务做端口 bind 。 即使在服务先运行的情况下,从 Linux kernel 5.6 开始,也有修改服务进程中 socket 为 reuseport 的方法。通过 pidfd_getfd 系统调用在打洞程序中通过 /proc/net/tcp{,6}和 /proc/[pid]/fd/扫出服务进程的 pid 和 fd ,在打洞程序中 dup 出 fd ,再通过 setsockopt 改为 reuseport 。 |
106
heiher 2022-10-01 19:37:36 +08:00
|
107
xiaolei0125 2023-02-28 12:18:43 +08:00
@heiher 这个代码的链接失效了 https://gist.github.com/heiher/261d3a9cd102c69592e0e0544713ccde#file-portmap-c-L129-L265 , 能重新分享一下这个修改服务进程中 socket 为 reuseport 的方法的代码吗? 非常感谢!
|
108
heiher 2023-02-28 12:52:07 +08:00 via Android
|
109
letmefly 2023-03-28 20:30:46 +08:00
在 openwrt 尝试了一下,出错了。我是看了青木幻境的文章
https://qingmuhy.com/index.php/archives/71/ 就照着在 openwrt 上跑了一下,出现这个提示,特来请教。 python natter.py 80 File "/root/natter.py", line 74 <title>Natter/natter.py at master · MikeWang000000/Natter · GitHub</title> ^ SyntaxError: invalid character '·' (U+00B7) |
111
letmefly 2023-03-29 18:12:07 +08:00
重新下载了 zip 包,这次信息变成了 python natter.py -t 3456
[INFO] - Getting STUN server IP... [INFO] - Checking NAT Type for UDP... [INFO] - NAT Type for UDP: [ Port restricted (NAT 3) ] [WARNING] - The NAT type of your network is not full cone (NAT 1). TCP hole punc [INFO] - Checking NAT Type for TCP... [INFO] - NAT Type for TCP: [ Cone NAT ] [INFO] - Start punching... [INFO] - The TCP hole punching appears to be successful. Please test this addres01.21', 23810) ================================ ('125.xx.201.91', 23810) ================================ HTTP test server is enabled. Please check [ http://125.xx.201.91:23810/ ] [INFO] - TCP keep-alive... 可是访问这个网址,返回信息是 invalid request ,这个打洞也不知道算不算成功。 |
112
mikewang OP @letmefly NAT 3 是不行滴,检查光猫桥接情况,防火墙等等。
如果运营商给的确实是 NAT 3 ,那就玩不了了 |
113
letmefly 2023-03-30 07:48:49 +08:00
在用 natter 打洞之前,用 Nattypetester 测试过是 Fullcone 的。之前因为路由器是 nat3 还特意把固件换成 openwrt 。看过光猫是桥接的,防火墙也是关闭的。又重新下载了 0.9 版本,python natter.py 3456
[INFO] - Getting STUN server IP... [INFO] - >>> [TCP] ('100.64.3.112', 3456) -> ('1yy.94.2xx.21', 24320) <<< 我直接把 utorrent 的转发端口改成 24320 ,过一段时间后可以看到 utorrent 显示网络正常,出现了绿色的对勾,也不知道真假,但好歹心理有点安慰。 |
114
mikewang OP |
115
garibellee 2023-05-08 16:50:07 +08:00
得注意下 test 的机器自身防火墙
|
116
zhhww57 328 天前
@mikewang
有条件可以研究下下面这个: 一种利用 dns 的且不需要第三台服务器的 p2p nat 打洞方案 实现情况:双方都是内网,一方具有 ddns ,可以通过 ddns 知道对方的 ip 关键词:随机森林算法 端口猜解 双方都是 nat4 的 udp 打洞 |
117
zhhww57 328 天前
@mikewang
这篇里面提到了一个端口预测 其实就是端口猜解 针对 nat4 的 然后之前看了一篇文章 正常来猜测是很慢的 然后用随机森林算法配合来猜解 可以缩短时间 https://zhuanlan.zhihu.com/p/353838320 通过 ddns 来标识一方的公网 ip ,然后另一方通过域名知道了公网 ip 之后 然后展开端口猜解 里面提到了一句: 上述 tcp 连接过程,仅对 NAT1 、2 、3 有效,对 NAT4 (对称型)无效。 由于对称型 nat 通常采用规律的外部端口分配方法,对于 nat4 的打洞,可以采用端口预测的方式进行尝试。 |