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