====== 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