Dieses Praktikum beschäftigt sich wieder mit der Netzwerkschicht. Diesmal aber nicht mit Routing und Longest Prefix Matching, sondern mit Network Address Translation (NAT). NAT ist im heutigen Internet allgegenwärtig und daher ist es wichtig zu verstehen wie NAT funktioniert und was NAT einem bietet, bzw. was NAT erschwert. Dabei ist dies nicht nur für Netzwerk-Admins wichtig, sondern insbesondere für Anwendungsentwickler, bei denen im Zweifel eine Anwendung nicht mehr funktioniert, wenn NAT im Spiel ist.

Um NAT zu verstehen, bemühen wir wieder einmal Mininet und führen auch wieder Werkzeuge ein, die uns helfen, Experimente mit einer richtige NAT-Implementierung durchzuführen.

Netcat

Wir werden eine echte NAT-Implementierung untersuchen, um zu verstehen, wie eine solche intern arbeitet. Dazu müseen wir kontrolliert Zustand in der NAT-Tabelle erzeugen und dazu brauchen wir ein Werkzeug, das es uns erlaubt TCP-Verbindungen und UDP Pakete zu erzeugen, bei denen wir die genutzten Ports einstellen können, denn NAT nutzt diese zum Multiplexen/Demultiplexen. Darüber hinaus brauchen wir natürlich auch einen Server, der diese Pakete und Verbindungsversuche annimmt. Für diese Aufgabe eignet sich Netcat hervorragend. Die man page von Netcat hat folgendes zu seinem Anwednungsbereich zu sagen:

The nc (or netcat) utility is used for just about anything under the sun involving TCP, UDP, or UNIX-domain sockets. It can open TCP connections, send UDP packets, listen on arbitrary TCP and UDP ports, do port scanning, and deal with both IPv4 and IPv6. Unlike telnet(1), nc scripts nicely, and separates error messages onto standard error instead of sending them to standard output, as telnet(1) does with some.

Für unsere Zwecke also das ideale Werkzeug. Schauen wir uns die für dieses Praktikum wichtigsten Kommandozeilenschalter an.

TCP-Client

Das Netcat-Tool heißt auf der Kommandozeile nc. Als Client werden wir nur einige der verfügbaren Schalten benutzen. Der generelle Aufruf als TCP-Client sieht so aus:

nc [-p source_port] [-q seconds] [destination] [port]

Dabei ist:

Beispiel:

echo "hello" | nc -p 3456 -q -1 10.0.0.1 1234

Im Beispiel wird der String "hello" über eine TCP-Verbinding an 10.0.0.1 Port 1234, Quell-Port 3456 gesendet. Die TCP-Verbindung bleibt danach offen (-q -1).

TCP-Server

Einen Server mit Natcat aufzusetzen ist ebenso einfach. Der generelle Aufruf sieht dabei wie folgt aus:

nc -l -k [port]

Dabei ist:

Beispiel:

nc -k -l 1234

Hier wir ein TCP-Server gestartet, der beliebige Verbindungsanfragen auf Port 1234 annimmt.

UDP-Client

Bei UDP ist das Ganze noch ein wenig simpler. Ein Client wird einfach wie folgt gestartet:

nc [-u] [destination] [port]

Beispiel:

echo "hello" | nc -u 10.0.0.1 1234 

Hier wird ein UDP Datagram mit dem String "hello" als Inhalt an einen UDP-Server auf dem Host 10.0.0.1, Port 1234 geschickt.

UDP-Server

Der UDP-Server unterscheidet sich nur duch den -u Schalter, um UDP anstelle von TCP zu nutzen. D.h. ein UDP-Server sieht wie folgt aus:

nc -l -u -k [port]

Dabei ist:

Beispiel:

nc -k -u -l 1234

Hier wird ein UDP-Server gestartet, der Pakete auf Port 1234 annimmt und beliebige Pakete annimmt ohne sich zu beenden.

NAT-Tabelle anzeigen

Zu guter Letzt brauchen wir noch ein Werkzeug, dass die NAT-Tabelle anzeigen kann und dieses Werzeug heisst conntrack. Hier brauchen wir nur einen Schalter, und zwar -L, mit dem die Tabelle angezeigt wird. conntrack kann wesentlich mehr, aber für unsere Zwecke reicht das einfache Anzeigen der Tabelleneinträge.

Damit sind Sie für das Praktikum gerüstet.

Viel Erfolg!

Zunächst benötigen Sie die Datei nat_topo.py, die die Mininet-Topologie beinhaltet mit der wir arbeiten werden. Die Topologie besteht aus vier Hosts (h0 - h3), wovon h1, h2 und h3 sich jeweils hinter einem NAT-Router (nat1 und nat2) befinden. Starten Sie die Topologie in Mininet wie gewohnt mit:

sudo python nat_topo.py

Sie sollten nun die ihnen schon bekannte Mininet CLI vor sich haben. Das Netz was hier erzeugt wurde sieht wie folgt aus:

Topologie

h0 ist für heute sowas wie ein Server im Internet und h1 ist in einem privaten Subnetz hinter dem NAT-Router nat1. h2 und h3 sind ebenfalls hinter einem NAT-Router (nat2). Fangen wir an, das Verhalten dieser Router zu untersuchen.

  1. Versuchen Sie mit Hilfe von ping von Host h1 aus die beiden Hosts h0 und h2 zu erreichen. Erklären Sie warum es klappt/bzw. nicht klappt (ein Blick auf die Topologie hilft sicherlich).
  2. Starten Sie den Ihnen schon bekannten einfachen Webserver auf dem Host h0 mit:
h0 python3 -m http.server 80 &

Verbinden Sie sich nun auf nat1 und h1 (xterm h1 nat1). Starten sie Wireshark auf nat1 (wireshark &) und wählen Sie als Netzwerkschnittstelle zum Aufzeichnen die Schnittstelle mit dem Namen any, also alle verfügbaren Schnittstellen. Starten Sie die Aufzeichnung und kontaktieren Sie den eben gestarteten Webserver von h1 aus via:

`curl 10.0.0.1`

Schauen Sie sich in Wireshark an, wie der NAT-Router die Pakete verändert. Versuchen Sie anhand der Netzwerk-Topologie die Veränderungen nachzuvollziehen.

  1. Jetzt nutzen wir eines der einfachsten, aber auch nützlichsten Netzwerk-Tools: Netcat (nc). Wie schon gesehen, kann man mit Netcat einfach TCP-Verbindungen aufbauen und über diese Daten transportieren. Daten braucht es aber gar nicht, um das Verhalten der NAT zu beobachten. Zunächst aber, starten wir einen Server, der TCP-Verbindungen auf dem Host im "Internet" (h0) annimmt.
nc -k -l 1234

Als nächstes verbinden wir uns von Host h1 aus mit diesem Server:

nc -q -1 10.0.0.1 1234

Wichtig ist dabei, dass -q -1 gesetzt ist, so dass die Verbindung bestehen bleibt! Jetzt schauen Sie sich den NAT-Tabelleneintrag zu dieser TCP-Verbindung auf nat1 an via:

conntrack -L

Sie sollten einen Eintrag wie diesen hier sehen:

tcp 6 431970 ESTABLISHED src=192.168.1.100 dst=10.0.0.1 sport=34016 dport=1234 src=10.0.0.1 dst=10.0.0.5 sport=1234 dport=34016 [ASSURED] mark=0 use=1

Dabei handelt es sich um das Protokoll (tcp), die Protokollnummer wie sie im dazugehörigen IP-Feld steht (6), der Timeout für den Eintrag in Sekunden (431970), den Zustand der TCP-Verbindung (ESTABLISHED), das Viertupel von Paketen Richtung Internet, wie Sie LAN-seitig ankommen und das Viertupel von Paketen aus dem Internet, wie sie WAN-seitig ankommen. Die Viertupel werden für das Muliplexen/Demultiplexen benötigt und für das Umschreiben der Pakete. Den Rest brauchen wir hier nicht. Fällt Ihnen etwas am Viertupel auf? Wurde der Quellport verändert?

Beenden Sie den nc-Client (und nur den Client, denn den Server benötigen wir noch) und schauen Sie danach gleich wieder in die NAT-Tabelle. Was fällt Ihnen auf? Welcher Eintrag hat sich verändert und warum?

  1. Wir wollen nun das Verhalten der NAT beobachten, wenn ein bereits vergebener Port auf dem NAT-Router verwendet wird. Dazu benötigen wir aber zwei Hosts im LAN. Deshalb wechseln wir nun zur nat2 und den Hosts h2 und h3. Wir nutzen wieder Netcat und verbinden uns von h2 und h3 mit jeweils den gleichen Quell-Ports, damit die NAT nicht einfach die Ports wiederverwenden kann. Dazu verbinden sie sich jeweils von h2 und h3 mit dem eben gestarteten Server wie folgt:
nc -p 3456 -q -1 10.0.0.1 1234

Verbinden Sie sich mit nat2 und schauen Sie sich die NAT-Tabelle mit conntrack an. Erklären Sie sich die Ausgabe (insbesondere die Viertupel).

Sie haben gesehen, dass die NAT das TCP-Protokoll versteht! Warum? Nun offensichtlich hat der NAT-Router seinen Timeout angepasst nachdem er die FIN-Segmente der Endpunkte gesehen hat. D.h. der NAT-Router hat gesehen, dass die Verbindung abgebaut wurde. Und, der NAT-Router hat den Eintrag noch eine Zeit lang gehalten. Es scheint so, als würde der NAT-Router auch den TIME-WAIT-Zustand kennen. Wiederholen Sie das Experiment mit nc mit UDP und untersuchen Sie das Verhalten der NAT bezüglich der Dauer der Einträge.

Einen UDP Server können Sie mit

nc -k -u -l 1234

starten. Und um dem Server etwas zu schicken (dieses mal müssen wir etwas schicken, da es keinen Verbindungsaufbau gibt!), können Sie z.B. dies hier benutzen.

echo "hello" | nc -u 10.0.0.1 1234 

Was passiert, wenn ein Paket erneut verschickt wird, bevor der Eintrag gelöscht wird (setzen Sie dazu auch den Quellport mit dem -p-Schalter auf einen bestimmten Port).

Wir haben gesehen was passiert, wenn wir TCP und UDP nutzen, aber was ist, wenn wir ein Protokoll einsetzen, das gar keine Port-Nummern hat, wie z.B. ICMP? Probieren Sie es doch einfach aus!

  1. Führen Sie den gleichen Versuch wie gerade mit UDP nochmal durch, diesmal nutzen Sie allerdings nicht Netcat sondern ping, um h0 (10.0.0.1) zu erreichen. Schauen Sie wieder in die NAT-Tabelle von nat2. ping nutzt ICMP und ICMP benutzt kein Transportprotokoll und damit keine Port-Nummern. Welche Informationen nutzt der NAT-Router, um die ICMP-Antworten korrekt zu identifizieren? Schauen Sie sich die Informationen auf dem Hin- und Rückweg genau an.
  2. Jetzt schicken Sie hintereinander viele einzelne ping-Anfragen hintereinander von h2 Richtung h0 indem sie ein paarmal hintereinander:
ping -c 1 10.0.0.1 

ausführen. Schauen Sie nun in die NAT-Tabelle von nat2. Versuchen Sie zu erklären, warum Sie so viele Einträge sehen.

In der Vorlesung haben wir den Dijkstra-Algorithmus kennengelernt. Berechnen Sie die kürzesten Wege, einmal ausgehend von A und einmal ausgehend von G im hier dargestellten Netz mithilfe von Dijkstra.

Topologie

In der Vergangenheit wurde neue Transportprotokolle entwickelt. Unter anderem SCTP und DCCP. Glauben Sie nach dem heutigen Praktikum, dass NAT evtl. ein Grund ist, dass diese Transportprotokolle nie eine weite Verbreitung erfahren haben? Welchen weiteren Nachteil hat NAT, den wir in der Vorlesung noch nicht besprochen hatten?

Ich würde mich über Ihre Kritik und Verbesserungsvorschläge freuen. Auch Lob, klar. Eine kurze Umfrage finden Sie hier.