编程 NAT 时的想法或算法

use*_*896 1 python sockets networking nat

我正在使用 TUNTAP 进行 Python 隧道项目。在 TUNTAP 接口上接收的数据包含原始 IP 数据包,包括所有标头。我可以做两件事之一。

在传入端,我正在听 Twisted。在传出端,我将有一个转储 IP 数据包的原始套接字。在转储数据包之前,程序会将源地址与服务器的源地址交换。它还重新计算 TCP 和 UDP 校验和。它还使用以下方法之一交换端口。此信息在 NAT 表中进行跟踪

1)每个用户使用一个端口,例如

 US.ER.01.IP:10000 ----> SE.RV.ER.IP:3000 ----> facebook.com:80
 US.ER.01.IP:10001 ----> SE.RV.ER.IP:3000 ----> facebook.com:80
 US.ER.02.IP:3000 ----> SE.RV.ER.IP:3001 ----> facebook.com:80
Run Code Online (Sandbox Code Playgroud)

如果第二个用户同时请求 facebook,这会导致问题吗?系统如何知道如何路由 facebook 的回复。它在端口 3000 上传入,因此它属于 user1,但它是否被映射回 10000 或 10001?

2)为每个连接使用唯一的端口,例如

 US.ER.01.IP:10000 ----> SE.RV.ER.IP:3000 ----> facebook.com:80
 US.ER.01.IP:10001 ----> SE.RV.ER.IP:3001 ----> facebook.com:80
 US.ER.02.IP:3000 ----> SE.RV.ER.IP:3002 ----> remoteHost.com:22
Run Code Online (Sandbox Code Playgroud)

我如何知道何时从 NAT 表中删除条目?使用这种方法,我可以看到 NAT 表很快被填满。对此的解决方案是:

  I could wit for FIN packets from the server.  This will not work with UDP though.
  I could age the NAT entry on each hit.  I could then run garbage collection 
     every N seconds.  I see this being an issue if garbage collection runs
     and how would a server's delayed response get to the proper host if it gets
     deleted from the table.
Run Code Online (Sandbox Code Playgroud)

还有从原始套接字读取的问题。我知道如何发送一个,但是否有可能接收单个 IP 数据包。原始套接字是否可以每个 sock.recieve(65535) 调用接收一个数据包可能接收多个 IP 数据包?

哪种实现最好?我应该注意其他任何提示或事项吗?

编辑:

好的,所以我有 N 个客户。如果您误解了我的意思,则在客户端和自身之间使用了 entre /30。这只是使隧道成为可能的抽象。我也不认为这很重要,但是 websocket 实际上通过 LAN 上的“代理”(IPdata 只是重新打包到一个新的 websocket 中,但是映射是唯一的)。我不想让解释变得如此混乱。我看不出这会如何改变任何事情。

      Client PC     CLIENT PC              Client PC----->LAN                               INTERNET    
 Client 1: 10.1.1.2 ----> 10.1.1.1 ----> Websocket(IPdata) ----> Browser ---> newWebSocket(IPData) ----> SE.RV.ER.IP
 Client 2: 10.1.1.4 ----> 10.1.1.3 ----> Websocket(IPdata) ----> Browser ---> newWebSocket(IPData) ----> SE.RV.ER.IP
 Client 3: 10.1.1.6 ----> 10.1.1.5 ----> Websocket(IPdata) ----> Browser ---> newWebSocket(IPData) ----> SE.RV.ER.IP
Run Code Online (Sandbox Code Playgroud)

每个客户端将其默认路由设置为隧道端点(例如 10.1.1.1)。客户端获取 IP 数据报,将其放入 websocket,将 websocket 发送到 LAN 上的浏览​​器,然后将其发送到服务器(或者可能是另一个代理)。websocket 的内部包含原始 IP 数据报(来源为 10.1.1.2 或其他一些内部 IP)。

重要的是要注意,服务器从 Internet 收到一条包含商品(带有私有源地址)的 websocket 消息。python服务器将如何使用它?创建一个新的隧道,然后将原始数据包转储到隧道中并适当路由?

或者我可以使用映射?

我如何能够在这个 websockets 链上“映射”隧道抽象?客户端没有到 Internet 的路由,但可以访问可以访问 Internet 的“浏览器”。这似乎与 VPN 隧道的情况相同。抽象如下:

 Client 1: 10.1.1.2 ----> 10.1.1.1 ----> Websocket(IPdata) ----> Browser ---> newWebSocket(IPData) ----> SE.RV.ER.IP -> Internet
           10.1.2.2------------------------------------------------------------------------------------> 10.1.2.1 ----> Internet
Run Code Online (Sandbox Code Playgroud)

如果您知道任何资源可以让我走上正轨,那就太好了!

Cel*_*ada 5

实施 NAT

您必须为每个连接使用唯一的端口,而不是每个用户使用一个端口,这正是您在问题中概述的原因:如果您不这样做,那么您可以(并且将!)最终使用相同的 5 多个连接-tuple (protocol,local-address,local-port,remote-address,remote-port) 并且您将无法消除它们的歧义。

此外,如果您想使用一些执行 NAT 穿越的协议,那么您应该尽可能不要重新映射原始源端口,也就是说,如果它与现有连接冲突,则仅将其重新映射(到新的随机端口)你在跟踪。

要正确实施 NAT,您必须跟踪每个连接的状态。

对于 TCP,这意味着观察标志,在看到 a 时设置新状态SYN,并在FIN从双方看到s时拆除状态。您跟踪的状态必须至少包含原始源端口和重新映射的源端口(可能相同,请参见上文)。如果您想支持 FTP,那么您还必须嗅探 FTP TCP 控制连接的内容并重写其中包含的 IP 地址(这意味着您将需要跟踪更多状态,因为有时您可能需要扩大 TCP 段意味着您需要开始重新映射序列号)。您还应该有一个与每个跟踪连接相关联的超时,以便在端点消失而没有正确关闭连接的情况下摆脱它。

对于 UDP,这意味着观察本地和远程端口号的组合,并为您看到的每个唯一组合(地址和端口的 4 元组)创建状态。由于 UDP 是无连接的,因此您必须根据超时使此状态信息过期。此超时将比您用于 TCP 的超时短得多(以分钟而不是小时为单位),以防止您的状态表变得太大。

对于 ICMP 回显请求,您应该以类似于 UDP 的方式进行处理,其中 icmp_id 扮演端口号的角色。

对于其他类型的 ICMP,如目标不可达,您必须检查 ICMP 数据包以查看它是否是您正在跟踪的 TCP 或 UDP 连接的一部分,并尝试将其转换回原始源。

为了防止路由循环,您还应该在转发已转换的数据包时递减 IP TTL。

可能有一些更重要的部分我忘记了。简而言之,实现 NAT 很像为路由器实现 IP 堆栈!这就是为什么 NAT 是虚拟的,总是固定在内核中的 IP 堆栈上,而不是在用户空间中实现。

发送和接收数据包

所以我理解的架构是这样的:

  1. 客户端发起一个进入 TUNTAP 接口的数据包
  2. 您的软件获取此数据包,将其封装在 Websocket 消息中,然后将其发送出去
  3. 你的 Twisted 服务器得到它并发挥它的魔力
  4. 转换后的数据包通过原始套接字从服务器发出

返回路径:

  1. 回复以某种方式返回到您的服务器(可能是libpcap
  2. 您的代码具有相反的魔力
  3. 您的服务器通过 Websocket 将结果传输回客户端
  4. 客户端通过 TUNTAP 接口看到生成的 backet。

我认为处理前向路径中的最后一步和返回路径中的第一步的最简单方法是第二个 TUNTAP 设备:tun服务器上的接口。