Network Block Device

aus Wikipedia, der freien Enzyklopädie

Ein Network Block Device (engl. für Netzwerk-Blockgerät, abgekürzt NBD) ist eine Art virtuelle Festplatte, auf die ein Rechner via Internetprotokoll zugreifen kann. Das NBD wird von einem NBD-Server bereitgestellt. Er bietet hierfür eine eigene Festplatte, Festplattenpartition oder eine Datei als NBD bestimmten anderen Rechnern (Clients) an. Ein anderer Rechner (oder auch der gleiche) kann sich über eine TCP-Verbindung mit dem NBD-Server verbinden und anschließend das NBD wie eine eigene lokale Festplatte benutzen.

Derzeit existiert nur für Linux eine vollständige NBD-Implementierung. Linux spricht sämtliche Massenspeicher als sogenannte Blockgeräte an. Wenn ein Linux-Rechner ein Network Block Device nutzen soll, muss NBD support in der Linux-Kernel-Konfiguration aktiviert sein, bzw. das Kernel-Modul nbd.ko geladen sein. Ein Userspace-Hilfsprogramm namens nbd-client stellt nun die TCP-Verbindung zum NBD-Server her, gibt die bestehende Verbindung an den Kernel weiter und beendet sich dann. Dies hat den Vorteil, dass der Kernel sich nicht mit dem Verbindungsaufbau (und einer eventuellen Authentisierung usw.) befassen muss.

Der NBD-Server ist betriebssystemunabhängig. Er kann also auch auf einem Nicht-Linux-System laufen, da keine Linux-spezifischen Funktionen benötigt werden. Es existiert ein Programm namens nbd-server, das nichts weiter tut, als eine gegebene Datei (oder Partition etc.) an einem angegebenen TCP-Port bereitzustellen.

Prinzipiell ist es möglich, über NBD einen festplattenlosen Rechner zu betreiben, der als einzigen Massenspeicher ein NBD besitzt. Da jedoch zum Aufbau der Verbindung noch ein externes Programm (nbd-client) benötigt wird, ist dies nur mit Konzepten wie der init-ramdisk zu realisieren, einem virtuellen Dateisystem, welches im RAM gehalten wird und im Kernel selbst gespeichert ist, sodass es nach dem Booten zur Verfügung steht.

Da die Originalversion von NBD einige Schwächen hat (z. B. die Begrenzung auf 4 Gigabyte pro NBD), gibt es verschiedene Erweiterungen, die teilweise als "enhanced NBD" bezeichnet werden. Diese sind jedoch inkompatibel zum Original-NBD.

NBD-Protokoll (ab Version 2.6)

Das Protokoll ist ein Binärprotokoll. Sämtliche Mehrbytewerte werden dabei in Network Byte Order gesendet.

Handshake

Zuerst kommt eine Initialisierungsphase, bei der Daten zwischen dem NBD-Server und dem NBD-Clientprogramm ausgetauscht werden. Dieses Protokoll ist unabhängig vom NBD-Treiber im Linux-Kernel und variiert bei verschiedenen NBD-Implementierungen.

Version ≤2.9.16

Das alte Handshake-Protokoll unterstützt genau ein Block Device pro Port. Sobald ein Client sich zum NBD-Server verbunden hat, sendet der Server folgende Datenstruktur:

NBD Initialization packet (Server→Client)[1]
Offset Datentyp Name Beschreibung
0 char[8] INIT_PASSWD Identifizierungsstring {'N', 'B', 'D', 'M', 'A', 'G', 'I', 'C'}
8 uint64_t cliserv_magic Magic Number 0x00420281861253
16 uint64_t export_size Größe des exportierten Blockdevices (in Byte)
24 uint32_t flags Flags:
  • Bit 0: Es sind Flags vorhanden
  • Bit 1: Gerät ist read-only
  • Bit 2: Gerät unterstützt "FLUSH"-Befehl zum Leeren von Schreibcaches
  • Bit 3 und 4: ungenutzt
  • Bit 5: Gerät unterstützt den TRIM-Befehl, mit dem das Dateisystem dem Blocklayer freigewordene mitteilen kann
28 char[124] reserved Reserviert (Derzeit gefüllt mit Nullbytes)

Akzeptiert der Client den Identifizierungsstring oder die Magic Number nicht, schließt er die Verbindung. Andernfalls gilt die Verbindung als erfolgreich aufgebaut.

Version ≥ 2.9.17

Das neue Handshake-Protokoll benutzt den IANA-registrierten Port 10809 und ein anderes Nachrichtenformat, das dem Server erlaubt, über einen TCP-Port mehrere Blockgeräte anzubieten, aus denen der Client über ihren Namen eines auswählen kann. Zusätzlich wurden die 32 Bit flags in 2 16-Bit-Teile aufgespalten, die es erlauben, serverglobale und geräteabhängige Flags zu trennen.

Server Init packet (Server→Client)[2]
Offset Datentyp Name Beschreibung
0 char[8] INIT_PASSWD Identifizierungsstring {'N', 'B', 'D', 'M', 'A', 'G', 'I', 'C'}
8 uint64_t cliserv_magic Magic Number 0x49484156454F5054 (="IHAVEOPT")
16 uint16_t server_flags Flags, die für den gesamten Server gelten. Üblicherweise haben die Flags den Wert 0003hex. Die Bits bedeuten im Einzelnen:
Bit 0
NBD_FLAG_FIXED_NEW_STILE: Gesetztes Bit zeigt an, dass ein bestimmter Handshake-Bug im Server gefixt ist
Bit 1
NBD_FLAG_NO_ZEROES: Gesetztes Bit zeigt an, dass die Headernachricht nicht mit 124 Nullbytes aufgefüllt wird

Der Client antwortet mit seinen Flags. Da bisher keine Flags definiert sind, bestehen sie nur aus 32 Nullbits:

Client Init packet (Server→Client)
Offset Datentyp Name Beschreibung
0 uint32_t client_flags bisher gleiche Bedeutung wie server_flags, also ebenfalls üblicherweise 0000'0003hex.

Anschließend sendet der Client verschiedene Optionen, die der Server entsprechend akzeptierend oder ablehnend quittiert:

Option packet (Client→Server)
Offset Datentyp Name Beschreibung
0 uint64_t cliserv_magic Magic Number 0x49484156454F5054 (="IHAVEOPT")
8 uint32_t option_number Kennnummer/Typ der Option
12 uint32_t option_length Länge der Option (in Bytes)
16 variabel option_data Daten der Option (abhängig vom Optionstyp)

Bisher sind 3 Optionen definiert:

NBD Optionen
Name Wert Bedeutung
NBD_OPT_EXPORT_NAME 1 Client wählt Namen des Blockgerätes: der Name folgt im option_data-Feld. Diese Option beendet automatisch die Optionsliste. Der Server schickt den geräteabhängigen Teil der Initialisierung (siehe unten).
NBD_OPT_ABORT 2 Client möchte die Verbindung beenden
NBD_OPT_LIST 3 Client möchte eine Liste mit den Namen der exportierten Blockgeräte

Der Server antwortet auf ein Option Paket mit einem Reply-Paket:

Reply packet (Server→Client)
Offset Datentyp Name Beschreibung
0 uint64_t reply_magic Magic Number 0x0003e889045565a9
8 uint32_t option_number Kennnummer/Typ der Option, die beantwortet wird[3]
12 uint32_t reply_type Typ der Antwort
16 uint32_t reply_length Länge der Antwortdaten
20 variabel reply_data Antwortdaten, sofern reply_length > 0

Folgende Antworttypen sind bisher definiert:

Reply types
Name Wert Bedeutung
NBD_REP_ACK 1 Der Server akzeptiert die Option oder hat keine weiteren Antwortdaten (bei NBD_OPT_LIST)
NBD_REP_SERVER 2 Beschreibung des Blockgerätes. Es folgt die Länge des Namens als 32-Bit-Nummer, der Name, und – falls noch Platz im Antwortpaket ist – evtl. weitere beschreibende Details als Klartext.
NBD_REP_ERR_UNSUP 8000 0001hex Client hat eine unbekannte Option geschickt
NBD_REP_ERR_POLICY 8000 0002hex Der Server hat die Option verstanden, aber dem Server ist nicht erlaubt, die Option anzunehmen (z. B. NBD_OPT_LIST kann in der Konfigurationsdatei erlaubt oder verboten werden)
NBD_REP_ERR_INVALID 8000 0003hex Der Server hat die Option verstanden, doch sie war syntaktisch ungültig
NBD_REP_ERR_PLATFORM 8000 0004hex Die Option wird von der Plattform, auf der der Server läuft, nicht unterstützt. (derzeit ungenutzt)
Hexdump des Datenverkehrs in der Initialisierungsphase zwischen NBD-Client und -Server. Der Client fragt die Liste der angebotenen Geräte ab und der Server listet zwei Geräte (mit Namen "alfa" und "bravo") auf.

Die Aushandelsphase ist abgeschlossen, sobald der Server die NBD_OPT_EXPORT_NAME-Option positiv quittiert hat. Er schickt daraufhin die Kenndaten des exportierten Blockgerätes an den Client:

Device Init packet (Server→Client)
Offset Datentyp Name Beschreibung
0 uint64_t device_size Größe des exportierten Blockgerätes (in Byte)
8 uint16_t device_flags Flags, die für das exportierte Gerät gelten:
  • Bit 0: Es existieren Flags
  • Bit 1: Gerät ist read-only
  • Bit 2: Server & Gerät unterstützen das NBD_CMD_FLUSH-Kommando
  • Bit 3: Server & Gerät unterstützen das NBD_CMD_FLAG_FUA-Flag
  • Bit 4: NBD_FLAG_ROTATIONAL: exportierte Daten liegen auf einem rotierenden Medium (klassische Festplatte), was der Client bei dem Zugriffsmuster auf die Blöcke berücksichtigen kann
  • Bit 5: Server & Gerät unterstützen das NBD_FLAG_SEND_TRIM-Kommand
10 uint8_t[124] padding ungenutzt, alle 0

Datenphase

Der NBD-Client leitet die Informationen über die Größe des Blockdevices, eventuelle Flags und den geöffneten Socket über spezielle Systemaufrufe an den Kernel weiter und beendet sich. Der Kernel übernimmt dann die weitere Kommunikation über diesen Socket.

Der Kernel auf Clientseite stellt nun Lese- und Schreibanfragen (Requests) an den Server. Diese haben folgenden Paketaufbau:

NBD Request (Client→Server)[4]
Offset Datentyp Name Beschreibung
0 uint32_t magic Magic Number 0x25609513
4 uint32_t type 0: Lesezugriff; 1: Schreibzugriff; 2: kontrolliertes Verbindungsende; 3: Flush cache; 4: TRIM-Kommando
8 char[8] handle 8 Bytes, die im Reply identisch mitgeschickt werden, um dieses einem Request zuordnen zu können
16 uint64_t from Offset (in Bytes), ab dem gelesen/geschrieben werden soll
24 uint32_t len Länge des Datenblocks

Bei Schreibzugriffen folgen unmittelbar darauf die zu schreibenden Daten. Der Server beantwortet jeden Request mit einer Antwort (Reply). Diese hat folgenden Aufbau:

NBD Reply (Server→Client)[4]
Offset Datentyp Name Beschreibung
0 uint32_t magic Magic Number 0x67446698
4 uint32_t error 0=OK (kein Fehler aufgetreten)
8 char[8] handle Kopie des Handles im zugehörigen Request

Bei Antworten auf Lese-Requests folgen unmittelbar darauf die angeforderten Daten.

Siehe auch

  • Loop device: Die gleiche Idee mit einem lokalen Gerät
  • iSCSI: Ein konkurrierendes System
  • Network File System: Agiert auf einer anderen Ebene, hat dafür aber auch einen weitaus größeren Bekanntheitsgrad

Weblinks

Einzelnachweise

  1. Aus dem Quellcode von nbd-2.9.13/cliserv.h
  2. Aus dem Quellcode von nbd-3.2/proto.txt
  3. Wird entgegen der Protokoll-Beschreibung in Host-Byteorder gesendet. Wert vom Client beim Einlesen ignoriert.
  4. a b Aus dem Header /usr/include/linux/nbd.h