[.NET] P2P NAT Hole Punching

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Hoi allemaal,

Ik ben bezig met een P2P applicatie in .NET. In eerste instantie heb ik WCF gebruikt omdat WCF de PeerChannel ondersteund om P2P applicaties te gebruiken. Op een LAN netwerk werkt het netwerk perfect zonder problemen. Het probleem komt naar voren als ik verbinding wil maken tussen 2 peers over internet. Omdat de peers achter een NAT zijn kunnen ze niet direct met elkaar communiceren. Skype gebruikt de zogenaamde 'hole punching' methode om een verbinding tussen 2 peers tot stand te brengen tijdens het bellen.

WCF werkt helaas niet naar behoren. Het heeft Teredo nodig om communicatie tot stand te brengen via IPv6, maar Teredo wordt niet goed ondersteund en werkt niet goed op XP systemen.

Op internet kwam ik een aantal onderwerpen tegen die als hulpmiddel gebruikt kunnen worden om hole punching uit te voeren. STUN en STUNT/TURN zijn protocolen ontwikkeld voor dit. Ook UPnP wordt hier genoemd. De idee is om eerst een poort in de NAT te openen voor de applicatie voordat deze gebruikt gaat worden voor communicatie.

Op CodeProject heb ik het volgende project gevonden en toegepast: http://www.codeproject.com/KB/cs/STUN_client.aspx

Let op: De standaard stunserver.org host werkt niet meer, gebruik stun01.sipphone.com op poort 3478

Ik heb voor de test STUN toegepast en heb uiteindelijk mijn IP adres ontvangen met de bijbehorende geopende poort. De STUN server waarmee ik de verbinding maak hiervoor geeft mij het resultaat terug als:

code:
1
Public binding: <mijn publieke ip adres>:<poort>


Achter deze poort is geen UDP listener, dus ik kan nog geen data ontvangen. Hoe zorg ik ervoor nu dat ik een listener maak die de geopende poort meteen gaat gebruiken? De UDP socket in het project boven wordt gebruikt om de IP en Poort te achterhalen en wordt niet afgesloten.

Nadeel van STUN is dat het niet binnen elk netwerk wordt ondersteunt. Als UDP uit staat in de router, dan werkt dit allemaal niet. Skype werkt achter alle soorten firewalls zonder problemen. Ik denk dat Skype een combinatie gebruikt van STUN, STUNT/TURN, UPnP of meer...

Op CodePlex is ook een STUNT (voor TCP) versie van het hierboven genoemde, SharpSTUNT (http://sharpstunt.codeplex.com). Dit project is nog in ontwikkeling en werkt nog niet goed.

Op internet kon ik geen .NET library (dll) of source code vinden waar alles in 1 is toegepast. Weet iemand een alternatief? De bedoeling is dus om 2 peers die achter een NAT zijn te laten communiceren met elkaar. Liefst via TCP, maar UDP mag ook... Een C/JAVA/Python poort naar .NET kan ook.

Alvast heel erg bedankt voor de hulp,
Sead

Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
Wat is er nou helemaal zo moeilijk aan hole punching zelf bouwen?
  • A en B zitten beiden achter NAT.
  • Beiden leggen contact met trusted third party C.
  • Uit de datagrams die C ontvangt leert deze de return port die door A en B open gezet is.
  • C stuurt de port van B naar A en van A naar B.
  • A en B kunnen direct communiceren over de return port die via een trigger tijdelijk naar de correcte machine achter de NAT router gemapped staat.
Om te zorgen dat het gemaakte 'gat' zichzelf niet sluit, moeten A en B wel op de zoveel tijd contact leggen met C. Beste is dan om elke keer opnieuw hun port info naar C te versturen. Mocht het gat zichzelf gesloten hebben en opnieuw geopend zijn, zeg aan de zijde van A, dan weet C meteen de correcte nieuwe port informatie om door te sturen naar B.

[ Voor 46% gewijzigd door R4gnax op 23-10-2009 12:57 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
R4gnax,

Dank voor de reactie. Is hier een werkend simpel voorbeeld te vinden in .NET? En is 20 sec genoeg om de poorten open te houden?

Dus als ik jou goed begrijp moet ik eerst A en B verbinding laten maken met C om de poort te openen in hun NAT. Vervolgens moet C deze IP en Poort adressen doorgeven aan beide zodat A en B straks elkaar kunnen benaderen. Vervolgens moet C om de zoveelste sec. een dummy bericht sturen naar A en B. Klopt dat?

Maar, waarom moet C data blijven doorsturen om de zoveelste tijd? Als A en B op die poort al communiceren, blijft de poort toch wel beschikbaar of niet? En dit principe werkt alleen met UDP, dus TCP zal in dit geval niet werken?

Acties:
  • 0 Henk 'm!

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
Hoogstwaarschijnlijk zul je via een relay server moeten gaan werken. Dat is namelijk de enigste manier hoe je kunt garanderen dat je app een verbinding kan maken.
Hoe je die gaat opzetten is dan nog even de vraag. Je zou kunnen overwegen om daar het nieuwe Microsoft Azure framework voor te gebruiken. Kost geld, maar als je het alleen wilt gebruiken om een relay service te hosten dan werkt t als n trein.

Deze links zijn wellicht wel interesant lees materiaal, als je ze zelf nog niet gevonden hebt.
http://stackoverflow.com/...459/direct-p2p-connection
http://stackoverflow.com/...p-connections-in-p2p-apps
http://msdn.microsoft.com...274%28lightweight%29.aspx

Skype gebruikt trouwens ook een variant van NAT Hole punching. Hij switcht naar een directe verbinding indien mogelijk.

Een simpel voorbeeld: Heb je zelf al genoemd.Namelijk dat codeproject artikel. Je moet het alleen zelf even poorten naar wcf. In WCF kan je niet echt makkelijk achterhalen wat de poort en ip adres is van de bron waar je bericht vandaan komt. Dus dan is het bouwen van een STUN server niet echt makkelijk :P

Maar je kunt ook een niet WCF STUN Server hebben waarvan je eerst de poort en ip adres van ophaalt om vervolgens met WCF daarnaartoe te verbinden.

[ Voor 14% gewijzigd door D-Raven op 23-10-2009 13:49 ]


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

R4gnax schreef op vrijdag 23 oktober 2009 @ 12:52:
Wat is er nou helemaal zo moeilijk aan hole punching zelf bouwen?
  • A en B zitten beiden achter NAT.
  • Beiden leggen contact met trusted third party C.
  • Uit de datagrams die C ontvangt leert deze de return port die door A en B open gezet is.
  • C stuurt de port van B naar A en van A naar B.
  • A en B kunnen direct communiceren over de return port die via een trigger tijdelijk naar de correcte machine achter de NAT router gemapped staat.
Bovenstaand werkt natuurlijk alleen als host A en host B beide port-forwarding hebben ingesteld op hun router.
Als B geen port-forwarding heeft, dan kan A nooit een verbinding opzetten met B.

Aangezien een P2P meestal meerdere verbindingen nodig heeft om goed te kunnen werken (lees: efficiënt).
Wij hebben een aantal trusted relay servers. Zowel A en B hebben een permanente verbinding met relay server C. Als A een 'packet frame' wilt ophalen bij B, verstuurd deze een request via server C naar B. B verwerkt het bericht en stuurt vervolgens als antwoord een serie packet frames terug naar server C welke ze doorstuurt aan A over dezelfde verbinding waarmee het verzoek is gestuurd redelijk vergelijkbaar met hoe ftp omgaat met poort 20 (data) en 21 (commando's).

Zoals aangegeven zal een P2P client pas efficiënt kunnen werken als het data parallel kan binnenhalen. Een mogelijk oplossing hiervoor is dat A voor het request naar B via C wordt gestuurd eerst meerdere verbindingen legt met C en in het request aangeeft dat het poortnummer x1, x2, x3 ... xn heeft gereserveerd voor de response. C kan op dat moment voor host B een nieuwe port openen. C kan vervolgens doorgeven aan B dan A verzoekt om bestand Y via 4 verbindingen op de zojuist geopende poort op C. C kan vervolgens elke connectie van B op de deze speciale poort mappen naar de poorten van A. Zorg wel dat je de packet frame klein houd. Wij knippen zelf alle bestanden op in delen van 1024 bytes.

Wat betreft het vinden van een voorbeeld, kom op zeg. Jij bent bezig een gespecialiseerde netwerk applicatie te schrijven en bent vervolgens op zoek naar simpele voorbeelden?

Je kunt dit echt niet voor elkaar krijgen met wat knip en plak werk en ik kan je nu al vertellen dat als jij dezelfde mogelijkheden als Skype wilt maken je zeker minimaal 300 uur ruw programmeer werk voor de boeg hebt. Je dacht toch hopelijk niet dat Skype in 5 minuten geschreven was?? Daarnaast wil je dit soort infrastructuur echt allemaal zelf schrijven omdat als een library niet goed werkt je dagen aan het debuggen bent om alleen maar te achterhalen hoe de flow van de library is voordat je je probleem zelf kun debuggen als je überhaupt al bij de source van de library kunt komen.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • _js_
  • Registratie: Oktober 2002
  • Laatst online: 18-08 21:31
Natuurlijk werkt dit wel bij NATs zonder port forwarding, daar zijn juist al die hole punching technieken voor bedacht. Je wilt in een p2p oplossing geen server er tussen, want dat kost ontiegelijk veel bandbreedte (en het is geen p2p meer).

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 12:01
Niemand_Anders schreef op vrijdag 23 oktober 2009 @ 14:50:
[...]
Daarnaast wil je dit soort infrastructuur echt allemaal zelf schrijven omdat als een library niet goed werkt je dagen aan het debuggen bent om alleen maar te achterhalen hoe de flow van de library is voordat je je probleem zelf kun debuggen als je überhaupt al bij de source van de library kunt komen.
Dat geldt toch voor elke 3rd party library die je gebruikt? Ik neem aan dat jij niet overal je eigen libs voor maakt?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Niemand_Anders schreef op vrijdag 23 oktober 2009 @ 14:50:

Wat betreft het vinden van een voorbeeld, kom op zeg. Jij bent bezig een gespecialiseerde netwerk applicatie te schrijven en bent vervolgens op zoek naar simpele voorbeelden?
Nou, ik wil geen voorbeeld van een complete opzet :) Ik vroeg naar een voorbeeld om een simpele verbinding tot stand te brengen tussen A, B en C als relay. In het algemeen is dat niet per se een gespecialiseerde netwerk applicatie, maar een techniek die ik wil toepassen in mijn applicatie. De complexe applicatie zelf is geen probleem als ik maar kan communiceren via Hole Punching ;)
Je kunt dit echt niet voor elkaar krijgen met wat knip en plak werk en ik kan je nu al vertellen dat als jij dezelfde mogelijkheden als Skype wilt maken je zeker minimaal 300 uur ruw programmeer werk voor de boeg hebt. Je dacht toch hopelijk niet dat Skype in 5 minuten geschreven was?? Daarnaast wil je dit soort infrastructuur echt allemaal zelf schrijven omdat als een library niet goed werkt je dagen aan het debuggen bent om alleen maar te achterhalen hoe de flow van de library is voordat je je probleem zelf kun debuggen als je überhaupt al bij de source van de library kunt komen.
Ik snap dat volledig. Ik ben wel ervaren programmeur en ben dus niet op zoek naar kant en klare software voor copy-paste. Sorry als je dat zo hebt begrepen. Zoals gezegd, ben ik alleen op zoek naar eventuele bibliotheken die mij tijd en geld zullen besparen om de communicatie tussen twee peers werkend te krijgen. Ik heb zoals gezegd, WCF PeerChannel toegepast, maar een directe verbinding tussen twee peers werkt alleen via IPv6 waar Teredo de tunneling verzorgd. Helaas wordt IPv6 niet goed ondersteund overal, dus daarom wil ik een IPv4 oplossing maken met Hole Punching.

Een aantal open source C oplossingen heb ik wel gevonden, zoals eMule. eMule gebruikt ook een soortgelijke techniek om dit voor elkaar te krijgen. Het probleem in de source code is dat het onoverzichtelijk is waar wat staat en zal ik erg lang bezig zijn om dit uit te vissen in de C code van eMule. Daarnaast wil ik geen C oplossing, maar een .NET oplossing.

Ik ben ondertussen bezig met het bouwen van een simpele relay server via UDP en een A en B client voor het testen... Dit is eerder in dit topic vermeldt, dus maar uitproberen... Maar goed, ondertussen, mocht iemand nog meer suggesties hebben, zijn die natuurlijk van harte welkom...

Acties:
  • 0 Henk 'm!

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
Er zijn wel 1 of 2 opensource .net gebaseerde STUN/Relay servers. Maar die zien er behoorlijk dood en onvolledig uit. Valt me eerlijk gezegd n beetje tegen hoe weinig projecten te vinden zijn. De theorie an sich lijkt mij behoorlijk generiek, dus je zou verwachten dat er al wel ergens zoiets zou zijn.

Daarnaast. Er zijn zoveel programma's die p2p technieken gebruiken om te communiceren en waar het "just works". Zouden die allemaal het wiel opnieuw uit hebben gevonden?

Acties:
  • 0 Henk 'm!

Verwijderd

Niemand_Anders mag zich gaan schamen als hij dat een P2P applicatie wil noemen :X

On-Topic:
Hole-punching werkt omdat UDP connection-less is en de manier waarop firewalls connecties tracken.

Bijvoorbeeld client A stuurt een DNS request naar een DNS server. DNS gebruikt UDP poort 53 dus A bouwt een UDP datagram en stopt dat in een IP packet met als afzender bijvoorbeeld 192.168.1.3 poort 37455 en stuurt dit pakket via de router/firewall naar de DNS server op bijvoorbeeld 32.55.67.4 poort 53. De router/firewall welke de NAT doet vervangt de afzender in het IP packet van 192.168.1.3 naar bijvoorbeeld 64.55.67.4 en noteert dit uitgaande pakket in zijn NAT tabel. De DNS server ontvangt het IP pakket en kan in de IP header zien waar het packet vandaan kwam en dus waar het antwoord naar toe moet. De DNS server bouwt dus een UDP datagram en stopt dat in een IP packet met als afzender dus 32.55.67.4 poort 53 en stuurt dit naar 64.55.67.4 poort 37455. De router/firewall ziet dus een IP pakket dat lijkt op een antwoord op het eerdere uitgaande IP pakket en zal dit pakket dan ook doorlaten. Dat is de basis hoe NAT werkt.

UDP NAT hole-punching is dan ook niet echt moeilijk. De truck is om de router/firewall van A te laten denken dat het pakket van B een antwoord is op een eerder pakket van A en visa-versa. Het enige wat A en B hoeven te doen is mekaar een datagram sturen op eenzelfde poort en dat is waar C om de hoek komt kijken. Het enige wat C doet is coordineren op welke poort A en B versturen en ontvangen. Als de router/firewall eenmaal is ge-punched voorkomt een simpel keep-alive pakketje elke zoveel seconden/minuten dat het "gat" verdwijnt.

Hole punching gaat dus alsvolgt. A legt contact met C en vertelt dat deze op poort 37655 wil gaan versturen. C vertelt aan B dat deze een packet van A kan verwachten op poort 37655. A bouwt nu een UDP datagram met als afzender 192.168.1.3 poort 37655 en stuurt dit naar B op bijvoorbeeld IP 72.55.67.4 poort 37655. De router/firewall van A zal opnieuw het IP vervangen van 192.168.1.3 naar 64.55.67.4 en het pakket noteren in zijn NAT tabel. De router/firewall van B weet echter van niks en zal het pakket van A dan ook niet doorlaten. Ondertussen bouwt B ook een UDP datagram met als afzender 192.168.2.4 poort 37655 en stuurt dit naar 64.55.67.4 poort 37655. Net zoals de router/firewall van A zal de router/firewall van B de afzender vervangen van 192.168.2.4 naar 72.55.64.4 en dit noteren in zijn NAT tabel. Echter nu komt op de router/firewall van A een packet binnen vanaf 72.55.64.4 op poort 37655 en zie hier het gat is geboren want de router/firewall van A denkt nu dat dit pakket van B een antwoord is op het eerdere pakket van A terwijl die nooit is aangekomen bij B. Het enige wat A nu nog moet doen is een antwoord opnieuw naar B versturen zodat ook de router/firewall van B denkt dat het een antwoord is. A en B kunnen nu mekaar direct pakketjes sturen zonder port-forwarding.

Een paar problemen die kunnen voorkomen zijn dat de router/firewalls niet alleen het afzender ip address vervangen maar ook de poort. Dit kan bijvoorbeeld als die poort al in gebruik is door een andere client op het lokale netwerk van A. In het ergste geval wordt dan een pakket van B per-ongeluk bij die client afgeleverd omdat de router/firewall denkt dat het pakket van B bij die connectie hoort. Echter zodra B een pakket van A ontvangt kan deze in de IP header zien dat A's pakket op een andere poort wordt verstuurt. B kan het dan nog eens proberen op die poort. Dit geldt natuurlijk ook voor pakketen van B naar A.

A moet dus minimaal 2 pakketen sturen en B mogelijk 3. Om zijn eigen firewall te punchen zodat pakket 2 van A wel door komt, omdat A op een andere poort zit dan verwacht en/of omdat B misschien per-ongeluk al een pakket verstuurt voordat A zijn pakket heeft verstuurt. Timing is dus nodig en C kan hierbij natuurlijk helpen hoewel gewoon een time-out van een seconde of 2 ook genoeg is. Eigenlijk hoeft C alleen maar een bericht van A door te geven aan B over welke poort er wordt gebruikt en daarna is C helemaal niet meer nodig.

Het echte moeilijke van UDP NAT hole-punching is dan ook het vinden van C maar A heeft C altijd nodig om te weten dat B een peer is en daarom is UDP NAT hole-punching altijd maar 1 kant van het verhaal. Ook als alle peers dezelfde poort gebruiken wat met UDP kan omdat het connection-less is kunnen meerdere "connecties" op dezelfde poort zitten, dan nog heb je C nodig om B te vinden. C kan op zijn beurt ook gewoon een peer zijn maar dan eentje die bijvoorbeeld port-forwarding heeft ingesteld of upnp gebruikt en zo een ontstaat een echt Peer-tot-Peer netwerk waar geen (centrale) server aan te pas komt.

Een naive implementatie gebruikt een soort addressboek waarin bekende peers staan die geen NAT gebruiken en probeert 1 van die peers te bereiken tijdens het opstarten. Dit kan bijvoorbeeld ook een IPv6 peer zijn aangezien die geen NAT gebruiken. Die peer verstuurt dan zijn actuele addressboek naar de opstartende peer die dat synchroniseert en zo een zo compleet mogelijk addressboek probeert te bouwen. UDP werkt op zowel IPv4 als IPv6. En als je addressboek leeg is en je hebt geen andere bronnen om een start addressboek van te bouwen dan rest je niets anders dan gewoon willekeurig IP addressen te gaan uitproberen in de hoop dat daar een niet ge-NATte peer achter zit. Het daarom handig om 1 vaste poort te gebruiken anders vergroot je het aantal mogelijke peers met een factor 65565. Daarnaast is het ook vriendelijk tegen firewall administrators.

Je netwerk zou ook een soort broadcast relay systeem kunnen gebruiken waarbij een peer een bericht ontvangt van een andere peer en die dan doorstuurt naar minimaal 2 andere peers als die deze broadcast nog niet eerder heeft ontvangen. In theorie zou je zo in slechts 32 stappen elk mogelijke peer op het IPv4 internet kunnen bereiken. Op die manier kunnen bijvoorbeeld ook het bestaan van niet ge-NATte peers snel worden verspreid zodat iedereen die in zijn addressboek zet. De basis van een simpele P2P implementatie bestaat dus gewoon uit het uitwisselen van berichten en UDP is daar best geschikt voor. Vergeet niet dat UDP wel onbetrouwbaar is en je datagram lang niet altijd aankomt. Dus wees er bereid op om je datagram nog eens te versturen.

Om het bovenstaande te implementeren is echt niet moeilijk en hooguit een dagje werk. Sterker nog het is waarschijnlijk minder programmeer werk dan een client-server model omdat alle peers dezelfde code gebruiken. De system calls die je zoekt zijn bind() om te bepalen op welke poort je verstuurt en dan sendto() en recvfrom(). Ik weet zo even niet wat de .Net versies ervan zijn maar WinSock heeft ze ook dacht ik. Kortom maak gewoon een klasse welke korte berichten in UDP datagrams verstuurt en kan ontvangen en punch away!
Pagina: 1