Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoRawSocket = errors.New("raw socket access denied")
ErrNoRawSocket is returned by Precheck when raw-socket access is missing.
Functions ¶
func Precheck ¶
Precheck verifies that the calling process can open libpcap on the chosen interface. It is a fast smoke test that pcap_open_live succeeds — exactly the same call ARPWorker will make. If it fails, the user has no raw socket privilege (Linux), Npcap is missing or not in WinPcap-API-compatible mode (Windows), or BPF read access is missing (macOS — install ChmodBPF or run with sudo), and the rest of the program will be useless.
Types ¶
type ARPWorker ¶
ARPWorker passively sniffs ARP packets on the given interface and emits an Update for every packet seen.
type ActiveWorker ¶
type ActiveWorker struct {
Subnet *net.IPNet
HostIPs []net.IP // pre-enumerated subnet hosts
Gateway net.IP // default-route gateway IP for the gateway-resolver hostname probe
Interval time.Duration
WorkerCount int
// KnownIPs returns the set of ARP-confirmed IP addresses (string form).
// When set, the worker bypasses the liveness gate for these IPs and
// runs the enrichment chain regardless — useful for hosts that
// stealth-drop ICMP/TCP probes (Windows 11 default firewall) but
// still respond to UDP-based queries like NBNS. Optional.
KnownIPs func() map[string]struct{}
}
ActiveWorker periodically probes every host in Subnet plus any IP it has learned about, calling probe.Ping, probe.ScanPorts, and probe.ResolveHostname. One full sweep emits one Update per responding host.
type Config ¶
type Config struct {
Iface *netiface.Info
MergerOptions MergerOptions
}
Config describes the scanner's runtime parameters.
type MDNSWorker ¶
type MDNSWorker struct{}
MDNSWorker browses for mDNS services on the given interface and emits an Update for each discovered instance.
type Merger ¶
type Merger struct {
// contains filtered or unexported fields
}
Merger owns the live device map and emits DeviceEvents.
func NewMerger ¶
func NewMerger(opts MergerOptions) *Merger
NewMerger constructs an idle merger; call Run to start consuming updates.
func (*Merger) KnownIPs ¶
KnownIPs returns the set of IP addresses owned by ARP-confirmed devices (those keyed by MAC). IP-only entries are excluded since they were created by the active worker itself and don't constitute independent confirmation.
Used by the active worker to skip the liveness gate for IPs we already know are real — e.g., Windows hosts that stealth-drop ICMP and TCP probes but were captured by the passive ARP listener.
type MergerOptions ¶
type MergerOptions struct {
StaleAfter time.Duration // Online → Stale after this idle period (default 60s)
LeftAfter time.Duration // Stale → Offline + emit Left after this idle period (default 5m)
SweepInterval time.Duration // how often to scan for status transitions (default 30s)
}
MergerOptions tunes the merger's timing behavior.
type Scanner ¶
type Scanner struct {
// contains filtered or unexported fields
}
Scanner wires the three workers and the merger together.
func (*Scanner) Events ¶
func (s *Scanner) Events() <-chan model.DeviceEvent
Events returns a read-only channel of DeviceEvent.
func (*Scanner) TriggerSweep ¶
TriggerSweep runs a single out-of-band active sweep using the same worker pool as the periodic scan. Safe to call concurrently with Run.
type Update ¶
type Update struct {
Source string // "arp" | "mdns" | "active"
Time time.Time // when the observation happened
MAC string // lowercase, colon-separated; "" if unknown
IP net.IP // required for nearly every update
Hostname string // mDNS or rDNS
Vendor string // OUI lookup may have already been applied
OpenPorts []model.Port // active prober only; empty replaces existing
Services []model.ServiceInst // mDNS only; appended/deduped
RTT time.Duration // active prober only
Alive bool // active prober: is the device responding?
TTL int // active prober only; raw TTL for OSDetect
NBNSResponded bool // active prober only; true when probe.NBNS returned a name
}
Update is what a Worker emits to the merger. Every field except Source is optional; the merger merges non-zero fields into the Device.
func SeedFromKernelARP ¶
SeedFromKernelARP reads the kernel's ARP cache from /proc/net/arp and returns one Update per neighbor that the kernel has already resolved for ifaceName within subnet. Returns nil on any read error — seeding is best-effort and not worth failing startup over.
The passive ARPWorker only sees ARP packets that traverse the wire during the scan window. When the kernel cache is already populated, ICMP/TCP probes reuse those entries without emitting fresh ARP requests, leaving affected hosts visible to the active prober but MAC-less in the device map. Seeding closes that gap.