← Back to team overview

p2psp team mailing list archive

Re: NAT Traversal Set of rules implementation

 

Hi Vicente,

On 09.07.2015 12:31, Vicente Gonzalez wrote:
>  
>
>     Taking a few ideas from this paper [1], I added subtypes of the
>     symmetric NAT type depending on the public source port allocation
>     (port preservation (SYMPP), sequential port allocation (SYMSP), or
>     random port allocation (SYMRP)) to the documentation and the
>     script. I added a result table below, but have to recheck the
>     values to be certain about the results (the table is not
>     symmetric, I marked the odd results in bold).
>
>
> Please, refresh my memory: Which is the difference between Peer1 and
> Peer2?
Peer 1 and 2 are completely identical except that Peer 2 is started
after Peer 2, so the arrival order is different: Peer 1 is already a
team member, whereas Peer 2 is the newly joining peer.
An asymmetric behaviour could happen, as it makes a difference if the
existing or the newly joining peer is behind a NAT.

As for the asymmetry I posted this week: I reran the test a few times
and got different results each time. I think the result should be "yes"
but somehow a "hello" packet are lost and therefore the connection
fails. So it seems sending keepalive packets (to open the NAT hole in
case the "hello" packet gets lost) until the peer has joined correctly
could improve the situation.

>     This implementation of the symmetric NAT subtypes is to determine
>     if the (now following) NTS implementation for symmetric NATs works.
>     An algorithm I would suggest to the P2PSP protocol is to determine
>     if the NAT is a "port preserving symmetric NAT", i.e. the public
>     source port is the same as the source port of the local host
>     behind the NAT (which can easily be determined).
>
>     Then the only case where a connection cannot be established would
>     be with two symmetric NATs with random port allocation, or with
>     sequential allocation and a "port stepsize" greater than 1 (which
>     seems not supported by the P2PSP algorithms).
>
>
> That's right. Well, however, if we are able to determine the port
> allocation scheme used by a NAT, do you think that we could traverse it?
Yes, exactly. I suppose that NAT traversal should be possible in all
cases except between two NATs with indeterminable (i.e. random) port
allocation (or with NATs with some weird behaviour).
To determine the port allocation scheme, a newly arriving peer should
send packets to the splitter and the monitor¹, and the splitter then
sends the NAT type back and to the other peers to facilitate the
connection establishment.
The timing would look like this (in contrast to the current NTS
algorithms, the NAT type determination happens at the splitter instead
of the monitor):

  * new peer connects via TCP to splitter and receives UDP address and
    port of splitter and monitor
  * new peer sends UDP packets to splitter and monitor (the packets
    include the local source port of the new peer that is used for the
    respective receiver)
  * the monitor forwards the local source port (as stated in the packet)
    and the global public source port of the new peer to splitter
  * the splitter waits for response of the monitor and determines the
    NAT type and port allocation scheme (*)
  * the splitter sends the guessed destination port numbers to the
    existing peers and tells the new peer to send "hello"
  * new peer and existing peers send "hello" to the respective
    destination port

How do you think about this approach, do you have suggestions/comments?
Is it ok to directly implement this, or should I first implement the
algorithms as stated in the P2PSP paper, and afterwards add support for
other NAT types?
Some thoughts: The step marked with a (*) above seems the most difficult
one to implement: The splitter has to keep track of newly arriving peers
and wait asynchronously (maybe with a timer) for the response from the
monitor, while storing the information about the new peer. The
splitter's timers and arriving peers list should be limited, to prevent
DoS when a malicious third party sends many joining requests.

A related thing to consider: If two peers behind the same NAT want to
join the team simultaneously, the splitter and monitor must
differentiate between the two peers. For a symmetric NAT, the splitter
cannot match the source ports received by the monitor to the source
ports received by itself, as all are different. So an idea is to assign
random unique peer IDs at the splitter and send them to the peer over
the TCP connection. Then for the joining process, the ID is sent with
each packet to identify the peer.


In Peer_NTS, to handle different NTS-specific messages, I just copied
over the process_next_message method from Peer_DBS and added another
case. However, it seems simpler to handle specific messages in
inheriting classes (i.e. Peer_NTS) and forward all other messages to be
handled by the base class (Peer_DBS). I have submitted a pull request
that implements this technique [1]; it would be great if you could have
a look at it and shared your thoughts.

Just a thought that appeared when extending the process_next_message
method: Currently, to differentiate between message types, the length of
the message is compared to the expected struct size. For a small number
of message types, this seems a good and easy to understand technique,
however it could lead to collisions if some protocols are changed or the
number of message types increases. Is there another approach (e.g. a
message type prefix) planned for later versions of the P2PSP implementation?

Thanks, and sorry for the long email :)
Max


¹ Different addresses for splitter and monitor would be ideal, as some
symmetric NATs do not change the source port when only the destination
port differs, but this address-only sensitivity is rare.

[1] https://github.com/P2PSP/p2psp/pull/48

Follow ups

References