I am now convinced that direct "UDP hole punching" between 2 people each of them behind a NAT, without a 3rd party server is really difficult / sometimes impossible (see question here and my answer below).
So, how to do a UDP hole punching with a 3rd party server?
Example:
peerA$ nc -u -p 7777 serverIP 8888
Now this packet is translated by peerA's NAT from localnetworkIP to peerApublicIP and the source port is translated from 7777 to 55123.
Now the server sees this packet arriving and remembers peerA's public IP + port 55123. It gives these information (peerA's public IP + 55123) to peerB, and vice versa (the similar process described before happens for peerB, and server remembers peerB's publicIP + source port that has been translated by peerB's NAT from 8888 to 42000).
Now peerA can do this
peerA$ nc -u -p 7777 peerBpublicIP 8888 # will be dropped by peerB but at least it will
# punch a hole in peerA's firewall, port 55123
and peerB knows that, to connect peerA, he has to use port destination 55123 (this info was given by server):
peerB$ nc -u -p 8888 peerApublicIP 55123
Question: this seems to work under the following assumption:
If
peerA$ nc -u -p 7777 serverIP 8888
gets its source port translated from 7777 to 55123, then
peerA$ nc -u -p 7777 peerBpublicIP 8888
Will also get source port 7777 translated to 55123.
But is this really true on most routers?
If not (example: when connecting to server, NAT translates 7777 to 55123, but when connecting to peerB, NAT translates 7777 to 51098), how is UDP hole punching working?
TL;DR: More generally, how to do an UDP hole punching with just netcat, involving peerA, peerB, and a server (the latter used only at the beginning, and not later)?
Note: I already read this article, but it's not obvious if the port translation is done in the same way when connecting server and then peerB.