Sitzung: Jeden Freitag in der Vorlesungszeit ab 16 Uhr c. t. im MAR 0.005. In der vorlesungsfreien Zeit unregelmäßig (Jemensch da?). Macht mit!

Benutzer:Grey/UDP-Testskript

UDP-Testskript

Versuchsaufbau

Es gibt verschiedene Varianten des Aufrufs von FWTEST und FWAGENT, näheres hier: Versuchsaufbauten

netdate-Beispiel

An Hand eines Beispiels wollen wir die Implementation eines Testskripts darstellen. Wir wollen zum Beipiel den netdate-Dienst von www.heise.de in Anspruch nehmen (wir nehmen den Dienst von Heise nicht wirklich in Anspruch, sondern simulieren lediglich den Traffic) und uns die aktuelle Uhrzeit zurückgeben lassen. Netdate funktioniert auf verschiedenen Protokollen, wir nehmen mal die Variante mit UDP-Paketen.

Wenn man also mit Ethereal oder tcpdump den Verkehr belauschen würde, sollen die Pakete so aussehen als wären sie echt und nicht von uns erstellt (mehr dazu unter: Grenzen von Tests).

Im Grunde arbeiten wir folgende Punkte nacheinander ab:

  1. default IP-Paket(Achen -> Bchen) erstellen
  2. default IP-Paket(Bchen -> Achen) erstellen
  3. default UDP-Paket(Achen -> Bchen) erstellen
  4. default UDP-Paket(Bchen -> Achen) erstellen
  5. IP-Pakete anpassen
  6. UDP-Pakete anpassen
  7. IP- und UDP-Pakete zusammenfügen
  8. UDP/IP-Pakete mittels transceive-Funktion versenden
  9. Auswertung




Nun wollen wir uns das Testskript im detail anschauen und von Anfang an aufbauen, am Ende ist der Link zum kompletten Source-Code vom netdate-Skript.


Als erstes müssen wir unsere wrapper-Klassen(mit dem Präfix fwt_ benannt) und andere benötigte Klassen importieren:

import fwt_ip      # wrapper-Klasse zum erstellen/ändern von IP-Paketen
import fwt_udp     # wrapper-Klasse zum erstellen/ändern von UDP-Paketen
import fwt_tcp     # wrapper-Klasse zum erstellen/ändern von TCP-Paketen
import posix       # TODO: Erklärung


Danach definieren wir lediglich zum Debuggen zwei Variablen. dry_run ist für einen sogenannten Trockenlauf, der das Paketerstellen durchführt, aber das endgültige Absenden der Pakete ausläßt.

Die Zweite bewirkt, daß nur Bchen die Antwort an Achen schickt. Achen schickt also nicht erst eine Anfrage, so daß Bchen antwortet.

dry_run=0	    # if set the transceive function is skipped
response_only=0    # send time protocol response only


TODO Wir müssen die Beiden Agenten Achen und Bchen mit einer IP-Adresse festlegen.

ACHEN='10.000.2.2'
BCHEN='10.128.2.2'


TODO

FILTER_A="not tcp port 1500 and src " + BCHEN + " and dst " + ACHEN + " or icmp"
FILTER_B="not tcp port 1500 and src " + ACHEN + " and dst " + BCHEN + " or icmp"


Als Grundlage brauchen wir natürlich unser IP-Paket, daß wir hier generieren. Wir können unsere wrapper-Klasse fwt_ip bemühen und erstellen damit ein defaultPacket.

ip_packet = fwt_ip.Packet(fwt_ip.defaultPacket)


Da bei unserem defaultPacket noch nicht das Protokoll spezifiziert ist, passen wir es mit der set-Funktion unserer wrapper-Klasse fwt_ip an. Alle Felder des IP-Paket können mit der set-Funktion geändert werden.

ip_packet.set ({
    fwt_ip.HEADER_PROTOCOL: 17,
})


Da zum Beispiel das Berechnen der IHL(Internet Header Length) und der Header Checksum nicht durchgeführt wurden, lassen wir das von unserer FIXUP-Funktion erledigen. Die FIXUP-Funktion wird von unserer wrapper-Klasse zur Verfügung gestellt. Das FIXUP_ALL bewirkt, daß alle Felder(TODO: WELCHE?) korrigiert oder angepaßt werden. Unser IP-Paket wird später noch zum Beispiel mit der SourceIP und der DestinationIP bestückt.

ip_packet.fixup (fwt_ip.FIXUP_ALL)


Momentan haben wir ein IP-Paket als Grundlage für beide UDP-Paket, benötigen aber noch die UDP-Pakete selbst. Wir definieren uns mit Hilfe unserer wrapper-Klasse zwei UDP-Pakete, einmal von Achen nach Bchen und zurück. Diese Pakete werden später der Payload der IP-Pakete sein und haben momentan nur Grundeinstellungen.

udp_pkt_ab = fwt_udp.Packet(fwt_udp.defaultPacket)
udp_pkt_ba = fwt_udp.Packet(fwt_udp.defaultPacket)


Zwar sind die Grundeinstellungen schon ganz gut, aber trotzdem müssen wir einige Änderungen vornehmen. Als erstes beim UDP-Paket von Achen nach Bchen...

udp_pkt_ab.set({
    fwt_udp.HEADER_CHECKSUM:      0,
    fwt_udp.HEADER_SOURCEPORT:   53,
    fwt_udp.HEADER_DESTPORT:     37,
    fwt_udp.PAYLOAD:	         '\x0a'
})

...und dann von Bchen nach Achen.

udp_pkt_ba.set({
    fwt_udp.HEADER_CHECKSUM:      0,
    fwt_udp.HEADER_SOURCEPORT:   37,
    fwt_udp.HEADER_DESTPORT:     53,
    fwt_udp.PAYLOAD: '\x69\xdc\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77\x05\x68\x65\x69\x73\x65\x02\x64\x65\x00\x00\x01\x00\x01',
})


Achten Sie darauf, daß wenn Sie einen bestimmten Dienst eines Servers oder Rechners in Anspruch nehmen, an den korrekten Destinationport das Paket zu schicken. Den Payload eines UDP-Paketes müssen Sie hardcoden in Hexadezimal.

Wie bei dem IP-Paket machen wir ebenfalls ein FIXUP_ALL beider UDP-Pakete, was die Länge und die Header Checksum korrigiert oder anpaßt.

udp_pkt_ab.fixup(fwt_udp.FIXUP_ALL, ACHEN, BCHEN);
udp_pkt_ba.fixup(fwt_udp.FIXUP_ALL, BCHEN, ACHEN);


Nachdem wir nun unser IP-Grundpaket und unseren UDP-Pakete von Achen nach Bchen und zurück erstellt haben, wollen wir die Pakete vereinen. Das IP-Paket von oben dient uns als Vorlage für unser UDP/IP-Paket von Achen nach Bchen. Wir übergeben unserem Konstruktor das IP-Paket als Dictionary.

ip_udp_pkt_ab = fwt_ip.Packet(ip_packet.as_dict())


Das UDP/IP-Paket braucht natürlich das Ziel, daß es ansteuern soll und der Empfänger muß auch wissen wohin er die Antwort schicken müßte. Als Payload, wie schon weiter oben erwähnt, übergeben wir das UDP-Paket in Form eines String.

ip_udp_pkt_ab.set({
    fwt_ip.HEADER_SOURCEIP:	ACHEN,
    fwt_ip.HEADER_DESTIP:	BCHEN,
    fwt_ip.PAYLOAD: udp_pkt_ab.as_str()
})


Da jetzt im IP-Paket die SourceIP- und die DestinationIP-Adresse gesetzt wurde, muß auch die Header Checksum neu berechnet werden. Genau das machen wir mit unserem FIXUP.

ip_udp_pkt_ab.fixup(fwt_ip.FIXUP_ALL)


Das gleiche müsssen wir nun mit dem UDP-Paket von Bchen nach Achen machen.

ip_udp_pkt_ba = fwt_ip.Packet(ip_packet.as_dict())
ip_udp_pkt_ba.set({
    fwt_ip.HEADER_SOURCEIP:	BCHEN,
    fwt_ip.HEADER_DESTIP:	ACHEN,
    fwt_ip.PAYLOAD: udp_pkt_ba.as_str()
})
ip_udp_pkt_ba.fixup(fwt_ip.FIXUP_ALL)


Zum Ausgeben der empfangenen Pakete brauchen wir eine print-Funktion (printResult). Sie prüft, ob an Achen oder Bchen ein Paket ankam und gibt es aus, andernfalls wenn gar kein Paket ankam, wird der Fehler Timeout ausgegeben.

def printResult (result):
    if result[0] is not None:
         print 'Received on A:\n%r\n' % result[0]
    if result[1] is not None:
         print 'Received on B:\n%r\n' % result[1]
    if result[0] is None and result[1] is None:
         print 'Timeout'


Wir schauen, ob die Option dry_run gleich 1 ist. Sollte sie 1 sein, werden lediglich die Pakete auf der Konsole ausgegeben.

if dry_run == 1:
    print 'dry_run: skip all transceive functions'
    print 'Would send IP packet a->b:\n%s\n' % repr(ip_udp_pkt_ab.as_dict())
    print 'Would send IP packet b->a:\n%s\n' % repr(ip_udp_pkt_ba.as_dict())


Ab diesem Punkt wird es ernst und die Pakete werden wirklich gesendet.

TODO

else:
    fwt_ip.set_coarse_filter_and_arp (fwt_ip.IFACE_A, [ ("10.000.0.2", "10.127.255.255")])
    fwt_ip.set_coarse_filter_and_arp (fwt_ip.IFACE_B, [ ("10.128.0.2", "10.255.255.255")])

Auch hier kommt eine der Zwei Variablen von oben ins Spiel, so daß bei response_only gleich 1 nur das UDP/IP-Paket von Bchen nach Achen gesendet wird. Mittels transceive-Funktion wird dann unser UDP/IP-Paket von Bchen nach Achen versendet. Das angekommene Paket wird in result an die erste Stelle gespeichert und ausgegeben. TODO:an welche stelle wird paket in result gespeichert?

    if response_only == 1:
         print 'Will send IP packet b->a:\n%r\n' % ip_udp_pkt_ba
         result = fwt_ip.transceive (
                        fwt_ip.IFACE_B, 
                        ip_udp_pkt_ba, 
                        filterA=FILTER_A, 
                        filterB=FILTER_B, 
                        timeoutA=500, 
                        timeoutB=0
                  )
         printResult (result)

Andernfalls wird erst das UDP/IP-Paket von Achen nach Bchen und dann zurück gesendet. Die Ergebnissse werden ebenfalls in result gespeichert.

    else:
         print 'Will send IP packet a->b:\n%r\n' % ip_udp_pkt_ab
         result = fwt_ip.transceive (
                        fwt_ip.IFACE_A, 
                        ip_udp_pkt_ab,  
                        filterA=FILTER_A, 
                        filterB=FILTER_B, 
                        timeoutA=0, 
                        timeoutB=500 
                  )
         printResult (result)
         if result[1] is not None:
              print 'Will send IP packet b->a:\n%r\n' % ip_udp_pkt_ba
              result = fwt_ip.transceive (
                             fwt_ip.IFACE_B, 
                             ip_udp_pkt_ba, 
                             filterA=FILTER_A, 
                             filterB=FILTER_B, 
                             timeoutA=500, 
                             timeoutB=0
                       )
              printResult (result)
         else:
              print 'Lost origin packet -- Skip sending answer'


kompletter Code von netdate.

transceive-Funktion

Die transceive-Funktion ist im Grunde das Herzstück unseres Testskripts. Es ist zuständig für das Versenden von Paketen von Source-IP zu Destination-IP. Um ein besseres Verständnis über die transceive-Funktion zu bekommen, sollten wir uns einige Fragen stellen:

Was macht die transceive-Funktion genau?

Welche Parameter werden von der transceive-Funktion erwartet?

Ein typischer Aufruf der transceive-Funktion wäre zum Beispiel:

result = fwt_ip.transceive (
               fwt_ip.IFACE_B, 
               ip_udp_pkt_ba, 
               filterA=FILTER_A, 
               filterB=FILTER_B, 
               timeoutA=500, 
               timeoutB=0
        )


Der erste Parameter definiert das Interface von wo aus das Paket gesendet wird, gefolgt vom UDP/IP-Paket(hier von Bchen zu Achen oder genreller gesagt von Source-IP zur Destination-IP). Der Dritte und vierte definiert die Filter für Achen und Bchen, gefolgt von den Timeout für Achen und Bchen.

Was wird von von der transceive-Funktion zurückgeliefert?

Nachdem transceive das Paket sendete, wird ein python-dictionary zurückgegeben, das die Werte von dem auf der anderen Seite empfangenen Paket enthält. In unserem netdate-Beispiel ist das erste empfangene Paket in result[1] gespeichert, so daß wir prüfen können, ob ein Paket angekommen ist.