PPTP这个虚拟专用网协议是建立在GRE协议之上的,PPTP非常坑,主要提现在两个方面
1、固定死1723端口
2、GRE协议很难穿透路由器
所以就会经常出现使用Windows链接PPTP客户端的时候出现“619错误”
而这种情况通常出现在服务端和客户端不在同一子网内或者不同时在公网内引起的。
举例说明,如果PPTP服务端架设在192.168.1.1,客户端IP为192.168.1.2那么连接起来一般没什么问题,但如果我们把服务端放到一个路由器后面去并同时映射1723端口,客户端就有很有可能出现连不上的情况。
比如路由器的地址为192.168.1.1,PPTP服务端的地址在这台路由器后方,IP为172.17.0.2,并端口映射1723到路由器上,那么当客户端192.168.1.2以192.168.1.1为服务器进行链接时一般就连接不上。同理在公网上,如果把PPTP服务端直接放在公网IP上一般没什么问题,但是一般我们不会这样做,一般具有公网IP的设备都是防火墙或者堡垒机,PPTP一般部署在内网上然后通过端口映射出1723。所以这样公网上的PPTP设备也很难链接上。
这种放到路由器后面就失灵的问题一般是由于GRE协议导致的。
GRE协议是于TCP,UDP协议平级的三层协议,它有一个最大的特点是没有端口这一概念。与OpenVPN不同,OpenVPN使用的是广泛的TCP和UDP协议,所有路由都能够支持,可以自由的NAT和端口映射。而GRE协议就不行了,因为没有端口的概念,导致路由器在进行NAT的时候无从下手,对于路由器来说,当它接收到一个GRE数据包,只能观察到它的源IP和目标IP,但是源IP由于NAT的原因可能这一个IP后面是好多台主机,同时目标IP也是这样,所以路由器无法判断这个数据包应该送到哪个主机上。
对于服务端来说,我们需要分成两种情况来讨论:一种是客户端与服务端之间不经过NAT,第二种是客户端与服务端经过NAT,而且不止一次
一、不经过NAT
向上面讲的,如果客户端和服务端在同一网段内,或者可以通过配置路由表的方式彼此互通,那么就不需要经过NAT。在上面的例子中我们可以通过配置路由表的方式让192.168.1.2这个客户端直接ping通172.17.0.2,反过来也是可以,那么就保证这中间没有NAT了。
除了内网的机器以外,如果服务端和客户端都具备公网IP那么也是不经过NAT的。
简单来说,如果客户端和服务端可以互相PING通即满足本段讨论的条件
在没有NAT的情况下,GRE协议的数据包可以准确无误的在两台主机之间传送,不会出现收不到的情况,所以问题就变得很简单了,我们直接在客户端配置中填服务端的IP,加密方式和密码就可以了。
这里要注意Ubuntu的图形界面默认是不开其MPPE加密的,会导致连不上,其他客户端也记得要开。
二、经过NAT
这种方式应该是重点掌握,里面门道也很多。
首先我们需要做一个1723的端口映射,把服务端的TCP 1723端口映射到路由器上去,如下
比如我在服务端172.17.0.2前面的路由器192.168.1.1上来这么一行:
iptables -t nat -A PREROUTING -p tcp --dport 1723 -j DNAT --to 172.17.0.2
这句话做了目的地址转换,此时如果还用第一种方式提到的直接写服务端物理地址,并且客户端和服务端还能互相PING 通时,这条iptables规则感觉写了和没写一个样,使用上面直接填服务端物理地址的方法一样可以拨上PPTP如果我们把客户端的服务器地址填路由器的地址192.168.1.1,我们会发现拨了很长一段时间以后拨不上,错误619
(注意,如果是1723端口没有打开不用1秒就会提示你拨不上,而这种情况一般会等待20s或更长时间才中断)
使用tcpdump就可以发现如下数据包
IP 192.168.1.2 > 172.17.0.2: ICMP 192.168.1.2 protocol 47 unreachable, length 69意思就是说GRE数据包(协议ID为47)无法抵达172.17.0.2
现在我们明白了,由于我们上面只做了TCP 1723端口的NAT,没有做GRE协议的NAT,所以客户端无法和服务端建立GRE链接。
那么我们赶紧给GRE协议也做一个NAT吧(在路由器192.168.1.1上)
iptables -t nat -I PREROUTING -p 47 -j DNAT --to 172.17.0.2
但是居然发现还是拨不通192.168.1.1(此时直接写服务端的物理地址172.17.0.2还是能拨通的)
使用tcpdump查看还是会报出IP 192.168.1.2 > 172.17.0.2: ICMP 192.168.1.2 protocol 47 unreachable, length 69
看来上面那条对于GRE协议的DNAT没有起作用啊,怎么办。
到网上查了一下发现由于没有加载ip_nat_pptp模块,所以如果客户端不填172.17.0.2而是填192.168.1.1这个路由器的地址,那么是拨不通的。即使你用iptables做了端口映射和GRE的DNAT也无济于事。
所以说,没有这个ip_nat_pptp模块,GRE协议就不能穿透路由器到达172.17.0.2
在ubuntu系统中(我这里的这个路由器192.168.1.1就是ubuntu16.04的系统),加载这个模块的方法就是执行modprobe ip_nat_pptp,卸载这个模块就执行
modprobe -r ip_nat_pptp
当我们加载上这个模块之后,同时做TCP 1723端口的端口映射,删掉前面的GRE协议的DNAT(不删也可以,很神奇吧),然后在客户端填写路由器的地址192.168.1.1,发现终于可以顺利拨通了。我们终于解决了路由器的内网穿透问题
注意此时我们还是让客户端192.168.1.2和服务端172.17.0.2互相可以PING通,我们再试试在加载ip_nat_pptp模块后无NAT模式能否成功
发现现在已经拨不通了
也就是说,我们开启了那个模块后,只能通过NAT的方式以路由器为服务器地址,不能使用服务端的真实物理地址了。使用tcpdump查看又发现GRE不能穿透的问题了
IP 192.168.1.4 > 192.168.1.11: ICMP 192.168.1.4 protocol 47 unreachable, length 69
(注意此时没有GRE协议DNAT的iptables规则)
如果我们加上GRE协议的DNAT的iptables规则呢
iptables -t nat -I PREROUTING -p 47 -j DNAT --to 172.17.0.2
在拨号时使用tcpdump可以发现如下重试过程
08:52:33.471700 IP 192.168.1.2.54132 > 172.17.0.2.1723: Flags [.], ack 189, win 1369, options [nop,nop,TS val 27513846 ecr 1000800], length 0
08:52:36.596319 IP 192.168.1.2 > 172.17.0.2: GREv1, call 3072, seq 1, length 40: LCP, Conf-Request (0x01), id 1, length 26
08:52:39.476110 IP 192.168.1.2 > 172.17.0.2: GREv1, call 3072, seq 2, length 40: LCP, Conf-Request (0x01), id 1, length 26
08:52:42.483813 IP 192.168.1.2 > 172.17.0.2: GREv1, call 3072, seq 3, length 40: LCP, Conf-Request (0x01), id 1, length 26
08:52:45.472068 IP 192.168.1.2 > 172.17.0.2: GREv1, call 3072, seq 4, length 40: LCP, Conf-Request (0x01), id 1, length 26
08:52:48.553421 IP 192.168.1.2 > 172.17.0.2: GREv1, call 3072, seq 5, length 40: LCP, Conf-Request (0x01), id 1, length 26
08:52:51.472217 IP 192.168.1.2 > 172.17.0.2: GREv1, call 3072, seq 6, length 40: LCP, Conf-Request (0x01), id 1, length 26
08:52:54.607697 IP 192.168.1.2 > 172.17.0.2: GREv1, call 3072, seq 7, length 40: LCP, Conf-Request (0x01), id 1, length 26
08:52:57.492930 IP 192.168.1.2 > 172.17.0.2: GREv1, call 3072, seq 8, length 40: LCP, Conf-Request (0x01), id 1, length 26
08:53:00.481296 IP 192.168.1.2 > 172.17.0.2: GREv1, call 3072, seq 9, length 40: LCP, Conf-Request (0x01), id 1, length 26
大意就是客户端发给服务端的数据包服务端不给响应
此时即使是把前面做的1723端口映射删掉也还是同样的结果
换句话说,一旦我们在路由器上开启了ip_nat_pptp协议之后,服务端又在路由器后面的,我们就不能使用直接填写服务端真实地址这种方式了,只能填写路由器的地址进行端口映射方式连接。
另外还有一个很重要的情况必须要考虑到,那就是客户端的NAT问题,上面只解决了服务端的NAT,服务端收到的GRE数据包的源IP就是客户端的真实IP,但是如果客户端也不具备公网地址,或者和服务端以及服务端的路由器均不在同一网段,那么问题就会更加复杂。
客户端的路由器很有可能不支持PPTP穿透,比如OpenWrt系统的路由器一般是不支持的。另外我手里还有一块netcore官方固件的路由器,号称支持pptp穿透功能,而我发现只有Win7系统使用自带PPTP可以拨上公网服务器,而Linux系统,Unix系统,比如Ubuntu,安卓,IOS均无法通过这个路由器PPTP拨通。而Linux,Unix设备(安卓,苹果手机)使用移动2G,3G,4G网络是可以拨通公网上的PPTP服务器的。使用该路由器经过PPTP服务端路由器tcpdump抓包发现如下提示(120.24.231.*为服务端的地址)
09:18:47.678845 IP 120.24.231.67 > 39.69.108.158: GREv1, call 11288, seq 0, length 41: LCP, Conf-Request (0x01), id 1, length 27
09:18:47.730869 IP 39.69.108.158.49883 > 120.24.231.67.1723: Flags [P.], seq 325:349, ack 189, win 16378, length 24: pptp CTRL_MSGTYPE=SLI PEER_CALL_ID(2432) SEND_ACCM(0xffffffff) RECV_ACCM(0xffffffff)
09:18:47.767282 IP 120.24.231.*.1723 > 39.69.108.158.49883: Flags [.], ack 349, win 245, length 0
09:18:50.679411 IP 120.24.231.* > 39.69.*.*: GREv1, call 11288, seq 1, length 41: LCP, Conf-Request (0x01), id 1, length 27
09:18:53.682501 IP 120.24.231.* > 39.69.*.*: GREv1, call 11288, seq 2, length 41: LCP, Conf-Request (0x01), id 1, length 27
09:18:56.685581 IP 120.24.231.* > 39.69.*.*: GREv1, call 11288, seq 3, length 41: LCP, Conf-Request (0x01), id 1, length 27
09:18:59.688683 IP 120.24.231.* > 39.69.*.*: GREv1, call 11288, seq 4, length 41: LCP, Conf-Request (0x01), id 1, length 27
09:19:02.691375 IP 120.24.231.* > 39.69.*.*: GREv1, call 11288, seq 5, length 41: LCP, Conf-Request (0x01), id 1, length 27
09:19:05.694439 IP 120.24.231.* > 39.69.*.*: GREv1, call 11288, seq 6, length 41: LCP, Conf-Request (0x01), id 1, length 27
09:19:08.695622 IP 120.24.231.* > 39.69.*.*: GREv1, call 11288, seq 7, length 41: LCP, Conf-Request (0x01), id 1, length 27
09:19:11.695844 IP 120.24.231.* > 39.69.*.*: GREv1, call 11288, seq 8, length 41: LCP, Conf-Request (0x01), id 1, length 27
09:19:14.697351 IP 120.24.231.* > 39.69.*.*: GREv1, call 11288, seq 9, length 41: LCP, Conf-Request (0x01), id 1, length 27
大意就是服务端发给客户端的GRE数据包客户端不响应,原因就是客户端的路由器没有把GRE数据包发送给客户端,导致客户端收不到服务端的响应,服务端也一样。
然后我又去客户端的OpenWrt路由器抓包,发现OpenWrt路由器根本就没有NAT GRE协议数据包的能力,所有出网卡数据全部以内网地址出去的
01:57:49.534306 IP 120.24.231.67.1723 > 192.168.2.155.49379: Flags [.], ack 349, win 243, options [nop,nop,TS val 232070159 ecr 906070629], length 0
01:57:49.664387 IP 192.168.2.155 > 120.24.231.*: GREv1, call 5376, seq 1, ack 0, length 40: LCP, Conf-Request (0x01), id 1, length 22
01:57:52.710630 IP 192.168.2.155 > 120.24.231.*: GREv1, call 5376, seq 2, ack 0, length 40: LCP, Conf-Request (0x01), id 1, length 22
01:57:55.746533 IP 192.168.2.155 > 120.24.231.*: GREv1, call 5376, seq 3, ack 0, length 40: LCP, Conf-Request (0x01), id 1, length 22
01:57:58.785196 IP 192.168.2.155 > 120.24.231.*: GREv1, call 5376, seq 4, ack 0, length 40: LCP, Conf-Request (0x01), id 1, length 22
01:58:01.819019 IP 192.168.2.155 > 120.24.231.*: GREv1, call 5376, seq 5, ack 0, length 40: LCP, Conf-Request (0x01), id 1, length 22
01:58:04.866103 IP 192.168.2.155 > 120.24.231.*: GREv1, call 5376, seq 6, ack 0, length 40: LCP, Conf-Request (0x01), id 1, length 22
01:58:07.909266 IP 192.168.2.155 > 120.24.231.*: GREv1, call 5376, seq 7, ack 0, length 40: LCP, Conf-Request (0x01), id 1, length 22
01:58:10.056942 IP 192.168.2.155.49379 > 120.24.231.67.1723: Flags [.], ack 189, win 8223, length 0
01:58:10.100322 IP 120.24.231.67.1723 > 192.168.2.155.49379: Flags [.], ack 349, win 243, options [nop,nop,TS val 232075300 ecr 906070629], length 0
01:58:10.958569 IP 192.168.2.155 > 120.24.231.*: GREv1, call 5376, seq 8, ack 0, length 40: LCP, Conf-Request (0x01), id 1, length 22
01:58:14.004692 IP 192.168.2.155 > 120.24.231.*: GREv1, call 5376, seq 9, ack 0, length 40: LCP, Conf-Request (0x01), id 1, length 22
01:58:17.035035 IP 192.168.2.155 > 120.24.231.*: GREv1, call 5376, seq 10, ack 0, length 40: LCP, Conf-Request (0x01), id 1, length 22
进一步通过tcpdump对eth0.2这张网卡观察发现,除了GRE协议数据包,其他协议的数据包均经过了NAT转换,地址由192.168.2.155转换成了192.168.1.5
这里之所以OpenWrt路由器不能NAT GRE而服务端能收到的原因是我在OpenWrt路由器前面又加了一个路由器用来NAT OpenWrt路由器发来的流量。OpenWrt路由器无法NAT GRE流量,我使用iptables强制进行NAT如下
iptables -t nat -I POSTROUTING -p 47 -j SNAT --to-source 192.168.1.5
但是使用tcpdump抓包后发现仍然没用,没有NAT GRE的流量
那么我干脆不用OpenWrt的NAT了,即所有流量都不进行NAT,包括TCP和UDP,统一交给前端路由器进行NAT。由于路由表已经配置好了,前端路由器192.168.1.1可以ping通PPTP客户端192.168.2.155,于是所有NAT工作都交给号称支持PPTP穿透的netcore路由器,可还会毫无卵用,客户端和服务端抓包结果和上面一样。
也就是说,不仅OpenWrt路由器有问题,磊科路由器原生固件一样也无法支持PPTP穿透,在磊科路由器上,只有Windows系统可以实现PPTP穿透,而且可以开好几个Windows客户端。
看来客户端有非常重要的一点就是,尽量减少NAT和多用Windows系统。
家用路由器的NAT穿透能力也非常堪忧