In diesem Praktikum verlassen wird die Anwendungsschicht und begeben uns eine Schicht tiefer, in die Transportschicht. Die beiden Transportprotokolle, die wir kennengelernt haben heißen TCP und UDP und wir werden deren Verhalten experimentell untersuchen, insbesondere das von TCP. TCP ist ein vergleichsweise komplexes Protokoll, aber von zentraler Bedeutung. Die Eigenschaften und das Verhalten von TCP zu kennen ist allerdings wichtig für Anwendungsprogrammierer, damit man versteht, welchen Dienst und welche Dienstgüte TCP anbietet. Heute geht es daher darum, das Verhalten von TCP zu verstehen, indem wir praktisch mit TCP und relevanten Werkzeugen arbeiten. Darüber hinaus werden wir Mininet
kennenlernen, ein Tool, um ein beliebiges Netzwerk auf einem einzigen Rechner abzubilden und ip
erf, ein Werkzeug, um den Durchsatz eines Pfades zu ermitteln. Letzteres wird benötigt, da wir eine Anwendung brauchen, die Daten erzeugt, damit wir das Verhalten von TCP beobachten können und iperf
sendet Daten, so schnell es kann und das so lange man möchte. Da der Verhalten von TCP bei Belastung am interessantesten ist, bietet sich iperf
für unsere Zwecke an.
Mininet
ist ein Tool, dass die Virtualisierungsmöglichkeiten des Linux-Kernels nutzt, um virtuelle Netzewerke aus verschiedenen Komponenten aufzubuauen, die zwar auf einem Host-System laufen, sich allerding genauso verhalten, als wären Sie über ein Netz miteinander verbunden. Was sich kompliziert und ressourcenintensiv anhört ist mit Hilfe von Mininet einfach und ressourcenschonend, so dass auch größere Netzwerke problemlos aufgebaut werden können. Vielleicht beschreibt es dieser Text von der Mininet-Webseite am besten:
Mininet creates a realistic virtual network, running real kernel, switch and application code, on a single machine (VM, cloud or native), in seconds, with a single command. Because you can easily interact with your network using the Mininet CLI (and API), customize it, share it with others, or deploy it on real hardware, Mininet is useful for development, teaching, and research.
Mininet ist aus Nutzersicht ein Kommandozeilentool, das nach dem Start eine eigene, neue Kommandozeile bereitstellt - die Mininet-CLI. Da Mininet priviligierte Befehle ausführt, muss Mininet mit Super-User-Rechten gestartet werden. Am einfachsten startet man Mininet so:
sudo mn
Wenn man diese Befehl ausgeführt hat, dann werden Statusnachrichten ausgegeben und relativ schnell erwartet einen die Mininet-CLI, die man am Prompt gut erkennt:
*** Creating network *** Adding controller *** Adding hosts: h1 h2 *** Adding switches: s1 *** Adding links: (h1, s1) (h2, s1) *** Configuring hosts h1 h2 *** Starting controller c0 *** Starting 1 switches s1 ... *** Starting CLI: mininet>
Mininet teilt einem hier mit, was alles erstellt und angelegt wurde. Wir sehen z.B. zwei Hosts (h1, h2), einen Switch (s1) und Links, die die beiden Hosts mit dem Switch verbinden. Am Ende wartet die Mininet-CLI auf Eingaben. Bevor wir uns mit der CLI genauer beschäftigen fällt noch etwas auf. Der Controller. Diesen brauchen wir nicht, denn hier handelt es sich um einen OpenFlow-Controller, der einen OpenFlow-Switch steuern kann. Um es an dieser Stelle kurz zu machen, OpenFlow wird wenig in der Industrie eingesetzt und schwebt irgendwo zwischen Hype und Hoffnung als Technologie. Wir sind an Standardkomponenten interessiert, also einem einfachen Ethernet-Switch. Das müssen wir Mininet beim Start mitteilen. Dazu müssen wir Mininet zunächst beenden. In der Mininet-CLI einfach exit
eingeben und Mininet neu starten, und diesmal so:
sudo mn --switch lxbr
lxbr
steht dabei für Linux Bridge
, und bridge
ist ein anderer Begriff für switch
.
Damit sind wir wieder in der Mininet-CLI, erkennbar am Prompt (mininet>
). Mit der Mininet-CLI kann man das gesamte, gerade erzeugte Netz zentral steuern. D.h. heisst von hier aus kann man sich den Status des Netzes anzeigen lassen, Mininet-Befehle absetzten, oder, und das ist das wahrscheinlich wichtigste, man kann einfach Linux-Kommandos und Programme ausführen. Sprich, alles was auf dem Linux Host-System installiert ist, kann man auf den virtuellen Komponenten unseres Netzes ausführen, als wären es echte Komponenten, isoliert vom Host-System und isoliert von den anderen virtuellen Komponenten. Bevor wir dies tun aber noch ein Wort bezüglich des Hilfesystems in Mininet.
Mit:
help
kann man in der Mininet-CLI sich die von der Mininet-CLI bereitgestellten Befehle anzeigen lassen:
mininet> help Documented commands (type help <topic>): ======================================== EOF gterm iperfudp nodes pingpair py switch dpctl help link noecho pingpairfull quit time dump intfs links pingall ports sh x exit iperf net pingallfull px source xterm You may also send a command to a node using: <node> command {args} For example: mininet> h1 ifconfig The interpreter automatically substitutes IP addresses for node names when a node is the first arg, so commands like mininet> h2 ping h3 should work. Some character-oriented interactive commands require noecho: mininet> noecho h2 vi foo.py However, starting up an xterm/gterm is generally better: mininet> xterm h2
Hier steht schon einiges an wichtiger Information. Die Tabelle gleich am Anfang der Hilfeseite zeigt die in der Mininet-CLI eingebauten Befehle. Wer mehr über einen dieser Befehle wissen möchte muss nur:
help <topic>
eingeben. D.h. wer mehr über den Befehl switch
wissen möchte muss kann bekommt mit diesem Befehl eine kurze Erklärung dazu:
mininet> help switch Starts or stops a switch
Manchmal sind diese Hilfetexte ein wenig kurz geraten, denn der obige Hilfetext sagt nichts über die Benutzung des Befehls. Aber wenn man es einfach ausprobiert, dann komm typischerweise ein Hilfetext, der die Nutzung beschreibt.
mininet> switch
invalid number of args: switch <switch name>{start, stop}
Viele Befehle geben Statusinformationen über das virtuelle Netz und seine Knoten aus. Dazu gehören z.B. die Befehle links
, nodes
, ports
, net
, intfs
oder der Befehl, der eine Gesamtübersicht ausgibt dump
.
mininet> dump <Host h1: h1-eth0:10.0.0.1 pid=2976> <Host h2: h2-eth0:10.0.0.2 pid=2978> <LinuxBridge s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=2984> <Controller c0: 127.0.0.1:6653 pid=2969>
Oben sieht man z.B. die Ausgabe von dump
. Man sieht es gibt zwei Endhosts namens h1
und h2
, einen Switch s1
dem man nun ansieht, dass es ein ganz einfacher Standard-Switch ist. Den Controller vernachlässigen wir für unsere Experimente. Man sieht hier auch die IP Adressen der Hosts, was oft hilfreich ist.
Eine weitere Klasse von Befehlen ermitteln die Erreichbarkeit zwischen den Komponenten des Netzes. So kann z.B. mit pingall
herausfinden, ob sich alle Hosts anpingen können. In dem kleinen Standardnetz von Mininet sieht das dann so aus:
mininet> pingall *** Ping: testing ping reachability h1 -> h2 h2 -> h1 *** Results: 0% dropped (2/2 received)
Hier erreicht jeder jeden. Stellen wir mal den Switch aus und probieren es erneut.
mininet> switch s1 stop mininet> pingall *** Ping: testing ping reachability h1 -> X h2 -> X *** Results: 100% dropped (0/2 received)
Und schon erreichen sich die Hosts nicht mehr (es dauert ein wenig, bis das Ergebnis vorliegt, da Ping es einige Male versucht).
Wie schon erwähnt erlaubt es Mininet die Hosts (hier h1
und h2
) wie ganz normale (Linux-)Rechner zu verwenden. D.h. man könnte ganz einfach z.B. auf h1
das ping
-Kommando selbst ausführen, um die Erreichbarkeit zu testen. Dazu gibt es zwei Möglichkeiten. Die erste ist es direkt aus der Mininet-CLI den Befehl zu starten. Dazu schreibt man als erstes (!) den symbolischen Namen des Hosts auf dem das Kommando ausgeführt werden soll und dann das Kommando. Dazu kann man das Kommando so schreiben, wie man es auf der Kommandozeile des Hosts selbst tun würde, oder in der Mininet-CLI kann man auch als Parameter die symbolischen Namen verwenden (nicht vergessen den Switch wieder einzuschalten mit switch s1 start
):
h1 ping 10.0.0.2
oder
h1 ping h2
Die korrekte IP Adresse bekommt man z.B. mit dem eingebauten dump
Befehl. Bei einfachen Kommandos wie ping
funktionieren aber meist auch die symbolischen Namen.
Alternativ zur zentralen Steuerung durch die Mininet-CLI, kann man sich aber auch direkt eine Console auf den Komponenten öffnen mit xterm
oder gterm
:
xterm h1
Hier sollte sich ein neues Fenster mit einer Kommandozeile auf der gewünschten Komponente öffnen. gterm
ist dabei die schönere Konsole, aber nicht in jeder Linux-Distribution vorhanden. Hier, können jetzt beliebige Befehle eingegeben werden und Anwendungen gestartet werden, die alle auf diesem virtuellen Rechner laufen. Wichtig dabei ist aber, dass die symbolischen Namen der Mininet-CLI nicht funktionieren, d.h. in dieser Konsole ist h1
oder h2
nicht bekannt und man muss die IP Adressen benutzen (also z.B. ping 10.0.0.2
in der Konsole von h1
).
Manchmal stürzt Mininet ab, oder es fängt an sich seltsam zu verhalten. Dann sollte man Mininet neu starten, aber bevor Mininet in solchen Situationen neu startet sollte man unbedingt die möglichen Überbleibsel der virtuellen Umgebung aufräumen. Das macht man so:
sudo mn -c
Obiges macht man natülich nicht in der Mininet-CLI, sondern nachdem man Mininet verlassen hat/gecrashed ist und bevor man es neu startet.
Apropos starten. Wenn man Mininet startet, dann kann man mit einigen Kommandozeilenschaltern das Verhalten von Mininet, bzw. das Verhalten und die Struktur der Netzwerktopologie steuern, ohne, dass man hier skripten müssten, was auch möglich wäre. Hier die vielleicht gängigsten Schalter beim Start von Mininet.
Zuächste kann man die Topologie, d.h. wie die einzelnen Komponenten miteinander verbunden sind, steuern. Mininet kennt ein paar einfache, vorgefertigte Topologien, die man noch parametrisieren kann. Hier ein Beispiel:
sudo mn --switch lxbr --topo single,3
Hier wird eine Topologie erzeugt mit einem einzigen Switch und drei Hosts sind mit diesem verbunden. Die Anzahl der Hosts kann man beliebig einstellen. Wichtig ist, dass nach --topo
nur ein Argument stehen darf, daher ist single,3
ein String ohne Leerzeichen. Weitere mögliche Topologien sind linear
|minimal
|reversed
|single
|torus
|tree
, wobei minimal
die Standardtopologie mit dem Switch und den beiden Hosts ist, die wir schon kennengelernt haben.
Die Verbindung der Komponenten untereinander ist nur ein Aspekt des Netzwerks, dass sich einstellen lässt. Die Eigenschaften der Links zwischen der Komponenten, aber auch die Tiefe der Puffer der Switches kann eingestellt werden, und zwar mit dem --link
Schalter (auch hier darauf achten, dass alles hinter dem --link
Schalter ein String ohne Leerzeichen ist):
sudo mn --switch lxbr --topo single,3 --link tc,bw=10,delay=10ms,loss=1,max_queue_size=200
Nicht alle Parameter müssen benutzt werden, aber der Vollständigkeit halber sind hier die gängigsten aufgezeigt. Auch die Reihenfolge ist egal. Dabei bedeuten die Parameter folgendes:
bw
(bandwidth) : Bandbreite der Links (default in Mbit/s)delay
: der One-Way-Delay des Links, d.h. die Verzögerung bei der Übertragung über den Linkloss
: Paketverlustwahrscheinlichkeit in Prozentmax_queue_size
: Die Anzahl an Paketen, die in einer Warteschlange gespeichert werden könnenSoviel zu Mininet.
iperf
ist, wie schon erwähnt, ein Kommandozeilenwerkzeug, um die verfügbare Bandbreite zu ermitteln, bzw. um einfach Verkehr zu erzeugen. Die man-page sagt es wohl wieder am besten:
iperf is a tool for performing network throughput measurements. It can test either TCP or UDP throughput. To perform an iperf test the user must establish both a server (to discard traffic) and a client (to generate traffic).
Da diese Operationen wenig komplex sind, ist es kaum verwunderlich, dass iperf selbst wenig komplex in der Bedienung ist. Man braucht auf der einen Seite einen iperf-Server und auf der anderen einen iperf-Client. Der Client erzeugt die Pakete und der Server verwirft diese einfach. Beide erzeugen Statistiken, die nach jedem Test ausgegeben werden.
Beim Start muss man den Client und der Server natürlich richtig konfigurieren. Da iperf sowohl TCP als auch UDP unterstützt und entweder als Client oder als Server fungiert gibt es zunächst diese vier Grundkonfigurationen:
iperf -s
: startet einen TCP iperf-Serveriperf -c <server IP>
: startet einen TCP iperf-Clientiperf -u -s
: startet einen UDP iperf-Serveriperf -u -c <server IP>
: startet einen UDP iperf-ClientDazu können sich noch ein paar andere Schalter gesellen. Einer den wir später noch brauchen werden ist -P
nach dem die Zahl der Threads folgt, die den Test durchführen sollen. Bei TCP erzeugt jeder Thread eine eigene TCP-Verbindung. Also mit iperf -c 10.0.0.1 -P 2
wird ein TCP iperf-Client erzeugt gestartet, der zwei Threads nutzt und damit zwei TCP-Verbindungen. Der Server als Gegenstück läuft dabei auf der 10.0.0.1. Die länge der Datenübertragung kann man mit -t
steuern. -t
erwartet die Messdauer in Sekunden. Ein weiterer Schalter wird beim UDP Client benötigt und zwar -b
. Nach -b
kommt die Geschwindigkeit mit der der UDP Client senden soll. Mit iperf -u -c 10.0.0.1 -t 30 -b 5M
wird ein UDP Client gestartet, der 30 Sekunden lang mit 5 Mbit/s sendet.
Soviel zu iperf. Jetzt kann es losgehen.
Viel Erfolg!
Zunächst ein paar Fingerübungen, um mit Mininet vertraut zu werden. Wir werden Mininet ab jetzt auch in den folgenden Praktika nutzen, daher ist der sichere Umgang damit wichtig.
Wir benutzen Mininet heute mit einer einfachen Topologie, bei der drei Hosts mit einem Switch verbunden sind.
sudo mn --switch lxbr --topo single,3 --link tc,bw=10,max_queue_size=200,delay=10ms
Was hier erzeugt wird, sollte Sie nun wissen.
Bearbeiten Sie nun folgende Aufgaben in der Mininet-Topologie:
pingall
) und sehen Sie sich die Information aller Hosts (nodes
) der Links (links
) und Netzwerkschnittstellen (intfs
) an. Nutzen Sie auch das ping Programm, um die Latenz zwischen h1
und h3
zu testen. Entspricht die Latenz Ihren Erwartungen? Sie sollte im Bereich von 40ms liegen. Warum ist das so?xterm h1
). Pingen Sie von diesem Host aus die anderen beiden Hosts an (nicht vergessen, Sie müssen die IP Adressen nutzen, die symbolischen Namen h1
, h2
... sind nur in der Mininet-CLI verfügbar).h1 python3 -m http.server 80 &
). Mit dem ihnen schon bekannten Tool curl
können Sie nun die Webseiteanfragen (h2 curl h1
). Wie sie sehen haben Sie ein virtuelles Netz auf Ihrem PC und können dort beliebige Programme starten und über dieses Netz darauf zugreifen. Jetzt sind Sie bereit sich TCP genauer anzuschauen. Lassen Sie Mininet für die weiteren Aufgaben laufen. Beenden Sie aber den Webserver mit: h1 killall python3
.Starten Sie ein xterm-Fenster auf allen 3 Hosts. Schauen wir zuerst, ob TCP es schafft die 10 Mbit/s zwischen zwei Hosts bei 40ms Rundlaufzeit zu nutzen. Sollte das nicht funktionieren, wäre das keine Gute Nachricht. Starten Sie einen iperf TCP-Server auf Host 1 (iperf -s &
).
iperf -u -s &
). Geben Sie auf Host 2 das Kommando ein, um einen UDP iperf-Client zu starten, der 20 Sekunden mit 5 Mbit/s sendet, aber geben Sie noch kein ein! Geben Sie das Kommando ein, um auf Host 3 einen iperf-Client mit TCP zu starten der ebenfalls 20 Sekunden sendet, aber geben Sie noch kein ein. Versuchen Sie jetzt beide möglichst zügig hintereinander zu starten. Wie wird die Bandbreite aufgeteilt und warum?-P 4
). Wie wird die Bandbreite aufgeteilt und warum? (hier müssen im virtuellen Netz und mit echten Implementierungen evtl. Abstriche gemacht werden, überlegen Sie wie die Bandbreite modellhaft aufgeteilt werden müsste)sudo mn --switch lxbr --topo single,3 --link tc,bw=10,max_queue_size=200,delay=10ms,loss=1
). Starten Sie nun auf einem Host wieder einen iperf TCP-Server und testen Sie die Bandbreite von einem anderen Host zu diesem Server. Die Bandbreite sollte geringer sein, aber warum?sudo mn --switch lxbr --topo single,3 --linktc,bw=10,max_queue_size=200,delay=1ms,loss=1
). Starten Sie nun auf einem Host wieder einen iperf TCP-Server. Auf dem Host, auf dem der iperf-Client laufen soll, starten Sie Wireshark und die Aufzeichung der Pakete. Testen Sie anschliessend die Bandbreite von diesem Host zum Server. Die Bandbreite sollte wieder gestiegen sein, aber warum? Schauen Sie sich den Paketverlust in Wireshark an. Finden Sie duplizierte ACKs und die Neu-übertragung von Paketen (wann werden diese neu gesendet)?Jetzt wollen wir uns TCP auch nochmal in Wireshark anschauen. Laden Sie die Datei tcp-trace.pcap herunter und öffnen Sie diese in Wireshark. Im Trace finden Sie einen HTTP-Post Request. Damit dieser Sie nicht verwirrt und sie nur TCP-Segmente sehen, können Sie Wireshark instruieren HTTP nicht zu interpretieren. Dazu entfernen Sie das Häkchen im Menü Analyze/Enable Protocols bei HTTP.
Zu guter Letzt wollen wir uns TCP auch nochmal live anschauen. Starten Sie Aufzeichnung mit Wireshark und nutzen Sie den Browser (ganz ohne Mininet), um eine beliebige Webseite zu laden. Stoppen Sie die Aufzeichnung und finden Sie einen beliebigen TCP Verbindungsaufbau.
TCP ist durch Optionen erweiterbar. Sie sollten z.B. im SYN
und SYN/ACK
die Window Scale Option vorfinden. In dieser steht ein Wert x
der bedeutet, dass der Wert des Receive Windows (Empfangsfenster) mit 2 hoch x multipliziert werden muss, um den wahren Wert des Receive Windows zu berechnen.
Ermitteln Sie folgende Werte:
Mininet
. Für den weiteren Verlauf des Praktikums werden wir immer wieder Mininet
verwenden, daher ist der sichere Umgang mit Mininet wichtig! Wie oft haben Sie ein fast beliebiges Netzwerk, mit dem sie bedenkenlos spielen können? Tun Sie es!Ich würde mich über Ihre Kritik und Verbesserungsvorschläge freuen. Auch Lob, klar. Eine kurze Umfrage finden Sie hier.