Documentation · v0.3.0

LAN Fence documentation

Everything you need to run a scan, tune it, and read the output. For installation see Download & install.

Concepts

LAN Fence combines two ways of seeing the network:

ModeWhat happens
activeAn ARP sweep of the subnet — lanfence scan runs one, lanfence monitor repeats it every scan_interval_seconds.
passiveInside lanfence monitor, LAN Fence also listens for ARP traffic on the wire between sweeps, so a device that joins mid-interval is caught sooner.

Every sighting is folded into a persistent SQLite database keyed by MAC address, which tracks each device's lifecycle: new_device the first time it's ever seen, reappeared if it had gone offline and came back, and disconnected when an active sweep no longer sees it. Only a device newly joining or reappearing produces a finding — a plain lifecycle disconnect does not.

Fingerprinting

Each device is checked against three things: an offline OUI → vendor lookup, the built-in rogue-device signatures (matched against vendor and hostname), and whether its MAC is locally administered — the U/L bit set, indicating a randomized or spoofed address rather than one assigned by a hardware vendor. Extend the vendor table and signatures with your own vendor_file: / rogue_signatures_file: in config — both are merged with the packaged data, never replace it.

Commands

CommandPurpose
lanfence scanOne-time active ARP scan; prints the device table and any findings.
lanfence monitorContinuous: active sweeps on an interval plus passive ARP sniffing, dispatching alerts as findings occur, until Ctrl+C.
lanfence reportSummarize device events and findings from the database over a window (--since 24h).
lanfence allow [MAC]Trust a device (--name, --notes); findings about it are downgraded to info. --list shows entries, --remove untrusts one, no arguments also lists.
lanfence checkVerify the host — root, scapy, interface/subnet auto-detection, database writability — before a run.
lanfence linkSymlink the launcher onto root's PATH (default /usr/local/bin, --bin-dir to choose) so sudo lanfence works. Re-execs under sudo itself; --remove undoes it, --no-sudo skips the escalation.
lanfence upgradeCheck PyPI directly for a newer release and run the right upgrade command for how this copy was installed. --check reports without installing.

scan / monitor options

OptionMeaning
--interface, -iNetwork interface to scan/monitor. Default: auto-detect.
--subnet, -sCIDR subnet to sweep. Default: derived from the interface's own address.
--interval(monitor) Active-sweep interval override, in seconds.
--passive / --no-passive(monitor) Also passively sniff ARP traffic between sweeps. Default: on.
--alert(scan) Dispatch alerts for findings via configured channels. Default: off.
--alert / --no-alert(monitor) Dispatch alerts as findings occur. Default: on.
--format, -ftable or json. Default: table.
--config, -c FILEYAML config file.
--fail-on-findings(scan / report) Exit non-zero when medium+ findings are present.
--verbose, -vRepeatable: -v info logs, -vv debug.

Usage

# Check the host is ready
lanfence check

# One-time active ARP scan
sudo lanfence scan
sudo lanfence scan --format json | jq .

# Continuous monitoring: active sweeps + passive sniffing, with alerts
sudo lanfence monitor
sudo lanfence monitor --interface eth0 --interval 30

# Trust your own hardware so repeat sightings stay quiet
lanfence allow 52:8a:1c:99:f4:2d --name "Ross's laptop"
lanfence allow --list
lanfence allow --remove 52:8a:1c:99:f4:2d

# Summarize the last day, and fail CI if anything medium+ showed up
lanfence report --since 24h --fail-on-findings

sudo: lanfence: command not found? A pipx / pip install --user install puts lanfence in ~/.local/bin, which is not on root's PATHsudo resets PATH to a fixed secure_path. Run lanfence link once — it re-execs under sudo and symlinks the launcher into /usr/local/bin, so sudo lanfence then just works. (Or use sudo "$(which lanfence)" … ad hoc.)

Exit codes

lanfence scan --fail-on-findings and lanfence report --fail-on-findings exit based on the highest finding severity in the result:

Highest severityExit code
none / info0
medium10
high20

Without the flag, both commands always exit 0 on a completed run.

10 built-in rogue-device signatures

Heuristics, not proof — a MAC vendor prefix or hostname is trivially spoofed by anyone who wants to hide, so a match here is a lead to investigate, not a verdict. Alongside these keyword signatures, any device with no vendor OUI match and a locally administered MAC (the U/L bit set — common for MAC-randomization privacy features, VMs/containers, or spoofed hardware) is flagged as locally_administered_mac, severity info.

SignalCategorySeverityWhy it matters
Vendor contains "espressif" esp32_esp8266 medium ESP32/ESP8266 Wi-Fi chipset. Extremely common in legitimate IoT (smart plugs, sensors), but also the basis for cheap DIY hidden cameras, rogue access points, and ESP32-based Wi-Fi implants.
Vendor contains "raspberry pi" raspberry_pi info Widely used for entirely legitimate purposes, but also the common hardware basis for network-tap / rogue-implant projects (P4wnP1, home-built taps).
Vendor contains "asix electronics" usb_ethernet_gadget medium USB-to-Ethernet chipset used both by ordinary dongles and by commercial BadUSB / implant tools (Bash Bunny, LAN Turtle, O.MG cable) presenting as a wired adapter.
Hostname contains "pwnagotchi" pwnagotchi high Pwnagotchi, an offensive Wi-Fi handshake-capture tool. Its default hostname is distinctive; a real one on your network is not accidental.
Hostname contains "bashbunny" bash_bunny high Hak5 Bash Bunny, a USB attack platform that can present itself as a network adapter once plugged into a host.
Hostname contains "lanturtle" lan_turtle high Hak5 LAN Turtle, a covert USB-Ethernet implant designed to be left plugged into a network for remote access.
Hostname contains "pineapple" wifi_pineapple high Hak5 WiFi Pineapple, a rogue-access-point / Wi-Fi auditing platform. Legitimate on an authorized pentest; otherwise a strong signal of an unauthorized AP.
Hostname contains "flipper" flipper_zero medium Flipper Zero, a multi-tool device that can also appear on the network via its Wi-Fi dev board add-on.
Hostname contains "omg-cable" omg_cable high An O.MG Cable — a USB cable with an embedded implant that can expose a Wi-Fi access point / network interface for remote control.
Hostname contains "esp32" esp32_esp8266 info Suggests an ESP32-based device — the weaker, hostname-only counterpart to the vendor-based ESP32 signature.

Extend or override these with your own rogue_signatures_file: (same YAML shape as lanfence/data/rogue_signatures.yaml, with vendor_keywords / hostname_keywords lists) and vendor_file: (same tab-separated shape as lanfence/data/oui_vendors.txt) in config — both are merged with the packaged data.

Allowlist

lanfence allow <mac> --name X registers a device you trust. When that device next produces a new_device or reappeared event, its finding's severity is downgraded to info and the title and rationale note that it's allowlisted — the finding still appears, it just stops being loud. lanfence allow --list shows current entries; lanfence allow --remove <mac> untrusts one. The allowlist lives at allowlist_file (default ~/.config/lanfence/allowlist.yaml) and is written atomically.

Alerting

Findings can be dispatched to up to three channels, gated by alerts.min_severity (default medium): syslog (local socket, configurable facility), email (SMTP, with or without TLS) and a webhook (HTTP POST to a URL you choose). lanfence monitor dispatches alerts by default as findings occur (--no-alert to disable); lanfence scan only dispatches when you pass --alert. The only network destinations LAN Fence ever contacts are the ones you configure here.

Configuration

Optional YAML config, passed with --config lanfence.yaml to any command. Every field is optional and has a sensible default.

scan:
  interface: null              # null = auto-detect
  subnet: null                 # null = derive from the interface's own address
  scan_interval_seconds: 60    # how often `monitor` repeats an active sweep
  active_scan_timeout_seconds: 3
  passive: true                # also sniff ARP traffic between sweeps
  resolve_hostnames: true      # try reverse DNS for each device
  dns_timeout_seconds: 1

alerts:
  min_severity: medium         # info | medium | high - dispatch threshold
  syslog:
    enabled: false
    address: /dev/log
    facility: user
  email:
    enabled: false
    smtp_host: localhost
    smtp_port: 587
    use_tls: true
    username: null
    password: null
    from_addr: null
    to_addrs: []
  webhook:
    enabled: false
    url: null
    timeout_seconds: 5

db_path: ~/.local/share/lanfence/lanfence.db
allowlist_file: ~/.config/lanfence/allowlist.yaml
vendor_file: null             # extra OUI table, merged with the packaged one
rogue_signatures_file: null   # extra signatures, merged with the packaged ones

Report & JSON structure

lanfence scan --format json prints one ScanResult for that sweep; lanfence report --format json --since 24h prints the events and findings accumulated across a time window from the database:

{
  "started_at": "...", "ended_at": "...",
  "interface": "eth0", "subnet": "192.168.1.0/24",
  "mode": "active | passive | active+passive",
  "devices": [
    { "mac", "ip", "hostname", "vendor", "status: online|offline",
      "first_seen", "last_seen", "allowlisted", "allowlist_name",
      "fingerprints" }
  ],
  "events": [
    { "mac", "event_type: new_device|reappeared|disconnected",
      "timestamp", "ip", "hostname" }
  ],
  "findings": [
    { "mac", "title", "severity: info|medium|high",
      "rationale", "recommendation", "evidence" }
  ],
  "errors": []
}

All device- and finding-supplied text (hostnames, vendor strings, evidence lines) is length-bounded and stripped of control characters before it reaches a report, a console line or an alert, so a hostile device on the network cannot inject terminal escapes or oversized fields.

Third-party dependencies

LAN Fence does not vendor third-party source code. It depends on a small set of Python libraries.

PackageLicenceNotes
typerMITCLI framework
pydanticMITdata models
PyYAMLMITconfig, allowlist and signatures parsing
richMITconsole rendering
scapyGPL-2.0-onlyoptional (lanfence[scan]) — only needed for scan/monitor's raw ARP send/receive; imported as an ordinary, unmodified dependency, never vendored
pytestMITdev only

See Security & trust for the note on distributing something built on LAN Fence alongside GPL-licensed scapy.

Scope & non-goals

  • CLI only — no web dashboard.
  • Observation only: LAN Fence sends nothing beyond a standard ARP "who-has" request. It never joins, deauthenticates, spoofs, blocks or throttles another device.
  • No offensive capability of any kind, by design.
  • Cannot prove a device is malicious, or that a MAC address is genuine — see the honest limit on Security & trust.

See Contributing for how to propose changes, and Security & trust for how to report vulnerabilities.