Pakkit.net
← Back to blog

Infrastructure

When a Scan Can't See the Whole Network

A ping or ARP sweep tells you which hosts are awake right now, not which addresses are actually owned — so a scan is a useful sanity check, never your source of truth for IP allocation.

  • Infrastructure
  • Networking
  • Homelab
  • IPAM

I keep a small lab subnet for the virtual machines I spin up and tear down, and for a while I tracked free addresses the lazy way: sweep the range, see what answers, hand the next clone an address nothing replied on. It works right up until the day it doesn’t, and when it fails it fails quietly — two hosts on the same address, an ARP fight, a service that flickers for reasons that make no sense. The lesson I should have started with: a network scan tells you who’s awake, not who owns an address.

A sweep is a snapshot of who’s powered on

A ping or ARP sweep is a question asked at one instant: “who’s listening right now?” Anything powered off, mid-reboot, suspended, or firewalled into silence doesn’t answer — and a non-answer reads exactly like a free address. So the moment your environment contains anything that isn’t always on (a VM you shut down to save resources, a box that only boots for testing, a node that’s crashed), the scan starts lying to you by omission.

That’s the whole trap. The scan can only ever show you a lower bound on what’s in use. It can prove an address is taken; it can never prove one is free.

A ping sweep measures who picked up the phone, not who owns the number.

”Free on the wire” is not “free to use”

I started writing “free on the wire now” next to addresses that didn’t answer, because that phrase is the honest version of what a scan returns. An address can be free on the wire and still be reserved — for a host that’s off, for a appliance that ignores pings, for a future allocation someone already promised. Treating “didn’t respond” as “available” is how you reassign an address that has an owner who simply wasn’t home.

The dual-stack version is sneakier. I use a convention where a host’s IPv6 address mirrors its IPv4 last octet, so the two are supposed to move together. But an IPv6-only host consumes its v6 address while leaving the matching v4 address un-pinged — so a v4 sweep reports that octet “free” even though the pair is spoken for. Scan one protocol and you get a confidently wrong answer about the other.

The source of truth is a list you keep, not a scan you run

The fix isn’t a better scanner. It’s accepting that allocation is a record-keeping problem, not a discovery problem. The authoritative answer to “what’s using this address?” lives in a table I maintain on purpose — address, host, purpose, when it was assigned — and the scan’s only job is to check that table against reality, not replace it.

That table is what a “free” decision should be made from:

  • It remembers hosts that are powered off; a scan forgets them.
  • It records intent (“reserved for the next three nodes”), which no sweep can see.
  • It captures the dual-stack pairing so a v6-only allocation still blocks the matching v4 octet.
  • It gives you a history, so when something does collide you can see who got the address and when.

A scan is the smoke detector. The allocation table is the floor plan. You need both, but only one of them is allowed to be the source of truth.

Conventions only hold if something keeps them honest

The mirror-the-last-octet convention is great until someone (me, in a hurry) deviates — uses a memorable number instead of the matching one, or grabs an address out of order. A convention with no enforcement is a suggestion, and a suggestion drifts. So the table notes deviations loudly, and I reconcile it against a fresh sweep on a cadence: anything answering that isn’t in the table is a surprise to investigate, and anything in the table that’s been off for a long time gets re-verified before I trust the slot.

The point isn’t ceremony. It’s that the gap between “what I think is allocated” and “what’s actually on the wire” only grows if nothing ever compares the two.

The loop I run before assigning an address

When I need to place a new host now, the order is deliberate:

  1. Read the table first. Pick the lowest free entry it claims, including its dual-stack partner.
  2. Probe that specific address from a host on the segment — both protocols, not just one — to catch a silent occupant the table missed.
  3. Assign, then write it down immediately, before the next distraction. An allocation that lives only in my head is already lost.
  4. Reconcile periodically, treating mismatches in either direction as bugs to chase, not noise to ignore.

None of this is exotic; it’s just refusing to let a point-in-time measurement masquerade as a system of record. The same instinct shows up whenever I keep a map of my environments and in the messier homelab lessons that taught me to distrust a green light I didn’t earn. If you’ve got an IP allocation horror story — and if you run a lab, you do — I’m easy to reach.