====== std.net.arp ====== ARP-Client nach RFC 826 — Address Resolution Protocol für IPv4 über Ethernet. Baut ARP-Request- und Reply-Frames, parst empfangene Frames und löst IP-Adressen ohne Raw-Socket über den Kernel-ARP-Cache auf. import std.net.arp; import std.alloc; // MAC-Adresse für 192.168.1.1 aus Kernel-Cache auflesen var result: int64 := alloc(ARP_RESULT_SIZE); if (ArpResolve(IPPack(192, 168, 1, 1), result) == 0) { var macPtr: int64 := result + ARP_RESULT_SENDER_MAC; // macPtr[0..5] = aufgelöste MAC-Adresse } free(result, ARP_RESULT_SIZE); > **Berechtigung:** ''ArpResolve'' benötigt **keinen** Raw-Socket und **kein** root — nur ein UDP-Socket für den ''SIOCGARP''-ioctl. ''ArpBuildRequest/Reply'' erzeugen nur den Frame-Puffer; das Senden über einen Raw-Socket erfordert ''CAP_NET_RAW''. ---- ===== Imports ===== * ''std.net.socket'' * ''std.alloc'' ---- ===== Konstanten ===== ==== Frame-Feldoffsets (ARP_FRAME_SIZE = 42 Bytes) ==== Ein ARP-Frame besteht aus einem 14-Byte-Ethernet-Header und einem 28-Byte-ARP-Payload. Alle Mehrbytefelder sind Big-Endian (Network Byte Order). **Ethernet-Header (Bytes 0–13):** ^ Konstante ^ Offset ^ Größe ^ Inhalt ^ | ''ARP_ETH_DST'' | 0 | 6 | Ziel-MAC-Adresse (bei REQUEST: ff:ff:ff:ff:ff:ff) | | ''ARP_ETH_SRC'' | 6 | 6 | Quell-MAC-Adresse | | ''ARP_ETH_TYPE'' | 12 | 2 | EtherType = 0x0806 (ARP) | **ARP-Payload (Bytes 14–41):** ^ Konstante ^ Offset ^ Größe ^ Inhalt ^ | ''ARP_HRD'' | 14 | 2 | Hardware-Typ (1 = Ethernet) | | ''ARP_PRO'' | 16 | 2 | Protokoll-Typ (0x0800 = IPv4) | | ''ARP_HLEN'' | 18 | 1 | Hardware-Adresslänge (6) | | ''ARP_PLEN'' | 19 | 1 | Protokoll-Adresslänge (4) | | ''ARP_OP'' | 20 | 2 | Operationscode (1=REQUEST, 2=REPLY) | | ''ARP_SHA'' | 22 | 6 | Sender Hardware Address (MAC) | | ''ARP_SIP'' | 28 | 4 | Sender IP-Adresse | | ''ARP_THA'' | 32 | 6 | Target Hardware Address (bei REQUEST: 00:00:00:00:00:00) | | ''ARP_TIP'' | 38 | 4 | Target IP-Adresse | ==== Operationscodes ==== ^ Konstante ^ Wert ^ Bedeutung ^ | ''ARP_OP_REQUEST'' | 1 | Wer hat IP X? — Broadcast | | ''ARP_OP_REPLY'' | 2 | IP X hat MAC Y — Unicast | | ''ARP_OP_RREQUEST'' | 3 | Reverse ARP Request | | ''ARP_OP_RREPLY'' | 4 | Reverse ARP Reply | ==== Wire-Konstanten ==== ^ Konstante ^ Wert ^ Bedeutung ^ | ''ARP_HTYPE_ETHER'' | 1 | Hardware-Typ Ethernet | | ''ARP_PROTO_IPV4'' | 2048 (0x0800) | Protokoll-Typ IPv4 | | ''ARP_ETYPE_B0'' | 8 (0x08) | EtherType High-Byte | | ''ARP_ETYPE_B1'' | 6 (0x06) | EtherType Low-Byte | ---- ===== Result-Struct (ARP_RESULT_SIZE = 48 Bytes) ===== Wird vom Caller alloziert. MAC-Adressen werden als 6 aufeinander folgende Bytes ab dem angegebenen Offset gespeichert (mit ''peek8'' byteweise lesen). ^ Offset-Konstante ^ Offset ^ Inhalt ^ | ''ARP_RESULT_SENDER_IP'' | 0 | Sender-IP (''IPPack''-Format) | | ''ARP_RESULT_SENDER_MAC'' | 8 | Sender-MAC (Bytes +8 bis +13) | | ''ARP_RESULT_TARGET_IP'' | 16 | Ziel-IP (''IPPack''-Format) | | ''ARP_RESULT_TARGET_MAC'' | 24 | Ziel-MAC (Bytes +24 bis +29) | | ''ARP_RESULT_OP'' | 32 | Operationscode (1 oder 2) | | ''ARP_RESULT_OK'' | 40 | 1 = gültiges Ergebnis | ---- ===== Funktionen ===== ==== Frame-Builder ==== ^ Signatur ^ Beschreibung ^ | ''ArpBuildRequest(buf: int64, srcMac: int64, srcIP: int64, dstIP: int64): int64'' | Baut ARP-REQUEST-Frame in ''buf'' (≥ 42 Bytes). Ethernet-Ziel = Broadcast (ff:ff:ff:ff:ff:ff); ARP-THA = 00:00:00:00:00:00. Gibt ''ARP_FRAME_SIZE'' (42) zurück. | | ''ArpBuildReply(buf: int64, srcMac: int64, srcIP: int64, dstMac: int64, dstIP: int64): int64'' | Baut ARP-REPLY-Frame. ''srcMac''/''srcIP'' = antwortender Host; ''dstMac''/''dstIP'' = ursprünglicher Fragesteller. Gibt ''ARP_FRAME_SIZE'' (42) zurück. | ==== Frame-Parser ==== ^ Signatur ^ Beschreibung ^ | ''ArpParse(buf: int64, len: int64, result: int64): void'' | Parst rohen Ethernet-Frame. Setzt ''ARP_RESULT_OK=1'' wenn EtherType=0x0806, hlen=6, plen=4. Füllt alle Felder des Result-Structs. ''buf'' muss den vollständigen 42-Byte-Frame enthalten. | ==== Kernel-Cache-Auflösung ==== ^ Signatur ^ Beschreibung ^ | ''ArpResolve(ip: int64, result: int64): int64'' | Liest MAC-Adresse für ''ip'' aus dem Kernel-ARP-Cache (''SIOCGARP''-ioctl, kein Raw-Socket, kein root). Setzt ''ARP_RESULT_OK=1'', ''ARP_RESULT_SENDER_MAC'' und ''ARP_RESULT_TARGET_MAC'' auf die aufgelöste MAC, ''ARP_RESULT_TARGET_IP'' auf ''ip''. Gibt 0 bei Erfolg, -1 wenn IP nicht im Cache oder bei Fehler. | ---- ===== Frame-Layout ===== Byte: 0 6 12 14 16 18 19 20 22 28 32 38 ┌────┬────┬──┬──┬──┬─┬─┬──┬────┬────┬────┬────┐ ETH: │dst │src │08│06│ │ │ │ │ │ │ │ │ ARP: │HRD│PRO│HL│PL│OP│SHA │SIP │THA │TIP │ └────┴────┴──┴──┴──┴─┴─┴──┴────┴────┴────┴────┘ |←── 14B Ethernet ──→|←──────── 28B ARP ───────→| 42 ---- ===== Codebeispiel — ARP-Request senden und antwortenden Frame parsen ===== import std.net.arp; import std.net.socket; import std.net.types; import std.alloc; fn resolveViaRaw(srcMac: int64, srcIP: int64, targetIP: int64): int64 { // Raw-Socket für Ethernet-Layer (erfordert CAP_NET_RAW) var rawSock: int64 := sys_socket(AF_PACKET, SOCK_RAW, 0x0806); if (rawSock < 0) { return 0 - 1; } // ARP-Request bauen und senden var frame: int64 := alloc(ARP_FRAME_SIZE); ArpBuildRequest(frame, srcMac, srcIP, targetIP); // ... frame über rawSock senden ... // Antwort empfangen und parsen var reply: int64 := alloc(ARP_FRAME_SIZE); var result: int64 := alloc(ARP_RESULT_SIZE); // ... reply über rawSock empfangen ... ArpParse(reply, ARP_FRAME_SIZE, result); if (peek64(result + ARP_RESULT_OK) == 1) { var mac: int64 := result + ARP_RESULT_SENDER_MAC; // mac[0..5] = aufgelöste MAC } free(result, ARP_RESULT_SIZE); free(reply, ARP_FRAME_SIZE); free(frame, ARP_FRAME_SIZE); return 0; } fn resolveViaCache(ip: int64) { // Einfacher Weg: Kernel-Cache abfragen (kein root nötig) var result: int64 := alloc(ARP_RESULT_SIZE); if (ArpResolve(ip, result) == 0) { var base: int64 := result + ARP_RESULT_SENDER_MAC; Print(peek8(base)); Print(":"); Print(peek8(base + 1)); Print(":"); Print(peek8(base + 2)); Print(":"); Print(peek8(base + 3)); Print(":"); Print(peek8(base + 4)); Print(":"); PrintLn(peek8(base + 5)); } free(result, ARP_RESULT_SIZE); } ---- ===== Hinweise ===== * **ArpResolve ohne root**: Nutzt ''SIOCGARP'' (ioctl 0x8954) über einen normalen UDP-Socket. Erzeugt **keinen** Netzwerkverkehr — liest nur den bestehenden Kernel-Cache. Die IP muss vorher durch Ping, connect() oder ähnliches in den Cache gebracht worden sein. * **Raw-Socket für Request**: ''ArpBuildRequest'' und ''ArpBuildReply'' bauen nur den Puffer. Zum tatsächlichen Senden auf Layer 2 wird ein ''AF_PACKET''-Raw-Socket benötigt (''CAP_NET_RAW'' oder root). * **IP-Format**: Alle IP-Felder verwenden das ''IPPack''-Format aus ''std.net.types'' — Big-Endian uint32 in int64. * **MAC-Zugriff**: MACs im Result-Struct werden mit ''peek8'' byteweise gelesen (6 Bytes ab dem jeweiligen Offset-Konstante). * **ArpResolve result**: Setzt ''SENDER_MAC'' und ''TARGET_MAC'' auf dieselbe aufgelöste MAC; ''TARGET_IP'' wird auf die angefragte IP gesetzt, ''SENDER_IP'' bleibt 0. * **RARP**: ''ARP_OP_RREQUEST/RREPLY'' sind als Konstanten definiert (3/4); ''ArpBuildRequest/Reply'' unterstützen sie nicht direkt — den OP-Code nach dem Build-Call manuell per ''arpWrite16BE'' (intern) oder ''poke8'' setzen. ---- ===== Quelldatei ===== ^ Unit ^ Datei ^ | ''std.net.arp'' | ''std/net/arp.lyx'' | Letzte Aktualisierung: 2026-06-17