一、NAT分类

NAT大致有4种类型:

1. Full Cone NAT

完全锥形NAT,所有从同一个内网IP和端口号发送过来的请求都会被映射成同一个外网IP和端口号,并且任何一个外网主机都可以通过这个映射的外网IP和端口号向这台内网主机发送包。

2. Restricted Cone NAT

限制锥形NAT,它也是所有从同一个内网IP和端口号发送过来的请求都会被映射成同一个外网IP和端口号。与完全锥形不同的是,外网主机只能够向先前已经向它发送过数据包的内网主机发送包。

3. Port Restricted Cone NAT

端口限制锥形NAT,与限制锥形NAT很相似,只不过它包括端口号。也就是说,一台IP地址X和端口P的外网主机想给内网主机发送包,必须是这台内网主机先前已经给这个IP地址X和端口P发送过数据包。

4. Symmetric NAT

对称NAT,所有从同一个内网IP和端口号发送到一个特定的目的IP和端口号的请求,都会被映射到同一个IP和端口号。如果同一台主机使用相同的源地址和端口号发送包,但是发往不同的目的地,NAT将会使用不同的映射。此外,只有收到数据的外网主机才可以反过来向内网主机发送包。

我们知道,使用五元组可以定位一条流,分别是源端口(SPORT),源IP(SIP),协议(Protocol,如TCP/UDP等),目的端口(DPORT),目的地址(DIP)。

不同类型的NAT,其实就是在做NAT哈希以及匹配时使用和检查的五元组数据不同。

上述4种NAT分类的区别如下(假设协议为UDP):

NAT类型 NAT哈希使用的信息 NAT匹配检查的信息(针对回应报文) 备注
完全锥形NAT SIP,SPORT

映射后的IP和端口号

由于在NAT哈希时只使用了SIP和SPORT,若SIP和SPORT一样,都会映射成同一个IP和端口(无论DIP和DPORT是否一样)。

这3类NAT的区别就在于对回应报文的检测方式不同。

假设有3台外网主机A,B,C。内网主机使用相同的源IP和源端口向主机A和B发送报文,映射后的IP和端口都是一样的。

针对完全锥形NAT,外网主机A,B,C可以使用任何源端口向映射的IP和端口发送报文,报文都会被送达内网主机;

针对限制锥形NAT,外网主机A,B可以使用任何源端口向映射的IP和端口发送报文,主机C发送的报文会被丢弃

(由于内网主机没有向主机C发送过报文,无法通过源IP检查);

针对端口限制锥形NAT,外网主机A,B只能使用指定源端口(内网主机报文的目的端口)向映射的IP和端口发送报文,

若使用其它端口作为源端口,则报文会被丢弃(无法通过源端口检查)。主机C发送的报文会被丢弃

(由于内网主机没有向主机C发送过报文,无法通过源IP和源端口检查);

限制锥形NAT

映射后的IP和端口号

回应报文的源IP

端口限制锥形NAT

映射后的IP和端口号

回应报文的源IP和源端口

对称NAT SIP,SPORT,Protocol,DPORT,DIP

映射后的IP和端口号

回应报文的源IP和源端口

对称NAT对回应报文的检查和端口限制锥形NAT一样,区别在于做NAT哈希时使用的信息。五元组内任意一个元组发生变化,则映射后的IP或端口都会发生变化。

二、NAT类型检查

了解了NAT的分类后,可以设计一个NAT类型检查的方式。

此方式要求至少有2台外网服务器,分别为主机A和B。

流程如下:

步骤1:内网主机发送一个UDP报文给A,其中源端口为13357,目的端口为6000,源IP为内网主机的私网地址,目的IP为外网主机A的地址。

此报文(在负载中指定)要求服务器使用映射后的IP和端口作为目的IP和目的端口进行回应,源IP和源端口与报文的目的IP和端口相同,并在回应中包含映射后的IP和端口信息。

这个回应报文肯定能被收到,除非网络不通。

收到此报文后,通过报文中包含的映射后的IP和端口信息,内网主机可以做如下判断:

1,映射后的IP和端口与内网主机地址和源端口一样。可以判断主机位于公网,没有NAT。流程结束。

2,映射后的IP和端口与内网主机地址和源端口不一样。则可以判断主机位于内网,到公网之间有NAT设备。(当然,这里不一样有3种情况,即1,仅地址不一样;2,仅端口不一样;3,地址和端口都不一样;一般是第3种情况。第1种情况也有可能,因为NAT的哈希算法计算后的映射端口可能与源端口恰好一样。第2种情况属于异常场景,暂不考虑。)

步骤2:内网主机发送一个UDP报文给A,其中源端口为13357,目的端口为6000,源IP为内网主机的私网地址,目的IP为外网主机A的地址。(与步骤1发送的报文的信息一样)

此报文(在负载中指定)要求服务器使用映射后的IP和端口作为目的IP和目的端口进行回应,源IP与报文的目的IP相同,但修改源端口为其它端口(与上行报文的目的端口不一样),并在回应中包含映射后的IP和端口信息。

根据NAT类型的不同,内网主机不一定能够收到此回应报文。

1,收到此回应报文。

可以判断NAT类型为完全锥形NAT(类型1)或限制锥形NAT(类型2)。因为根据另外两种NAT的定义,不可能收到这种回应报文。

当然,也可以对负载再次进行检查,期望负载中包含的映射后的IP和端口与第一个回应一样,若不一样,则属于异常情况,暂不考虑。

2,未收到回应报文。

可以判断NAT类型为端口限制锥形NAT(类型3)或对称NAT(类型4)。因为根据另外两种NAT的定义,肯定会收到这种回应报文。(暂时不考虑丢包的情况。实际实现时,可以通过重传此报文来解决可能的丢包场景。)

步骤3:此步骤要根据步骤2的情况来进行。分两种情况。

1,步骤2中收到了回应报文。

此时再次发送一个与步骤1和2中类似的报文,IP头部信息不变,但在负载中携带信息要求服务器A通知服务器B,让服务器B来给内网主机发送回应。(服务器A和服务器B之间的交互不在本文的范围内)。

若收到此回应,则表明NAT类型是完全锥形NAT(类型1),否则就是限制锥形NAT(类型2)。因为限制锥形NAT会检查源IP,服务器B发送的回应由于使用了不同的源IP,不能通过此检查。

到此流程结束。

2,步骤2中未收到回应报文。

此时需要在端口限制锥形NAT(类型3)或对称NAT(类型4)中做判断。

与步骤1类似,内网主机给服务器B发送一个报文,并检查回应中携带的映射后的IP和端口信息。

若与步骤1中回应的一样,则表明是端口限制锥形NAT(类型3),否则就是对称NAT(类型4)。

至此,一个简易的NAT类型检测流程就完成了。

上述流程中,没有考虑丢包,异常处理等。需要在实现时完善。

另外,UDP报文中携带的信息,可以参考RFC3489/5389/5766这3篇文档,利用里面定义的消息类型和选项,包括使用事务ID来匹配请求和回应等。

重传机制,如超时时间,重传次数等,都需要在实现时考虑。

本文是在研究了比特彗星(Bitcomet)的NAT类型发现功能后写出的。只是一个猜想,跟比特彗星或其它商业软件的实现大概率不一致。

另外,上述步骤中,步骤2的回应报文修改了源端口,当然也可以直接修改源IP,甚至源IP和源端口都修改,后续步骤根据情况调整,但原理大致是一样的,这里不再赘述。

欢迎留言指导讨论!

NAT类型检测方案的更多相关文章

  1. STUN: NAT 类型检测方法

    STUN(Simple Transversal of UDP through NATs)[21]是RFC3489 规定的一种NAT 穿透方式,它采用辅助的方法探测NAT 的IP 和端口. STUN 的 ...

  2. NAT类型与穿透 及 STUN TURN 协议

    STUN : Simple Traversal of User Datagram Protocol [UDP] Through Network Address Translators [NATs] S ...

  3. Linux入侵痕迹检测方案【华为云技术分享】

    背景说明 扫描是一切入侵的基础,通过扫描来发现目标主机是否为活动主机.操作系统是什么版本.开放了哪些服务等.扫描技术纷繁复杂,新的扫描技术也层出不穷,不可能穷举所有扫描技术,下面按入侵步骤对主机扫描. ...

  4. js类型检测总结

    类型检测: 类和对象: Call,Apply,Bind

  5. javascript类型与类型检测

    1.javascript类型: 注:包装对象:如"hello".length实际为js为我们隐式创建了一个String临时对象,去调用该对象的length属性,调用过后再将该临时对 ...

  6. 谈谈JavaScript类型检测

      javascript内置的类型检测机制并非完全可靠.比如typeof操作符,并不能准确的判断数据是哪个类型,比如:数组和对象就不能通过typeof来区分. typeof [] ==="o ...

  7. JS数组类型检测

    在强类型语言,数组类型检测是非常容易的事情(typeof就可以解决),而在弱语言JS数据类型就很容易混淆了. JS中常见的数据类型有:number.string.boolean.undefined.f ...

  8. 小结 javascript中的类型检测

    先吐槽一下博客园的编辑器,太不好用了,一旦粘贴个表格进来就会卡死,每次都要用html编辑器写,不爽! 关于javascript的类型检测,早在实习的时候就应该总结,一直拖到现在,当时因为这个问题还出了 ...

  9. Objective-C 【动态类型检测&响应方法】

    ------------------------------------------- 动态类型检测 代码: #import <Foundation/Foundation.h> @inte ...

  10. [C++]C++中的运行时类型检测

    Date:2014-1-3 Summary: 使用C++中的运行时类型检测.(文章重点在于记录本人的使用情况,并非深层讨论RTTI) Contents:写习惯C#的我,在C++依然存在哪些.NET的惯 ...

随机推荐

  1. Oracle和达梦:相似度函数:UTL_MATCH

    UTL_MATCH介绍: Oracle的UTL_MATCH包是一个提供字符串匹配和相似度计算功能的工具包.它包含了一系列函数,用于执行字符串比较.相似度计算和模式匹配等操作. UTL_MATCH包中的 ...

  2. 羽夏壳世界—— PE 解析的实现

    写在前面   此系列是本人一个字一个字码出来的,包括代码实现和效果截图. 如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后 ...

  3. CSS自适应网页(CSS第一篇)

    ​CSS的属性: 用浏览器自带的审查元素对一些页面进行调整,快捷键是F12. 网页允许宽度自适应: 在代码的头部加入一行viewport元标签. <meta name="viewpor ...

  4. three.js介绍和学习资料说明

    1.three.js能做什么 Three.js是基于原生WebGL封装运行的三维引擎,在所有WebGL引擎中,Three.js是国内文资料最多.使用最广泛的三维引擎.既然Threejs是一款WebGL ...

  5. 统计学习:EM算法及其在高斯混合模型(GMM)中的应用

    1. EM算法的基本思想 我们在应用中所面对的数据有时是缺损的/观测不完全的[1][2].我们将数据分为: 可观测数据,用\(Y\)表示: 缺失数据,用\(Z\)表示; 完全数据,用\(X=(Y, Z ...

  6. wpf布局递归

    wpf布局递归的调用到底是怎样的顺序?我一直挺模糊. 按照继承顺序. 已知: 1.1.UIElement:声明了Measure 1.2.UIElement:声明了MeasureCore,返回Size( ...

  7. kubelet gc 源码分析

    代码 kubernetes 1.26.15 问题 混部机子批量节点NotReady(十几个,丫的重大故障),报错为: 意思就是 rpc 超了,节点下有太多 PodSandBox,crictl ps - ...

  8. 在 Flask 项目中配置 Session:简明指南

    在 Flask 项目中配置 Session:简明指南 本文介绍如何在 Flask 项目中配置会话 1. Flask 内置会话 Flask 自带会话管理功能,使用客户端 Cookie 存储会话数据.默认 ...

  9. Tkinter界面实操

    常用opencv-python进行图像处理,有时需要图形用户界面,写个Demo以备不时之需. Tkinter 1. 导入库 由于 Tkinter 是内置到 python 的安装包中.只要安装好 Pyt ...

  10. CF914C

    problem & blog 数位 dp 模板题. 经过一次操作,可以把 \(n\) 变成一个小于 \(10^3\) 的数. 所以我们可以把所有小于 \(10^3\) 的数操作的次数全部处理出 ...