UDP hole punching can't connect externally - java

I have previously made a simple peer to peer chat program using UDP hole punching that worked and now I am trying to do something similar but within a game made using libGDX. The game itself runs fine and the connections work on a LAN but I have difficult trying with external connections. I understand how UDP hole punching works as follows:
If both person A and B know each other's IP addresses and ports then:
A sends a UDP packet to B which punches a hole in A's NAT but is dropped by B's firewall
A waits for a reply
B sends a UDP packet to A which punches a hole in its own NAT and goes through A's firewall
B waits for a reply
A receives B's initial message and sends a second message to B
B receives A's message
My networking code is in one class:
private boolean connected;
private DatagramSocket socket;
private DatagramPacket packet;
private InetAddress peerIP;
public NetworkManager(InetAddress peerIP){
this.peerIP = peerIP;
log("Created with peer ip: " + peerIP.getHostAddress());
connected = false;
}
#Override
public void run(){
try{
log("Setting up socket");
socket = new DatagramSocket(Constants.CLIENT_PORT);
log("Socket successfully setup");
//Punch hole
log("Punching UDP Hole");
sendBytes("one");
//Receive
log("Waiting for peer reply");
receiveBytes(3);
//Send second message
if(Arrays.equals(packet.getData(), "one".getBytes()) ){
sendBytes("two");
}
}catch(Exception e){
log("Error connecting to peer");
return;
}
log("Successfully connected");
connected = true;
}
private synchronized void receiveBytes(int length) throws Exception {
packet = new DatagramPacket(new byte[length], length);
log("Receiving " + length + " bytes...");
socket.receive(packet);
log("Received bytes " + packet.getData()+ " from " + packet.getAddress());
}
private synchronized void sendBytes(String s) throws Exception {
byte[] sendBytes = s.getBytes();
packet = new DatagramPacket(sendBytes, sendBytes.length, peerIP, Constants.CLIENT_PORT);
log("Sending " + sendBytes.length + " bytes...");
socket.send(packet);
log("Bytes sent");
}

If both A and B are in the same network, i.e if they are in the same LAN, A can connect to B and vice versa. You don't need UDP Hole punching here.
But if they are in different networks, or behind different NATs, you can try to achieve a direct connection using UDP hole punching. Here you need a mediator server. And A and B need to ping mediator server, which will pass the IP and port number to the other corresponding clients.
Most important point to note here is that not all NATs supports hole punching. It is upto the NAT implementation, which is mostly not available in public domain. So even if you use UDP Hole Punching, you need a relay server as a fallback. In case it doesn't get hole punched, the relay server can pass the messages to each other.
http://www.brynosaurus.com/pub/net/p2pnat/
This link explains UDP Hole Punching architecture.

Related

Router does not forward multicast packet across interfaces

My router has three network interfaces:
Ethernet;
2.4GHz antenna;
5GHz antenna;
I have a program that sends and receives packets on a multicast address (I set it to 239.5.6.7) on port 10468.
For my tests I have two devices (one sender and one receiver) that exchange information.
If both devices are connected to the same interface of the router (let's say, both connected to the 5GHz WiFi) then the devices will be able to communicate.
On the other hand, if the devices are on two different interfaces (one 5GHz and one Ethernet), packets will not be exchanged (as if the router does not forward multicast traffic to one interface to the others).
All three interfaces support multicast (examining traffic with WireShark shows that every host is able to join the multicast group), but it seems like the router is blocking this cross-interface exchange.
It really is a shitty router issue? Or I did something wrong on my end?
Both the sender and receiver communicate with the multicast group with all their interfaces (as long as they have a link-local IP address).
getActiveInterfaces() is a method that gathers such interfaces and saves them in an array list.
Sender snippet (getActiveInterfaces() returns only the ethernet connection):
static final int port = 10468;
public static void main(String[] args) {
try {
ArrayList<NetworkInterface> interfaces = getActiveInterfaces();
// Initialize sockets
ArrayList<MulticastSocket> sockets = new ArrayList<>(interfaces.size());
for (NetworkInterface networkInterface : interfaces) {
MulticastSocket socket = new MulticastSocket();
socket.setOption(StandardSocketOptions.IP_MULTICAST_TTL, 1);
socket.setNetworkInterface(networkInterface);
sockets.add(socket);
}
InetSocketAddress group = new InetSocketAddress(InetAddress.getByName("239.5.6.7"), port);
byte[] buf = new byte[128];
String msg = "Hello";
DatagramPacket toSend = new DatagramPacket(msg.getBytes(), msg.getBytes().length, group);
while (true) {
Thread.sleep(1000);
for (MulticastSocket socket : sockets)
socket.send(toSend);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Receiver snippet (getActiveInterfaces() returns only the 2.4GHz wireless connection):
static final int port = 10468;
public static void main(String[] args) {
try(MulticastSocket s = new MulticastSocket(port)) {
InetSocketAddress group = new InetSocketAddress(InetAddress.getByName("239.5.6.7"), port);
ArrayList<NetworkInterface> activeInterfaces = getActiveInterfaces();
for (NetworkInterface networkInterface : activeInterfaces) {
s.joinGroup(group, networkInterface);
}
DatagramPacket rec = new DatagramPacket(new byte[128], 128);
while(true) {
s.receive(rec);
System.out.println(new String(rec.getData(), StandardCharsets.UTF_8).replaceAll("\0", ""));
}
}catch (Exception e) {
e.printStackTrace();
}
}
After some investigations, I came to a conclusion that this is a router issue.
It seems that there is some sort of whitelisting going on.
For example, packets sent to the reserved Local Control Block multicast addresses (224.0.0.1-224.0.0.255) do not have this issue. Well-known multicast IPs are also whitelisted (like 239.255.255.250, used for SSDP).
Even changing to the next multicast address (239.255.255.249 or 239.255.255.251) leads to the same issue.
The only solution I could come up with is to use one of these whitelisted addresses and to use a different port number (not one linked with the actual address).
you may want to up your ttl to 3,
each time a multicast packet is received by a router the ttl is decremented by 1, when the ttl is 0 the packet is dropped.
Does it work with the sender and receiver on the same machine?
If it does, next as root, run
tcpdump multicast
on both machines
you should see something like
16:26:22.085822 IP debian.attlocal.net.51429 > 239.35.3.5.3535: UDP, length 1316

Pcap4j TCP packets being dropped after showing on Wireshark

For educational purposes I'm trying to perform a SYN flood attack on a Ubuntu 18.04 VM. I have enabled bridge mode in my VM settings and set up a web server (10.0.0.10) I can reach on my host pc (10.0.0.3) and vice versa with pings. Pinging from host to server shows Wireshark traffic on the server (request and response), pinging from server to host also shows Wireshark traffic on the server, but not on the host, even though the ping packets are correctly built.
The way I build my attack is to generate random IPs, construct TCP SYN packet and send it to the web server from my host through port 80 (open), which should send a TCP SYN/ACK packet back (I used iptables to route it back to my host pc).
If I construct a TCP packet through Pcap4J (Pcap library for Java) and subsequently send it through the handler, I see it pop up on the host Wireshark.
However, if I check the Wireshark on my VM, the packets do not arrive. The handler does not give an error and the program exits correctly and I am therefore unsure how to fix this problem.
Where is the packet dropped and what can I do to fix it? I need the packets to reach the web server VM (and the server to send them back).
Code:
Pcaphandle send_handle;
//nif_address is a constant of my ethernet connection defined in the file
try {
PcapNetworkInterface nif = Pcaps.getDevByAddress(nif_address);
if (nif == null) {
System.out.println("Networkinterface is null");
return;
}
// Open the device and get a send_handle
int snapshotLength = 65536; // in bytes
int readTimeout = 50; // in milliseconds
send_handle = nif.openLive(snapshotLength, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, readTimeout);
} catch (PcapNativeException e) {
System.out.println("Cannot bind NIF to variable from localhost");
e.printStackTrace();
return;
}
//Send packets, e.g. 1 packet by 5 different IPs
for (int i = 0; i < 5; i++) {
//generateIP() function not shown here, but is simply a randomizer and format to IP
InetAddress src_ip = generateIP();
Packet tcpPacket = constructSYNPacket(i, src_ip);
try {
send_handle.sendPacket(tcpPacket);
System.out.println(send_handle.getError());
} catch (PcapNativeException | NotOpenException e) {
e.printStackTrace();
}
}
private Packet constructSYNPacket(int packetNr, InetAddress src_address) {
TcpPacket.Builder tcpBuilder = new TcpPacket.Builder();
tcpBuilder
.syn(true)
.ack(false)
.rst(false)
.psh(false)
.urg(false)
.srcAddr(src_address)
.srcPort(TcpPort.getInstance((short) srcPort))
.dstAddr(dst_address)
.dstPort(TcpPort.getInstance((short) dstPort))
.correctLengthAtBuild(true)
.correctChecksumAtBuild(true)
.sequenceNumber(100000 + (packetNr*50));
IpV4Packet.Builder ipv4Builder = new IpV4Packet.Builder();
ipv4Builder
.srcAddr((Inet4Address)src_address)
.dstAddr((Inet4Address)dst_address)
.dontFragmentFlag(true)
.fragmentOffset((short)0)
.ihl((byte)5)
.correctLengthAtBuild(true)
.correctChecksumAtBuild(true)
.protocol(IpNumber.TCP)
.version(IpVersion.IPV4)
.tos((IpV4Packet.IpV4Tos) () -> (byte)0)
.ttl((byte)100)
.payloadBuilder(tcpBuilder);
EthernetPacket.Builder ethBuilder = new EthernetPacket.Builder();
ethBuilder
.srcAddr(nif_mac)
.dstAddr(dst_mac)
.type(EtherType.IPV4)
.payloadBuilder(ipv4Builder)
.paddingAtBuild(true);
Packet p = ethBuilder.build();
Note: I already disabled SYN cookies in the Ubuntu sysctl.
Even though I have not found out why bridging mode did not work, I managed to make it work using a host-only adapter. Pings and packets were received both ways when I used it.

UDP socket multicast

I will start by saying that I'm not absolutely a network expert.
I'm trying to create a UDP multicast socket system with four RaspBerry Pi model B.
Into each Rpi is running a listening script (I will call it "listen.py") socket client.
Case 1 - I'm perfectly able to send datagram if I run the socket server (I will call it "server.py") directly inside of the Rpi (by SSH, with my laptop as well).
I have:
-listen.py on boot of the Rpi (here I create a socket client);
-I run the server.py (the server.py is obviously into the Rpi)
In this case I receive answer from all 4 Rpi that joined the multicast group (224.1.1.1)
Case 2 - I created a socket server java and a socket client python in my laptop and everything works perfectly (same code, same multicast group, same multicast port).I tried also without multicast using just 'localhost' and everything works as well. With netstat I can see multicast group and port.
netstat -lu
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
udp 0 0 224.1.1.1:21581 *:* //MulticastGroupIp:Port
udp 0 0 192.168.1.103:ntp *:* //Static ip of one Rpi
udp 0 0 *:ntp *:*
My problem is that if I run the server.py from my laptop (no SSH) I have no answer from the listen.py, socket client, which is running into the Rpi. I absolutely need to send datagram from my laptop (I want a java class) to the 4 Rpi.
I have excluded the possibilities of bad code, wrong multicast ip group, wrong multicast port group. Firewall is off in my laptop and in the router as well.
The parameter net.ipv4.ip_forward on the Rpi is 1.
I know that UDP is unreliable but here I'm speaking about 100% of the datagram lost.
IGMP is enabled on my D-Link router.
I have the same problem with Ethernet and WiFi configuration.
I really need other ideas.....So I tried to write here.
Sorry, this is my first post and I'm newbie a bit of everything.
I will appreciate any suggestion.
Probably it's something stupid that I can't understand.
Maybe some problems with the routing table???? This is mine:
route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default D-Link.Home 0.0.0.0 UG 0 0 0 wlan0
192.168.1.0 * 255.255.255.0 U 0 0 0 wlan0
Thanks in advance
For me the code works well and the problem is not there but if can be useful to understand better the situation.....
Code of server.py (useless to say that I have python installed in my machine with Windows 7):
import socket
import sys
# import time
print 'message:'
n = sys.stdin.readline()
n = n.strip('\n')
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 21581
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
sock.sendto(n, (MCAST_GRP, MCAST_PORT))
or with java (It's exactly what I want...a server socket java but at the moment it's not the priority):
public class PythonScriptScan {
private static int portMulticasting = 21581;
// private boolean broadcast = true;
private DatagramSocket socket;
private String groupMulticast = "224.1.1.1"; // group address
private int delay = 3000;
public PythonScriptScan() {
try {
socket = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
System.exit(1);
}
}
public void start(String agentName) {
try {
InetAddress group = InetAddress.getByName(groupMulticast);
#SuppressWarnings("resource")
MulticastSocket s = new MulticastSocket(portMulticasting);
s.joinGroup(group);
// while (broadcast) {
byte[] buf = new byte[10240];
buf = agentName.getBytes();
DatagramPacket packet = new DatagramPacket(buf, buf.length, group, portMulticasting);
System.out.println(packet);
socket.send(packet);
// OK, I'm done talking - leave the group...
s.leaveGroup(group);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
System.exit(0);
}
// }
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
System.out.println("Insert Message");
#SuppressWarnings("resource")
Scanner sc = new Scanner(System.in);
String agentName = sc.nextLine();
PythonScriptScan agent = new PythonScriptScan();
agent.start(agentName);
}
and listen.py (It's running on boot in each Rpis, the configuration that I want is with Ethernet, each Rpis has a static ip and they are connected properly with my D-link router):
#!/usr/bin/python
import socket
import struct
import fcntl
import subprocess
import sys
import errno
import time
import os
# Create the socket
MCAST_GRP = "224.1.1.1"
MCAST_PORT = 21581
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
except socket.error, msg:
print 'Could not create socket. Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind to the server address
# sock.bind(('', MCAST_PORT))
sock.bind((MCAST_GRP, MCAST_PORT))
# Tell the operating system to add the socket to the multicast group on all interfaces
mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
# Receive loop
try:
while True:
print >>sys.stderr, '\nWAITING TO RECEIVE MESSAGE'
d = sock.recvfrom(10240)
data = d[0]
addr = d[1]
print data.strip(), addr
finally:
print >>sys.stderr, 'closing socket'
sock.close()

Java UDP transmission with Go back N

I have a simple working program that can transfer files with UDP . But for each client and server, I have two sockets which send data at a port and receive data at a different port.
For example, my client socket_out sends data packet at port 9000 and receives data with socket_in which listens at port 9001. My server socket_in listens at port 9000 and sends ACK data packet at port 9001.
Now I want to simplify the design and use only one port number for receiving and sending messages at each client and server. For example, both client and server program sends and receives data at port 9000.
Is it possible to do that? How should I make the change? I tried to create two socket for send and receiving at the same port number, but I always get this error:
java.net.BindException: Address already in use
I googled found out two sockets cant share the same port number.
Adding on the code:
Sender:
public FileSender(String fileName, int unrelPort, String rcvFileName) {
DatagramSocket socket_out_client, socket_in_client;
System.out.println("Start Sending " + fileName + " through port " +unrelPort + " as " + rcvFileName + ".");
try {
// create sockets
socket_out_client = new DatagramSocket();
socket_in_client = new DatagramSocket(unrelPort);
// create input file
File inputFile = new File(fileName);
if (!inputFile.exists()) {
System.err.println("Input file does not exist");
System.exit(-1);
}
// create threads to process data
InThread th_in = new InThread(socket_out_client,socket_in_client);
OutThread th_out = new OutThread(socket_in_client, unrelPort, inputFile, rcvFileName);
th_in.start();
th_out.start();
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
Same for receiver
A port number is a number embedded in the network packets. Once a computer's operating system processes an inbound network packet, it needs to "know" which program to pass the pack to as input. The port number is used to lookup the program in the receiving operating system's port to program table.
That's why you cannot have two programs reading from the same port, as that roughly makes it impossible for the operating system to determine which of the two programs the packet should be sent to as input.
Note that this is not the only way you can get a port conflict. You could just have two copies of the program running on the same machine too.
First of all why did you create two sockets at client side for sending and receiving when you can use same socket_client to send and receive. You can do this by creating two threads one for sending and one for receiving with the same socket_client.
code: something like this
DatagramSocket sock = new DatagramSocket();
new Thread(new Runnable() {
#Override
public void run() {
try{
//create packet
//your logic
sock.send(packet);
}
}catch(Exception e){}
}
}).start();
System.out.println("Debug :: "+"thread 1 started");
new Thread(new Runnable() {
#Override
public void run() {
try{
//your logic
sock.receive(packet)
}
}catch(Exception e){}
}
}).start();
System.out.println("Debug :: "+"thread 2 started");

Java: Receiving an UDP datgram packet with multiple DatagramSockets

I am trying to implement a method that sends an UDP packet to multiple receivers. I thought that this should be doable setting setReuseAddress(true) on the receiving DatagramSocket instances.
My problem is that in certain conditions I need to limit the communication to the local computer - hence the localhost interface (useLocalhost=true in the demo code below). In such a case suddenly only the first receiver socket gets the incoming packet, the two other don't see anything.
I tested this on Windows (oracle 64bit) and Linux (OpenJDK 64bit), therefore I only see three possibilities:
This is an intended and known behavior (and I don't understand the whole mechanism - aka "bug in my brain")
There is a bug in the Java JRE
There is a bug in my code.
Does somebody have any experience on that topic and can me help to identify where the problem is located?
See below a minimal working example that demonstrates this. Note that I am using the broadcast address for simulating network packets that come from a real external host.
If everything goes right you should see three lines at the end (in this or a different order):
Thread-0 - packet received
Thread-1 - packet received
Thread-2 - packet received
 
public static void main(String[] args) throws Exception {
boolean useLocalhost = true;
InetSocketAddress addr;
String sendPacketTo = "192.168.1.255"; // we use broadcast so that packet comes from an real external address
if (useLocalhost)
sendPacketTo = "localhost"; // does not work (only listener 1 received packet)
addr = new InetSocketAddress(15002);
new MyThread(addr).start(); // Datagram socket listener 1
new MyThread(addr).start(); // Datagram socket listener 2
new MyThread(addr).start(); // Datagram socket listener 3
DatagramSocket so = new DatagramSocket();
so.setBroadcast(true); // does not change anything
so.connect(new InetSocketAddress(sendPacketTo, 15002));
so.send(new DatagramPacket("test".getBytes(), 4));
Thread.sleep(1000);
System.exit(0);
}
public static class MyThread extends Thread {
DatagramSocket socket;
public MyThread(InetSocketAddress addr) throws SocketException {
super();
setDaemon(true);
socket = new DatagramSocket(null);
socket.setReuseAddress(true);
socket.setBroadcast(true); // does not change anything
socket.bind(addr);
System.out.println("Listener started: " + socket.getLocalAddress());
}
public void run() {
byte[] buf = new byte[10];
DatagramPacket p = new DatagramPacket(buf, buf.length);
try {
socket.receive(p);
System.out.println(Thread.currentThread().getName() + " - packet received");
} catch (IOException e) {
e.printStackTrace();
}
}
}
192.168.1.255 is a broadcast address, so the datagram is broadcast, under the rules for UDP broadcast. 127.0.0.1 is a unicast address, so the packet is unicast. So you get different behaviour.
As #DavidSchwartz commented, your code is a mixture. Connecting to a broadcast address for example doesn't have a lot of meaning, and neither does binding to it. I think what you are looking for is multicast.
You can use multicast on localhost
However, there are several things you need to be careful of to make it work.
example:
lo0 (127.0.0.1)
en0 (192.168.0.111)
en1 (10.1.0.111)
for each interface 2 separate sockets, one for receiving, one for
sending. In the above example this means creating a total of 6
sockets.
Never bind() a socket that will send multicast UDP packets.
Always bind() a socket that will receive multicast UDP packets.
Never try to setsockopt() or reconfigure multicast sockets after you call bind()
Instead, when machine's interfaces change due to cables being unplugged/plugged,
destroy all send/receive multicast sockets and recreate them.
sample code:
iMulticastSocketInterfaceIPAddress would be one of the three interfaces
/* use setsockopt() to request that the kernel join a multicast group */
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr=inet_addr( "239.192.0.133" );
myAddress.sin_addr.s_addr = mreq.imr_multiaddr.s_addr;
mreq.imr_interface.s_addr=( htonl(iMulticastSocketInterfaceIPAddress) );
theErr = setsockopt( CFSocketGetNative( mSocketBroadcast ) ,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

Categories