Das OSI-Modell wirklich verstehen — eine einzelne HTTP-Anfrage in ihre sieben Schichten zerlegen

· · OSI-Modell, TCP/IP, Netzwerke, Wireshark, Ethernet, TCP, HTTP, C#, .NET, Socket, Technische Beratung

Das OSI-Modell ist das Erste, was in jedem Einsteigerbuch zu Netzwerken auftaucht — und doch hört man immer wieder denselben Satz: „Ich kann die Namen der sieben Schichten aufsagen, aber ehrlich gesagt habe ich kein wirkliches Gefühl dafür.“ Man lernt Bitübertragungsschicht, Sicherungsschicht, Vermittlungsschicht … auswendig, ohne dass sich je eine Verbindung zwischen diesem siebenstufigen Diagramm und dem eigenen C#-Code oder der gerade vor einem liegenden Netzwerkstörung einstellt.

Auf diesem Blog haben wir bisher schon Artikel zum Verhalten von L4 (der Transportschicht) veröffentlicht — etwa zum Missverständnis, dass man über TCP in denselben Einheiten empfangen kann, in denen man gesendet hat und zur Ursache und Eingrenzung von Kommunikationsabbrüchen bei Industriekameras durch TCP-Neuübertragungen — aber einen Artikel, der das zugrunde liegende Konzept der „Schicht“ selbst zusammenfasst, gab es bisher nicht.

Der Ansatz dieses Artikels ist einfach. Wir bauen tatsächlich einen einzelnen Ethernet-Frame, der eine einzelne HTTP-GET-Anfrage trägt, und zerlegen ihn von außen nach innen. Die sieben Schichten sind nicht nur etwas in einem konzeptionellen Diagramm — sie existieren physisch, verschachtelt in den 171 Bytes, die über das Netzwerk fließen. Sobald Sie das mit eigenen Augen im Hexdump und in Wireshark sehen, hört das OSI-Modell auf, etwas zu sein, das man auswendig lernt.

Der in diesem Artikel gezeigte Code ist als vollständiges, build- und lauffähiges Beispielpaket auf GitHub veröffentlicht (eine Bibliothek zum Zusammenbauen und Zerlegen von Frames, der Export einer pcap-Datei, die sich in Wireshark öffnen lässt, sowie Unit-Tests).

osi-model-packet-anatomy - komurasoft-blog-samples (GitHub)

1. Das Wichtigste zuerst

  • Das OSI-Modell ist ein „Vokabular“, keine „Implementierung“. Was im realen Internet tatsächlich läuft, ist die TCP/IP-Protokollsuite (effektiv vier Schichten); die eigentlichen 7-Schichten-Protokolle von OSI haben das Rennen um die Verbreitung verloren und werden nicht genutzt. Überlebt hat das Modell als gemeinsame Sprache — Formulierungen wie „lass uns das auf L2 eingrenzen“ oder „das ist ein L7-Problem“.12
  • Die sieben Schichten sind physisch als Byte-Folge ineinander verschachtelt. Bei einem Frame, der ein HTTP GET trägt, sind die ersten 14 Bytes der Ethernet-Header (L2), die nächsten 20 Bytes der IPv4-Header (L3), die nächsten 20 Bytes der TCP-Header (L4) und der Rest der HTTP-Text (L7). Dass der Header jeder Schicht unmittelbar dort beginnt, wo die vorherige endet, lässt sich direkt im Hexdump dieses Artikels und in Wireshark nachvollziehen.
  • Was Ihr C#-Code direkt schreibt, ist ausschließlich die Byte-Folge von L7. An Socket.Send wird nur Anwendungsdaten übergeben; die TCP- und IP-Header fügt der Protokoll-Stack des Betriebssystems hinzu, den Ethernet-Header und das elektrische Signal die NIC. Genau deshalb ist die Wahl zwischen HttpClient, SslStream und Socket eine Entscheidung darüber, „welche Schicht und alles darunter man dem Betriebssystem überlässt“.3
  • L5 (die Sitzungsschicht) und L6 (die Darstellungsschicht) existieren im realen Stack nicht als eigenständige Schichten. TLS und Zeichenkodierung übernehmen einen Teil dieser Rolle, gelten im TCP/IP-Modell aber gemeinsam als Anwendungsschicht. Wenn Sie „nicht verstehen, was L6 sein soll“, liegt das nicht an Ihnen — genau hier klaffen Modell und Realität auseinander.2
  • Wo sich das OSI-Modell in der Praxis wirklich auszahlt, ist bei der Fehlersuche und im Gespräch. Eingrenzungen mit dem Vokabular der Schichten — etwa „der Ping geht durch, aber HTTP schlägt fehl, also lebt L3 noch und man sollte L4 oder höher verdächtigen“ — werden zu einer gemeinsamen Sprache zwischen Netzwerk-, Infrastruktur- und App-Entwicklern.

2. Warum das OSI-Modell zum reinen Auswendiglernen wird

Die meisten Erklärungen beginnen mit einer solchen Tabelle.

Schicht Name Beschreibung
L7 Anwendungsschicht Stellt Anwendungen Kommunikationsdienste bereit
L6 Darstellungsschicht Wandelt das Darstellungsformat der Daten um
L5 Sitzungsschicht Verwaltet Beginn und Ende einer Kommunikation
L4 Transportschicht Stellt eine zuverlässige Datenübertragung bereit
L3 Vermittlungsschicht Übernimmt Routenwahl und Adressierung
L2 Sicherungsschicht Überträgt Daten zwischen benachbarten Knoten
L1 Bitübertragungsschicht Wandelt Bits in elektrische Signale um

Diese Tabelle ist korrekt, aber da jede Zeile in abstrakten Begriffen formuliert ist, entsteht beim Lesen kein konkretes Bild. Sagt man jemandem „wandelt das Darstellungsformat der Daten um“, wird niemand sagen können, welche Zeile im eigenen Code damit gemeint ist.

Es gibt noch eine historische Tatsache, die man offen ansprechen sollte. Das OSI-Modell ist ein von der ISO (Internationale Organisation für Normung) und der ITU-T festgelegter Standard (ISO/IEC 7498-1, ITU-T X.200) und war ursprünglich als groß angelegtes Konzept gedacht, zu dem passend für jede der sieben Schichten ein eigenes OSI-Protokoll gehören sollte.1 Im Verbreitungswettbewerb der 1990er-Jahre setzte sich jedoch TCP/IP, das bereits funktionierende Implementierungen vorweisen konnte, faktisch als Standard durch, und die OSI-Protokolle selbst wurden am Ende kaum genutzt. RFC 1122, das die Grundlage des Internets festlegt, erklärt die Welt anhand von effektiv vier Schichten — Verbindungsschicht, Internetschicht (IP), Transportschicht und Anwendungsschicht — und kennt keine eigenständigen Schichten, die L5 oder L6 entsprechen.2

Mit anderen Worten: Der Grund, warum wir das OSI-Modell heute noch lernen, ist nicht, „weil so implementiert wird“, sondern um das Werkzeug „in Schichten denken“ und ein gemeinsames Vokabular für die Fehlersuche zu gewinnen. Geht man von dieser Prämisse aus, muss man sich nicht mehr darüber den Kopf zerbrechen, dass sich „das reale Gegenstück zu L5 und L6 nicht finden lässt“, und alles wird auf einen Schlag klarer.

3. Zur Sache: Eine einzelne HTTP-Anfrage zerlegen

Genug der Vorrede — schauen wir uns die Sache konkret an. Der folgende Hexdump zeigt einen Ethernet-Frame (insgesamt 171 Bytes), der eine einzelne HTTP-Anfrage, GET /index.html HTTP/1.1, trägt. Er wurde vom eingangs vorgestellten Beispielcode zusammengebaut, und da sowohl die IPv4-Header-Prüfsumme als auch die TCP-Prüfsumme korrekt berechnet sind, zeigt Wireshark ihn beim Einlesen als ganz normales Paket an.

0000  02 00 00 00 00 01 02 00  00 00 00 02 08 00 45 00  ..............E.
0010  00 9d 12 34 40 00 40 06  3b 99 c0 00 02 0a c6 33  ...4@.@.;......3
0020  64 50 cb 84 00 50 00 00  03 e8 00 00 07 d0 50 18  dP...P........P.
0030  ff ff a3 05 00 00 47 45  54 20 2f 69 6e 64 65 78  ......GET /index
0040  2e 68 74 6d 6c 20 48 54  54 50 2f 31 2e 31 0d 0a  .html HTTP/1.1..
0050  48 6f 73 74 3a 20 65 78  61 6d 70 6c 65 2e 63 6f  Host: example.co
0060  6d 0d 0a 55 73 65 72 2d  41 67 65 6e 74 3a 20 4b  m..User-Agent: K
0070  6f 6d 75 72 61 53 6f 66  74 44 65 6d 6f 2f 31 2e  omuraSoftDemo/1.
0080  30 0d 0a 41 63 63 65 70  74 3a 20 74 65 78 74 2f  0..Accept: text/
0090  68 74 6d 6c 0d 0a 43 6f  6e 6e 65 63 74 69 6f 6e  html..Connection
00a0  3a 20 63 6c 6f 73 65 0d  0a 0d 0a                 : close....

Betrachtet man die ASCII-Darstellung auf der rechten Seite, sieht man, dass ab einer bestimmten Stelle (Offset 0x36) für Menschen lesbarer Text beginnt: GET /index.html HTTP/1.1. Was aber sind die 54 Bytes davor? Füttert man sie in den Zerleger (Dissector) des Beispiels, meldet dieser Folgendes.

[ L2 Ethernet II | offset   0 |  14 bytes | dst=02:00:00:00:00:01 src=02:00:00:00:00:02 type=0x0800 (IPv4)
  [ L3 IPv4        | offset  14 |  20 bytes | 192.0.2.10 -> 198.51.100.80 TTL=64 proto=6 (TCP) checksum=OK
    [ L4 TCP         | offset  34 |  20 bytes | 52100 -> 80 [Psh, Ack] seq=1000 win=65535
      [ L7 HTTP        | offset  54 | 117 bytes | GET /index.html HTTP/1.1

Das ist die Darstellung, die ich in diesem Artikel am meisten sehen lassen möchte. Es gibt drei zentrale Punkte.

  1. Jede Schicht beginnt unmittelbar dort, wo die vorherige endet. Der Ethernet-Header liegt bei Byte 0–13, der IPv4-Header bei Byte 14–33, der TCP-Header bei Byte 34–53, und HTTP beginnt bei Byte 54. Eine Schicht ist keine begriffliche Kategorie — man kann sie als Byte-Bereich ab dem Frame-Anfang konkret benennen.
  2. Eine innere Schicht ist für die äußere Schicht nichts weiter als deren „Nutzlast“ (Fracht). Aus Sicht von Ethernet ist alles ab IPv4 einfach nur Fracht — ob der Inhalt TCP ist oder nicht, interessiert es nicht. Aus Sicht von IP ist alles ab TCP die Fracht, aus Sicht von TCP ist HTTP die Fracht. Jede Schicht liest nur ihren eigenen Header und reicht die Fracht unangetastet nach oben weiter. Genau diese Struktur des „Nicht-Kümmerns“ ist Kapselung.
  3. L1 (die Bitübertragungsschicht) sowie L5 und L6 tauchen in dieser Darstellung nicht auf. L1 hat die Aufgabe, diese Byte-Folge in elektrische Signale, Licht oder Funkwellen umzuwandeln, taucht also im Dump nicht auf, und L5/L6 haben, wie im vorigen Kapitel erklärt, bei unverschlüsseltem HTTP/1.1 keine eigenständige Entsprechung. „Von sieben Schichten tauchen nur vier im Dump auf“ — das ist die reale Situation.

Noch einmal: Das liegt nicht daran, dass HTTP etwas Besonderes wäre. Ob Datenbankverbindung, gRPC oder GigE Vision bei einer Industriekamera — solange es sich um TCP/IP-Kommunikation über Ethernet handelt, hat jedes einzelne Paket diese exakt gleiche verschachtelte Struktur. Es ändert sich nur der Inhalt der L7-Fracht.

4. L2, die Sicherungsschicht — Zustellung „zum Nachbarn“

Gehen wir die Ergebnisse der Zerlegung von außen nach innen durch. Die ersten 14 Bytes sind der Ethernet-II-Header.

02 00 00 00 00 01   Ziel-MAC-Adresse (6 Bytes)
02 00 00 00 00 02   Quell-MAC-Adresse (6 Bytes)
08 00               EtherType = 0x0800 (Fracht ist IPv4)

Die Aufgabe von L2 besteht darin, „innerhalb desselben Netzwerksegments zum benachbarten Knoten zuzustellen“. Zur Angabe des Ziels wird die MAC-Adresse verwendet. Eine MAC-Adresse ist eine der NIC zugewiesene Kennung und erreicht grundsätzlich nicht über einen Router hinweg den eigentlichen Kommunikationspartner. Bei einem Frame, der von einem Büro-PC an einen Webserver gesendet wird, ist die Ziel-MAC-Adresse nicht die des Webservers, sondern die des Standardgateways (Routers).

An dieser Stelle fragen sich viele mindestens einmal: „Wenn es schon eine IP-Adresse gibt, wozu braucht man dann noch eine MAC-Adresse?“ Die Antwort liegt in der Aufteilung der Verantwortung zwischen den Schichten. Die IP-Adresse ist die Adresse, die auf das „endgültige Ziel“ zeigt, die MAC-Adresse die Kennzeichnung dessen, der „den nächsten einzelnen Abschnitt transportiert“. Um es mit einem Paketdienst zu vergleichen: Die IP-Adresse ist die Zieladresse auf dem Versandschein, die MAC-Adresse der Name des „nächsten Verteilzentrums“. Der Versandschein (L3) ändert sich bis zum Schluss nicht, die Kennzeichnung (L2) wird aber auf jedem Abschnitt neu angebracht. ARP ist das, was anhand einer IP-Adresse die MAC-Adresse eines benachbarten Knotens ermittelt, und mit dem Befehl arp -a lässt sich die vom PC gespeicherte Zuordnungstabelle einsehen.

Auf Hardware-Ebene ist es der Switch (Switching-Hub), der anhand von L2 weiterleitet. Ein Switch wählt den Ausgangsport ausschließlich anhand der Ziel-MAC-Adresse und schaut sich die als Fracht mitgeführte IP-Adresse gar nicht erst an.

5. L3, die Vermittlungsschicht — Zustellung bis ans Ende der Welt

Die nächsten 20 Bytes sind der IPv4-Header. Hier die wichtigsten Felder im Überblick.

45          Version = 4, Header-Länge = 5 Worte (20 Bytes)
00 9d       Gesamtlänge: 157 Bytes (IP-Header + TCP + HTTP; die 14 Bytes von Ethernet sind nicht enthalten)
40 06       TTL = 64, Protokoll = 6 (Fracht ist TCP)
3b 99       Header-Prüfsumme
c0 00 02 0a Quell-IP    192.0.2.10
c6 33 64 50 Ziel-IP     198.51.100.80

Die Aufgabe von L3 besteht darin, „bis zum endgültigen Ziel-Host zuzustellen, ganz gleich, wie viele Router dazwischenliegen“. Während L2 nur einen einzigen Abschnitt (einen Hop) transportiert, bleibt die Ziel-IP-Adresse von L3 vom Anfang bis zum Ende der Kommunikation unverändert. Ein Router betrachtet die Ziel-IP-Adresse eines empfangenen Pakets, entscheidet, „an welchen Router als Nächstes übergeben wird“, ersetzt den L2-Header und sendet das Paket weiter.

TTL (Time To Live) ist das Feld, das diese Bewegung „über Router hinweg“ sichtbar macht. Es wird bei jedem überquerten Router um 1 verringert, und ein Paket, dessen TTL 0 erreicht, wird verworfen, woraufhin ein Fehler an den Absender zurückgemeldet wird. tracert (der Windows-Befehl zur Routenermittlung) sendet bewusst Pakete mit TTL = 1, 2, 3 … und deckt so nach und nach die Router entlang des Weges auf. Jede einzelne Zeile in der Ausgabe von tracert ist also das reale Abbild eines „L3-Hops“.

6. L4, die Transportschicht — Welcher Prozess bekommt es

Die nächsten 20 Bytes sind der TCP-Header.

cb 84       Quellport 52100
00 50       Zielport 80
00 00 03 e8 Sequenznummer
00 00 07 d0 Bestätigungsnummer
50 18       Header-Länge = 5 Worte, Flags [PSH, ACK]
ff ff       Fenstergröße
a3 05       Prüfsumme

Bis L3 ist das Paket beim Ziel-„Host (der Maschine)“ angekommen. Auf dieser Maschine kommunizieren aber möglicherweise ein Webserver, eine Datenbank und zahlreiche weitere Prozesse gleichzeitig. Eine der Aufgaben von L4 besteht darin, anhand der Portnummer zu entscheiden, „an welchen Prozess (welchen Socket) übergeben wird“. Zielport 80 ist eine Konvention — „die Nummer, auf der ein HTTP-Server lauscht“ — und das Betriebssystem schaut sich diese Nummer an und legt die Daten im Empfangspuffer des entsprechenden Prozesses ab.

Die andere große Aufgabe von TCP ist es, über Sequenznummern, Bestätigungen, Neuübertragung und Flusskontrolle einen „zuverlässigen Byte-Strom“ bereitzustellen.4 Dieses Verhalten und der Umgang damit sind jeweils tief genug für einen eigenen Artikel, weshalb ich hier auf den Artikel zu TCP-Nachrichtengrenzen und den Artikel zu TCP-Neuübertragungen verweise. Nehmen Sie an dieser Stelle einfach das Gefühl mit: „Oberhalb von L4 sieht nichts mehr wie ein Netzwerk aus — es wirkt wie eine Pipe, die einen Prozess direkt mit einem anderen verbindet.“

Eine interessante Tatsache zeigt die Kluft zwischen dem Ideal des Modells und der Realität. Die TCP-Prüfsumme wird nicht allein über das TCP-Segment berechnet, sondern ist so definiert, dass ihr ein „Pseudo-Header“ mit L3-Informationen (Quell- und Ziel-IP-Adresse) vorangestellt wird.4 Wären die Schichten sauber voneinander unabhängig, dürften L3-Adressen eigentlich nicht in eine L4-Berechnung einfließen. Ein gutes Beispiel dafür, dass das OSI-Modell letztlich nur eine Landkarte zur Orientierung ist, während reale Protokolle Schichtgrenzen überschreiten, wo es zweckmäßig ist.

7. L5 und L6 — Wo Modell und Realität auseinanderklaffen

In unserer Zerlegungsdarstellung fehlten L5 (die Sitzungsschicht) und L6 (die Darstellungsschicht). Das ist der Hauptgrund dafür, dass sich viele mit OSI schwertun, deshalb sei es hier klar gesagt.

L5 und L6 existieren im realen TCP/IP-Stack nicht als eigenständige Schichten. Im Internetmodell von RFC 1122 ist alles oberhalb von TCP schlicht „Anwendungsschicht“.2 Die vom OSI-Modell vorgesehenen Rollen sind in der Realität wie folgt verstreut und andernorts aufgegangen.

Was OSI vorsah Beispiele, wo es real aufgegangen ist
L5: Aufbau und Verwaltung eines Dialogs (einer Sitzung) TLS-Sitzung/-Handshake, HTTP-Cookies/-Tokens, anwendungseigene Login-Verwaltung
L6: Umwandlung und Verschlüsselung der Datendarstellung TLS-Verschlüsselung, Zeichenkodierung (UTF-8), Serialisierungsformate wie JSON

Bei HTTPS zum Beispiel wird TLS oberhalb von TCP (L4) eingeschoben, und HTTP fließt darin. „Zu welcher Schicht gehört TLS?“ ist die Art Frage, die man gerne zur Prüfungsfrage machen möchte, aber es hat keinen praktischen Wert, sich auf eine einzige richtige Antwort festzulegen. Wichtiger als sofort „L6“ antworten zu können ist, erklären zu können, dass TLS oberhalb von L4 und unterhalb von L7 liegt und sowohl Sitzungsverwaltung (eine L5-artige Rolle) als auch Verschlüsselung (eine L6-artige Rolle) übernimmt.

Im .NET-Code zeigt sich dieser Zusammenhang direkt darin, wie die Klassen ineinander verschachtelt werden. Umhüllt man den über TcpClient.GetStream() erhaltenen L4-Stream mit SslStream, wird der mit Write geschriebene Klartext zu TLS-Records verschlüsselt, bevor er überhaupt bei TCP ankommt.5

using var client = new TcpClient("example.com", 443);
// Den L4-Byte-Stream mit TLS (entspricht etwa L5/L6) umhüllen
using var ssl = new SslStream(client.GetStream());
await ssl.AuthenticateAsClientAsync("example.com");
// Hier wird der Klartext von L7 (HTTP) geschrieben. Die Verschlüsselung übernimmt SslStream
await ssl.WriteAsync(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n..."));

Dieses Muster, einen Stream mit einem Stream zu umhüllen, ist die Code-Entsprechung der Kapselung. Betrachtet man in Wireshark die Kommunikation auf Port 443, erscheint die TCP-Nutzlast nur noch als undurchsichtiger Block namens Application Data — ein Beleg aus der Gegenrichtung dafür, dass „L7 in eine L6-artige Hülle verpackt wurde“.

8. Welche Schicht berührt Ihr C#-Code eigentlich?

Ordnen wir das bisher Gesagte den Werkzeugen zu, die ein Windows-App-Entwickler tatsächlich verwendet.

Schicht Reales Beispiel Beispiel .NET-API Wer fügt den Header hinzu
L7 Anwendung HTTP, gRPC, eigenes Protokoll HttpClient, Grpc.Net.Client, selbst gebautes Nachrichtenformat Ihr Code / eine Bibliothek
(entspricht etwa L5/L6) TLS, Serialisierung, Zeichenkodierung SslStream, JsonSerializer, Encoding Eine Bibliothek
L4 Transport TCP, UDP Socket, TcpClient, UdpClient (Header-Erzeugung durch das Betriebssystem) Der Protokoll-Stack des Betriebssystems
L3 Vermittlung IP, Routing, ICMP Ping, NetworkInterface (nur lesend) Der Protokoll-Stack des Betriebssystems
L2 Sicherung Ethernet, Wi-Fi, ARP (von einer gewöhnlichen App normalerweise nicht direkt berührt) NIC-Treiber / NIC
L1 Bitübertragung Elektrisches Signal, Licht, Funkwellen NIC / Kabel / Luftschnittstelle

Aus dieser Tabelle lassen sich zwei Dinge ablesen.

Erstens ist die Wahl der API eine Entscheidung darüber, „welche Schicht und alles darunter man abgibt“. Mit HttpClient können Sie sogar den Aufbau von L7 delegieren; mit Socket müssen Sie L7 komplett selbst schreiben. Umgekehrt greift eine gewöhnliche Windows-App so gut wie nie direkt auf L3 oder darunter zu (Raw Sockets erfordern Administratorrechte und unterliegen starken Einschränkungen).

Zweitens übergeben Sie an Socket.Send „nur“ die Byte-Folge von L7 — nichts weiter. Teil 2 des Beispiels ist eine Demo, die tatsächlich eine HTTP-Anfrage über Loopback sendet; die Anwendung stellt dabei lediglich 60 Bytes HTTP-Text bereit, während die in Kapitel 3 gesehenen 54 Bytes an Headern von Betriebssystem und NIC hinzugefügt werden. Der Netzwerkcode Ihrer App erledigt in Wirklichkeit nur die Aufgabe, die innerste Fracht von sieben Schichten zu erzeugen — das ist die genaue Position Ihres Codes im OSI-Modell.

using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await socket.ConnectAsync(IPAddress.Loopback, port);

byte[] request = Encoding.ASCII.GetBytes(
    $"GET / HTTP/1.1\r\nHost: localhost:{port}\r\nConnection: close\r\n\r\n");

// Übergeben wird nur die Byte-Folge von L7. Die TCP/IP/Ethernet-Header
// werden jenseits dieses Aufrufs (von OS und NIC) hinzugefügt
int sent = await socket.SendAsync(request);

9. Selbst ausprobieren — einen Frame zusammenbauen und in Wireshark öffnen

Damit aus „gelesen und verstanden“ ein „gesehen und verstanden“ wird, zeige ich Ihnen, wie Sie den Beispielcode ausführen.

Im Zentrum des Beispiels steht SampleFrameBuilder, der den Frame aus Kapitel 3 exakt in der Reihenfolge zusammenbaut, in der er von L7 nach L2 umhüllt wird. Was beim Senden auf jeder Schicht passiert, spiegelt sich direkt in der Reihenfolge der Methoden wider.

public static byte[] BuildHttpGetFrame()
{
    // L7: Die Anwendung übergibt an Send „nur“ diese Byte-Folge
    byte[] http = Encoding.ASCII.GetBytes(HttpRequest);

    // L4: Der Protokoll-Stack des Betriebssystems stellt den TCP-Header voran
    byte[] tcp = WrapInTcp(http);

    // L3: Zusätzlich wird der IPv4-Header vorangestellt
    byte[] ip = WrapInIpv4(tcp);

    // L2: Kurz vor dem NIC-Treiber wird der Ethernet-Header vorangestellt
    return WrapInEthernet(ip);
}

Führt man die Demo aus, wird zusätzlich zur Anzeige von Hexdump und verschachtelter Ansicht dieser Frame als pcap-Datei geschrieben.

dotnet run --project samples/Demo

Öffnen Sie die erzeugte Datei sample-http-get.pcap in Wireshark.6 Im mittleren Bereich erscheinen vier Zeilen untereinander — Ethernet II / Internet Protocol Version 4 / Transmission Control Protocol / Hypertext Transfer Protocol — und klickt man eine davon an, wird im Hexdump darunter genau der zugehörige Byte-Bereich hervorgehoben. Ein Industriestandard-Tool zeigt Ihnen also genau dasselbe wie die verschachtelte Ansicht aus Kapitel 3. Sobald Sie das bestätigt haben, geht es weiter mit echtem Datenverkehr: Starten Sie eine Aufzeichnung in Wireshark, setzen Sie den Filter etwa auf tcp.port == 443, öffnen Sie im Browser irgendeine Website, und Sie können bestätigen, dass jede echte Kommunikation aus genau dieser Verschachtelung besteht.

Auch in der Implementierung des Zerlegers (PacketDissector) steckt eine praxisrelevante Lektion. Beim Herausschneiden der IPv4-Fracht muss man sich zum Beispiel auf das TotalLength-Feld des Headers verlassen, statt einfach den gesamten Rest des Empfangspuffers zu nehmen. Der Grund: Ethernet hat eine Mindest-Frame-Länge (60 Bytes), und an kurze Pakete wird am Ende bedeutungsloses Padding angehängt. „Ein Anliegen der äußeren Schicht (Padding) mithilfe der Längenangabe der inneren Schicht entfernen“ — auch das ist ein Beispiel dafür, wie sich die Aufteilung der Verantwortung zwischen den Schichten in der Implementierung niederschlägt, und es wird über einen Unit-Test abgesichert.

10. Das OSI-Modell in der Praxis nutzen — Eingrenzung mit dem Vokabular der Schichten

Eingangs habe ich geschrieben, dass „OSI als Vokabular überlebt hat“. Hier sind zwei konkrete Situationen, in denen sich dieses Vokabular tatsächlich bewährt.

Fehlersuche. Übersetzt man eine Meldung wie „ich komme nicht zum Server durch“ in das Vokabular der Schichten, lässt sie sich systematisch eingrenzen. Der Standardansatz ist, von unten nach oben zu prüfen.

Prüfung Verwendetes Mittel Schicht, deren Funktionsfähigkeit bestätigt wird
Verbindungsanzeige / Wi-Fi-Status Sichtprüfung L1–L2
Ping zum Gateway im selben Segment ping 192.168.x.1 L3 in der eigenen Umgebung
Ping zum Ziel-Host ping <Ziel> L3 entlang des gesamten Pfads
TCP-Verbindung zum Zielport Test-NetConnection <Ziel> -Port 443 L4 (plus etwaige Firewall unterwegs)
HTTP-Anfrage senden curl -v oder die Anwendung selbst L7 (plus TLS)

Wenn zum Beispiel „der Ping durchgeht, aber Test-NetConnection fehlschlägt“, ist alles bis L3 in Ordnung, und der Verdacht verengt sich auf etwas, das den L4-Port blockiert (ein gestoppter Dienst, eine Firewall, eine falsch konfigurierte Portnummer). Wenn „die TCP-Verbindung zustande kommt, HTTP aber 400 zurückliefert“, liegt das Problem nicht am Netzwerk, sondern an L7 (dem Inhalt der Anfrage). Statt wahllos Kabel zu ziehen und wieder einzustecken, Schicht für Schicht feststellen, bis wohin noch alles funktioniert — so wird das OSI-Modell in der Praxis eingesetzt.

Gemeinsame Sprache für Gespräche. Formulierungen wie „das sieht nach einem L2-Problem aus, könntest du dir den Switch-Port ansehen“ oder „das ist eine L7-Sache, das geht ans App-Team“ funktionieren präzise zwischen Netzwerkverantwortlichen, Infrastruktur-Teams und App-Entwicklern. Die Schichtnummer ist ein branchenweit geteiltes Koordinatensystem, um einen Verantwortungsbereich knapp zu benennen.

11. Verbreitete Missverständnisse richtigstellen

Zum Schluss stellen wir häufig anzutreffende Missverständnisse rund um das OSI-Modell richtig.

  • „Das Internet läuft auf den sieben Schichten von OSI.“ — Tut es nicht. Implementiert ist TCP/IP (effektiv vier Schichten); OSI ist ein Referenzmodell für Erklärung und Gespräche.2
  • „Die vier Schichten von TCP/IP entsprechen sauber den sieben Schichten von OSI.“ — Die Zuordnung von L5 bis L7 ist von Natur aus unscharf. Man sieht oft Tabellen mit „TCP/IP-Anwendungsschicht = OSI L5 + L6 + L7“, aber wie in Kapitel 7 gezeigt, sind die Rollen von L5 und L6 in der Realität auf TLS und Serialisierungsformate verstreut.
  • „TLS ist ein Protokoll der Schicht 6 (oder Schicht 5).“ — Es hat keinen Sinn, es auf eine einzige Schicht festzulegen. Die zutreffende Beschreibung lautet: Es liegt oberhalb von L4, unterhalb von L7, und kombiniert Rollen, die L5 und L6 entsprechen.
  • „Am Portnummer erkennt man das Protokoll.“ — Port 80 bedeutet nicht zwingend HTTP. Eine Portnummer ist eine Konvention; was tatsächlich fließt, weiß man erst, wenn man sich die Nutzlast ansieht. Deshalb erkennt der Zerleger im Beispiel HTTP nicht anhand der Portnummer, sondern anhand der führenden Zeichen des Inhalts.
  • „Switches sind L2-, Router sind L3-Geräte.“ — Als Ausgangspunkt ist das richtig, aber in der Realität gibt es ganz normal Geräte, die mehrere Schichten überspannen: L3-Switches, Load Balancer, die auf L4-Basis verteilen, oder WAFs, die L7 inspizieren. Genauer erfasst man das mit der Frage: „Bis zu welcher Schicht liest dieses Gerät eigentlich?“

12. Zusammenfassung

  • Das OSI-Modell ist keine Implementierung, sondern ein Vokabular, um in Schichten zu denken. In der Realität läuft TCP/IP (effektiv vier Schichten)
  • Die sieben Schichten sind kein konzeptionelles Diagramm, sondern physisch in der Byte-Folge eines einzelnen Frames verschachtelt (L2: 0–13, L3: 14–33, L4: 34–53, L7: ab 54)
  • Jede Schicht liest nur ihren eigenen Header und kümmert sich nicht um die Fracht (die inneren Schichten) — das ist Kapselung
  • L5 und L6 existieren im realen Stack nicht eigenständig; TLS und Kodierung haben ihre Rollen übernommen
  • Ihr C#-Code schreibt nur die Byte-Folge von L7. Die TCP/IP-Header fügt das Betriebssystem hinzu, Ethernet die NIC
  • In der Praxis nützlich ist es zur Fehlersuche, indem man Schicht für Schicht von unten nach oben die Funktionsfähigkeit prüft, sowie als gemeinsame Sprache zwischen Teams
  • Baut man mit dem Beispielcode einen Frame zusammen und öffnet ihn in Wireshark, lässt sich alles in diesem Artikel mit eigenen Augen nachvollziehen

Verwandte Artikel

Verwandte Beratungsbereiche

Die KomuraSoft LLC (合同会社小村ソフト) übernimmt das Design und die Implementierung von Windows-Anwendungen, die über TCP/IP kommunizieren, die Ursachenanalyse bei Kommunikationsproblemen mit Industriegeräten (“bricht gelegentlich ab”, “wird langsamer”) sowie die Unterstützung bei der Fehlersuche mithilfe von Paketmitschnitten.

Referenzen

  1. ITU-T, X.200 : Information technology - Open Systems Interconnection - Basic Reference Model: The basic model. Die Originalquelle des OSI-Basisreferenzmodells (inhaltsgleich mit ISO/IEC 7498-1). Zur Definition jeder der sieben Schichten.  2

  2. IETF, RFC 1122 - Requirements for Internet Hosts – Communication Layers. Die RFC, die die von Internet-Hosts zu implementierenden Anforderungen festlegt. Zur Gliederung der Protokollsuite in vier Schichten: Verbindungsschicht, IP-Schicht, Transportschicht und Anwendungsschicht.  2 3 4 5

  3. Microsoft Learn, Socket Class (System.Net.Sockets). Die Socket-API in .NET. Dazu, dass die Anwendung die Nutzlast übergibt und die Erzeugung der Protokoll-Header vom Protokoll-Stack des Betriebssystems übernommen wird. 

  4. IETF, RFC 9293 - Transmission Control Protocol (TCP). Die aktuelle TCP-Spezifikation. Zur Bereitstellung von Zuverlässigkeit durch Sequenznummern und Bestätigungen sowie zur Verwendung eines Pseudo-Headers mit IP-Adressen bei der Prüfsummenberechnung.  2

  5. Microsoft Learn, SslStream Class (System.Net.Security). Zur Klasse, die einen bestehenden Stream (in der Regel TCPs NetworkStream) umhüllt und Verschlüsselung sowie Authentifizierung über TLS bereitstellt. 

  6. Wireshark Foundation, Wireshark User’s Guide. Zum Öffnen von Aufzeichnungsdateien, zur zusammenhängenden Anzeige von Paketdetail- und Byte-Folge-Pane sowie zur Verwendung von Anzeigefiltern. 

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Werden die sieben Schichten des OSI-Modells im realen Internet tatsächlich verwendet?
Nein. Was im realen Internet tatsächlich läuft, ist die TCP/IP-Protokollsuite (effektiv vier Schichten); die eigentlichen 7-Schichten-Protokolle von OSI verloren in den 1990er-Jahren das Rennen um die Verbreitung und wurden am Ende kaum genutzt. RFC 1122, das die Grundlage des Internets legte, erklärt die Welt anhand von vier Schichten: der Verbindungsschicht, der IP-Schicht, der Transportschicht und der Anwendungsschicht. Überlebt hat das Modell als gemeinsames Vokabular für Fehlersuche und Gespräche — Formulierungen wie „lass uns das auf L2 eingrenzen“ oder „das ist ein L7-Problem“.
Wo existieren die Sitzungsschicht (L5) und die Darstellungsschicht (L6) eigentlich wirklich?
Sie existieren im realen TCP/IP-Stack nicht als eigenständige Schichten. Die vom OSI-Modell vorgesehenen Rollen sind in der Praxis verstreut und anderswo aufgegangen: L5 (Aufbau und Verwaltung eines Dialogs) entspricht dem TLS-Handshake oder HTTP-Cookies und -Tokens, und L6 (Umwandlung und Verschlüsselung der Datendarstellung) entspricht der TLS-Verschlüsselung, der Zeichenkodierung und Serialisierungsformaten wie JSON. Es hat keinen praktischen Wert, TLS auf eine einzige Schicht festzulegen — wichtiger ist, erklären zu können, dass es oberhalb von L4 und unterhalb von L7 liegt und Rollen kombiniert, die L5 und L6 entsprechen.
Wenn es schon eine IP-Adresse gibt, wozu braucht man dann noch eine MAC-Adresse?
Weil die Schichten die Verantwortung unterschiedlich aufteilen. Die IP-Adresse (L3) ist die Adresse, die auf das endgültige Ziel zeigt, und sie ändert sich vom Anfang bis zum Ende der Kommunikation nicht. Die MAC-Adresse (L2) ist die Kennzeichnung dessen, der den nächsten einzelnen Abschnitt transportiert, und sie wird bei jedem Überqueren eines Routers ausgetauscht. Ein Frame, der von einem Büro-PC an einen Webserver gesendet wird, hat als Ziel-MAC-Adresse nicht den Webserver, sondern das Standardgateway (den Router). ARP ist das, was die MAC-Adresse eines benachbarten Knotens anhand einer IP-Adresse ermittelt, und die Zuordnungstabelle lässt sich mit dem Befehl arp -a einsehen.
Wofür ist das OSI-Modell in der Praxis wirklich nützlich?
Für die Fehlersuche und die Kommunikation zwischen Teams. Eine Meldung wie „kann keine Verbindung zum Server herstellen“ lässt sich systematisch eingrenzen, indem man Schicht für Schicht von unten nach oben prüft, was noch funktioniert: Sichtprüfung der Verbindungsanzeige (L1-L2), Ping zum Gateway (L3), Bestätigung einer TCP-Verbindung zum Zielport (L4) und Senden einer HTTP-Anfrage (L7). Wenn beispielsweise der Ping durchgeht, die TCP-Verbindung aber fehlschlägt, verengt sich der Verdacht auf etwas, das den L4-Port blockiert, etwa einen gestoppten Dienst oder eine Firewall. Es dient außerdem als branchenweit geteiltes Koordinatensystem, um einen Verantwortungsbereich knapp zu benennen, wie in „das klingt nach einem L2-Problem, kannst du bitte den Switch prüfen“.

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Öffentliche Links

Zurück zum Blog