package network import ( "fmt" "strings" "sync" "github.com/bettercap/bettercap/v2/core" "github.com/bettercap/bettercap/v2/log" ) type ArpTable map[string]string var ( arpWasParsed = false arpLock = &sync.RWMutex{} arpTable = make(ArpTable) ) func ArpUpdate(iface string) (ArpTable, error) { arpLock.Lock() defer arpLock.Unlock() // Signal we parsed the ARP table at least once. arpWasParsed = true // Run "arp -an" (darwin) or "ip neigh" (linux) and parse the output output, err := core.Exec(ArpCmd, ArpCmdOpts) if err != nil { return arpTable, err } newTable := make(ArpTable) for _, line := range strings.Split(output, "\n") { m := ArpTableParser.FindStringSubmatch(line) if len(m) == ArpTableTokens { log.Debug("ARP TABLE MATCH: %v", m) ipIndex := ArpTableTokenIndex[0] hwIndex := ArpTableTokenIndex[1] ifIndex := ArpTableTokenIndex[2] address := m[ipIndex] mac := m[hwIndex] ifname := iface if ifIndex != -1 { ifname = m[ifIndex] } if ifname == iface { log.Debug(" %s = %s", address, mac) newTable[address] = mac } } } arpTable = newTable return arpTable, nil } func ArpLookup(iface string, address string, refresh bool) (string, error) { // Refresh ARP table if first run or if a force refresh has been instructed. if !ArpParsed() || refresh { if _, err := ArpUpdate(iface); err != nil { return "", err } } arpLock.RLock() defer arpLock.RUnlock() // Lookup the hardware address of this ip. if mac, found := arpTable[address]; found { return mac, nil } return "", fmt.Errorf("Could not find mac for %s", address) } func ArpInverseLookup(iface string, mac string, refresh bool) (string, error) { if !ArpParsed() || refresh { if _, err := ArpUpdate(iface); err != nil { return "", err } } arpLock.RLock() defer arpLock.RUnlock() for ip, hw := range arpTable { if hw == mac { return ip, nil } } return "", fmt.Errorf("Could not find IP for %s", mac) } func ArpParsed() bool { arpLock.RLock() defer arpLock.RUnlock() return arpWasParsed }