package main import ( "fmt" "time" ) /* // Ideas and much code from: // https://opensource.apple.com/source/network_cmds/network_cmds-457/arp.tproj/arp.c //#include //#include //#include //#include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef SA_SIZE #define SA_SIZE(sa) \ ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ sizeof(uint32_t) : \ 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(uint32_t) - 1) ) ) #endif typedef void (action_fn)(struct sockaddr_dl *sdl, struct sockaddr_inarp *s_in, struct rt_msghdr *rtm); extern void pushARPEntry(char *, char *, char*); static int arp_search(in_addr_t addr, action_fn *action) { int mib[6]; size_t needed; char *lim, *buf, *newbuf, *next; struct rt_msghdr *rtm; struct sockaddr_inarp *sin2; struct sockaddr_dl *sdl; char ifname[IF_NAMESIZE]; int st, found_entry = 0; // This is the mib entry for the "route info" for all protocols - it does not seem to have // any text equivalent. (I tried to use Go's syscall.Sysctl() directly with no luck - all // the possible name variations I could think of fail to be found.) mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_FLAGS; mib[5] = RTF_LLINFO; if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) err(1, "route-sysctl-estimate"); if (needed == 0) // empty table return 0; buf = NULL; for (;;) { newbuf = realloc(buf, needed); if (newbuf == NULL) { if (buf != NULL) free(buf); errx(1, "could not reallocate memory"); } buf = newbuf; st = sysctl(mib, 6, buf, &needed, NULL, 0); if (st == 0 || errno != ENOMEM) break; needed += needed / 8; } if (st == -1) err(1, "actual retrieval of routing table"); lim = buf + needed; for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; sin2 = (struct sockaddr_inarp *)(rtm + 1); sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); //if (rifname && if_indextoname(sdl->sdl_index, ifname) && // strcmp(ifname, rifname)) // continue; if (addr) { if (addr != sin2->sin_addr.s_addr) continue; found_entry = 1; } (*action)(sdl, sin2, rtm); } free(buf); return (found_entry); } static char *arp_print_lladdr(struct sockaddr_dl *sdl) { static char buf[256]; char *cp; int n, bufsize = sizeof (buf), p = 0; bzero(buf, sizeof (buf)); cp = (char *)LLADDR(sdl); if ((n = sdl->sdl_alen) > 0) { while (--n >= 0) p += snprintf(buf + p, bufsize - p, "%02x%s", *cp++ & 0xff, n > 0 ? ":" : ""); } return (buf); } static void arp_push_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *addr, struct rt_msghdr *rtm) { static char ifname[IF_NAMESIZE]; char *ipaddr = inet_ntoa(addr->sin_addr); char *macaddr = (char *)""; if (sdl->sdl_alen) { macaddr = arp_print_lladdr(sdl); } int useifname = 0; useifname = if_indextoname(sdl->sdl_index, ifname) != NULL; pushARPEntry( ipaddr, macaddr, useifname ? ifname : ""); } static void full_arp_search() { arp_search(0, arp_push_entry); } */ import "C" type ARPEntry struct { IPAddress string MACAddress string IFName string } var ARPTable []ARPEntry //export pushARPEntry func pushARPEntry(ipaddr *C.char, macaddr *C.char, ifname *C.char) { ARPTable = append(ARPTable, ARPEntry{ IPAddress: C.GoString(ipaddr), MACAddress: C.GoString(macaddr), IFName: C.GoString(ifname), }) } func main() { start := time.Now() ARPTable = nil C.full_arp_search() fmt.Printf("%#v (%v)\n", ARPTable, time.Since(start)) }