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
44 lines
1.3 KiB
Go
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
|
|
}
|