【问题标题】:Ideas or algorithms when programming an NAT编写 NAT 时的想法或算法
【发布时间】:2012-11-04 01:03:57
【问题描述】:

我正在使用 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

如果第二个用户同时请求 1 个 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

我如何知道何时从 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.

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

哪种实现最好?还有什么其他提示或我应该注意的事情吗?

编辑:

好的,我有 N 多个客户。如果您误解了我,则使用 enitre /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

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

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

或者我可以使用映射?

我如何能够在这个 websocket 链上“映射”隧道抽象?客户端没有路由到 互联网,但可以访问可以访问互联网的“浏览器”。这似乎与 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

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

【问题讨论】:

    标签: python sockets networking nat


    【解决方案1】:

    实现 NAT

    您必须为每个连接使用一个唯一端口,而不是每个用户使用一个端口,这正是您在问题中概述的原因:如果您不这样做,那么您可以(并且将会!)最终使用多个连接相同的 5 元组(协议、本地地址、本地端口、远程地址、远程端口),您将无法消除它们的歧义。

    此外,如果您想与一些执行 NAT 遍历的协议配合使用,那么您应该尝试尽可能重新映射原始源端口,也就是说,仅重新映射它(到一个新的随机端口),如果它与您正在跟踪的现有连接冲突。

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

    对于 TCP,这意味着观察标志,当你看到 SYN 时设置新状态,当你从两边看到 FINs 时拆除状态。您跟踪的状态必须至少包含原始源端口和重新映射的源端口(可能相同,见上文)。如果您想支持 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 接口返回。

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

    【讨论】:

    • 哇。感谢您的详细回复。我可能没有很好地解释它。基本上,客户端使用两个私有 IP (10.1.1.2--->10.1.1.1) 与自己打开一个隧道。隧道的接收端通过 websocket 将 IP 数据包定向到扭曲服务器。扭曲接收器的数据来自 user1 的公共 IP,但数据本身是一个 IP 数据包(很可能来自 10.1.1.2)。然后服务器进行 NAT,然后将其发送出去。
    • 然后我会监听传入的数据包,在 NAT 表中查找它,将客户端的私有 IP 放回去,将其打包到 websocket 中并发送回来。然后客户端的 TUNTAP 将接收它(在 10.1.1.1 上)并将其发送回 10.1.1.2。你是对的,服务器上的第二个 TUNTAP 会更容易。这可能吗?
    • 有没有办法可以在 websocket 上创建一个 TUN 桥接器和\或以某种方式使用内核的 NAT 功能而不是实现我自己的功能?而不是IP over GRE,它是IP over WebSocket。编辑:顺便说一句,我非常感谢您的帮助。
    • 我认为这个项目的设计超出了 Stackoverflow 问题的范围......我知道 10.xxx/30 对每个客户端都是私有的,但它对服务器很重要的原因是这些私有IP地址之一是客户端发送并到达(封装在websockets中)服务器的数据包的源IP地址。反正服务器拿到后,就可以在它的末端射出tun接口。服务器的内核现在获取原始数据包,并且必须对其进行编程以对其进行 NAT(如果是 Linux,则使用 iptables 完成,否则使用其他方法)。
    • 分片是 NAT 通常要考虑的另一件事。除了第一个数据报,分片数据包的所有其他数据报将包含传输头。所以我们需要使用它们的 IP ID 字段来跟踪这些碎片数据报(发送到哪个端口)。此外,碎片数据报可能在包含传输头的数据报之前到达,因此我们必须对它们进行缓冲。 Celada 非常详细的回答。
    猜你喜欢
    • 2014-01-19
    • 1970-01-01
    • 2017-01-29
    • 2023-03-03
    • 2011-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-16
    相关资源
    最近更新 更多