网络地址转换技术系列讲座四

接上文,上文讲了小明访问Internet,NAT所做的一些点滴工作。

和小明位于同一个叶子网络里的小美,也在访问Internet,她的五元组为:

访问步骤和小明的类似,故省略,那么NAT小姐姐也需要登记一下,如下表所示:

这个表咋看好像没有什么问题,但瞅得时间长了,会发现这里有问题。

当返程得IP包返回时,其五元组应该是这个样子的:

NAT查询NAT映射表,是将1.1.1.1替换为10.1.1.1好呢,还是替换为10.1.1.2好呢?

有同学说,那不是很显然吗?必须10.1.1.2啊,通过目的端口==58612就可以判断出啊!

但NAT映射表里没有记录什么端口号,只有IP地址。

那意味这张NAT映射表是有问题的,不仅要记录IP地址,还需要记录端口号信息,对吗?

这张NAT映射表看起来就清晰了许多,也不会造成NAT小姐姐混淆彼此的局面。

返程的IP报文如果目的端口== 1025,那么报文是发给10.1.1.1的。

返程的IP报文如果目的端口== 58612,那么报文是发给10.1.1.2的。

简单明了,非常好理解。

Timeout

这些动态产生的NAT映射表项就这么一直腻在那里吗?

那怎么可能,NAT的outside port资源,满打满算只有16位,理论上最多有65535个端口,如果NAT映射表没有超时回收机制,那么可用的端口号会越来越稀少。

最极端的情况是,NAT没有可用端口提供给用户上网使用。

所以NAT映射表需要超时机制,再来看一下添加了timeout的NAT映射表。

RFC推荐timeout = 300秒,最小值不得低于120秒。

300秒超时定时器随着时间的流逝而递减,一旦减为之后,NAT会删除之,这样就将端口释放给别的TCP连接使用。

关于NAT,第一个槽点

在没有NAT存在的情况下,客户端、服务器如同谈恋爱的男女朋友,一旦勾搭上了,自由自在地交流着。即使好长时间没有给对方发消息,双方依然是爱人关系。

突然半路杀出一个NAT,电灯泡一样的存在着,让双方有局促感。

双方如果在300秒没有任何交流,处于idle状态,NAT设备就神不知鬼不觉地删除表项。

双方并不知情,之后当任何一方再给对方发消息时,由于NAT已经不存在了,这是NAT的不同反应将会有不同的效果。

1.丢弃

丢弃对于NAT设备来说,干脆利落,易于实现。

报文被丢的一方,会一直重传,直到到达重传次数的上限而删除连接,这个过程通常为几分钟,对于用户来说,非常漫长。

通信的另外一方,即使对方的连接已经删除,己方的TCP连接却依然活的好好的。如果没有keealive检测机制,这个TCP连接会一直存在着,直到重启或关机的那一刻,这其实是一种资源的浪费。

2.丢弃的同时发Reset

可以立马通知数据发送方,连接需要重置,发送方会立马释放掉TCP连接,重新再连接服务器。

通信的另外一方,与情况1相同。

3.创建新的NAT映射表项

之所以区分方向,是因为大多数的NAT设备默认只允许inside到outside方向创建NAT映射表,相反的方向默认不允许,除非通过配置明确允许。

Inside---- > Outside

如果创建的NAT映射表,outside IP + outside Port和删除前是完全相同的,这个对于通信双方是最最happy的一种结果,因为双方压根感受不到NAT表删除、NAT创建的过程,因为删除前、删除后是一样的。这个技术就是作者将要阐述的端口保留(Port Reserved )

如果创建的NAT映射表,outside IP + outside Port与删除前是不同的,那么即使报文到达对方,对方也会发现这个报文不属于任何session,从而触发对方发送Reset。

Reset报文到来会触发客户端新的TCP连接,新的连接用于继续传输数据。

但老的TCP连接,却依旧活在服务器芯中,继续浪费内存资源。

Outside---- > Inside

丢弃处理

通过上述三种处理的介绍,会发现只有做端口保留的NAT设备,才会使通信双方继续使用原有的五元组通信,其它的情况都会造成通信的中断。

即使做端口保留的NAT设备,也需要outside到inside方向有流量先流出,然后创建NAT映射表,outside到inside方向的流量才能进来。

NAT映射表刷新(Refresh)

为了避免NAT映射表超时删除而中断通信,对于TCP长连接需要做应用层的Keepalive机制,从而周期性地刷新NAT定时器,即使在很长时间内没有任何应用层数据需要发送。

应用层的Keepalive与TCP层的Keepalive有何区别?

TCP层的Keepalive默认为2小时,其主要目的是为了探测对方是否依然健在,如果多次连续探测不到对方的回应,会删除连接。

这是回收TCP连接资源的一种机制,避免长期处于idle状态的僵尸连接,占据着内存资源。

2个小时太漫长了,如果依赖TCP的Keepalive的刷新NAT映射表,黄花菜都要凉了!

应用层的Keepalive,其主要目的是为了刷新NAT映射表!

既然是为了刷新NAT映射表,Keepalive刷新周期要小于NAT表的存活时间。

为了避免单次的Keepalive报文被丢,通常需要在一个NAT存活时间周期内,需要发送Keepalive 2­ -3次。

哪个方向的流量可以刷新NAT映射表?

RFC规定,inside ---> outside方向必须(must)可以刷新NAT映射表。

outside --- > inside方向可以(may)刷新NAT映射表。

以上主要是出于安全考虑,outside通常是一个公共的Internet区域,如果允许outside --- > inside刷新NAT映射表,攻击者可以周期性地发送Keepalive消息,让NAT设备上该端口号一直处于open状态,那么攻击方就可以利用该端口来攻击内网。

端口保留(PortReserved )

上文提到了端口保留,通俗地说,就是NAT设备不改变终端用户的源端口号,如下表所示:

InsidePort == Outside Port

同学们肯定会有一个问题,如果表中的两个session的源端口号都为1025,如何实现端口保留?

针对这个问题,无法给出一个统一的答案,同学们知道不同NAT设备厂商实现方式千奇百怪。造成这一现状的原因是,早期的NAT RFC文档并没有严格标准(Standard Track),只是一些建议(Best Practice)或信息(Information),这样厂商NAT实现差异很大也不足为奇了。

案例一

有以下两个五元组:

方式1:随机端口

Outside port和inside port没有任何关联,NAT设备会随机挑选自己可用的端口号来生成NAT映射表。

此种方式,由于内部用户的端口号对Internet呈现的端口号,是随机的、不可预测的,所以这种NAT实现对TCP打洞是最不友好的。

方式2:顺序分配,端口不冲突,则保留

NAT设备尽其可能,在没有端口冲突的情况下,使用端口保留,使得inside port == outside port。

但一旦发生端口冲突,后创建的NAT映射表挑选可用的端口号,这里为1027。

这种方式是RFC推荐的一种,即尽可能保留内网用户的端口,以便于Internet上的用户可以访问内网资源。

方式3:完全端口保留

端口重用(PortReuse),这种情况是NAT设备即使豁出老命,也要帮助用户保留他们的端口号。

同学们肯定发现这张表是有问题的,因为NAT设备无法分辨返程的IP报文1.1.1.1:1025,是给10.1.1.1的,还是10.1.1.2的。

通过增添通信另一方的IP、PORT就可以帮助NAT设备,分辨返程的IP报文1.1.1.1:1025是属于哪个的了,对吗?

这看起来很强大,内网用户可以保留自己端口号,即使有端口冲突的状况,也可以使用端口重用予以克服。

但是有一种情况是无法克服的,来看看以下两个五元组:

对于这种源端口冲突的情况,再能干的NAT设备也无法将两个session的端口都保留了,只能帮助先连接的TCP连接保留端口号,接下来的TCP连接,则必须使用别的端口号。

方式4:主机相同的InsideIP/Port,总是映射相同的OutsideIP/Port

方式3是inside port == outside port,而方式4是insideportoutside port,但只要主机每次通信都使用相同的insideport,那么每次都会被映射成相同的outside port。

这种方式同样便于peer-to-peer通信。hostA使用端口号portA访问第三方服务器S,S将hostA的IP/portA告诉hostB,然后hostA通过端口重用继续使用portA与hostB通信,幸运的是,NAT继续将portA分配给hostA使用,这时hostB可以使用目的端口== portA与hostA通信。

安全

有同学会问,端口保留会有安全隐患吗?Internet用户只要知道NAT设备的全球IP,以及内部主机的侦听端口号,那么是不是就可以从Internet上访问该主机的位于该端口号的服务?

完全端口保留,为了便于NAT设备后方的主机,和同样位于世界某个角落NAT设备后方的主机peer-to-peer通信。

假如有两台位于不同NAT设备后的hostA、hostB,通过第三方服务服务器的消息传递,分别知道对方的公网IP以及端口号,然后就尝试着用对方的公网IP、端口号去和对方联系。

这时hostA能够联系上对方吗?不能,没有hostB从里面配合,外面的消息根本进不去。

因为NAT映射表还是空的,必须里面的hostB先发一个SYN报文,让NAT-a建立映射表,有了映射表,hostB的SYN报文才会进入,也许这个过程会有少许的SYN报文会丢失,但TCP会重传这些丢失报文。

里面的hostB发的SYN报文,目的IP、目的端口号自然是hostA的,那么也意味着只有hostB愿意并准备接受通信的hostA的报文才能进入内网。

同理,对于hostA来说,只有hostA愿意并准备接受通信的hostB的报文才能进入内网。

如果没有这个前提,其它所有的主机报文都无法贸然进入。

只有做端口保留的NAT,才能将从inside发出的报文与从outside进入的报文无缝对接上,做牵线搭桥的自然是它们拥有相同的端口号。相同的端口号,使无缝对接成为可能。

如果没有做端口保留,outside port不等于inside port,那么inside与outside报文就无法对接再一起,形成一个完整连接。

端口保留,为peer-to-peer通信提供了极大的便利,与不做端口保留的NAT方式相比,安全性并没有降低。

如果喜欢文章却无法用语言表达,请赞赏表达您的喜悦。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180604G18JD500?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券