Use getifaddrs to find interfaces on *BSD

This commit is contained in:
Simon Kelley
2012-03-07 19:08:11 +00:00
parent 6ffeff86be
commit 08456c61f6

136
src/bpf.c
View File

@@ -18,22 +18,12 @@
#if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) #if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
static struct iovec ifconf = {
.iov_base = NULL,
.iov_len = 0
};
static struct iovec ifreq = {
.iov_base = NULL,
.iov_len = 0
};
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <net/route.h> #include <net/route.h>
#include <net/if_dl.h> #include <net/if_dl.h>
#include <netinet/if_ether.h> #include <netinet/if_ether.h>
#include <ifaddrs.h>
#ifndef SA_SIZE #ifndef SA_SIZE
#define SA_SIZE(sa) \ #define SA_SIZE(sa) \
@@ -50,8 +40,12 @@ int arp_enumerate(void *parm, int (*callback)())
struct rt_msghdr *rtm; struct rt_msghdr *rtm;
struct sockaddr_inarp *sin2; struct sockaddr_inarp *sin2;
struct sockaddr_dl *sdl; struct sockaddr_dl *sdl;
struct iovec buff;
int rc; int rc;
buff.iov_base = NULL;
buff.iov_len = 0
mib[0] = CTL_NET; mib[0] = CTL_NET;
mib[1] = PF_ROUTE; mib[1] = PF_ROUTE;
mib[2] = 0; mib[2] = 0;
@@ -67,9 +61,9 @@ int arp_enumerate(void *parm, int (*callback)())
while (1) while (1)
{ {
if (!expand_buf(&ifconf, needed)) if (!expand_buf(&buff, needed))
return 0; return 0;
if ((rc = sysctl(mib, 6, ifconf.iov_base, &needed, NULL, 0)) == 0 || if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
errno != ENOMEM) errno != ENOMEM)
break; break;
needed += needed / 8; needed += needed / 8;
@@ -77,7 +71,7 @@ int arp_enumerate(void *parm, int (*callback)())
if (rc == -1) if (rc == -1)
return 0; return 0;
for (next = ifconf.iov_base ; next < (char *)ifconf.iov_base + needed; next += rtm->rtm_msglen) for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
{ {
rtm = (struct rt_msghdr *)next; rtm = (struct rt_msghdr *)next;
sin2 = (struct sockaddr_inarp *)(rtm + 1); sin2 = (struct sockaddr_inarp *)(rtm + 1);
@@ -88,18 +82,12 @@ int arp_enumerate(void *parm, int (*callback)())
return 1; return 1;
} }
#endif #endif
int iface_enumerate(int family, void *parm, int (*callback)()) int iface_enumerate(int family, void *parm, int (*callback)())
{ {
char *ptr; struct ifaddrs *head, *addrs;
struct ifreq *ifr;
struct ifconf ifc;
int fd, errsav, ret = 0;
int lastlen = 0;
size_t len = 0;
if (family == AF_UNSPEC) if (family == AF_UNSPEC)
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
@@ -112,95 +100,62 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (family == AF_LOCAL) if (family == AF_LOCAL)
family = AF_LINK; family = AF_LINK;
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) if (getifaddrs(&head) == -1)
return 0; return 0;
while(1) for (addrs = head; addrs; addrs = addrs->ifa_next)
{ {
len += 10*sizeof(struct ifreq); if (addrs->ifa_addr.sa_family == family)
if (!expand_buf(&ifconf, len))
goto err;
ifc.ifc_len = len;
ifc.ifc_buf = ifconf.iov_base;
if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
{ {
if (errno != EINVAL || lastlen != 0) int iface_index = if_nametoindex(addrs->ifa_name);
goto err;
}
else
{
if (ifc.ifc_len == lastlen)
break; /* got a big enough buffer now */
lastlen = ifc.ifc_len;
}
}
for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len) if (iface_index == 0)
{ continue;
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
unaligned accesses. */
len = sizeof(struct ifreq);
#ifdef HAVE_SOCKADDR_SA_LEN
ifr = (struct ifreq *)ptr;
if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
#endif
if (!expand_buf(&ifreq, len))
goto err;
ifr = (struct ifreq *)ifreq.iov_base;
memcpy(ifr, ptr, len);
if (ifr->ifr_addr.sa_family == family)
{
if (family == AF_INET) if (family == AF_INET)
{ {
struct in_addr addr, netmask, broadcast; struct in_addr addr, netmask, broadcast;
broadcast.s_addr = 0; addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr; netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1) broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
continue; if (!((*callback)(addr, iface_index, netmask, broadcast, parm)))
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (!((*callback)(addr,
(int)if_nametoindex(ifr->ifr_name),
netmask, broadcast,
parm)))
goto err; goto err;
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (family == AF_INET6) else if (family == AF_INET6)
{ {
struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr; struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
unsigned char *netmask = &(unisgned char *) addrs->ifa_netmask)->sin6_addr;
int scope_id = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
int i, j, prefix = 0;
for (i = 0; i < IN6_ADDRSZ; i++, prefix += 8)
if (netmask[i] != 0xff)
break;
if (i != IN6_ADDRSZ && netmask[i])
for (j = 7; j > 0; j--, prefix++)
if ((netmask[i] & (1 << j)) == 0)
break;
/* voodoo to clear interface field in address */ /* voodoo to clear interface field in address */
if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr)) if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
{ {
addr->s6_addr[2] = 0; addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0; addr->s6_addr[3] = 0;
} }
/* We have no way to determine the prefix, so we assume it's 64 for now....... */
if (!((*callback)(addr, 64, if (!((*callback)(addr, prefix, scope_id, iface_index, 0, parm)))
(int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
(int)if_nametoindex(ifr->ifr_name), 0,
parm)))
goto err; goto err;
} }
#endif #endif
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
else if (family == AF_LINK) else if (family == AF_LINK)
{ {
/* Assume ethernet again here */ /* Assume ethernet again here */
struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr; struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifa->ifr_addr;
if (sdl->sdl_alen != 0 && !((*callback)((int)if_nametoindex(ifr->ifr_name), if (sdl->sdl_alen != 0 &&
ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm))) !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
goto err; goto err;
} }
#endif #endif
@@ -211,7 +166,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
err: err:
errsav = errno; errsav = errno;
close(fd); freeifaddrs(head);
errno = errsav; errno = errsav;
return ret; return ret;
@@ -228,13 +183,10 @@ void init_bpf(void)
while (1) while (1)
{ {
/* useful size which happens to be sufficient */ sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
if (expand_buf(&ifreq, sizeof(struct ifreq))) if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
{ return;
sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
return;
}
if (errno != EBUSY) if (errno != EBUSY)
die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET); die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
} }