Confused about NAT UDP Hole Punching

Started by
9 comments, last by hplus0603 12 years, 2 months ago
I've asked about this in a couple of places but havn't found anyone that knows how to do it.
I've also been reading up at http://www.brynosaur...pub/net/p2pnat/ but some of it is a bit fuzzy to me so maybe you guys can help.

First, the problem:

  • Game Server is UPnP enabled. works perfectly.
  • Client has no UPnP protocols on their router so it requires manual port settings (which kills any modern game) or NAT Punching.
  • "Master Server" is just an HTTP PHP scripted web server that receives Form Submissions to add, remove, and output current Game Server IP:Port infos for clients to download and browse through. (Server Browser data for clients)

[indent=1]currently i have:

[color=DarkOliveGreen]Server starts, contacts master server via HTTP request and informs it "This is my port/ip/name/etc..."

[color=Navy]Client loads and makes an HTTP request to the master server and gets the serverlist, including our target [color=DarkOliveGreen]server.

[color=DarkOliveGreen]Server Listens on: <Server Customized>
[color=DarkOliveGreen]Server Sends To: 45,000

[color=DarkSlateBlue]Client Listens On: 45,000
[color=DarkSlateBlue]Client Sends To: <Server customized>

[color=DarkSlateBlue]Client does HTTP request to a master server to get the target server's IP and <ServerCustomized>port (working)

The [color=DarkSlateBlue]client sends UDP request to the [color=DarkOliveGreen]server join the [color=DarkOliveGreen]server targeting <ServerCustomized> port. (working)

[color=DarkOliveGreen]Server hears the [color=DarkSlateBlue]client request (working)
and replies to [color=DarkSlateBlue]client listening port 45000 "yes you may join" (NOT working - NON-UPnP routers are blocking)

as far as i can tell, the[color=DarkOliveGreen] server is sending it, but the[color=DarkSlateBlue] client doesn't hear it due to it's NAT router.

[size=1]Q: OMG! Why are you doing hole punching?
A:Because i've already found 2/3 testers that DONT have upnp routers (eg: WANPPPConnection:1 and WANIPConnection:1).

From what i understand from http://www.brynosaur...pub/net/p2pnat/ , it appears that the game server won't need a 3rd negotiating machine so long as the game server is properly configured to receive connection requests from game clients - no problem. got that.

According to the document and what I've been able to scrounge up on the web it seems the game server will need to reply to the client on the end points:

  • LAN

    .IP

    .xxx

    .xxx

    :MachinePort "Token #1"
  • WAN

    .IP

    .xxx

    .xxx

    :RouterPort "Token #2"
  • WAN

    .IP

    .xxx

    .xxx

    :MachinePort "Token #3"


(as far as i understand, endpoint means IP:PORT )

The game client will reply back with a "I herd "Token #n " and the server locks in on that information to run the game connection.

Now my questions:

  1. Do i understand correctly?
  2. How do i find the external WAN port of the client?

LAN IP, PORT no problem.
WAN IP, no problem
WAN Port - don't know how to get it.

On a side question of the same topic:
on section 2.2 of the document http://www.brynosaur...pub/net/p2pnat/,
how do either of the machines receive the Hole Punch UDP Data Packets from the Server S if the client routers are not yet holepunched to get through the NAT?



VB.NET 2010 Ultimate
Win 7 Ultimate
You can argue with me all day long about which programing language is best...
but it all comes down to which language is best FOR YOU.

vb.net 2010 Visual Studio Ultimate
Advertisement
First, you cannot generally do NAT punch-through with a typical web server PHP set-up, because NAT punch-through generally requires persistent server processes with the ability to accept bound port requests.

Second, if the server is properly port forwarded, then you can use PHP as a simple service discovery ("server list") and it will work fine. The client simply needs to connect to the IP/port of the listed server, and no NAT discovery or punch-through is needed. This is because, when a connection is initiated from the inside of a NAT, anything that comes back to that same client from the server the connection was initiated to will work -- else, even a regular web page wouldn't work behind NAT!
enum Bool { True, False, FileNotFound };
The "Master Server" is just server browsing data. it just tells the Game Client "This is a list of servers." the PHP Master Server is not trying to set up a NAT punch through. Just pass information for "You can find a server at 123.123.123.123:5555"

The Game Server is trying to set up a connection. It can hear, but the client can't.


Game Server---> UPnP Router---> Public Cloud <---- NAT Non-UPnP Router <---- Game Client

The client simply needs to connect to the IP/port of the listed server, and no NAT discovery or punch-through is needed.[/quote]
So then the connection error i'm having is likely in my code and not in a closed client router port?

-----
EDIT:
it can't be a programing error because any client with UPnP router OR with a client machine directly connected to the internet with no router is able to hear the Game Server say "Yes, you may join the game".
You can argue with me all day long about which programing language is best...
but it all comes down to which language is best FOR YOU.

vb.net 2010 Visual Studio Ultimate
As far as I understand, the problem is that your server is sending to a pre-determined known port. I believe it should go as follows:

- The client sends a UDP packet to a known port on the server. The UDP packet will include the port the client is listening on.
- The NAT will intercept this, change the IP address in the IP header, and potentially change the client port in the UDP header as well.
- The server receives the UDP packet. It can reply whenever it wants using the IP address from the IP header, and the client port from the UDP header, which may not be the same as what the client thinks it is listening on.
- The NAT translates it back.
- The client listens on the port that it chose.

Note that for UDP the packets must be regular, or the NAT may decide the channel is idle and remove the address translation from its internal table.
I already thought of that last week.


Public Sub SendData(ByVal cSUM As String, ByVal Data() As Byte)
' TODO: Optimize routine
' 1 - double sending.
' 2 - don't make it repeatedly calculate port and ip. have it determined once and
' stored in varable for faster recall.
' 3 - Fine tune error checking.

Try
msg("Send Data To:" & cSUM)
msg("Client IP from String = " & GetClientIPFromString(cSUM))
msg("Client Port from String = " & GetClientPortFromString(cSUM))
ServerSocket.Send(Data, Data.Length, GetClientIPFromString(cSUM), ClientPort)
ServerSocket.Send(Data, Data.Length, GetClientIPFromString(cSUM), GetClientPortFromString(cSUM))
Catch ex As Exception
msg("ERROR IN SENDDATA() TRY :: " & cSUM & " , " & Data.ToString, ConsoleColor.Red, ConsoleColor.White)
EventRaise(EventPointer.errEncounter, ex)
End Try
End Sub


Server console Output: (i removed the last oct. of IP addresses manualy.)

>> New Player Requesting Connection: $REQ$BRIANTITAN$ | 174.108.69.xxx:53139
>> creating client: 174.108.69.xxx:5139
>> IGNORING CONNECTION: Client is already connected. Ghost? !!TODO! disabled ignore for debugging packetloss.
>> Replying To: 174.108.69.xxx:53139
>> Client IP from String = 174.108.69.xxx
>> Client Port from String = 5139
>> Replying To: 174.108.69.xxx:53139
>> Client IP from String = 174.108.69.xxx
>> Client Port from String = 5139
>> Replying To: 174.108.69.xxx:53139
>> Client IP from String = 174.108.69.xxx
>> Client Port from String = 5139
[/quote]

so, currently, i'm actualy sending to both the 45000 port and the port received from the endpoint.
You can argue with me all day long about which programing language is best...
but it all comes down to which language is best FOR YOU.

vb.net 2010 Visual Studio Ultimate

EDIT:
it can't be a programing error because any client with UPnP router OR with a client machine directly connected to the internet with no router is able to hear the Game Server say "Yes, you may join the game".


Of course it can be a programming error. The client should not need UPnP at all. Assuming there is proper port forwarding on the server side router, from external ip/port IP/PORT, and the client connects to that IP/PORT, and the server simply does a return to that connection using accept or/recvfrom, it will just work. The only reason you'd need to screw with the client router would be if you want to MAKE a connection INTO the client. Which you don't -- the connection is already made from the client to the server.

Btw: your system should also support "plain" port forwards, not just UPnP, for servers. Allow a mechanism where servers can be configured with what their external IP address "is," and put that on the listing server. You might want to make a test connection from the listing server to the game server when it lists itself, to make sure that the indicated IP/PORT actually is listening correctly.

Finally, I am not at all a fan of UPnP for configuring firewalls. It has at least two problems:
1) It allows random software on the inside to re-configure your firewall. That's not what you want when trying to mitigate the effects of trojan malware etc.
2) What if two nodes behind the router demand the same port? Only one can have it.

The right solution is to use proper NAT punch-through using a persistent publicly visible introducer server -- but that's more complex, as I think you already know.
enum Bool { True, False, FileNotFound };
Ah. I hope I didn't sow confusion there, hplus. My post was based on reading rather than personal experience. So you can reply using accept/recvfrom, but not send a new packet to the client on the same IP/port? What's the difference? I thought packet-wise it would be the same thing.

Ah. I hope I didn't sow confusion there, hplus. My post was based on reading rather than personal experience. So you can reply using accept/recvfrom, but not send a new packet to the client on the same IP/port? What's the difference? I thought packet-wise it would be the same thing.


Are you confusing a few concepts here? Accept is for TCP, recvfrom is for UDP. TCP ports are NOT the same as UDP ports.

Edit: The server should be the only peer that reads packets from a fixed port. The server will reply to the address returned from recvfrom(). That's because the client's router will probably assign some random port number the first time it sends a package to the server.

For NAT-Punchthrough you will need a master server that can read UDP packets. When the server registers itself with the master server it will send a datagram to it. The master will store the port it sees and tell that port to the clients querying the server list so they can connect. However, the server will have to send a keep-alive packet to the master server because its router might change the assigned port if it is not used for a while. This is not a problem however, since the master server should delete dead servers from its list anyway.

The real problem is that you probably don't have the possibility to run a UDP service on your of-the-shelf webhost.

Are you confusing a few concepts here? Accept is for TCP, recvfrom is for UDP. TCP ports are NOT the same as UDP ports.


Not confusing concepts, I just normally work with higher-level APIs, so directly quoting from hplus with only an educated guess what those functions do. ;)
Oh ok. :)

This topic is closed to new replies.

Advertisement