Probleme beim TCP Hole Punching

Nach dem ich ja nun schon mal einen Code für UDP Hole Punching gepostet habe dachte ich mir : legste mal noch ne Schippe drauf und versuchst dich an TCP Hole Punching.
Leider kann ich es nur eingeschränkt testen da mein “Gegenüber” lediglich über eine Software-Firewall verfügt (also kein NAT-Router vorgeschaltet hat) und ich den Traffic auf der WAN-Seite meines Routers nicht prüfen kann.

Für TCP Hole Punching sind ja nach Wikipedia nur 2 Bedingungen nötig :

  1. kein RST auf ein eingehendes SYN senden wenn es keinen NAT-Eintrag für diese Verbindung gibt
  2. ein eingehendes SYN erlauben für das in der NAT-Table ein ausgehendes SYN vermerkt ist

Ich denke in meinem Test kommt es aber zu einem RST auf das eingehende SYN vom remote bevor ich nicht “von innen” ein SYN zum remote geschickt habe. Der 2. Punkt muss zumindest auf einer Seite passen denn ich kann letzten Endes eine Verbindung erfolgreich herstellen.

Folgender Code wird genutzt (selbst zusammengebastelt) :

import java.io.*;
import java.net.*;
public class Punch implements Runnable
{
	private Socket socket;
	public static void main(String[] args) throws Exception
	{
		int localPort=Integer.parseInt(args[0]);
		String remoteHost=args[1];
		int remotePort=Integer.parseInt(args[2]);
		
		Socket socket=new Socket();
		socket.bind(new InetSocketAddress(localPort));
		
		try
		{
			socket.connect(new InetSocketAddress(remoteHost, remotePort), 5000);
		}
		catch(Exception e) { e.printStackTrace(); }
		if(!socket.isConnected())
		{
			try
			{
				socket.connect(new InetSocketAddress(remoteHost, remotePort), 5000);
			}
			catch(Exception e) { e.printStackTrace(); }
		}
		
		new Thread(new Punch(socket)).start();
	}
	private Punch(Socket socket) { this.socket=socket; }
	public void run()
	{
		try
		{
			BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
			String line="";
			while((line=in.readLine())!=null)
			{
				System.out.println(line);
			}
		}
		catch(Exception e) { e.printStackTrace(); }
	}
}```
Fragt nicht warum, aber Zeile 25 ist nötig.

Sehr merkwürdig sind die Exception in Abhängigkeit auf welcher Seite ich beginne. Starte ich den Code zu erst auf meinem Server wirft dieser mir vor Ablauf des 5000ms folgendes :

[user]@[host]:~> java tcp.Punch 7460 [DNS/IP meines Anschlusses] 7459
java.net.NoRouteToHostException: Keine Route zum Zielrechner
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:579)
at tcp.Punch.main(Punch.java:18)
java.net.SocketException: Socket closed
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:579)
at tcp.Punch.main(Punch.java:25)
java.net.SocketException: Socket is not connected
at java.net.Socket.getInputStream(Socket.java:878)
at tcp.Punch.run(Punch.java:37)
at java.lang.Thread.run(Thread.java:745)


Starte ich hingegen erst auf meinem Rechner und dann auf meinem Server wird die Verbindung erfolgreich hergestellt.
Starte ich auf dem Server nicht und lasse das connect() auf meinem Rechner ins Timeout laufen kommt entsprechend eine Timeout-Exception.


Mir geht es eigentlich nur darum zu klären : Warum wirft mein Server mir noch vor erreichen des Timeouts eine NoRouteToHostException obwohl traceroute eine Route zu meinem Modem liefert und es umgekehrt ja funktioniert ? Ich würde halt behaupten es liegt daran das von meinem NAT ein RST zurückgeht wenn von meinem Rechner noch keine Ausgehende Route in der NAT-Table steht.
Wenn ich hingegen von meinem Rechner aus beginne landet das erste SYN lediglich an der iptables-Firewall meines Servers was vermutlich ein anderes Ergebnis zur Folge hat (Verzögerung, RST+noch ein FLAG).

Verwendete Systeme :

Client :
Win 7 SP1 Ulti x64
Oracle Java 8 Update 25
NAT : AVM FritzBox 6360 Cable
Firewall : Kaspersky Internet Security 2015

Server :
OpenSuSE 13.1 x64 - OVH (hostet by OVH.com)
OpenJDK 7 Update 55
NAT : vermutlich irgendeine Art komplexer Switch- und Router-Struktur mit entsprechenden Routing-Tables, keine Ahnung ob da wirklich irgendwo richtiges NAT stattfindet
Firewall : SuSE Firewall 2 (ist lediglich ein GUI-Frontend für iptables)

Vermutlich wird man es anhand der vorliegenden Struktur sicher irgendwas erklären können (z.B. RST von meiner FB oder halt irgendwas Netzwerk-technisches bei OVH) das es zu besagter Exception kommt, aber eine andere "Test-Strecke" habe ich aktuell leider nicht.
Wenn jemand eine Möglichkeit zum Testen hat kann er seine Ergebnisse ja gerne hier posten.

Ich weis nicht obs dir hilft aber hier: UCE RMI - TCP Hole Punching Sockets
Das ist eine API die das unterstützt. Eventuell hilfts dir wenn du den source anguckst.

Das hatte ich auch schon gefunden. Soweit ich gesehen habe wird auch nur in einem for()-loop mehrfach versucht die Verbindung herzustellen. Bringt aber schlecht was wenn halt auf Grund der NoRouteToHostException das Timeout ignoriert wird, da wird diese Lib das gleiche Problem haben.