Failing to perform TCP hole punching using STUN - networking

I have two hosts A and B. They're in different networks, behind different NATs and ISPs. I'm trying to set up a p2p connection between them by using hole punching. I use a STUN server to obtain mapped IP addresses and ports for both A and B. It goes on like this:
For A:
.\stunclient.exe --mode behavior stunserver.stunprotocol.org 3478
Binding test: success
Local address: 192.168.0.110:54709
Mapped address: 186.233.160.141:28769
Behavior test: success
Nat behavior: Endpoint Independent Mapping
For B:
.\stunclient.exe --mode behavior stunserver.stunprotocol.org 3478
Binding test: success
Local address: 192.168.3.1:57015
Mapped address: 45.70.35.52:12870
Behavior test: success
Nat behavior: Endpoint Independent Mapping
Then I try to perform the TCP hole punching technique (using netcat) by executing these two lines simultaneously and multiple times on A and B:
On A:
ncat -p 54709 45.70.35.52 12870
Ncat: TIMEOUT.
On B:
ncat -p 57015 186.233.160.141 28769
Ncat: TIMEOUT.
I always get "Ncat: Timeout" as output (not immediatelly, it takes some time), however, I could make a direct connection between A and B via UDP hole punching by running the following commands three times:
On A:
ncat -u -p 54709 45.70.35.52 12870
On B:
ncat -u -p 57015 186.233.160.141 28769
So the problem is TCP hole punching isn't working. Any ideas why?

Many issues that might be making this a challenge.
First, stunclient defaults to UDP whereas ncat defaults to TCP. So your first issue is that you aren't passing the flag (-u on most systems) to tell ncat to run as UDP. Or, you can try running stunclient in tcp mode. (e.g. stunclient --protocol tcp stunserver.stunclient.org), but TCP NAT traversal is much less reliable than UDP - especially with rudimentary command line tools )
I don't understand how your output above can have Host A and Host B behind the same NAT, yet both machines appear to have the same local IP address, using the same local port, but printing the same local ip address 192.168.3.3. How is this a thing? Is this just a typo? Or is one machine a VM host of the other and they are sharing an IP?
The behavior you are trying to achieve, having two hosts behind the same NAT connect via the public ip address is called hairpinning. This relies on the NAT to be smart enough to see that an outbound packet is really meant for a host behind the router itself and to loop it back through its own routing table instead of going out on the WAN adapter. Not all NATs support hairpinning. So what you have to do is try connecting through to both the local and remote ip addresses.
Also, try to avoid picking hardcoded ports like 20000. Let stunclient.exe pick a randomly available port for you. (i.e. don't specify --localport parameter). Then when you issue the ncat command, use the local port it picked to connect to the remote mapped port of the other ip address.
Hypothetical usage:
Host A
stunclient.exe stunserver.stunprotocol.org
Binding test: success
Local address: 192.168.1.2:1111
Mapped address: 45.70.35.52:2222
Host B
stunclient.exe stunserver.stunprotocol.org
Binding test: success
Local address: 192.168.1.3:3333
Mapped address: 45.70.35.52:4444
Address candidates passed from A to B: {45.70.35.52:2222, 192.168.1.2:1111}
Address candidates passed from B to A: {45.70.35.52:4444, 192.168.1.3:3333}
Host A then runs these command in parallel. But oops, ncat may not allow sharing the socket port between two running programs. Look at the documentation to see if the SO_REUSEADDR flag is exposed implicitly as a command line param. It may do this implicitly.
ncat -u -p 1111 45.70.35.52 4444
ncat -u -p 1111 192.168.1.3:3333
Host B then does this in two separate consoles:
ncat -u -p 3333 45.70.35.52 2222
ncat -u -p 3333 192.168.1.2:1111
In other words, try all 4 combinations of A to B and B to A.
I was about to mention making sure you don't have address dependent mapping by running the behavior test. (i.e. "symmetric NAT"). Symmetric NATs make p2p connectivity very difficult for the connection to "go direct". But you've got endpoint independent, which is good.

Related

Can I make Wireguard VPN peers to talk to each other?

I have a server running Wireguard, and I have multiple clients (peers) connected to it up and running. I am not very sure how VPN works, but this is my current setup.
The /etc/wireguard/wg0.conf of my server looks like this.
[Interface]
Address = 172.16.16.1/24
SaveConfig = true
ListenPort = 8999
PrivateKey = XXX
[Peer]
PublicKey = XXX
AllowedIPs = 172.16.16.2/32
[Peer]
PublicKey = XXX
AllowedIPs = 172.16.16.3/32
And the configuration on my clients wg0.conf looks like this.
[Interface]
PrivateKey = XXX
Address = 172.16.16.x/32
[Peer]
PublicKey = XXX
AllowedIPs = 172.16.16.0/24
PersistentKeepalive = 30
With everything up and running, from my client with IP address 172.16.16.2, I am able to ping the server 172.16.16.1. I am able to do the same from my other client with 172.16.16.3, I can ping the server 172.16.16.1.
Interestingly, from my server, I am able to ping all the peers! That is, from within 172.16.16.1, I can ping both 172.16.16.2 and 172.16.16.3. But that is the prime purpose of the setup!
Now, I want my peers to talk to each other, that is, I must be able to ping 172.16.16.2 from my other peer 172.16.16.3 and vice-versa, but this is not working. It says that the network is unreachable.
The idea is, I want it to work like a LAN server, where one server that acts as a gateway, and multiple peers/clients that can talk to each other, and also talk to the server.
Is this possible? If yes, what am I missing?
After the whole evening of searching through the internet, I found some useful links that talks about the same problem that I am facing.
Link: https://lists.zx2c4.com/pipermail/wireguard/2018-August/003250.html
That says, we must enable ip forwarding in the server to make it work like an edge router.
By default, the IPv4 policy in linux kernels disables support for IP forwarding. This prevents machines that run linux server from functioning as dedicated edge routers. To enable IP forwarding, use the following command:
[root#myServer ~ ] # sysctl -w net.ipv4.ip_forward=1
This configuration change is only valid for the current session; it does not persist beyond a reboot or network service restart. To permanently set IP forwarding, edit the /etc/sysctl.conf file as follows:
Locate the following line:
net.ipv4.ip_forward = 0
Edit it to read as follows:
net.ipv4.ip_forward = 1
Use the following command to enable the change to the sysctl.conf file:
[root#myServer ~ ] # sysctl -p /etc/sysctl.conf
Read more: https://docs.fedoraproject.org/en-US/Fedora/18/html/Security_Guide/sect-Security_Guide-Firewalls-FORWARD_and_NAT_Rules.html
With this done, now all my peers are able to talk to each other, and this functions just like a LAN network!
I followed all the steps suggested by Sibidharan but also needed to add an iptables command on the server to forward the wireguard traffic from peer to peer.
iptables -A FORWARD -i wg0 -o wg0 -j ACCEPT
I was then able to connect via ssh from peer to peer.
Just set your interface up on the client to a prefix of /24

gre tunnel issues - one sided communication

I have two machines:
Ubuntu 16.04 server VM (172.18.6.10)
Proxmox VE5 station (192.168.6.30)
they are communicating through a third machine that forwards packets between the two. I want to create a gre tunnel between the two machines and to do that and make it persistent I have edited the /etc/network/interfaces and added a gre interface and tunnel to be made on boot up as the following:
After they were created I have tried to ping one machine from the other to check connectivity, pinging the gre interface IP address (10.10.10.1 and 10.10.10.2). The issue is that when I ping the Proxmox machine from Ubuntu I get no feedback, but when I run tcpdump on gre1 on Porxmox I see that the packets are received and there is a ICMP reply outgoing:
When I run the ping the other way around and check it with tcpdump on the Ubuntu machine I get nothing. I understand that the issue is when packets leave Proxmox to Ubuntu via gre1 and get lost or blocked because Ubuntu can clearly send Proxmox packets but the reply never comes back. How can I fix this?
Check if you have packet forwarding enabled for the kernel of the 3rd machine that you user for the communication of the other 2 machines
Check /etc/sysctl.conf and see if you have this:
net.ipv4.ip_forward = 1
if it's commented (#) uncomment it, save the file and issue a:
sysctl -p
Then try again the pings...

UDP hole punching for Server/Client communication under NAT with STUN

Problem
I'm trying to develop a communication system where:
A, B are machines under NAT, A is the server B is the client
S is the STUN server
S is running on a machine reachable on the Internet
The flow is as follows:
A hits S with an opcode saying he's the server
S registers A as server
B hits S with an opcode saying he's the client
S sends to A B's external infos (IP, PORT)
S sends to B A's external infos (IP, PORT)
A starts sending B an opcode saying he's the server every 500ms
and meanwhile listens for packets saying he's got a client
B starts sending A an opcode saying he's the client every 500ms
and meanwhile listen for packets saying he's got the server
Trouble
Here's where the troubles start, the STUN server does its job, since both ends receive correct infos about the other.
But then I do never receive the other end's message, so both ends keep listening without receiving the handshake opcode nor anything else.
NAT's Behaviour
I did examine this NAT's behaviour and seems it does like this
A is at 192.168.X.X, on port 4444
connects to the outside exposing N.N.N.N:4444
so the port number is kept as long as it's free, gets a new (random ?) one if not available.
Tests
The tests I run have seen both ends (A, B) hosted on the same machine, both bound to the machine's internal IP, tried to bind to 127.0.0.1, 0.0.0.0, nothing changed.
If while they're listening for handshakes I echo something with nc to localhost, it is received and displayed (as an unrecognised message) without any problem. The connection routed via the NAT doesn't wotk tough, every packet is discarded.
Also tried with A hosted on the machine, B on an Android phone under mobile data, with a simple app written ad-hoc. Still locks waiting for something, like the nodejs tests.
Update:
Another thing I tried to do is to open an hole with nc
On two different machines under the same NAT I ran:
echo "GREET UNKOWN PEER" | nc -u <NAT IP> 4567 -p 4568
echo "GREET UNKOWN PEER" | nc -u <NAT IP> 4568 -p 4567
Different times for each machine. From my understanding this should punch an hole in the NAT with the first packets discarded and the subsequent forwarded. But nothing happened, no end got the message.
I've also tried:
from local machine
echo "GREET UNKOWN PEER" | nc -u <PUBLIC IP> 4567 -p 4568
from public machine
echo "GREET UNKOWN PEER" | nc -u <NAT IP> 4568 -p 4567
this one works, the local machine under NAT contacts the public one and after the first discarded packet is able to receive and send on the assigned port. I wonder why this doesn't work on two machines under the same NAT (???)
Code
I didn't show any code because I think there is some kind logic flaw in this, however here's the github project for that.
index.js contains the STUN server, the tests folder contains the test cases: test.js starts the stun server, PeerClientTest.js and PeerServerTest.js are mockups of the client and server.
Run node tests/test.js to start the server on a public machine (change IPs in config.js and tests/config.js)
then node tests/PeerServerTest.js to start the server ("A") and node tests/PeerClientTest.js to start the client ("B"). Both will recognize each other via STUN, then listen for the other end's handshake opcode while sending their own handshake opcode. This never happens so they just keep sending/listening forever.
Node is not required, so if there are better solutions in other languages just tell, will be appreciated.
B's NAT is filtering A's packets and is not letting them through. NAT filters unknown packets sent to it. Your server A is sending packet to client B. But client B previously never sent a packet through NAT to A. So to B's NAT A's packets are unknown and being discarded.
You need to punch a hole in B's NAT for the NAT to allow the incoming packets. Send a packet from B to A NAT's IP:Port. After that when you send a packet from A to B, B's NAT won't discard A's packet.
This won't work if A and B's NAT has a combination like Symmetric and Symmetric/PRC NAT. In this case you will have to use a TURN relay server.

How can I control the source port of a TCP packet?

To test my implementation of a NAT, I want to send TCP packets from one internal host to two different external hosts, and make sure that the source port for both streams of packets that leave the NAT have the same source port. How can I control the source port? wget uses different source ports for separate TCP connections.
Maybe you want to try netcat with -p option, if you don't want to write code by yourself, example:
$ nc -p 31337 www.google.com 80
Here is the explanation for "-p" option from man page:
Specifies the source port nc should use, subject to privilege restrictions and availability. It is an error to use this option in conjunction with the -l option.
Note though to use any port under 1024 requires root permission.
Bind the socket to a specific local port before you connect it.

How do I set an ip address for TUN interface on OSX (without destination address)?

How do I set an IP address for a TUN interface on OSX? I cannot figure out how to set up an ip address for my interface without specifying a destination IP. I don't want to do that- I'm want to more or less build a tunnel to an arbitrary address at a later point in time. Prior questions which are unhelpful:
There's a question that has an unclear answer, so I tried following the reference.
This question sets a point to point ip address for a tun device, so it has a destination, which is exactly what I don't want.
On the page for osxtuntap it says:
ifconfig tap0 10.1.2.3 up
I cannot make this work on OSX 10.6 for a TUN interface:
$ sudo ifconfig tun0 10.1.2.3 up
ifconfig: ioctl (SIOCAIFADDR): Destination address required
Adding a netmask doesn't help- OSX seems to demand a destination address:
$ ifconfig tun0 10.0.0.1/24 netmask 255.255.255.0
ifconfig: ioctl (SIOCAIFADDR): Destination address required
For linux, I get how it works. According to this page, you open() the interface, and use the ip command, and do this, and I've done this before with zero issues:
$ ip link set tun0 up
$ ip addr add 10.0.0.1/24 dev tun0
All I want to do is the same thing that I can do in linux.
EDIT:
I'm writing a little UDP tunnel app. Like so:
tun1 -> udp app #1 -> udp tunnel -> udp app #2 -> tun2
If the udp apps are on different computers (let's say local and remote), I'd like to associate their respective tun devices with an ip address, so I can send a packet from local to remote via the tunnel by sending the packet to the ip address of the tun device on the remove machine.
To borrow more from the linux tutorial, the author sets up a tun device on local and remote, associates ips, and runs a simple tunneling app, and then pings the other end of the tunnel:
[remote]# ip link set tun3 up
[remote]# ip addr add 192.168.0.2/24 dev tun3
[remote]$ ./simpletun -i tun3 -s
# server blocks waiting for the client to connect
[local]# ip link set tun11 up
[local]# ip addr add 192.168.0.1/24 dev tun11
[local]$ ./simpletun -i tun11 -c 10.2.3.4
# nothing happens, but the peers are now connected
[local]$ ping 192.168.0.2
By default, tun devices operate in the layer 3 mode, aka point to point. You're asking for layer 2 mode which more closely resembles a generic Ethernet device. Linux calls these tap devices. In OpenBSD you can switch a tun device into layer 2 mode with "ifconfig tun0 link0". The Macintosh tuntaposx driver mimics Linux' device schism; open a tap device instead.
You might want to review https://community.openvpn.net/openvpn/wiki/BridgingAndRouting to determine if you really want tap devices. They add a little overhead. If you just need two boxes to pass IP packets between each other and no bridging or broadcasting to a larger subnet, point to point should be sufficient.
For example, if you have two machines, one we label "local" with a LAN IP address like 192.168.0.12 and another we label "remote" with a LAN IP address like 192.168.1.14, you can assign tunnel IP addresses thusly:
ifconfig tun0 inet 10.0.0.1 10.0.0.2 up
on the local system, and:
ifconfig tun0 inet 10.0.0.2 10.0.0.1 up
on the remote system. Note the reversed perspective on the remote machine. Do not set your point to point addresses to anything on an existing subnet; it will not route properly.
I can't stress this enough: read and re-read the manual pages ("man ifconfig" and "man tun", probably others) until they make sense. My ifconfig examples above may differ slightly from your operating system.
And for another perspective you might look into GRE tunnels as their functionality mirrors what you describe for your program. However, GRE is likely not viable in today's TCP-centric networks nor is it a good idea due to major security issues.
If your goal is to circumvent an overbearing firewall, be aware that many such firewalls block UDP (and especially GRE) packets. In such a case, try SSH interface tunneling to set up tun/tap interfaces and forward packets. You get encryption and optionally compression as well. :)

Resources