ARP scanning tool (Rust). On Linux, scan performs address resolution protocol discovery across the selected interface’s IPv4 subnet using raw AF_PACKET / SOCK_RAW sockets. On macOS it does the same over a Berkeley Packet Filter (/dev/bpf*) device, with identical command-line flags, output, and exit codes. monitor listens on that same path without transmitting and reports local IPv4 address conflicts.
Copyright © Peter Aleksander Bizjak.
Licensed under the GNU Affero General Public License v3.0 only. See LICENSE.
new-arp-scan interfaces
new-arp-scan monitor [--interface <NAME>] [--timeout-ms <MILLISECONDS>]
new-arp-scan scan [--interface <NAME>] [--host <IPv4>] [--timeout-ms <MILLISECONDS>] [--pacing-ms <MILLISECONDS>] [--attempts <COUNT>] [--bandwidth <BITS_PER_SECOND>] [--interval-ms <MILLISECONDS>] [--backoff <FACTOR>] [--mac-vendor-file <PATH>] [--vlan <VID>] [--pcp <PRIORITY>] [--dei] [--svlan <VID>] [--spcp <PRIORITY>] [--sdei] [--padding <HEX>] [--arpspa <IPv4|dest>] [--llc] [--destaddr <MAC>] [--srcaddr <MAC>] [--arpsha <MAC>] [--arptha <MAC>] [--arphrd <UINT>] [--arppro <UINT>] [--arphln <UINT>] [--arppln <UINT>] [--arpop <UINT>]
- On Linux and macOS,
interfaceslists interfaces that are usable for ARP scanning (Ethernet hardware type, administratively up, not loopback, notNOARP, with an IPv4 address, netmask, and a non-zero hardware address). Output is a plain aligned table; if none qualify, the tool printsno usable interfaces foundand exits successfully. macOS enumerates interfaces withgetifaddrs(3)rather than Linuxioctl, but applies the same usability rules. - On Linux and macOS,
monitorlistens on the selected interface and transmits nothing: no scan requests, probes, announcements, or address, DHCP, or routing changes. Interface selection and privileges matchscan.--timeout-msdefaults to30000and must be at least1(zero is a usage error). The listen covers every IPv4 address configured on that interface name. A request or reply is a local conflict when its sender protocol address is one of those addresses and its sender hardware address is not the interface MAC; frames that match both the local MAC and a local IPv4 address are suppressed. Other well-formed ARP is printed asobserved. A nonzero, nonlocal sender protocol address claimed by two or more hardware addresses is a separateduplicate-ipline, not a local conflict. This is a diagnostic report, not RFC 5227 address conflict detection: there is no announcement, defense, abandonment, orDEFEND_INTERVALbehavior. ARP is unauthenticated, so a reported conflict can be spoofed. After the window the tool prints labeled lines with opcode, sender and target hardware and protocol addresses, and a repeat count. With no local conflict it printsno conflicts observedand still exits successfully. Warnings and onemonitor complete: interface <NAME>, <N> conflict(s), <N> observation(s), <N> duplicate-ip claim(s), <MS> msline go to standard error. At most 4,096 distinct packet records are retained; one truncation warning is printed and later new identities are dropped. Those dropped packets do not create or extendduplicate-ipclaims. There is no vendor lookup, timestamp, or JSON output, and scan-only flags are rejected. On Linux the socket is bound toETH_P_ALLso the existing VLAN and SNAP parsers can see frames; the kernel still strips the outermost VLAN tag and this command does not requestPACKET_AUXDATA. On macOS it reuses the scan BPF device with sent frames already hidden. Alias names such aseth0:1stay separate interfaces. - On Linux,
scanreads the interface IPv4 address, netmask, and Ethernet hardware address viaioctl, opens a raw packet socket bound to ARP (ETH_P_ARP) — or to every Ethernet protocol (ETH_P_ALL) when--vlan,--svlan, or--llcis set — then runs--attemptsfull rounds (default1). With--host <IPv4>, each round sends one broadcast ARP request for that address only; the address must be strictly interior on the interface subnet (not the network or broadcast address, and not off-subnet). Otherwise each round sends one broadcast ARP request per target address in the subnet (excluding network and broadcast, but always including the interface’s own IPv4 address when it falls outside that open range). Between rounds it sleeps--pacing-msmilliseconds after each round except the last (default0). After the last round it collects replies until--timeout-mselapses (default3000). In single-host mode, only replies whose sender IPv4 equals--hostare recorded; timing flags behave the same as for a full subnet scan. Values larger than Linuxpoll(2)accepts in milliseconds are clamped internally. Discovered hosts are printed as<IPv4> <MAC>on standard output in ascending IPv4 order; with--mac-vendor-file <PATH>,ieee-oui.txtin the current directory, or a--features bundled-mac-vendorsbuild, the line becomes<IPv4> <MAC> <vendor>using longest-prefix IEEE MA-L / MA-M / MA-S / IAB matching, or(Unknown)when no prefix matches. The library represents each MAC asMacAddressonDiscoveredHost::media_access_control_address(colon-separated lowercase hex, same as the binary). Non-fatal issues (for example a failed send, a malformed ARP frame, or a conflicting duplicate address resolution reply for the same IPv4) are reported aswarning: ...lines on standard error. After the scan’s standard output lines, the binary prints one timing summary line on standard error with the stable templatescan complete: interface <NAME>, <N> host(s), <R> round(s), <MS> ms(singularhost/roundwhen the count is one). If nothing responds, the tool printsno hosts foundon standard output, still prints the timing summary on standard error, and exits successfully. An invalid--hostfor the selected interface (for example the subnet network address) exits with an error before opening the socket. - Outbound rate limiting is opt-in and is not congestion control.
--bandwidthand--interval-msare mutually exclusive; using both is a usage error (exit code 2). Without either flag, each round still sends every target as fast as the socket allows, and--pacing-mssleeps only between rounds. With either flag, the first send is immediate and each later send waits until the previous send completed plus a strict minimum interval. A send that finishes late does not cause a catch-up burst, and a failed send still consumes its schedule slot.--bandwidthis a positive decimal integer with an optional case-insensitiveK(1,000) orM(1,000,000) suffix. The derived interval is the ceiling ofmax(encoded frame octets + 4-octet FCS, 64) * 8 / bits per secondin nanoseconds, so 256 kbit/s on a minimum-size frame is 2 ms. VLAN tags, LLC/SNAP, and--paddingchange that interval only when they make the encoded frame longer than the 60-octet minimum (without FCS).--interval-mssets the same gap directly in whole milliseconds (minimum 1). On the rate-limited path the scanner receives after each round, not after every frame, fortimeout * backoff^(round-1). Replies can therefore queue during a paced round. The default backoff is1.5when a rate is set and--backoffis omitted.--backoffrequires a rate flag and must be a finite factor of at least1. Answered targets are removed, later rounds send only what is still unanswered, and the scan stops when none remain. Before another round, the next send is no earlier than both the strict inter-send deadline and the end of that receive window, and--pacing-msis extra delay after that point. The default burst path still uses one receive window after the last round and ignores--backoff. Invalid or overflowing schedules are rejected before interface discovery or a raw socket is opened. - On macOS,
scanbehaves the same as on Linux (interface resolution, target expansion, rounds, pacing, timeout,--host,--arpspa,--llc, output, and exit codes) but uses a Berkeley Packet Filter device: it reads the interface IPv4 address, netmask, and Ethernet address withgetifaddrs(3), opens/dev/bpf*, attaches it to the interface, installs a filter so it captures untagged ARP, IEEE 802.1Q-tagged ARP, IEEE 802.1ad service-tagged ARP, and RFC 1042 LLC/SNAP ARP under any of those framings, and reads/writes complete Ethernet frames. - Transmitted frames are RFC 826 Ethernet II ARP requests, zero-padded to the IEEE 802.3 60-octet minimum without the frame check sequence, unless
--llcis set. With--vlan <VID>(0..=4095) each request carries a single IEEE 802.1Q tag (TPID0x8100; PCP and DEI default to zero).--pcp <0..=7>and--deirequire--vlanand fill the rest of the TCI.--svlan <VID>(0..=4095) requires--vlanand wraps that customer tag in an IEEE 802.1ad service tag (S-TAG, TPID0x88A8), producing the interoperable two-tag QinQ frame;--spcp <0..=7>and--sdeirequire--svlanand fill the service TCI. Omitted service PCP and DEI are zero.--svlanwithout--vlan, or--spcp/--sdeiwithout--svlan, is a usage error (exit code 2). With--llc, requests use IEEE 802.3 length plus RFC 1042 LLC/SNAP (AA AA 03+ OUI00:00:00+ EtherType0x0806); the length field is LLC+SNAP+ARP (36 octets for IPv4 ARP) plus any--padding.--padding <HEX>appends hex-encoded octets (no0xprefix, even number of digits) after the ARP PDU; the frame is still zero-padded to 60 octets when shorter. Padding that would exceed the 1500-octet IEEE 802.3 MAC client data maximum is rejected.--arpspa 0.0.0.0sends an RFC 5227 ARP Probe;--arpspa destsends an RFC 5227 ARP Announcement (ar$spaequals each target); any other--arpspa <IPv4>overrides the sender protocol address. Omitted--arpspauses the interface IPv4 address.--destaddrsets the Ethernet destination (default broadcast);--srcaddrsets the Ethernet source (default interface MAC) independently of--arpsha(RFC 826ar$sha).--arptha,--arphrd,--arppro,--arphln,--arppln, and--arpopoverride the remaining ARP header fields with originalarp-scannames and RFC 826 defaults. Received frames may be Ethernet II, a single IEEE 802.1Q customer tag, one IEEE 802.1ad service tag (TPID0x88A8) wrapping exactly one customer tag (TPID0x8100), or RFC 1042 LLC/SNAP under any of those framings; PCP, DEI, and VID are decoded for both tags. Every other tag arrangement is rejected so the innerEtherTypeis never read from the wrong offset: a service tag with no customer tag, a tag stacked inside a customer tag (including two0x8100tags and the reverse customer-then-service order), three or more tags, and the unofficial TPIDs0x9100/0x9200/0x9300. Note that the Linux kernel always strips the outermost VLAN tag from received frames before userspace sees them (a single-tagged reply arrives untagged, and an IEEE 802.1ad reply arrives with only the customer tag inline; hosts are still recorded), a NIC or avlanNsub-interface may strip further, and no decoder can recover a tag that was already removed; on macOS the BPF tap delivers both tags inline. Well-formed ARP that is not a reply (for example a request on the LAN, or a copy of our own request) is ignored without a warning; RFC 5494 reserved opcodes still warn as malformed.--hostis a single-target scan of one interior IPv4 address, not itself an RFC 5227 Probe. - On Linux and macOS, when
scanis run without--interface/--iface, the tool selects an interface automatically only when exactly one usable interface exists; otherwise it exits with an error that names the ambiguity or states that no usable interface was found. - On operating systems without a raw link-layer backend,
scan,monitor, andinterfacesreturn an unsupported-platform error without calling platform-only APIs. A zeromonitortimeout is rejected before that check.
Creating the raw packet socket requires Linux capability CAP_NET_RAW (often available to the superuser); permission denied when opening the socket is surfaced with an explicit CAP_NET_RAW hint. On macOS, opening a Berkeley Packet Filter device requires access to /dev/bpf* — typically root (run with sudo), unless your system grants BPF access to your user. See Linux packet(7) / capabilities(7) and macOS bpf(4).
To verify frames on the wire, run tcpdump or Wireshark on the same interface (for example tcpdump -ni eth0 arp, or tcpdump -ni eth0 'vlan and arp' when using --vlan, or tcpdump -ni eth0 'ether proto 0x88a8 and ether[16:2] == 0x8100 and ether[20:2] == 0x0806' when using --svlan (libpcap's own vlan and vlan and arp also works but is broader: its vlan primitive matches 0x8100, 0x88A8, and 0x9100 alike), or tcpdump -ni eth0 without an arp filter when using --llc because SNAP uses an IEEE 802.3 length field rather than EtherType 0x0806, or tcpdump -ni en0 arp on macOS) while scanning; this is optional manual validation and is not part of automated tests. For a full acceptance check on hardware you control, run a privileged scan (for example sudo ./target/debug/new-arp-scan scan --interface eth0, or sudo ./target/debug/new-arp-scan scan --interface en0 on macOS) or a single-host probe (for example sudo ./target/debug/new-arp-scan scan --interface en0 --host 192.168.1.50) and compare custom --timeout-ms, --pacing-ms, --attempts, --vlan, --pcp, --svlan, --spcp, --padding, --arpspa, and --llc values with the defaults documented above. A privileged listen is the same class of manual check: sudo ./target/debug/new-arp-scan monitor --interface eth0 (or en0 on macOS). tcpdump will not show frames sent by monitor, because that command does not transmit. CI never runs these privileged commands.
Run new-arp-scan --help, new-arp-scan interfaces --help, new-arp-scan monitor --help, or new-arp-scan scan --help for built-in examples.
scan never downloads IEEE listings. Vendor annotation is opt-in and uses a local ieee-oui.txt mapping (arp-scan format: <hex-prefix><TAB><vendor>).
Search order
--mac-vendor-file <PATH>ieee-oui.txtin the current working directory- A compile-time embedded snapshot, only when the binary was built with
--features bundled-mac-vendors
If none of those are present, host lines stay <IPv4> <MAC>. When a registry is loaded and no prefix matches, the vendor column is (Unknown).
Refresh the mapping (operators and release builders)
make update-mac-vendorsThat runs the workspace tool mac-vendor-updater, which uses system curl (HTTPS only) to fetch IEEE MA-L, MA-M, MA-S, and IAB CSVs, converts them, validates the result with MacVendorRegistry, and atomically replaces ./ieee-oui.txt. Duplicate assignments keep the last source row; stderr prints source, emitted, and removed counts. The generated file starts with a provenance header (UTC retrieval timestamp and the four source URLs). ieee-oui.txt is gitignored and is not committed.
There is no 24-hour lockout and no automatic refresh cadence: regenerate when you need newer assignments. CI never contacts IEEE; tests use fixtures and mac-vendor-updater --from-dir.
Release binaries that embed the snapshot
Default and CI builds leave bundled-mac-vendors off. To embed the generated file in a release binary:
make update-mac-vendors
cargo build --release --features bundled-mac-vendorsOr set NEW_ARP_SCAN_BUNDLED_MAC_VENDOR_FILE to another mapping path at compile time. Feature-only packaging does not refresh GitHub Releases for you; builders must run the updater first.
IEEE Registration Authority listings were authorized for this project by the owner on 2026-09-17. Any further attribution or redistribution terms in that permission are not checked into this repository.
The binary uses a minimal, deterministic exit code contract:
0— successful command, includingno hosts found,no conflicts observed,no usable interfaces found, printing help when invoked with no arguments, and successful--helpinvocations.1— any operational failure returned from the library (AppError), including unsupported platform, invalid interface or target, and raw socket errors (including missingCAP_NET_RAWwhen reported as permission denied).2— command-line usage or parse errors from the argument parser (typically unknown flags or invalid flag values).
- Rust toolchain with Cargo (
rustc,cargo fmt,cargo clippy) - GNU Make (optional but recommended for the targets below)
| Command | Description |
|---|---|
make build |
cargo clean then cargo build --release |
make test |
cargo test --workspace, cargo test --tests, then fixture-bundled --features bundled-mac-vendors |
make lint |
cargo fmt --all, then clippy with and without --all-features (-D warnings) |
make coverage |
cargo llvm-cov --workspace --all-targets --summary-only unbundled and fixture-bundled (install once: cargo install cargo-llvm-cov; first run may need rustup component add llvm-tools-preview) |
make update-mac-vendors |
Fetch IEEE MA-L / MA-M / MA-S / IAB CSVs with curl and atomically write ieee-oui.txt (network; not run in CI) |
make clean |
cargo clean |
Run the same commands manually if you prefer not to use Make.
See CONTRIBUTING.md.
Additional notes live under docs/:
| Guide | Audience |
|---|---|
| Contributor onboarding | First-time build, lint, test, and pull-request checklist |
| Architecture overview | Module map, unsafe boundaries, packet flow, testing strategy |
| Linux platform support | AF_PACKET / raw sockets, capabilities, CI vs local testing, namespaces |
| macOS platform support | Berkeley Packet Filter, root requirements, interface naming, tcpdump validation |
| Operator reference (HTML) | CLI behavior, output, and library overview (static site) |