//go:build !windows package deploy import ( "bufio" "net" "os" "os/exec" "strings" ) // arpHosts returns IPv4 hosts in the ARP cache on the local subnets. // On Linux it reads /proc/net/arp; on macOS/BSD it falls back to running // `arp -n`. Returns nil if nothing useful is found (caller does full scan). func arpHosts() []string { hosts := arpFromProc() if len(hosts) == 0 { hosts = arpFromCmd() } return hosts } // arpFromProc parses Linux's /proc/net/arp. // Format: IP address HW type Flags HW address Mask Device func arpFromProc() []string { f, err := os.Open("/proc/net/arp") if err != nil { return nil } defer f.Close() local := getLocalIPs() subnets := make(map[string]bool) for _, ip := range local { subnets[getSubnet(ip)] = true } var hosts []string seen := make(map[string]bool) scanner := bufio.NewScanner(f) for scanner.Scan() { fields := strings.Fields(scanner.Text()) if len(fields) < 4 { continue // skip header } ip := net.ParseIP(fields[0]) if ip == nil || ip.To4() == nil { continue } // Flags = 0x0 means incomplete — skip if fields[2] == "0x0" { continue } ipStr := ip.To4().String() sub := getSubnet(ipStr) if !subnets[sub] || seen[ipStr] { continue } seen[ipStr] = true hosts = append(hosts, ipStr) } _ = scanner.Err() return hosts } // arpFromCmd runs `arp -n` for macOS/BSD where /proc/net/arp doesn't exist. func arpFromCmd() []string { raw, err := exec.Command("arp", "-n").Output() if err != nil { return nil } out := string(raw) local := getLocalIPs() subnets := make(map[string]bool) for _, ip := range local { subnets[getSubnet(ip)] = true } var hosts []string seen := make(map[string]bool) for _, line := range strings.Split(out, "\n") { fields := strings.Fields(line) if len(fields) < 2 { continue } ip := net.ParseIP(fields[0]) if ip == nil || ip.To4() == nil || ip.IsLoopback() { continue } // arp -n marks incomplete entries as "(incomplete)" if strings.Contains(line, "incomplete") { continue } ipStr := ip.To4().String() sub := getSubnet(ipStr) if !subnets[sub] || seen[ipStr] { continue } seen[ipStr] = true hosts = append(hosts, ipStr) } return hosts }