Created
June 14, 2016 18:47
-
-
Save bradleypeabody/21ab61ca639bff2008bde2e28b9f5aef to your computer and use it in GitHub Desktop.
Read arp table using cgo on macos
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 <net/route.h> | |
//#include <net/if_arp.h> | |
//#include <net/ethernet.h> | |
//#include <sys/sysctl.h> | |
//#include <sys/socket.h> | |
#include <sys/param.h> | |
#include <sys/file.h> | |
#include <sys/socket.h> | |
#include <sys/sockio.h> | |
#include <sys/sysctl.h> | |
#include <sys/ioctl.h> | |
#include <sys/time.h> | |
#include <net/if.h> | |
#include <net/if_dl.h> | |
#include <net/if_types.h> | |
#include <net/route.h> | |
#include <netinet/in.h> | |
#include <netinet/if_ether.h> | |
#include <arpa/inet.h> | |
#include <ctype.h> | |
#include <err.h> | |
#include <errno.h> | |
#include <netdb.h> | |
#include <nlist.h> | |
#include <paths.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <strings.h> | |
#include <unistd.h> | |
#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)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment