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