Skip to content

IP Address Management (IPAM)

IPAM owns pools and the address lifecycle: allocation, reservation, assignment, quarantine and release, with an append-only history trail.

IP pools

IP addresses

Canonical addresses

Every comparison, import and duplicate check goes through a canonical address value object: inet_pton-validated parse and IPv6 compressed to lowercase canonical form. A unique (pool_id, address) index is the raw-string backstop; application-layer uniqueness is canonical.

Pool types

allocation_type: subnet, range, routed_single or prefix_pool. Pools carry:

  • start_address / end_address (range pools),
  • allow_customer_extra_ips (whether secondaries may come from this pool),
  • quarantine_seconds (release quarantine window; 0 recycles immediately),
  • default_reverse_zone and rdns_driver (manual | provider | none).

Subnet pools of /22 or smaller and range pools are pre-expanded (network + broadcast + gateway skipped, idempotent). Larger subnets, routed_single and prefix_pool pools create rows on demand. IPv6 pools are never pre-generated.

Bulk import (admin)

previewBulkImport classifies every line without writing: valid, duplicates (existing or repeated), outside the pool, invalid, reserved (network/broadcast/gateway). The admin UI shows the buckets; the commit inserts the valid set in one transaction, defensively skipping rows that raced in.

Allocation algorithms

  • IPv4 — claim rows inside one transaction with row locks, conditional updates and the unique index as a hard guard.
  • IPv6 — compose addresses from the pool CIDR and insert directly, retrying on unique violations. A membership guard skips any composition outside the pool CIDR.
  • Prefix delegation (prefix_pool) — walk child prefixes under a pool lock, recycle expired quarantines first, then insert with a canonical collision check and a unique index backstop.

Address lifecycle

status: free → reserved → assigned, with quarantined between release and re-availability.

  • reserveSpecific — admin hold of a specific address.
  • releaseWithHistory — the single release path; honors the pool's quarantine window and always writes history.
  • quarantineSweep — returns expired quarantines to free.
  • promoteToPrimary — atomically demotes the old primary, flips is_primary and updates the instance's denormalized primary columns.

Concurrency guarantees

  1. Row locks serialize candidates per pool.
  2. Conditional updates with row-count assertions lose races safely.
  3. UNIQUE(pool_id, address) is the hard guard.
  4. Prefix allocation serializes on the pool row lock + canonical pre-check + unique index.
  5. Floating-IP transitions serialize on a per-address cache lock.

Audit

php artisan pterocloud:ipam:audit
php artisan pterocloud:ip:inspect <address>

pterocloud:ipam:audit checks the ledger invariants and exits 1 on findings.