Geekend 2019.01/Offline-Pong

Aus /usr/space Wiki
Version vom 16. April 2019, 15:57 Uhr von Pludikovsky (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „Der [https://segvault.space/ Segmentation Vault] hatte anscheinend am Geekend die Idee für einen [https://segvault.space/ping Offline Ping] via Postkarte stat…“)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springen Zur Suche springen

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:

Segvault Offline-Ping notice.png

Was war die Beschäftigung? Das hier:

Segvault Offline-Ping Postkarte.jpg

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

  1. Nicht unbedingt sofort klar
  2. 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:

  1. 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
  2. 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!