Java UDP Hole Punching

bei Verlust eines fragmentierten IP-Paketes ist die UDP-Checksumme kaputt und das Paket wird verworfen. Du kannst dich anstellen wie du willst, UDP ist non-reliable aber es garantiert du, dass du nur Nachrichten bekommst, wenn sie korrekt sind (Checksumme übereinstimmt).
Du bekommst keine Garantie, dass die Reihenfolge stimmt oder alle ankommen, aber wenn eine ankommt, dann stimmt sie, daran gibts nix zu rütteln.
 
Also wir haben mal folgende NAT-PMP Library probiert:
http://sourceforge.net/projects/jnat-pmplib/

Code:
NatPmpDevice pmpDevice = null;
		
		try {
			pmpDevice = new NatPmpDevice(true);
			ExternalAddressRequestMessage extAddr = new ExternalAddressRequestMessage(
				null);
			pmpDevice.enqueueMessage(extAddr);
			pmpDevice.waitUntilQueueEmpty();
			
			Inet4Address extIP = extAddr.getExternalAddress();
			MapRequestMessage map = new MapRequestMessage(false, 52000, 52000,120,null);
			pmpDevice.enqueueMessage(map);
			pmpDevice.waitUntilQueueEmpty();
			
			// Let's find out what the external port is.
			int extPort = map.getExternalPort();
			System.out.println("extPort " + extPort);
			// All set!
			
		} catch (NatPmpException ex) {
			System.out.println(ex.getStackTrace());
		}

Das braucht jedenfalls meistens eine halbe Ewigkeit bis eine Fehlermeldung erscheint.
"com.hoodcomputing.natpmp.NatPmpException: The network gateway address is not RFC1918 compliant." und "Exception in thread "NatPmpDevice:ShutdownHook" java.lang.IllegalStateException: Shutdown in progress
at java.lang.ApplicationShutdownHooks.remove(Unknown Source)
at java.lang.Runtime.removeShutdownHook(Unknown Source)
at com.hoodcomputing.natpmp.NatPmpDevice.setShutdownHookEnabled(NatPmpDevice.java:158)
at com.hoodcomputing.natpmp.NatPmpDevice.shutdown(NatPmpDevice.java:200)
11:07
at com.hoodcomputing.natpmp.NatPmpDevice$1.run(NatPmpDevice.java:168)
at java.lang.Thread.run(Unknown Source)"

Oder ein Nullpointer Exception auf Zeile 16 (auf dem Netopia3347N Router). Dies ist der Fall da der gefundene external Port auf null gesetzt ist beim Abfragen. Es wurde also nicht gefunden mit der Portforwarding Nachricht.

Hat vielleicht jemand bereits diese Library mal angeschaut oder hat eine Idee weshalb eine dieser Fehlermeldung erscheint?


Im Zusammenhang mit UPnP wird auch oft eine Library erwähnt dessen URL nicht mehr gültig ist: http://www.sbbi.net/site/upnp/index.html (Verlinkt zum Beispiel von http://upnp-portmapper.sourceforge.net/)
Kurz darauf habe ich dann aber noch folgende Lib gefunden:
http://code.google.com/p/weupnp/
Mit der funktioniert das ganze zumindest schon auf einem Router/Nat (Netopia 3347NWG (Netopia SmartModem)) jedoch nicht auf einem anderen Router/Nat den wir getestet haben. Gibt es zum SimpleServiceDiscoveryProtocoll eine gute Dokumentation? Auf MSDNA habe ich nur eine leere Seite dazu gefunden.

Besten Dank,
tsserver
 
Zuletzt bearbeitet:
Nachtrag:
Nach etlichen Stunden Recherche im Internet bin ich unter anderem auf folgenden Artikel gestossen:
http://www.goto.info.waseda.ac.jp/~wei/file/wei-apan-v10.pdf

Darin steht, dass weder UpNP, noch STUN oder Teredo mit symmetrischen NATs funktionieren (welche wir ja offenbar besitzen). Bei symmetrischen NATs wird vorgeschlagen, am Besten mit Port Prediction zu arbeiten.
 
Folgende theoretischer Ansatz auf den ich gekommen bin (Ich bin mir allerdings nicht sicher in wie weit er korrekt und realisierbar ist):

Du hast 2 Clients die miteinander kommunizieren sollen. Du hast einen Server, der listened, die Clients verbinden sich ganz kurz, es gibt einen Adressenaustausch, dann verschicken die Clients nur noch untereinander Daten mit gespoofter IP des Servers als Absender. Somit müssten die Router die eindeutig zuordnen können.
 
Ich habe nun versucht, in einem simplen Testprojekt die Anleitung für port prediction im bereits zitierten Artikel umzusetzen (Kapitel 4: NEW METHOD, siehe Link oben).

Phasen I und II sind für die Tests vorerst überflüssig, da ich ja den punching mode meines NATs bereits kenne (nämlich incremental). Den Test führe ich an mir selber durch (d.h. Peer1, Peer2 und Server sind alle hinter dem gleichen NAT, der Verbindungsaufbau erfolgt aber selbstverständlich übers Internet zum Testen).

Folgendes geschieht:
- Peer1 nimmt mit dem Server Kontakt auf.
- Peer2 nimmt mit dem Server Kontakt auf.
- Der Server sieht die beiden externen Ports der Peers, wobei der Port von Peer 2 immer um 1 höher ist als der von Peer 1, da die beiden Kontaktaufnahmen innerhalb sehr kurzer Zeit geschehen. Der Server schickt den externen Port des jeweils anderen Peers an beide Peers zurück.
- Peer1 schiesst sofort ein Loch in das NAT. Zieladresse des lochbohrenden UDP-Pakets ist dabei der externe Port von Peer2 + 2.
- Peer2 sendet ein Paket an den externen Port von Peer2 + 1.

+ 1 und + 2 deshalb, weil sich alle Peers hinter dem gleichen NAT befinden. Peer1 hat also nur ein einziges Loch in die NAT geschossen, da sich pro UDP Hole die Portzahl erhöht. Wenn ich also 100 Löcher in die NAT schiesse, steigt der verwendete freie Port in der NAT um 100, das habe ich getestet.

Bisher hat das nicht funktioniert.

Was ich in der Anleitung nicht verstehe, ist:
Based on this information, the echo server sends a large number of packets. These packets have a fixed destination port and a low TTL value.
Wenn ich das richtig verstehe, ist das Ziel also doch, nur ein einziges Loch zu schiessen - da ja der Zielport immer der gleiche ist ? "A large number of packets" wird nur geschickt, damit das Loch über längere Zeit offen bleibt, oder verstehe ich das falsch ?

The echo server binds the port. The packets are then sent to the echo client.
Was ist mit "binds the port" gemeint ?
 
Zuletzt bearbeitet:
Du meinst F8:

B weiß jetzt sein Ziel-Port, jetzt sendet er sehr viele Pakete mit unterschiedlichem Quellport und sehr geringer TTL (damit spart man Bandbreite) an A. Damit öffnet der Router all diese Ports.
Dann (F9) weiß A die Ports von B und versucht dann (F10) zu all denen zu verbinden, wenn ein Paket durchkommt, dann hat man ja ein Loch gemacht. Alle anderen Verbindungen werden dann abgelehnt.
 
Ach so, verschiedene Quellports, das macht Sinn. ^^

B weiß jetzt sein Ziel-Port
Das setzt also voraus, dass A sich in meinem Fall (incremental mode) den nächsthöheren Port schnappen konnte (=der Zielport von B), nicht ? A muss mit anderen Worten sehr schnell machen, damit eine andere UDP-Verbindung ihm nicht zuvorkommt und ihm den Port wegschnappt - sonst klappt die ganze Port Prediction nicht und man muss das Ganze nochmals probieren. Ist das korrekt ?
 
Zuletzt bearbeitet:
Wieso eigentlich immer so kompliziert ..
Du kannst auch über andere Protokolle temporär Nachrichten auf deinem Server ablegen.
Abzurufen z.B. über httpget o.a.
Selbst bei ewig langen Diskussionen sind das nur ein paar MB im Monat.

Durch IP Freigaben können diese temporär gesendeten Nachrichten problemlos herangezogen und dargestellt werden.

Gleichzeitig bietet diese Möglichkeit eine externe Sicherung des gesammten Chatverlauf an.
 
Ah, das tönt ganz interessant. A kann also B ein UDP-Paket mit einer ganz geringen TTL schicken. Die TTL läuft natürlich ungenutzt ab, da NAT B das Paket mit dem (noch) unbekannten Port abblockt. NAT A schickt dementsprechend ein ICMP an A zurück, das auch den gemappten externen Port enthält.

Nur, was bringt mir nun dieser Port, ist doch die TTL bereits verstrichen und das UDP Hole entsprechend wieder geschlossen (oder kann ich davon ausgehen, dass das nächste UDP Hole mit gleichem Source Address/Port und Destination Address/Port wieder auf den gleichen Port gemappt wird) ? Und zudem hat A ja keine Ahnung, an welchen Port er dieses UDP-Paket schicken muss, denn NAT B wird für das von B geschickte Paket irgendeinen Port mappen - darin besteht ja gerade die Schwierigkeit bei symmetrischen NATs.

Kann mir jemand ein gutes Buch empfehlen zu der ganzen Problematik, ev. im Zusammenhang mit Java ?

Wieso eigentlich immer so kompliziert ..
Du kannst auch über andere Protokolle temporär Nachrichten auf deinem Server ablegen.
Wie ich in meinem ersten Post gesagt habe, geht es dabei nicht um den Chat - dafür ist ein Relay-Server völlig unproblematisch. Sondern um die Möglichkeit, eine P2P-Verbindung hinzukriegen für Funktionen, wo es mehr Sinn macht - ich denke dabei insbesondere an eine Dateiübertragung oder Musikstreaming. Diese beiden Sachen über einen Server abzuwickeln wäre meines Erachtens ziemlich ineffizient.
 
Zuletzt bearbeitet:
Wie ich in meinem ersten Post gesagt habe, geht es dabei nicht um den Chat - dafür ist ein Relay-Server völlig unproblematisch. Sondern um die Möglichkeit, eine P2P-Verbindung hinzukriegen für Funktionen, wo es mehr Sinn macht - ich denke dabei insbesondere an eine Dateiübertragung oder Musikstreaming. Diese beiden Sachen über einen Server abzuwickeln wäre meines Erachtens ziemlich ineffizient.

Das ist dann wirklich problematisch.
Zumindest eine Instanz müsste die entsprechenden Ports freischalten.

Ich kann mir allerdings nicht vorstellen dass es keine "TOP" Musterlösung gibt, es geht hier immerhin um Java.

Mal in Richtung Netzwerkfreigaben gedacht?
 
Zumindest einige einfachere Mappingstrategien könnte man so ja herausfinden, indem beide Teilnehmer erstmal ein paar kurzlebige Pakete schicken und dann feststellen, ob z.B. der Port gleich bleibt oder einfach nur inkrementiert wird. Sobald da Zufall reinspielt wird's natürlich schwierig, aber wenigstens für einige Router sollte das dann schon mal klappen.
 
Die TTL sagt nichts über die Portlebensdauer aus.
Die TTL steht dafür, wie lange ein Paket leben darf (heutzutage eigentlich nur die Anzahl der Hops, damit kein Paket im Kreis laufen kann und so ein Netz verstopfen kann).

Das Portmapping bleibt relativ lange erhalten, also mehrere Sekunden, wenn nicht sogar Minuten.

Meine Idee wäre, dass A ein Paket mit einer sehr geringen TTL losschickt und per ICMP dann die externe Kombination rausfindet.
Dann versucht B mit sehr vielen Paketen genau den Port zu treffen (den Quellport von A wird ja nicht verändert durch die NAT B da es dort der Ziel-Port ist, wenn B jetzt glück hat, mapped der Router genau den Port, den A als Ziel angegeben hat.)
 
Wenn das ginge wäre das ja der Hammer. Schickt denn jedes NAT ein ICMP zurück, wenn die TTL verstrichen ist ? Und wie kann ich dieses ICMP-Paket abfangen, auf Wikipedia steht, es sei Bestandteil des IP-Protokolls. Mit was für nem Client kann ich dieses Ding empfangen, wohl nicht mit einem UDP-Client oder ?
 
Es ist wieder etwas Zeit vergangen und folgendes haben wir nun gefunden:

Eine Liste des DCP, aufgelistet sind Gerätetypen und welche Dienste diese anbieten:
http://upnp.org/sdcps-and-certification/standards/sdcps/

Dann haben wir noch eine Liste von SDK's gefunden die Upnp vereinfachen/implementieren:
http://upnp.org/sdcps-and-certification/resources/sdks/

Davon haben wir jetzt das Projekt MiniUPnP (miniupnp.free.fr/files/) heruntergeladen und getestet und es funktioniert soweit ganz gut.
Obwohl wir eine kompilierte Version für Windows verwenden, sollte es wahrscheinlich auch möglich sein den Sourcecode für weitere Betriebsysteme zu kompilieren, was aber im Moment noch nicht so interessiert. :)

Besten Dank erneut allen die uns hier geholfen haben!
Grüsse,
tsserver
 
Zurück
Oben