Geekend 2019.01/Offline-Pong
Der Segmentation Vault hatte anscheinend am Geekend die Idee für einen Offline Ping via Postkarte statt Internet. Auch wir haben so eine Karte von ihnen erhalten, und die Antwort hat uns diese nette Notiz eingebracht:
Was war die Beschäftigung? Das hier:
Oben steht der Echo Request, und darunter unsere Antwort. Weil jede'r einfach nur PONG
, ICMP_ECHO_REPLY
, oder das ganze in einem ähnlichen Format darunter schreiben kann haben wir ein (hoffentlich) korrektes ICMP/IP-Paket zurück geschickt.
Und jetzt will ich versucht zu erklären wie das aufgebaut wird. Alle Werte sind üblicherweise als Hexadezimal zu interpretieren.
Der IP Header
Eine allgemeine Erklärung des IPv4-Headers findet sich netterweise sogar auf der deutschen Wikipedia.
Der Header selbst besteht aus
- Version
- Größe des Headers
- Type of Service (TOS)
- Gesamtgröße
- Identifikation
- Informationen für die Paket-Fragmentierung
- Time To Live (TTL)
- übertragenes Protokoll
- Prüfsumme
- Quelle
- Ziel
- optionalen Optionen und "Füllstoff" (Padding)
Der komplette Header bei diesem Beispiel ist
4500 0020 0000 0000 F801 E7D3 B060 8A73 5BDB 445A
Erste Zeile
4500 0020
Version
Das erste Nibble des ersten Bytes ist die Version, also 4
.
Header Größe
Die minimale Größe für den Header sind 20 Byte. Das ist auch unsere Größe, nachdem wir keine extra Optionen & Padding haben. Normalerweise wäre das eine Wert von 0x14
, im IP-Header wird hier aber das Vielfache von 32 Bit angegeben. Also 20 Byte * 8 Bit/Byte = 160 Bit / 32 Bit = 5, also 0x05
.
Type Of Service
Wir haben keine besonderen Anforderungen an Congestion Control, … also bleiben alle Flags auf 0
, und damit das ganze Feld 0x00
Gesamtgröße
Dies ist die komplette Größe des Paketes, inklusive übertragenen Inhalts. Also in unserem Fall die Größe des IP-Headers (20 Byte) plus die Menge an Daten im ICMP Paket (12 Byte) = 32 Byte oder 0x0020
.
Zweite Zeile
0000 0000
Identifikation
Nachdem wir nicht Fragmentieren brauchen wir keine eindeutige Identifikation, und setzen das Feld auf 0x0000
.
Flags und Fragment Offset
Nachdem wir nicht Fragmentieren brauchen wir keine Fragmentation Flags oder Offset, und setzen das Feld auf 0x0000
.
Dritte Zeile
F801 E7D3
Time To Live
Damit Pakete bei einer Router Loop oder anderen Netzwerkproblemen nicht ewig im Kreis geschickt wird gibt es eine Time To Live. Diese wird (bei IPv4) jede Sekunde bzw. bei jedem Hop um 1 verringert. Wenn das Paket bei 0 noch nicht angekommen ist wird es verworfen, und eine entsprechende Fehlermeldung zurück geschickt. traceroute
nutzt das übrigens um den Weg eines Pakets aufzuzeichnen, indem die Pakete mit steigender TTL abgeschickt, und die IPs der "TTL exceeded" Nachrichten ausgegeben, werden.
Bei uns sind das maximal 248 Hops, weil warum nicht.
Protokoll
Die Nummer des übertragenen Protokolls, wird von der IANA festgelegt.
Bei uns ist das ICMP, Protokollnummer 0x01
.
Prüfsumme
Der "lustige" Teil, weil die Regeln
- Nicht unbedingt sofort klar
- Aber trotzdem irrsinnig vernünftig
Es funktioniert so:
- Alle 16 Bit Werte im Header werden addiert. Die Prüfsumme wird dafür als 0 angenommen
- Addiert wird nach den Regeln des Einerkomplements, also jeder Überlauf über die 16 Bit wird auf der kleinsten Stelle addiert.
- Zum Schluss wird das Ergebnis noch mal invertiert. Dadurch soll die Summe der berechneten Prüfsumme (vor invertierung) plus Prüfsummenfeld gleich
0xFFFF
sein.
Warum ist das nicht sofort klar? Weil die Beschreibungen auf den meisten Seiten kein Beispiel dazu geben, sondern einfach annehmen dass das eh klar ist.
Warum ist das irrsinnig vernünftig? Aus 2 Gründen:
- Die Addition ist leicht & für CPUs mit minimalem Aufwand verbunden
- 2 Bytes vom Stack in ein 16 Bit-Register holen
- Zum aktuellen Wert addieren
- Carry Flag addieren
- Das alles
Headersize << 1
mal - Zum Schluss invertieren
- Ein Fehler in der Prüfsumme lässt sich durch eine weitere Addition plus Bitmasken-Vergleich finden
Beispiel
4500 0100 0101 0000 0000 Version, Headergröße, TOS + 0020 0000 0000 0010 0000 Gesamtgröße ================================ 4520 0100 0101 0010 0000 + 0000 0000 0000 0000 0000 Identifikation + 0000 0000 0000 0000 0000 Fragmentation Info + F801 1111 1000 0000 0001 TTL, Protokoll ================================ 13D21 1 0011 1101 0010 0001 - 10000 1 0000 0000 0000 0000 + 0001 0000 0000 0000 0001 ================================ 3D22 0011 1101 0010 0010 + 0000 0000 0000 0000 0000 "Prüfsumme" + B060 1011 0000 0110 0000 Quell-IP 1 ================================ ED82 1110 1101 1000 0010 + 8A73 1000 1010 0111 0011 Quell-IP 2 ================================ 177F5 1 0111 0111 1111 0101 - 10000 1 0000 0000 0000 0000 + 0001 0000 0000 0000 0001 ================================ 77F6 0111 0111 1111 0110 + 5BDB 0101 1011 1101 1011 Ziel-IP 1 ================================ D3D1 1101 0011 1101 0001 + 445A 0100 0100 0101 1010 Ziel-IP 2 ================================ 1182B 1 0001 1000 0010 1011 - 10000 1 0000 0000 0000 0000 + 0001 0000 0000 0000 0001 ================================ ^ 182C 0001 1000 0010 1100 ================================ E7D3 1110 0111 1101 0011
Vierte Zeile
B060 8A73
Quell-IP
Relativ einfach, die IP des absendenden Host, in dem Fall unser Web-Servers (176.96.138.115
), Octet für Octet konvertiert.
Fünfte Zeile
5BDB 445A
Ziel-IP
Wie bei der Quell-IP, nur diesmal die IP des Segmentation Vault Web-Servers (91.219.68.90
).
Das ICMP Paket
Auf für ICMP findet sich die Paket Beschreibung auf Wikipedia.
Das komplette ICMP ECHO REPLY
Paket besteht aus
- Typ
- Code
- Prüfsumme
- Daten, wobei hier eventuell weitere Informationen enthalten sein können (abhängig von Typ & Code)
und sieht bei uns so aus:
0000 8DA1 1CF2 0000 5CAC F8BF
Erste Zeile
0000 8DA1
Typ
Der Typ für ECHO REPLY
ist einfach eine 0x00
.
Code
Bei einem ECHO REPLY
gibt es keine zusätzlichen Codes, daher auch 0x00
.
Prüfsumme
Hier gilt wieder das gleiche wie bei der IPv4 Header Prüfsumme, nur eben für das ganze ICMP-Paket.
Zweite Zeile
1CF2 0000
Identifikation
Nachdem jeder Host theoretisch gleichzeitig mehrere Pings der gleichen Quelle beantworten könnte wird hier eine jeweils zum Prozess gehörende Identifikation eingetragen. Bei uns einfach ein Zufallswert.
Sequenz
Die Sequenz ist üblicherweise die Information der wie vielte Ping beantwortet wird, um eine Unterscheidung zu schaffen falls neuere Pakete ältere aufgrund unterschiedlichen Routings überholen. Weil wir hier die Antwort auf den ersten Request senden setzen wir das auf 0x00
Dritte Zeile
5CAC F8BF
Daten
Sollten die gleiche Daten wie im ECHO REQUEST
sein, sind hier aber Zufallsdaten. Manche Implementationen von Ping nutzen ein festes Muster für den Request.
Conclusio
Ja, es wäre einfacher gegangen, aber wer will das schon. Es war auf jeden Fall interessant & lehrreicht mal 2 Stunden lang mit Wikipedia, RFCs, Taschenrechner, Stift & Papier zu sitzen, und handschriftlich ein gültiges ICMP/IP Paket zu bauen. Vor allem wenn es dann auf der Gegenseite auf genug Interesse trifft um 5 Personen 30 Minuten zu beschäftigen!