====== std.net.dhcp ====== DHCP-Client nach RFC 2131 — vollständige IP-Adressvergabe über UDP-Broadcast. Deckt den gesamten DORA-Prozess ab (Discover → Offer → Request → Acknowledge) und stellt Paket-Builder, Reply-Parser und High-Level-Lease-Funktion bereit. import std.net.dhcp; import std.alloc; // Vollständigen Lease in einem Aufruf holen var mac: int64 := alloc(6); var result: int64 := alloc(DHCP_RESULT_SIZE); // mac mit 6 Byte Ethernet-Adresse befüllen ... poke8(mac, 0xAA); poke8(mac+1, 0xBB); // ... if (DhcpLease(mac, result) == 0) { var ip: int64 := peek64(result + DHCP_RESULT_IP); var gw: int64 := peek64(result + DHCP_RESULT_GW); var lease: int64 := peek64(result + DHCP_RESULT_LEASE); } free(result, DHCP_RESULT_SIZE); free(mac, 6); > **Berechtigung:** Port 68 < 1024 — erfordert root oder ''CAP_NET_BIND_SERVICE''. ---- ===== Imports ===== * ''std.net.socket'' * ''std.net.syscalls'' * ''std.net.types'' * ''std.alloc'' ---- ===== Konstanten ===== ==== Ports ==== ^ Konstante ^ Wert ^ Bedeutung ^ | ''DHCP_PORT_SERVER'' | 67 | UDP-Port des DHCP-Servers | | ''DHCP_PORT_CLIENT'' | 68 | UDP-Port des DHCP-Clients | ==== Operationscodes ==== ^ Konstante ^ Wert ^ Bedeutung ^ | ''DHCP_OP_REQUEST'' | 1 | BOOTREQUEST — Client→Server | | ''DHCP_OP_REPLY'' | 2 | BOOTREPLY — Server→Client | ==== DHCP-Nachrichtentypen (Option 53) ==== ^ Konstante ^ Wert ^ Bedeutung ^ | ''DHCP_MSG_DISCOVER'' | 1 | Client sucht DHCP-Server | | ''DHCP_MSG_OFFER'' | 2 | Server bietet IP an | | ''DHCP_MSG_REQUEST'' | 3 | Client fordert angebotene IP an | | ''DHCP_MSG_DECLINE'' | 4 | Client lehnt angebotene IP ab | | ''DHCP_MSG_ACK'' | 5 | Server bestätigt Lease | | ''DHCP_MSG_NAK'' | 6 | Server verweigert Lease | | ''DHCP_MSG_RELEASE'' | 7 | Client gibt IP frei | | ''DHCP_MSG_INFORM'' | 8 | Client fragt Konfiguration ohne IP-Zuweisung | ==== Options-Codes ==== ^ Konstante ^ Wert ^ Bedeutung ^ | ''DHCP_OPT_SUBNET'' | 1 | Subnet Mask | | ''DHCP_OPT_ROUTER'' | 3 | Default Gateway | | ''DHCP_OPT_DNS'' | 6 | DNS-Server (erster Eintrag wird gespeichert) | | ''DHCP_OPT_HOSTNAME'' | 12 | Hostname (nur deklariert, nicht gesetzt) | | ''DHCP_OPT_DOMAIN'' | 15 | Domain Name (nur angefordert, nicht geparst) | | ''DHCP_OPT_REQIP'' | 50 | Requested IP Address | | ''DHCP_OPT_LEASE'' | 51 | Lease-Zeit in Sekunden | | ''DHCP_OPT_MSGTYPE'' | 53 | Nachrichtentyp | | ''DHCP_OPT_SERVERID'' | 54 | Server Identifier (IP des DHCP-Servers) | | ''DHCP_OPT_PARAMREQ'' | 55 | Parameter Request List | | ''DHCP_OPT_PAD'' | 0 | Padding-Byte | | ''DHCP_OPT_END'' | 255 | Ende der Options | ==== Paket-Feldoffsets (RFC 2131 §2) ==== ^ Konstante ^ Offset ^ Größe ^ Inhalt ^ | ''DHCP_OFFS_OP'' | 0 | 1 | Operationscode | | ''DHCP_OFFS_HTYPE'' | 1 | 1 | Hardware-Typ (1 = Ethernet) | | ''DHCP_OFFS_HLEN'' | 2 | 1 | Hardware-Adresslänge (6 für MAC) | | ''DHCP_OFFS_HOPS'' | 3 | 1 | Relay-Agent-Hops | | ''DHCP_OFFS_XID'' | 4 | 4 | Transaction ID (Big-Endian) | | ''DHCP_OFFS_SECS'' | 8 | 2 | Sekunden seit Start | | ''DHCP_OFFS_FLAGS'' | 10 | 2 | Flags (Bit 15 = Broadcast) | | ''DHCP_OFFS_CIADDR'' | 12 | 4 | Client IP (bei Erneuerung) | | ''DHCP_OFFS_YIADDR'' | 16 | 4 | Angebotene/zugewiesene IP | | ''DHCP_OFFS_SIADDR'' | 20 | 4 | Nächster Server | | ''DHCP_OFFS_GIADDR'' | 24 | 4 | Relay Agent IP | | ''DHCP_OFFS_CHADDR'' | 28 | 16 | Client-Hardware-Adresse (MAC in Byte 0–5) | | ''DHCP_OFFS_SNAME'' | 44 | 64 | Server-Hostname | | ''DHCP_OFFS_FILE'' | 108 | 128 | Boot-Dateiname | | ''DHCP_OFFS_COOKIE'' | 236 | 4 | Magic Cookie (0x63825363) | | ''DHCP_OFFS_OPTIONS'' | 240 | — | Beginn der Options | | ''DHCP_MIN_LEN'' | — | 300 | Mindestgröße (BOOTP-kompatibel) | ==== Sonstiges ==== ^ Konstante ^ Wert ^ Bedeutung ^ | ''DHCP_FLAG_BROADCAST'' | 128 (0x80) | Broadcast-Bit (Byte 10, Bit 7) | | ''DHCP_MAGIC_B0..B3'' | 99,130,83,99 | Magic Cookie 0x63825363 | | ''DHCP_HTYPE_ETHER'' | 1 | Hardware-Typ Ethernet | ---- ===== Result-Struct (DHCP_RESULT_SIZE = 72 Bytes) ===== Wird vom Caller alloziert und mit Nullen initialisiert. ''DhcpParseReply'', ''DhcpDiscover'', ''DhcpRequest'' und ''DhcpLease'' schreiben hinein. Alle IP-Adressen sind im selben gepackten Format wie ''IPPack()'' aus ''std.net.types'' gespeichert (Big-Endian uint32 in int64). ^ Offset-Konstante ^ Offset ^ Inhalt ^ | ''DHCP_RESULT_IP'' | 0 | Zugewiesene IP-Adresse | | ''DHCP_RESULT_MASK'' | 8 | Subnetzmaske | | ''DHCP_RESULT_GW'' | 16 | Default Gateway | | ''DHCP_RESULT_DNS'' | 24 | Primärer DNS-Server (erster aus Option 6) | | ''DHCP_RESULT_LEASE'' | 32 | Lease-Zeit in Sekunden | | ''DHCP_RESULT_SERVER'' | 40 | DHCP-Server-IP (Option 54) | | ''DHCP_RESULT_XID'' | 48 | Transaction ID | | ''DHCP_RESULT_TYPE'' | 56 | Nachrichtentyp (2=OFFER, 5=ACK, 6=NAK) | | ''DHCP_RESULT_OK'' | 64 | 1 = Lease erfolgreich erhalten | ---- ===== Funktionen ===== ==== Paket-Builder ==== ^ Signatur ^ Beschreibung ^ | ''DhcpBuildDiscover(buf: int64, mac: int64, xid: int64): int64'' | Baut DHCPDISCOVER-Paket in ''buf'' (≥ ''DHCP_MIN_LEN'' Bytes). ''mac'' = Zeiger auf 6-Byte-MAC. Broadcast-Flag gesetzt; Parameter Request List: Subnet, Router, DNS, Domain. Gibt ''DHCP_MIN_LEN'' zurück. | | ''DhcpBuildRequest(buf: int64, mac: int64, xid: int64, offeredIP: int64, serverIP: int64): int64'' | Baut DHCPREQUEST-Paket. ''offeredIP'' und ''serverIP'' aus dem vorherigen OFFER übernommen (Option 50 + 54 werden gesetzt). Gibt ''DHCP_MIN_LEN'' zurück. | ==== Reply-Parser ==== ^ Signatur ^ Beschreibung ^ | ''DhcpParseReply(buf: int64, len: int64, result: int64): void'' | Parst DHCP-Server-Antwort (OFFER oder ACK). Prüft OP=REPLY und Magic Cookie. Setzt ''DHCP_RESULT_OK=1'' bei OFFER oder ACK; 0 bei NAK, ungültigem Paket oder zu kurzem Puffer. Füllt IP, Mask, GW, DNS, Lease, Server, Type. | ==== High-Level Lease-Funktionen ==== ^ Signatur ^ Beschreibung ^ | ''DhcpDiscover(mac: int64, xid: int64, result: int64): int64'' | Sendet DHCPDISCOVER (UDP-Broadcast) und wartet bis zu 5 Sekunden auf DHCPOFFER. Gibt 0 zurück wenn OFFER erhalten, -1 bei Fehler oder Timeout. | | ''DhcpRequest(mac: int64, xid: int64, result: int64): int64'' | Sendet DHCPREQUEST (liest offeredIP und serverIP aus ''result'') und wartet bis zu 5 Sekunden auf DHCPACK. Gibt 0 nur zurück wenn ''DHCP_RESULT_TYPE = DHCP_MSG_ACK''. | | ''DhcpLease(mac: int64, result: int64): int64'' | Vollständiger DORA-Prozess in einem Aufruf: Discover → Offer → Request → Acknowledge. XID wird aus den ersten 4 MAC-Bytes abgeleitet. Gibt 0 bei erfolgreichem Lease, -1 bei Fehler zurück. | ---- ===== DORA-Ablauf ===== Client Server (255.255.255.255:67) | | |── DHCPDISCOVER (Broadcast) ────>| xid = MAC[0..3] |<─ DHCPOFFER ────────────────────| yiaddr=192.168.1.100, srv=192.168.1.1 | | |── DHCPREQUEST (Broadcast) ─────>| opt50=192.168.1.100, opt54=192.168.1.1 |<─ DHCPACK ──────────────────────| Lease bestätigt, Lease-Zeit: 43200s ''DhcpLease()'' führt den gesamten Ablauf durch. ''DhcpDiscover()'' und ''DhcpRequest()'' erlauben manuelle Steuerung (z. B. für Fehlerbehandlung oder Logging zwischen den Schritten). ---- ===== Codebeispiel — Manueller DORA-Ablauf ===== import std.net.dhcp; import std.net.types; import std.alloc; fn acquireLease(mac: int64): int64 { var result: int64 := alloc(DHCP_RESULT_SIZE); var xid: int64 := peek8(mac); xid := (xid << 8) | peek8(mac + 1); xid := (xid << 8) | peek8(mac + 2); xid := (xid << 8) | peek8(mac + 3); if (DhcpDiscover(mac, xid, result) < 0) { free(result, DHCP_RESULT_SIZE); return 0 - 1; } var offeredIP: int64 := peek64(result + DHCP_RESULT_IP); var srvIP: int64 := peek64(result + DHCP_RESULT_SERVER); // Hier könnte Validierung des OFFER stattfinden ... if (DhcpRequest(mac, xid, result) < 0) { free(result, DHCP_RESULT_SIZE); return 0 - 1; } var ip: int64 := peek64(result + DHCP_RESULT_IP); var mask: int64 := peek64(result + DHCP_RESULT_MASK); var gw: int64 := peek64(result + DHCP_RESULT_GW); var dns: int64 := peek64(result + DHCP_RESULT_DNS); var lease: int64 := peek64(result + DHCP_RESULT_LEASE); // IP-Adresse z. B. an Netzwerk-Interface binden ... // Lease-Erneuerung nach lease/2 Sekunden nötig free(result, DHCP_RESULT_SIZE); return 0; } ---- ===== Hinweise ===== * **Berechtigung**: ''DhcpDiscover'' und ''DhcpRequest'' binden Port 68 — erfordert root oder ''CAP_NET_BIND_SERVICE''. Auf Linux genügt: ''setcap cap_net_bind_service+ep ./programm''. * **IP-Format**: Alle IP-Adressen im Result-Struct verwenden das ''IPPack''-Format aus ''std.net.types'' (Big-Endian uint32 in int64). Zum Anzeigen: ''IpToStr()'' verwenden. * **Timeout**: ''DhcpDiscover'' und ''DhcpRequest'' warten je maximal 5 Sekunden auf eine Antwort (''SocketCanRead(fd, 5000)''). Kein automatischer Retry. * **Kein Lease-Renewal**: Diese Unit macht keine automatische Lease-Erneuerung. Die Anwendung muss nach ''lease/2'' Sekunden ''DhcpLease'' erneut aufrufen. * **XID**: ''DhcpLease'' leitet die Transaction ID aus den ersten 4 MAC-Bytes ab. Bei mehreren gleichzeitigen Clients mit ähnlichen MACs kann es zu XID-Kollisionen kommen — in diesem Fall ''DhcpDiscover/DhcpRequest'' direkt mit einer zufälligen XID verwenden. * **DNS**: Nur der erste DNS-Server (Byte 0–3 von Option 6) wird gespeichert. Mehrere DNS-Server werden ignoriert. * **BOOTP-Kompatibilität**: Pakete sind mindestens 300 Bytes lang (''DHCP_MIN_LEN'') für ältere BOOTP-Server. ---- ===== Quelldatei ===== ^ Unit ^ Datei ^ | ''std.net.dhcp'' | ''std/net/dhcp.lyx'' | Letzte Aktualisierung: 2026-06-17