Sensor Coverage — deployment guide

Audience: SOC engineer, network admin, anyone deploying ARP Guardian in a real network. Read this before rolling out sensors. The default lab works out-of-the-box because of vSwitch promiscuous mode — production usually doesn't, and detection silently degrades.

If you just want to check what your sensor sees right now:
sudo arpg-sensor doctor --coverage 30 -i ens33

---

1. The problem in one paragraph

A typical sensor sits on a NIC on a switched network. By default the switch delivers to that port only:

  • broadcast frames (ARP requests on ff:ff:ff:ff:ff:ff, DHCP discover, …)
  • multicast that the NIC subscribed to
  • unicast addressed to the NIC's own MAC

…and nothing else. Unicast frames between two other hosts on the same VLAN (e.g. an ARP reply from gateway to victim) are filtered out by the switch's MAC-table. So a sensor on such a network sees roughly half of ARP traffic: the broadcasts. Detection rules that need to compare the attacker's unicast reply against the truth binding (BIND-FLIP, SIG-PROTECTED-FLIP, MAC-FLAP) will silently degrade — they fire only when the attack happens to be broadcast-shaped.

ARP Guardian tags every sensor with a coverage level (full / partial / broadcast_only / silent) and shows it on the Sensors page so this gap is visible, not silent.

2. Three deployment models

One small sensor on each protected host. Each sensor sees its own send/recv plus broadcasts. The controller correlates across all sensors and reconstructs the full picture.

                ┌──────────────┐
                │  controller  │  ← receives heartbeats + detections
                └──────┬───────┘
                       │ NATS
   ┌───────────┬───────┴───────┬───────────┐
   │           │               │           │
┌──▼──┐    ┌──▼──┐         ┌──▼──┐    ┌──▼──┐
│host1│    │host2│         │host3│    │host4│
│ +S  │    │ +S  │         │ +S  │    │ +S  │
└─────┘    └─────┘         └─────┘    └─────┘

Why: zero infrastructure work. Works on cloud VPCs (where promiscuous is forbidden), Kubernetes nodes, mixed hypervisors, bare-metal — anywhere a Linux binary runs. Linear scale; each new host enrolls itself.

Install (one-liner, per host):

curl -fsSL https://controller.example.com/install-sensor.sh \
  | sudo bash -s -- --token CEE6-4HH6-RK36-2VBD --controller https://controller.example.com

Generate the token in Sensors → Generate token in the UI. Token is one-time-use, TTL 60 min by default. Post-install runs arpg-sensor doctor --coverage 10 and prints the verdict.

Limitations:

  • Hosts without a sensor are invisible. If an attacker is on a host you don't
  • control (rogue laptop, BYOD), distributed mode sees only the broadcast portion of their attack on the wire.

  • Mitigation: combine with Model B for "uncontrolled" segments (printers,
  • IoT, BMC).

Model B — Centralized + SPAN/mirror (legacy + IoT)

One sensor per segment, attached to a switch SPAN port that mirrors all VLAN traffic. Sensor sees everything.

                ┌──────────────┐
                │  controller  │
                └──────┬───────┘
                       │ NATS
                  ┌────▼────┐
                  │ sensor  │
                  └────┬────┘
                       │ SPAN
                  ┌────▼─────┐
                  │  switch  │
                  └──┬─┬─┬─┬─┘
                     │ │ │ └── IoT device
                     │ │ └──── printer
                     │ └────── BMC
                     └──────── … any unprotected host

When to use: segments where you can't install host-agents (printers, IPMI BMC, IoT, network gear management, legacy boxes).

Cisco IOS / IOS-XE (SPAN session):

configure terminal
monitor session 1 source vlan 10
monitor session 1 destination interface GigabitEthernet0/24
end
write memory

Aruba ProVision:

mirror 1 port 24
vlan 10
   monitor port 1-23 mirror 1
exit

Juniper EX (port-mirror):

set forwarding-options analyzer my-mirror input ingress vlan vlan10
set forwarding-options analyzer my-mirror output interface ge-0/0/24.0
commit

MikroTik:

/interface ethernet switch port set ether24 mirror-source=ether1-23 mirror-target=ether24

After config, plug the sensor host into the destination port and install it:

curl -fsSL https://controller.example.com/install-sensor.sh | sudo bash -s -- --token XXXX

Model C — Hybrid (most real enterprises)

Distributed agents on hosts you control; one centralized SPAN sensor on segments full of unmanaged devices. Same UI — just more rows on the Sensors page.

3. Virtualization-specific notes

VMware vSphere (ESXi)

VMware vSwitches filter unicast by default. To give a VM full L2 visibility:

  1. vSphere Client → ESXi host → Networking → port group of the
  2. sensor VM.

  3. Click Edit.
  4. Under Security:
    • Promiscuous mode: Accept
    • MAC address changes: Accept
    • Forged transmits: Accept
  5. Save.

Distributed VMs (one sensor per VM) work without promiscuous mode — they only need to see their own traffic.

KVM / libvirt (Linux bridge)

Out of the box: Linux bridges flood everything to every tap. A sensor VM on a KVM bridge sees full coverage automatically.

If you've turned on bridge MAC filtering (rare), disable it on the sensor port:

sudo brctl setageing br0 0      # disable MAC aging on the bridge

Hyper-V

Hyper-V Manager → Virtual Switch Manager → External vSwitch → enable Allow management OS to share this network adapter is not enough.

For a SPAN-style sensor VM use Port mirroring mode:

Set-VMNetworkAdapter -VMName 'arpg-sensor' -PortMirroring Destination
Set-VMNetworkAdapter -VMName 'web-app-01'  -PortMirroring Source
Set-VMNetworkAdapter -VMName 'db-01'       -PortMirroring Source

(repeat Source for every VM whose traffic you want mirrored).

Docker / Kubernetes

The default Docker bridge docker0 is a Linux bridge — it floods. A container sensor sees all container-container traffic on the same network. External traffic (NAT'd) appears with the gateway MAC, not the original.

For Kubernetes — distributed model is the only safe choice (one DaemonSet sensor per node). Multi-CNI environments don't usually expose SPAN.

Cloud (AWS / GCP / Azure)

Cloud VPCs block promiscuous mode for tenants. Two options:

  • Distributed model (preferred): host-agent on each EC2/GCE/VM.
  • Cloud-native mirroring:
    • AWS: VPC Traffic Mirroring → mirror target is the sensor ENI.
    • GCP: Packet Mirroring → collector is the sensor instance.
    • Azure: vTAP (preview) or NSG flow logs (limited).

Cloud mirroring costs extra. Distributed is free.

4. Diagnostic: arpg-sensor doctor --coverage

Runs a 30-second capture (or whatever you pass as the arg), classifies the result, prints a verdict, exits with a code that maps to coverage level.

$ sudo arpg-sensor doctor --coverage 30 -i ens33
ARP Guardian sensor — coverage check
interface : ens33
listen    : 30s

own MAC   : 00:0c:29:23:a8:1b

listening ...

============================================================
Frames seen          : 14482
  broadcast          : 826
  multicast          : 1238
  unicast TO us      : 16299
  unicast to OTHERS  : 10195   ← decisive signal
Unique source MACs   : 10
VLANs observed       : [10]
============================================================
verdict              : FULL coverage  ✓
  This sensor sees unicast between other hosts.
  Likely cause: vSwitch promisc / SPAN port / TAP / Linux bridge.
Verdict Exit Meaning
FULL coverage 0 Everything visible. All rules active.
PARTIAL coverage 1 Own unicast + bcast only. Cross-host unicast missed.
BROADCAST-ONLY 2 Switched ethernet without mirroring.
SILENT 3 No traffic. Iface down or wrong VLAN.
Iface error 4 -i interface doesn't exist.
Cap missing 5 Needs root or setcap cap_net_raw,cap_net_admin+eip.

Use the exit code in scripts:

sudo arpg-sensor doctor --coverage 15 -i ens33 || {
  echo "Coverage check failed (exit=$?). See docs/SENSOR-COVERAGE.md"
  exit 1
}

5. UI: what coverage looks like in the console

  • Dashboard → KPI "Coverage Quality": composite score across all enabled
  • sensors. 100% = every sensor full. Click → Sensors page.

  • Sensors page → row badge: FULL / PARTIAL / BCAST-ONLY / SILENT /
  • COV ?.

  • Sensors page → click a row: Coverage panel shows the 5-minute rolling
  • measurements and which detection rules are effective given that coverage.

6. Troubleshooting

"My sensor went from FULL to BROADCAST-ONLY overnight"

Network team probably rebooted a switch or someone disabled the SPAN session. Re-run arpg-sensor doctor --coverage to confirm, then check upstream switch config. If you can't restore SPAN quickly, deploy host-agents on the visible hosts in that segment as a temporary measure.

"Coverage badge says PARTIAL but I configured SPAN"

SPAN sometimes only mirrors ingress or egress, not both. Check the monitor session direction (Cisco: both is the default; Aruba defaults to in). Also: some switches drop SPAN traffic when CPU is busy.

"Sensor shows SILENT but interface is UP and I see arp on the wire"

The sensor binary is running but doesn't have raw socket caps. Run:

sudo setcap cap_net_raw,cap_net_admin+eip /usr/local/bin/arpg-sensor
sudo systemctl restart arpg-sensor

"Sensor shows COV ? (unknown)"

The sensor is running pre-Coverage firmware (v0.1.x). Update the binary:

sudo arpg-sensor unenroll --keep-config
curl -fsSL https://controller.example.com/install-sensor.sh | sudo bash -s -- --token XXX

"I deployed distributed sensors but only half are showing in UI"

Sensor heartbeats are HMAC-signed (if you enrolled them) — check journalctl -u arpg-sensor for NATS connect failed or HMAC mismatch. For brand-new sensors, the Audit Log → action=sensor.heartbeat_rejected shows the reason.

7. FAQ

Q: Distributed model — what if attacker compromises a host and disables the sensor? A: Same threat model as any host-agent (antivirus, EDR). Mitigation:

  • Make the sensor immutable (systemd ProtectSystem=strict, ReadOnlyPaths).
  • Alert on sensor.disable / unenroll actions (already in Audit Log).
  • Combine with Model B for critical segments — a SPAN-fed sensor doesn't run
  • on the protected host.

Q: Can one sensor cover multiple VLANs? A: Yes, on a 802.1q trunk port. Create sub-interfaces on the host:

sudo ip link add link ens33 name ens33.10 type vlan id 10
sudo ip link add link ens33 name ens33.20 type vlan id 20
sudo ip link set ens33.10 up
sudo ip link set ens33.20 up

Then run one arpg-sensor live per sub-interface (separate --sensor-id). Each will report its own VLAN.

Q: TAP vs SPAN — when does it matter? A: Hardware TAPs are passive (cannot lose frames under load, cannot be disabled by a misconfigured switch, can't be detected by an attacker). SPAN/mirror is good enough for most environments. Use TAPs for financial / regulated / forensic-grade deployments.

Q: How big a performance hit does coverage measurement add? A: ~50ns per frame (hashset insert + 3 counter increments) on top of the already-cheap parse hot path. Negligible up to ~1 Mfps.

---

See also