无公网如何实现内网穿透

现在很多个人宽带已经没有动态公网IP了,这使得在家庭内部署一些网络服务失去了意义

如何判断自己是否具备公网IP:

访问 http://cip.cc 查看路由器或者拨号软路由拨号后的IP是否与其一致

若不一致,则说明大概率处于拨号后处于 NAT 网络,当然也可能处于 DMZ 模式下,这需要做一些测试

1. NAT网络

1.1. NAT

NAT 的全称是 Network Address Translation,即网络地址转换,指的是路由器等网络设备,在传输数据的过程中,改变数据中的 IP 地址的一种技术

NAT 技术示意图

随着全球联网设备越来越多,但 IPv4 地址资源有限,所以 NAT 技术在 IPV6 普及前都会是相当广泛的应用

1.2. NAT类型

NAT 分为好几种类型:

Full Cone NAT:“完全圆锥形 NAT”,内部设备与某个外部服务建立tcp链接后(如示意图),在未关闭该tcp链路前,任何外部设备可以通过此链路端口访问到内部端口 Restricted Cone NAT:“受限圆锥形 NAT”,在 Full Cone NAT 基础上,增加对访问链路的设备的 IP 限制,仅允许 tcp 链路的目标设备发送 tcp 数据包到内部设备 Port-Restricted Cone NAT:“端口受限圆锥形 NAT”,在 Restricted Cone NAT 的基础上,再增加端口检查,仅允许 tcp 链路的目标设备和目标端口发送tcp数据包到内部设备 Symmetric NAT:“对称 NAT”,与前三种不同,对称 NAT 会为每个新连接分配一个新的外部端口,即使是从同一个内部端口发起的连接

有许多服务依赖与网络通信,尤其是 P2P、游戏等,例如在Xbox、PlayStation上,可以检测对网络类型进行检测,结果分别如下:

open:拥有公网IP,最佳网络环境 moderate:对应前三种 NAT 类型,可用的网络环境 strict: 对称NAT,不可用的网络环境

1.3. 打洞

NAT 打洞技术是指通过一系列的技巧和协议,尝试在 NAT 上创建临时的映射或规则,使得两个设备可以在 NAT 网络后进行直接通信

Full Cone NAT 是最容易打洞的环境,而且对访问来源没有限制,可用于 NAS 文件分享、Web 服务等

而对于 Restricted Cone NAT 和 Port-Restricted Cone NAT 来说,打洞的效果则要打些折扣,打洞过程如下:

Restricted Cone NAT 和 Port-Restricted Cone NAT 的打洞流程复杂,且对客户端也有一定要求,但基本也满足游戏对战、P2P下载等服务

2. NAT 打洞

按照前面介绍的 NAT 类型,明确可以进行内网穿透的是 Full Cone NAT、Restricted Cone NAT、Port-Restricted Cone NAT三中

对称NAT 打洞成功率很低,Full Cone NAT 是最理想的打洞环境

2.1. 检测 NAT 类型

Windows:

https://github.com/HMBSbige/NatTypeTester

Linux/MacOS:

https://github.com/jtriley/pystun

Windows下载后运行即可,其他操作系统可以使用 pystun 来做检测,以 pystun 为例

在 debian12 下为例,首先安装 Python 的包管理器:

Bashcontent_copy
# 根据你的 python 版本来安装虚拟环境套件
sudo apt install python3.xx-venv

创建一个虚拟环境,然后安装 pystun:

Bashcontent_copy
python3.xx-venv -m venv venv
source venv/bin/active

安装 pystun :

Bashcontent_copy
pip install pystun

运行 pystun :

Bashcontent_copy
pystun3

下面是我的输出

Bashcontent_copy
(venv) ~$ pystun3
NAT Type: Full Cone
External IP: 12.125.45.119
External Port: 6622

可以看到我的 nat 类型是 Full Cone NAT

2.2. NAT 打洞实践

natmap 用于从ISP NAT公网地址到本地私有地址建立 TCP/UDP 端口映射

以 web 服务为例,运行一个 librespeed 测速服务为例,docker 运行如下:

Bashcontent_copy
sudo docker run \
  -e PUID=1000 \
  -e PGID=1000 \
  -e TZ=Etc/UTC \
  -e PASSWORD=chancel \
  -p 8080:80 \
  lscr.io/linuxserver/librespeed:latest

访问 http://localhost:8080 并测速,结果如下:

然后下载 natmap 并编译:

Bashcontent_copy
git clone --recursive https://github.com/heiher/natmap.git
cd natmap
make

# 可选
cp bin/natmap /usr/bin/natmap

运行 natmap 建立一个端口映射

Bashcontent_copy
/usr/bin/natmap -i ppp0 -s turn.cloudflare.com -h qq.com -t localhost -p 8080

参数解析:

-i:指定网卡,这里指定了宽带拨号产生的ppp0 -s:使用的外网 TURN 服务器 -h: 对于tcp而言,需要一个保持 tcp 长连接的对象,这里填了腾讯的服务器 -t/-p:指内网设备的IP和端口

运行输出如下:

Bashcontent_copy
/usr/bin/natmap -i ppp0 -s turn.cloudflare.com -h qq.com -t 127.0.0.1 -p 8080
12.125.45.119 4475 2001::223d:2c91:82b3 46701 tcp 14.213.121.3

第一个输出即公网IP,第二个输出则是公网端口,在其他网络下,访问 http://12.125.45.119:4475 后测速:

可以看到测速的结果有接近 50 Mbps,我的宽带是 100Mbps

2.3. 自动更新

从前面对 nat 技术的介绍不难看出,映射到公网IP和公网端口会随时间改变,所以可以结合 DDNS 等技术来实现长时间打洞效果

以 https://api.chancel.me/rest/api/v1/anyjson 接口为例实现长期打洞,该 API 使用如下:

Bashcontent_copy
$ curl -X POST https://api.chancel.me/rest/api/v1/anyjson?id=hello -H "Content-Type: application/json" -d '{"lihua": "hi!"}'
# 返回状态 1 ,表示存储成功
{"status":1,"msg":"Data stored successfully","data":null,"version":"V1.0.0"}

$ curl -X GET https://api.chancel.me/rest/api/v1/anyjson?id=hello
{"lihua":"hi!"}

这个接口可用于临时存储 json,所以写一个 bash 脚本 natmap.sh 如下:

Bashcontent_copy
#!/bin/sh

/usr/bin/curl -X POST https://api.chancel.me/rest/api/v1/anyjson?id=secret -H "Content-Type: application/json" -d "{\"ip\": \"${1}\",\"port\":\"${2}\"}"

其中 $1$2 是由 natmap 传入的,即公网IP和公网端口,这样 natmap 会在打洞成功后执行该脚本将打洞结果上传到接口中

运行试试:

Bashcontent_copy
chmod +x natmap.sh
/usr/bin/natmap -i ppp0 -s turn.cloudflare.com -h qq.com -t 127.0.0.1 -p 8080 -e natmap.sh

此时在其他网络中,使用curl来获取打洞结果

Bashcontent_copy
curl https://api.chancel.me/rest/api/v1/anyjson\?id\=secret
{"ip":"12.125.45.119","port":"4467"}

可以看到,成功获取到了打洞结果,在其他网络中就可以利用这个结果来访问对应服务

以上为示例,可以灵活结合 DDNS 技术来实现各种内网服务穿透

最后,结合 supervisor 来实现 natmap 后台自动运行:

INIcontent_copy
[program:natmap]
command=/opt/natmap/natmap -i ppp0 -s turn.cloudflare.com -h qq.com -t 127.0.0.1 -p 22 -e /opt/natmap/natmap.sh
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
stdout_logfile_maxbytes=32MB
user=app

3. 对称NAT

2024年后,广东大部分地区的宽带都变成了 Symmetric NAT,这非常糟糕

在光猫拨号情况下,即使是 Full Cone NAT 也会被识别为 Symmetric NAT

所以需要将光猫更改为桥接,拨号由路由器/软路由来拨号,再结合DMZ来做网络类型测试

光猫更改为桥接,这一点需要光猫的管理员账户,可以通过

找装宽带运维的师傅要 淘宝找店铺解决

文章来源:

Author:chancel
link:http://www.chancel.me/markdown/home-network-nat-traversal