Files
nmon/internal/hostaddr/ip.go
Vibe Coder c4d14a67d1 Add the optional IEEE vendor registry for MAC addresses and the l2/oui settings
The owner asked to see the vendor of each MAC where the IEEE registry can be
fetched, and never to depend on it. internal/oui downloads MA-L, MA-M and MA-S
weekly through the outbound allow-list (timeouts, a size cap and a row-count
sanity check, so a truncated or wrong download keeps the previous file), stores
one checksummed, atomically written file in <data-dir>/oui that nmon backup
includes, and looks MACs up by the longest prefix. A missing registry only
leaves the vendor empty. `nmon oui import DIR|FILE` loads the CSVs offline, and
the Ansible role can ship them next to the MIB sidecar.

internal/hostaddr parses the MAC, prefix, IP and CIDR forms the lookup accepts
and classifies a MAC as global, local or multicast. config gains the l2 and oui
sections (validated here; the L2 job that reads l2 follows).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4PdJUPrakM8fHvN8jYv2x
2026-10-24 01:30:11 +00:00

44 lines
1.3 KiB
Go

package hostaddr
import (
"encoding/hex"
"fmt"
"net/netip"
"strings"
)
// IPText is the canonical text of an address: IPv6 compressed, IPv4-mapped addresses as IPv4, no zone.
func IPText(a netip.Addr) string { return a.WithZone("").Unmap().String() }
// IPKey is a fixed-width hex form of an address that sorts in numeric order on every database collation, so a
// CIDR search is a range scan. IPv4 addresses use their IPv4-mapped IPv6 form so both families share one index.
func IPKey(a netip.Addr) string {
b := a.WithZone("").As16()
return hex.EncodeToString(b[:])
}
// KeyRange returns the inclusive IPKey range covered by a prefix.
func KeyRange(p netip.Prefix) (lo, hi string) {
p = p.Masked()
first := p.Addr()
bits := p.Bits()
if first.Is4() {
bits += 96 // the mapped form fixes the first 96 bits
}
b := first.As16()
last := b
for i := bits; i < 128; i++ {
last[i/8] |= 1 << (7 - uint(i%8))
}
return hex.EncodeToString(b[:]), hex.EncodeToString(last[:])
}
// ParseIP parses an IPv4 or IPv6 address (a zone id is dropped) into its canonical form.
func ParseIP(s string) (netip.Addr, error) {
a, err := netip.ParseAddr(strings.TrimSpace(s))
if err != nil {
return netip.Addr{}, fmt.Errorf("not an IP address: %q", s)
}
return a.WithZone("").Unmap(), nil
}