Peer to Peer Connection with NAT

Hi,

I want to implement peer to peer connection with two people behind NAT.
I know there are crates for doing this but I want to learn by myself.

What I know is:

If someone connects a server that has public IP, his router or whatever will open a port for him to reach outside of the world. Since these port is handling connections between public IP and person behind NAT. It also able to transfer packages comes to this port through person behind NAT.

I know that two people need to connect a public server then when server knows which port is available for them, it will let clients to connect from these specific ports and get out from connection. Then this connection becomes peer to peer.

I may have theoretical problems and I'm open to learn.

Beyond theoretical details I don't know how to implement this in code. I've already done couple of client-server architecture with TCP and WebSockets but I'm not sure how to manipulate TCP tunnel to connect 2 person behind NAT.

Every bit of information's appreciated.

  • There are different types of NAT, some are easier to traverse than others. They can require different strategies.
  • The type of NAT used by a router may also vary between TCP and UDP.
  • A user may be behind multiple layers of NAT.
  • NAT primarily applies to IPv4. In IPv6 in practice you don't have to deal with NATs (or port mapping to be precise), but firewalls can still be an issue.
  • For some NAT gateways a negotiation protocol like UPNP IGD, PCP and NAT-PMP can help.
  • NAT traversal is much easier for UDP compared to TCP. So you might want to try using QUIC as transport.

If it has to be TCP then you can also look into TCP simultanous open.

The simplest way of NAT traversal is that both nodes rely on a 3rd party to discover their external IPs and their NAT port mapping behavior and then simultaneously initiate connections to each other from/to agreed-on ports and this will open matching NAT mappings in their respective gateways. As mentioned above, this will only work for some types of NAT.

There also are a bunch of RFCs that might be worth reading

https://www.rfc-editor.org/rfc/rfc5128.html
https://www.rfc-editor.org/rfc/rfc7857.html

3 Likes

I'm able to collect IP and ports from clients with a main server but can't establish p2p with these exchanged addresses.

// Server Code
let udp_socket = UdpSocket::bind("0.0.0.0:2323").unwrap();
        let mut peers = vec![];
        for _ in 0..2 {
            let mut buf = [0; 100];
            let (size, peer) = udp_socket.recv_from(&mut buf).unwrap();
            peers.push(format!("{}:{}", peer.ip(), peer.port()));
            let data = buf.split_at(size).0;
            let data = String::from_utf8(data.to_owned()).unwrap();
        }

        udp_socket.send_to(peers[0].as_bytes(), &peers[1]).unwrap();
        udp_socket.send_to(peers[1].as_bytes(), &peers[0]).unwrap();

I send details to peers like this but when receivers take them, they are not able to use these information to create new connection.

Networks have many possible sources of errors. Try approaching the problem step by step. First have them run on the same PC (bound to 127.0.0.1, 127.0.0.2 and 127.0.0.3) without any NATs getting in the way and make sure that works. Then introduce a single NAT, and run wireshark on each to see which packets arrive and which don't. Then try with two NATs. Setting up a simulated network environment can also help with debugging since you control each part.

1 Like

It works in my internal network clearly.
If a client has public address and another is behind NAT, only client that has public address got message from other but NAT client can't get it.
If both person behind NAT, it's not working.

I tried to check my NAT type through this website: https://www.checkmynat.com/ and if this is correct, I have " Port Restricted Cone NAT" Is this the reason why my udp hole punching's not working ?

Is there a way to achieve this ?

This case should be trivial with both UDP and TCP. All that's needed is for the node behind the NAT to initiate the connection and then they can exchange messages. What it needs to do that is a notification from the coordinating/rendezvous node that someone wants to establish a connection.

If that doesn't work I suspect that your rendezvous protocol isn't working and you should add logging on each end to figure out where things go wrong.
If you're not getting messages from the rendezvous node then you're not keeping the NAT mapping between the NATed node and the rendezvous node alive.

Here is my whole code. I don't know what I'm doing wrong.
Firstly I send a message to public address then it must create NAT registry in my router or ISP systems.
Then exchange IP and port to each client and try to send data through opened NAT registry.

use std::{io, net::UdpSocket};

fn main() {
    println!("Hello, world!");
    println!("Addr");
    let mut addr = String::default();
    io::stdin().read_line(&mut addr).unwrap();
    let addr = addr.trim_end();
    println!("Server | Client");
    let mut input = String::default();
    io::stdin().read_line(&mut input).unwrap();
    let input = input.trim_end();
    match input {
        "server" | "Server" | "SERVER" | "s" => server(addr),
        "client" | "Client" | "CLIENT" | "c" => client(addr),
        _ => {}
    }
}

fn server(addr: &str) {
    let udp_socket = match UdpSocket::bind(addr) {
        Ok(udp_socket) => udp_socket,
        Err(err_val) => {
            eprintln!("{}", err_val);
            return;
        }
    };
    let mut buffer = [0_u8; 100];
    let mut peers = vec![];
    for _ in 0..2 {
        let (data_size, peer) = match udp_socket.recv_from(&mut buffer) {
            Ok((data_size, peer)) => (data_size, peer),
            Err(err_val) => {
                eprintln!("{}", err_val);
                return;
            }
        };
        peers.push(format!("{}:{}", peer.ip(), peer.port()));
        let data = buffer.split_at(data_size).0;
        let message = String::from_utf8_lossy(data).into_owned();
        println!("{}:{} = {}", peer.ip(), peer.port(), message);
    }

    match udp_socket.send_to(peers[1].as_bytes(), &peers[0]) {
        Ok(_) => println!("Sent First"),
        Err(err_val) => {
            eprintln!("{}", err_val);
            return;
        }
    };
    match udp_socket.send_to(peers[0].as_bytes(), &peers[1]) {
        Ok(_) => println!("Sent Second"),
        Err(err_val) => eprintln!("{}", err_val),
    };
}

fn client(addr: &str) {
    let udp_socket = match UdpSocket::bind(addr) {
        Ok(udp_socket) => udp_socket,
        Err(err_val) => {
            eprintln!("{}", err_val);
            return;
        }
    };
    match udp_socket.send_to(
        "Hello Sir".as_bytes(),
        "0.0.0.0", /* I change it with public address */
    ) {
        Ok(_) => {}
        Err(err_val) => {
            eprintln!("{}", err_val);
        }
    };
    let mut buffer = [0_u8; 100];
    let (data_size, peer) = match udp_socket.recv_from(&mut buffer) {
        Ok((data_size, peer)) => (data_size, peer),
        Err(err_val) => {
            eprintln!("{}", err_val);
            return;
        }
    };
    let data = buffer.split_at(data_size).0;
    let message = String::from_utf8_lossy(data).into_owned();
    println!("{}:{} = {}", peer.ip(), peer.port(), message);

    match udp_socket.send_to("Hello Dear".as_bytes(), message) {
        Ok(_) => {}
        Err(err_val) => {
            eprintln!("{}", err_val);
            return;
        }
    };

    let mut buffer = [0_u8; 100];
    let (data_size, peer) = match udp_socket.recv_from(&mut buffer) {
        Ok((data_size, peer)) => (data_size, peer),
        Err(err_val) => {
            eprintln!("{}", err_val);
            return;
        },
    };
    let data = buffer.split_at(data_size).0;
    let message = String::from_utf8_lossy(data).into_owned();
    println!("{}:{} = {}", peer.ip(), peer.port(), message);
}

You should also add what the server and both clients are printing in that situation. After all I don't know what kind of port mapping you're seeing in practice.
And since you seem to be handling this code in a very manual manner (typing into stdin) rather than coordinating things programmatically, timing can matter a lot. UDP NAT mappings stay only open for a little time, somewhere between 60 seconds to a few minutes maybe.

And as mentioned earlier, I recommend running wireshark on all machines to get a better view of all the packets. It'll also show ICMP messages if there are any, which can be useful.

And of course it's also possible that not just NATs but also firewalls are getting in the way.

3 Likes

I'd suggest getting a notebook and writing down each config you try. Slowly make the configuration worse to better troubleshoot the problematic conditions.

The other user's suggestion to use Wireshark is a great idea! Definitely get that set up.

You might also add in some additional logging to better pinpoint what steps are happening when. The tracing and tracing_subscriber crates are excellent for this, but some extra println!() invocations can help too.

1 Like

Firstly thanks for your timing concern, normally I hardcode details but just kept like this to show you.

First one is public server then others are my computer. These IP addresses are mine for sure.
As it can be seen in photo, I can transfer addresses. If I do same thing with another public IP client, only public IP client receives data. But with 2 NAT client it's problem. Even though it's same computer.

Seems like I'm able to send messages but when it comes to pair communication, probably my NAT doesn't allow them to each other. At least I read these details in this way, if I'm not wrong.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.