如果每个客户端都知道两个外部 IP 和两个内部端口,是否可以进行强力 UDP 打孔?

网络工程 纳特 安全 ip UDP
2021-07-30 19:10:01

所以我只是在阅读有关 UDP 和 TCP 的打孔技术。我读到如果已经建立了 UDP 连接,则可以轻松完成 TCP 打孔。所以这是我的理论设置。我有两个客户端 A 和 B 都在 NAT 后面。他们没有服务器来帮助他们打孔。两者都有动态 dns 设置,以便其他客户端可以硬编码以指向其他外部地址,也都硬编码使用相同的内部端口,例如我们会说是 22321。现在这些客户端知道如果他们都已启动且未连接,因此他们想要连接,所以每分钟他们向另一个客户端发送一个 UDP 数据报到每个端口,从最高端口开始,因为我猜测 NAT 将倾向于使用端口49152 以上是动态端口。每次完成此操作后,都会有一条记录进入 NAT 状态。如果客户端很幸运,那么两个客户端之一将在 NAT 打开后访问另一个外部端口,数据报将通过,然后侦听客户端可以通知发送客户端连接成功。如果他们不那么幸运,那么客户端只需要重复这个过程,这次每个从相反的两端开始,将时间减少一半,当一个连接到另一个时,接收者通知发送者连接已经建立。这种方法论可行吗?或者甚至可以通过跟踪来自 NAT 的 RST 响应来简化?如果客户端很幸运,那么两个客户端之一将在 NAT 打开后访问另一个外部端口,数据报将通过,然后侦听客户端可以通知发送客户端连接成功。如果他们不那么幸运,那么客户端只需要重复这个过程,这次每个从相反的两端开始,将时间减少一半,当一个连接到另一个时,接收者通知发送者连接已经建立。这种方法论可行吗?或者甚至可以通过跟踪来自 NAT 的 RST 响应来简化?如果客户端很幸运,那么两个客户端之一将在 NAT 打开后访问另一个外部端口,数据报将通过,然后侦听客户端可以通知发送客户端连接成功。如果他们不那么幸运,那么客户端只需要重复这个过程,这次每个从相反的两端开始,将时间减少一半,当一个连接到另一个时,接收者通知发送者连接已经建立。这种方法论可行吗?或者甚至可以通过跟踪来自 NAT 的 RST 响应来简化?这次每个从相对的两端开始,将时间减半,当一个连接到另一个时,接收方通知发送方连接已经建立。这种方法论可行吗?或者甚至可以通过跟踪来自 NAT 的 RST 响应来简化?这次每个从相对的两端开始,将时间减半,当一个连接到另一个时,接收方通知发送方连接已经建立。这种方法论可行吗?或者甚至可以通过跟踪来自 NAT 的 RST 响应来简化?

我们有客户端 A 和 B,它们位于 NAT 后面并希望进行通信。两个客户端都在监听 UDP 端口 22321 并且两个客户端都知道其他的全局 IP 地址,我们会说 A 的 IP 地址为 1.1.1.1,B 的全局 IP 地址为 2.2.2.2。所以A在每个端口上向2.2.2.2发送一个数据包,其内容基本上不需要,对于每个通过NAT的数据包,NAT都会将输出IP从A的本地地址即192.168.1.16更改为全局地址 1.1.1.1。当 NAT 将此数据包转发到客户端 B 时,它会为每个数据包创建一个状态条目,以便如果在任何这些端口上从 2.2.2.2 接收到数据包,它将知道将其转发到客户端 A (192.168.1.16)端口 22321。所有这些数据包都将在 2.2.2.2 处到达 B 的 NAT,并作为未经请求的数据包被丢弃。客户端 B 执行与客户端 A 的全局 IP 地址 1.1.1.1 完全相同的过程,当它命中客户端 A 已将数据包发送到客户端 B 的 NAT(此时基本上是其中的任何一个)的 NAT 端口时,它将转发数据包发送到客户端 A。然后客户端 A 将能够使用该连接从那时起响应。现在的问题是协商谁先去,然后第二个客户端应该何时开始,所以他们都只是在一分钟的确切开始时开始,很可能第一次尝试将无法产生连接,但如果他们都立即重复这个过程,那么应该有 1 个或什至可能有许多成功的连接,然后每个客户端可以丢弃除了第一个成功的连接之外的所有连接。1 当它到达 NAT 上的一个端口时,客户端 A 将数据包发送到客户端 B 的 NAT(此时基本上是其中的任何一个),它会将数据包转发到客户端 A。然后客户端 A 将能够使用响应那个连接从此就出来了。现在的问题是协商谁先去,然后第二个客户端应该何时开始,所以他们都只是在一分钟的确切开始时开始,很可能第一次尝试将无法产生连接,但如果他们都立即重复这个过程,那么应该有 1 个或什至可能有许多成功的连接,然后每个客户端可以丢弃除了第一个成功的连接之外的所有连接。1 当它到达 NAT 上的一个端口时,客户端 A 将数据包发送到客户端 B 的 NAT(此时基本上是其中的任何一个),它会将数据包转发到客户端 A。然后客户端 A 将能够使用响应那个连接从此就出来了。现在的问题是协商谁先去,然后第二个客户端应该何时开始,所以他们都只是在一分钟的确切开始时开始,很可能第一次尝试将无法产生连接,但如果他们都立即重复这个过程,那么应该有 1 个或什至可能有许多成功的连接,然后每个客户端可以丢弃除了第一个成功的连接之外的所有连接。s NAT(此时基本上是其中的任何一个)它将数据包转发到客户端 A。然后客户端 A 将能够使用该连接从那时起做出响应。现在的问题是协商谁先去,然后第二个客户端应该何时开始,所以他们都只是在一分钟的确切开始时开始,很可能第一次尝试将无法产生连接,但如果他们都立即重复这个过程,那么应该有 1 个或什至可能有许多成功的连接,然后每个客户端可以丢弃除了第一个成功的连接之外的所有连接。s NAT(此时基本上是其中的任何一个)它将数据包转发到客户端 A。然后客户端 A 将能够使用该连接从那时起做出响应。现在的问题是协商谁先去,然后第二个客户端应该何时开始,所以他们都只是在一分钟的确切开始时开始,很可能第一次尝试将无法产生连接,但如果他们都立即重复这个过程,那么应该有 1 个或什至可能有许多成功的连接,然后每个客户端可以丢弃除了第一个成功的连接之外的所有连接。

1个回答

是的。对的,这是可能的。网络必须能够处理所有的数据包(无一例外——丢包意味着死亡),机器必须能够足够快地发送数据包,另一端必须没有“洪水保护”,地址必须没有每次打开新连接时都会更改。这种方法设法将我大学 WiFi 后面的笔记本电脑与他的 WiFi 后面的朋友笔记本电脑连接起来,并通过我大学的对称 NAT 从我家的 WiFi 发送一个数据包。如果您使用的是手机,您应该尽可能快地发送数据包,因为手机速度很慢,但使用笔记本电脑,您可以使用 Thread.sleep() 同步大量 UDP 数据包

/**
 * Shoots empty UDP packets at every port between "start" and "end".
 */
public static void sendBarrage(int start, int end, InetAddress target) {

    final ByteBuffer to_send = ByteBuffer.allocate(0);

    // Start at a random value to avoid re-traversing failed paths on repeated attempts.
    final int starting_point = start + generator.nextInt(end - start);

    // Go from starting_point down to 1024. 
    for (int port = starting_point; port >= start; --port) { 
        try {
            if (made_connection == false) {
                myChannel.send(to_send, new InetSocketAddress(target, port));
            } else {
                break;
            }
        } catch (java.nio.channels.ClosedChannelException cce) {
            Application.printerr("Channel closed while on port: " + port);
            break;
        } catch (IOException e) {
            Application.printerr("Error sending in port: " + port);
            continue;
        }
    }

    // Go from 65535 down to say starting_point, not including starting_point.
    for (int port = end - 1; port > starting_point; --port) { 
        try {
            if (made_connection == false) {
                myChannel.send(to_send, new InetSocketAddress(target, port));
            } else {
                break;
            }
        } catch (java.nio.channels.ClosedChannelException cce) {
            Application.printerr("Channel closed while on port: " + port);
            break;
        } catch (IOException e) {
            Application.printerr("Error sending in port: " + port);
            continue;
        }
    }
}

^ 请确保在循环中调用此方法,可能需要多次尝试。请记住,此方法不适用于手机信号塔网络(除非您非常幸运并且公共 IP 地址不会更改)。如果一侧保持端口打开而另一侧进行猜测,而不是让双方都蛮力进行,这可能会更好。

另外,如果只有一侧保持端口打开,这种方法可能会很好:https : //www.goto.info.waseda.ac.jp/~wei/file/wei-apan-v10.pdf