Reviewed-on: #6 Reviewed-by: dragonslayer14@murena.io <4+dragonslayer_14@noreply.localhost>
mac2ip
Ein Kommandozeilen-Tool (CLI), das zuverlässig die aktuelle IP-Adresse zu einer gegebenen MAC-Adresse im lokalen Netzwerk findet.
Dazu wird ein 3-stufiger Algorithmus verwendet:
- Cache: Ein globaler, systemweiter Cache (für alle Nutzer dieses Rechners) wird geprüft. Ein Treffer wird nur verwendet, wenn er noch nicht abgelaufen ist (TTL) und die IP per Ping erreichbar ist.
ip neigh: Die Linux-Nachbartabelle (ip neigh show, der moderne Ersatz für den veraltetenarp-Befehl) wird nach der MAC-Adresse durchsucht. Ein Treffer wird nur verwendet, wenn die IP per Ping erreichbar ist.nmap: Als letzter Schritt wird ein Ping-/ARP-Scan (nmap -sn) der lokal angeschlossenen Subnetze durchgeführt. Da MAC-Adressen in der nmap-Ausgabe nur mit Root-Rechten sichtbar sind (ARP-Scans benötigen Raw-Socket-/libpcap-Zugriff), läuft dieser Schritt übersudo nmap(bzw. direktnmap, falls das Programm bereits als root läuft). Da ein Subnetz-Scan auf fremden Netzwerken heikel sein kann, wird dieser Schritt nur in einem als vertrauenswürdig eingestuften Netzwerk ausgeführt (siehe Netzwerk-Vertrauen). Wird auch hier keine erreichbare IP gefunden, bricht das Programm mit einem Fehler ("nicht gefunden") ab.
Features
- Zuverlässige MAC → IP-Auflösung über Cache,
ip neighundnmap, jeweils mit Erreichbarkeitsprüfung per Ping. - Globaler, systemweiter Cache (via
turso, lokal-dateibasiert) mit konfigurierbarer TTL. - Maschinenlesbare Ausgabe über
--json(unattended-Modus) — unterdrückt dabei alle sonstigen Log-Ausgaben. - Vollständig über die Kommandozeile konfigurierbar, mit Overlay-Kette CLI > Umgebungsvariable > Konfigurationsdatei > Standardwert.
- Funktioniert mit und ohne
sudo— Root-Rechte werden nur für den nmap-Schritt benötigt. - Rust Edition 2024, Multi-Architektur-Kompilierung (
x86_64,aarch64), native Linux-Paketierung (.deb,.rpm,.pkg.tar.zst) und automatisierte CI/CD-Pipelines via Gitea Actions.
CLI-Nutzung
mac2ip <MAC> [OPTIONEN]
Beispiel:
$ mac2ip aa:bb:cc:dd:ee:ff
aa:bb:cc:dd:ee:ff -> 192.168.1.42 (Quelle: cache)
Maschinenlesbare Ausgabe (unattended-Modus):
$ mac2ip aa:bb:cc:dd:ee:ff --json
{"status":"ok","mac":"aa:bb:cc:dd:ee:ff","ip":"192.168.1.42","source":"cache"}
Bei Fehlern (status: "error") ist source nicht enthalten, dafür ein error-Feld mit einer Beschreibung; der Exit-Code ist in beiden Fällen ungleich 0 (1) bei Fehlschlag.
Optionen
| Option | Umgebungsvariable | Standard | Beschreibung |
|---|---|---|---|
--json |
- | aus | Gibt das Ergebnis als einzeiliges JSON-Objekt aus; unterdrückt alle sonstigen Log-Ausgaben. |
--config <PATH> |
- | siehe unten | Benutzerdefinierter Pfad zur Konfigurationsdatei. |
--log-level <LEVEL> |
MAC2IP_LOG_LEVEL |
info |
Logging-Level: error, warn, info, debug. |
--cache-ttl-seconds <N> |
MAC2IP_CACHE_TTL_SECONDS |
1800 |
Wie lange ein Cache-Eintrag als gültig angesehen wird (zusätzlich zur Ping-Prüfung). |
--cache-db-path <PATH> |
MAC2IP_CACHE_DB_PATH |
/var/lib/mac2ip/cache.db |
Pfad zur globalen Cache-Datenbankdatei. |
--nmap-timeout-seconds <N> |
MAC2IP_NMAP_TIMEOUT_SECONDS |
120 |
Timeout für einen einzelnen nmap-Subnetz-Scan. |
--networks <CIDR,CIDR,...> |
MAC2IP_NETWORKS |
Auto-Erkennung | Kommagetrennte Liste von Subnetzen für den nmap-Scan; überschreibt die automatische Erkennung über ip route. |
--trusted-networks <MAC,MAC,...> |
MAC2IP_TRUSTED_NETWORKS |
leer | Kommagetrennte Liste von Gateway-MAC-Adressen, deren Netzwerke ohne Rückfrage für nmap-Scans (Schritt 3) vertraut werden; überschreibt die Konfigurationsdatei vollständig. |
--auto-trust-networks |
- | aus | Beantwortet die "Netzwerk vertrauen?"-Rückfrage vor Schritt 3 automatisch mit Ja (und merkt sich das Netzwerk dauerhaft im Cache), statt interaktiv nachzufragen bzw. im --json-Modus abzulehnen. |
Die Überschreibungs-Reihenfolge ist immer: CLI-Flag > Umgebungsvariable > Konfigurationsdatei > Standardwert.
Konfigurationsdatei
Die Konfiguration wird über config-ctdra verwaltet:
- Als root (bzw. beim nmap-Schritt via
sudo):/etc/mac2ip/config.toml - Als normaler Nutzer:
~/.config/mac2ip/config.toml - Oder explizit über
--config <PATH>
Beispiel:
cache_ttl_seconds = 1800
cache_db_path = "/var/lib/mac2ip/cache.db"
log_level = "info"
nmap_timeout_seconds = 120
networks = []
trusted_networks = []
Alle Felder sind optional (fehlende Felder verwenden den Standardwert) und können, wie oben beschrieben, zusätzlich per CLI-Flag oder Umgebungsvariable überschrieben werden.
Caching & TTL
Der Cache liegt standardmäßig unter /var/lib/mac2ip/cache.db — global für alle Nutzer des Rechners, nicht pro Benutzerkonto. Verzeichnis und Datei sind bewusst world-writable (0777/0666), damit auch unprivilegierte Nutzer den Cache lesen und schreiben können, ohne dass mac2ip dafür Root-Rechte bräuchte.
Ein Cache-Treffer wird nur verwendet, wenn:
- der Eintrag noch nicht älter als
cache_ttl_secondsist (Standard: 30 Minuten), und - die gespeicherte IP-Adresse aktuell per Ping erreichbar ist.
Die Ping-Prüfung ist die primäre Absicherung gegen veraltete Zuordnungen; die TTL ist eine zusätzliche Absicherung für den Fall, dass eine alte IP-Adresse inzwischen an ein anderes, ebenfalls erreichbares Gerät vergeben wurde (z. B. nach einem DHCP-Lease-Wechsel).
Kann das Cache-Verzeichnis beim Programmstart nicht angelegt/beschrieben werden (z. B. bei einem Entwicklungslauf ohne vorherige Paketinstallation), wird der Cache für diesen Lauf einfach deaktiviert (eine Warnung wird geloggt, im --json-Modus unterdrückt) — mac2ip führt den Lookup dann ohne Cache über ip neigh/nmap durch. Bei einer Installation über .deb/.rpm/.pkg.tar.zst wird das Verzeichnis automatisch mit den richtigen Rechten angelegt (siehe Paketierungs-Postinstall-Skripte unter packaging/).
Netzwerk-Vertrauen
Ein nmap-Subnetz-Scan (Schritt 3) ist auf Netzwerken, die man nicht selbst administriert (Firmen-/Gast-WLAN, Kundenstandort etc.), potenziell heikel — er kann IDS-Alarme auslösen oder gegen eine Nutzungsordnung verstoßen. mac2ip führt Schritt 3 deshalb nur in einem als vertrauenswürdig eingestuften Netzwerk aus.
Das aktuelle Netzwerk wird über die MAC-Adresse seines Default-Gateways identifiziert — sie bleibt stabil, solange derselbe Router im Einsatz ist, unabhängig von SSID oder wechselndem DHCP-Subnetz. Ein Netzwerk gilt als vertrauenswürdig, wenn eine der folgenden Bedingungen zutrifft:
- Die Gateway-MAC steht in
trusted_networks(Konfigurationsdatei oder--trusted-networks). - Das Netzwerk wurde bereits einmal per Rückfrage bestätigt — das Ergebnis wird dauerhaft im globalen Cache gespeichert, sodass beim nächsten Besuch (auch nach Neustart) keine erneute Rückfrage nötig ist.
--auto-trust-networksist gesetzt: Die Rückfrage wird automatisch mit Ja beantwortet und das Ergebnis ebenfalls im Cache gemerkt.
Ist keine der Bedingungen erfüllt:
- Im interaktiven Modus fragt mac2ip auf stderr nach, ob der Scan in diesem Netzwerk erlaubt werden soll.
- Im
--json-Modus ist keine Rückfrage möglich (die Ausgabe darf nicht durch einen Prompt verunreinigt werden) — der Scan wird sicherheitshalber abgelehnt und ein Fehler zurückgegeben. Für unbeaufsichtigte Läufe in bekannten Netzwerken also entwedertrusted_networksvorkonfigurieren oder einmalig interaktiv (ohne--json) bestätigen.
Kann die Gateway-MAC nicht ermittelt werden (z. B. kein Default-Gateway vorhanden), wird der Scan ebenfalls abgelehnt, außer --auto-trust-networks ist gesetzt.
Voraussetzungen
Neben Rust/Cargo zur Laufzeit benötigt werden folgende System-Tools:
ip(Paketiproute2) — für Schritt 2 (ip neigh) und die automatische Subnetz-Erkennung.ping(Paketiputils/iputils-ping) — für die Erreichbarkeitsprüfung.nmap— für Schritt 3.sudo— nur nötig, wenn Schritt 3 erreicht wird und das Programm nicht bereits als root läuft.
Im --json-Modus wird für Schritt 3 ausschließlich sudo -n (nicht-interaktiv) verwendet, damit das Programm niemals interaktiv nach einem Passwort fragt und dadurch ein Skript blockiert. Ist keine gültige sudo-Sitzung/NOPASSWD-Regel vorhanden, wird Schritt 3 abgebrochen und das Ergebnis als "nicht gefunden" zurückgegeben. Im interaktiven (Nicht-JSON-)Modus darf sudo regulär nach einem Passwort fragen.
Struktur des Repositories
├── .cargo/
│ └── config.toml # Cargo-Konfiguration (Linker für Cross-Compiling, Registry)
├── .gitea/
│ └── workflows/
│ ├── main.yaml # CI/CD-Workflow für stabile Releases (main-Branch)
│ └── testing.yaml # CI/CD-Workflow für Pre-Releases (testing-Branch)
├── packaging/
│ ├── deb/postinst # Debian-Postinstall: legt /var/lib/mac2ip an
│ └── arch/mac2ip.install # Arch-Install-Skriptlet: legt /var/lib/mac2ip an
├── scripts/
│ ├── get-build-number.py # Dynamische Ermittlung der nächsten Paket-Revisionsnummer
│ └── package-arch.py # Erstellung von Arch Linux .pkg.tar.zst Paketen
├── src/
│ ├── main.rs # Einstiegspunkt (CLI-Parsing, Wiring)
│ ├── lib.rs # Modul-Wurzel (für Integrationstests)
│ ├── cli.rs # clap-Kommandozeilen-Definition
│ ├── config.rs # AppConfig + config-ctdra-Integration + CLI-Overlay
│ ├── log.rs # JSON-Modus-bewusster logger-ctdra-Wrapper
│ ├── mac.rs # MacAddress-Typ (Parsing/Kanonisierung)
│ ├── cache.rs # Globaler Turso-Cache (MAC -> IP)
│ ├── network.rs # ip neigh / ping / nmap: Exec- und Parse-Funktionen
│ ├── resolver.rs # 3-Stufen-Algorithmus (Orchestrierung)
│ └── output.rs # Human- und JSON-Ausgabe
├── tests/ # Integrationstests (pure Parsing-/Logik-Funktionen)
├── Cargo.toml # Cargo Manifest & Paketierungsmetadaten (deb, rpm, arch)
├── LICENSE # Lizenzdatei (Standard: GPL-3.0-or-later)
├── AGENTS.md # Richtlinien und Leitfaden für KI-Coding-Agenten
└── README.md # Projektdokumentation
Lokale Entwicklung
Voraussetzungen
- Rust & Cargo (aktuelle Stable-Version, Edition 2024 unterstützt)
- Python 3 (für Hilfsskripte in
scripts/) - Zur Laufzeit:
ip,ping,nmap,sudo(siehe "Voraussetzungen" oben) - Für Cross-Compilation (optional):
rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu- Cross-Toolchains:
gcc-aarch64-linux-gnu
- Für lokales Paketieren (optional):
cargo install cargo-deb cargo-generate-rpm
Wichtige Befehle
# Projekt bauen
cargo build
# Tests ausführen
cargo test
# Release-Build erstellen
cargo build --release
# Cross-Compilation (Beispiel AArch64 / ARM64)
cargo build --release --target aarch64-unknown-linux-gnu
# Debian-Paket bauen (.deb)
cargo deb
# RPM-Paket bauen (.rpm)
cargo generate-rpm
# Arch Linux-Paket bauen (.pkg.tar.zst)
python3 scripts/package-arch.py --arch x86_64 --pkgrel 1
Hilfsskripte
scripts/get-build-number.py
Ermittelt dynamisch die nächste Build-/Paketnummer (pkgrel / revision).
- Liest Name und Version automatisch aus
Cargo.toml. - Fragt Gitea Releases und Package Registry ab, falls Token/URL vorhanden.
- Berücksichtigt lokal im Verzeichnis
target/liegende Pakete. - Aufruf:
python3 scripts/get-build-number.py
scripts/package-arch.py
Erstellt native Arch Linux-Pakete (.pkg.tar.zst), ohne dass makepkg oder eine Arch Linux-Installation erforderlich ist.
- Liest Konfiguration aus
[package.metadata.arch]inCargo.toml. - Installiert die Binary nach
/usr/bin/, sowieLICENSEundREADME.mdnach/usr/share/. - Erzeugt eine standardkonforme
.PKGINFO-Datei und komprimiert das Paket mitzstandard. - Unterstützt optional ein Install-Skriptlet (
[package.metadata.arch].install_script), das pacman-konform als<name>.installmitpost_install()/post_upgrade()eingebettet wird (bei mac2ip: legt/var/lib/mac2ipan). - Parameter:
--target: Rust Target-Triple (z. B.x86_64-unknown-linux-gnu)--arch: Zielarchitektur (z. B.x86_64,aarch64,i686)--pkgrel: Release-Nummer
CI/CD Workflow-Übersicht
| Branch | Workflow | Paket-Kanal | Release-Typ |
|---|---|---|---|
main |
.gitea/workflows/main.yaml |
stable |
Offizielles Release (v<VERSION>) |
testing |
.gitea/workflows/testing.yaml |
testing |
Pre-Release (v<VERSION>-preview) |
Lizenz
mac2ip steht unter der GPL-3.0-or-later-Lizenz.