From ca485ab26fffa241a3eac1899b2d2012dd1fdb73 Mon Sep 17 00:00:00 2001 From: Andreas Gustafsson Date: Wed, 21 Jul 1999 08:07:55 +0000 Subject: [PATCH] sysctl-based network interface list scanning --- configure | 80 ++++++--- configure.in | 19 +- lib/isc/include/isc/interfaceiter.h | 3 +- lib/isc/unix/ifiter_ioctl.c | 243 +++++++++++++++++++++++++ lib/isc/unix/ifiter_sysctl.c | 264 ++++++++++++++++++++++++++++ lib/isc/unix/interfaceiter.c | 232 ++---------------------- 6 files changed, 594 insertions(+), 247 deletions(-) create mode 100644 lib/isc/unix/ifiter_ioctl.c create mode 100644 lib/isc/unix/ifiter_sysctl.c diff --git a/configure b/configure index df93f3447f..3d007043a8 100755 --- a/configure +++ b/configure @@ -2086,10 +2086,40 @@ fi rm -f conftest* -echo $ac_n "checking for IPv6 structures""... $ac_c" 1>&6 -echo "configure:2091: checking for IPv6 structures" >&5 + +echo $ac_n "checking for interface list sysctl""... $ac_c" 1>&6 +echo "configure:2092: checking for interface list sysctl" >&5 cat > conftest.$ac_ext < +#include +#include +#ifdef NET_RT_IFLIST +found_rt_iflist +#endif + +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "found_rt_iflist" >/dev/null 2>&1; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_IFLIST_SYSCTL 1 +EOF + +else + rm -rf conftest* + echo "$ac_t""no" 1>&6 +fi +rm -f conftest* + + +echo $ac_n "checking for IPv6 structures""... $ac_c" 1>&6 +echo "configure:2121: checking for IPv6 structures" >&5 +cat > conftest.$ac_ext < @@ -2099,7 +2129,7 @@ int main() { struct sockaddr_in6 sin6; return (0); ; return 0; } EOF -if { (eval echo configure:2103: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2133: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* echo "$ac_t""yes" 1>&6 ISC_NET_HAVEIPV6="#define ISC_NET_HAVEIPV6 1" @@ -2202,7 +2232,7 @@ else fi echo $ac_n "checking build system type""... $ac_c" 1>&6 -echo "configure:2206: checking build system type" >&5 +echo "configure:2236: checking build system type" >&5 build_alias=$build case "$build_alias" in @@ -2231,7 +2261,7 @@ ac_prog=ld if test "$ac_cv_prog_gcc" = yes; then # Check if gcc -print-prog-name=ld gives a path. echo $ac_n "checking for ld used by GCC""... $ac_c" 1>&6 -echo "configure:2235: checking for ld used by GCC" >&5 +echo "configure:2265: checking for ld used by GCC" >&5 ac_prog=`($CC -print-prog-name=ld) 2>&5` case "$ac_prog" in # Accept absolute paths. @@ -2255,10 +2285,10 @@ echo "configure:2235: checking for ld used by GCC" >&5 esac elif test "$with_gnu_ld" = yes; then echo $ac_n "checking for GNU ld""... $ac_c" 1>&6 -echo "configure:2259: checking for GNU ld" >&5 +echo "configure:2289: checking for GNU ld" >&5 else echo $ac_n "checking for non-GNU ld""... $ac_c" 1>&6 -echo "configure:2262: checking for non-GNU ld" >&5 +echo "configure:2292: checking for non-GNU ld" >&5 fi if eval "test \"`echo '$''{'ac_cv_path_LD'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2294,7 +2324,7 @@ fi test -z "$LD" && { echo "configure: error: no acceptable ld found in \$PATH" 1>&2; exit 1; } echo $ac_n "checking if the linker ($LD) is GNU ld""... $ac_c" 1>&6 -echo "configure:2298: checking if the linker ($LD) is GNU ld" >&5 +echo "configure:2328: checking if the linker ($LD) is GNU ld" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gnu_ld'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2310,7 +2340,7 @@ echo "$ac_t""$ac_cv_prog_gnu_ld" 1>&6 echo $ac_n "checking for BSD-compatible nm""... $ac_c" 1>&6 -echo "configure:2314: checking for BSD-compatible nm" >&5 +echo "configure:2344: checking for BSD-compatible nm" >&5 if eval "test \"`echo '$''{'ac_cv_path_NM'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2347,7 +2377,7 @@ echo "$ac_t""$NM" 1>&6 echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 -echo "configure:2351: checking whether ln -s works" >&5 +echo "configure:2381: checking whether ln -s works" >&5 if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2391,8 +2421,8 @@ test x"$silent" = xyes && libtool_flags="$libtool_flags --silent" case "$host" in *-*-irix6*) # Find out which ABI we are using. - echo '#line 2395 "configure"' > conftest.$ac_ext - if { (eval echo configure:2396: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + echo '#line 2425 "configure"' > conftest.$ac_ext + if { (eval echo configure:2426: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then case "`/usr/bin/file conftest.o`" in *32-bit*) LD="${LD-ld} -32" @@ -2413,19 +2443,19 @@ case "$host" in SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" echo $ac_n "checking whether the C compiler needs -belf""... $ac_c" 1>&6 -echo "configure:2417: checking whether the C compiler needs -belf" >&5 +echo "configure:2447: checking whether the C compiler needs -belf" >&5 if eval "test \"`echo '$''{'lt_cv_cc_needs_belf'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2459: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* lt_cv_cc_needs_belf=yes else @@ -2540,12 +2570,12 @@ esac echo $ac_n "checking for inet_ntop""... $ac_c" 1>&6 -echo "configure:2544: checking for inet_ntop" >&5 +echo "configure:2574: checking for inet_ntop" >&5 if eval "test \"`echo '$''{'ac_cv_func_inet_ntop'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2602: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_inet_ntop=yes" else @@ -2592,12 +2622,12 @@ ISC_EXTRA_OBJS="$ISC_EXTRA_OBJS inet_ntop.$O" fi echo $ac_n "checking for inet_pton""... $ac_c" 1>&6 -echo "configure:2596: checking for inet_pton" >&5 +echo "configure:2626: checking for inet_pton" >&5 if eval "test \"`echo '$''{'ac_cv_func_inet_pton'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2654: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_inet_pton=yes" else @@ -2644,12 +2674,12 @@ ISC_EXTRA_OBJS="$ISC_EXTRA_OBJS inet_pton.$O" fi echo $ac_n "checking for inet_aton""... $ac_c" 1>&6 -echo "configure:2648: checking for inet_aton" >&5 +echo "configure:2678: checking for inet_aton" >&5 if eval "test \"`echo '$''{'ac_cv_func_inet_aton'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2706: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_inet_aton=yes" else diff --git a/configure.in b/configure.in index 5d3441c956..d176549962 100644 --- a/configure.in +++ b/configure.in @@ -13,7 +13,7 @@ dnl PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS dnl ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS dnl SOFTWARE. -AC_REVISION($Revision: 1.44 $) +AC_REVISION($Revision: 1.45 $) AC_PREREQ(2.13) @@ -260,6 +260,23 @@ AC_TRY_COMPILE([ ISC_NET_HAVESALEN="#undef ISC_NET_HAVESALEN"]) AC_SUBST(ISC_NET_HAVESALEN) +dnl +dnl Look for a sysctl call to get the list of network interfaces. +dnl + +AC_MSG_CHECKING(for interface list sysctl) +AC_EGREP_CPP(found_rt_iflist, [ +#include +#include +#include +#ifdef NET_RT_IFLIST +found_rt_iflist +#endif +], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IFLIST_SYSCTL)], + [AC_MSG_RESULT(no)]) + dnl dnl IPv6 dnl diff --git a/lib/isc/include/isc/interfaceiter.h b/lib/isc/include/isc/interfaceiter.h index 6ed3c1090b..73a9cbfa2f 100644 --- a/lib/isc/include/isc/interfaceiter.h +++ b/lib/isc/include/isc/interfaceiter.h @@ -58,8 +58,7 @@ typedef struct { char name[32]; /* Interface name, null-terminated. */ unsigned int af; /* Address family. */ isc_netaddr_t address; /* Local address. */ - isc_netaddr_t netmask; /* Network mask - (non-point-to-point only). */ + isc_netaddr_t netmask; /* Network mask. */ isc_netaddr_t dstaddress; /* Destination address (point-to-point only). */ isc_uint32_t flags; /* Flags; see below. */ diff --git a/lib/isc/unix/ifiter_ioctl.c b/lib/isc/unix/ifiter_ioctl.c new file mode 100644 index 0000000000..db091cdb37 --- /dev/null +++ b/lib/isc/unix/ifiter_ioctl.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 1999 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Obtain the list of network interfaces using the SIOCGIFCONF ioctl. + * See netintro(4). + */ + +#define IFITER_MAGIC 0x49464954U /* IFIT. */ +#define VALID_IFITER(t) ((t) != NULL && (t)->magic == IFITER_MAGIC) + +struct isc_interfaceiter { + unsigned int magic; /* Magic number. */ + isc_mem_t *mctx; + int socket; + struct ifconf ifc; + void *buf; /* Buffer for sysctl data. */ + unsigned int bufsize; /* Bytes allocated. */ + unsigned int pos; /* Current offset in + SIOCGIFCONF data */ + isc_interface_t current; /* Current interface data. */ + isc_result_t result; /* Last result code. */ +}; + + +/* + * Size of buffer for SIOCGIFCONF, in bytes. We assume no sane system + * will have more than a megabyte of interface configuration data. + */ +#define IFCONF_BUFSIZE_INITIAL 4096 +#define IFCONF_BUFSIZE_MAX 1048576 + +isc_result_t +isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) +{ + isc_interfaceiter_t *iter; + isc_result_t result; + + REQUIRE(mctx != NULL); + REQUIRE(iterp != NULL); + REQUIRE(*iterp == NULL); + + iter = isc_mem_get(mctx, sizeof(*iter)); + if (iter == NULL) + return (ISC_R_NOMEMORY); + + iter->mctx = mctx; + iter->buf = NULL; + + /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */ + if ((iter->socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "making interface scan socket: %s", + strerror(errno)); + result = ISC_R_UNEXPECTED; + goto socket_failure; + } + + /* + * Get the interface configuration, allocating more memory if + * necessary. + */ + iter->bufsize = IFCONF_BUFSIZE_INITIAL; + + for (;;) { + iter->buf = isc_mem_get(mctx, iter->bufsize); + if (iter->buf == NULL) { + result = ISC_R_NOMEMORY; + goto alloc_failure; + } + + iter->ifc.ifc_len = iter->bufsize; + iter->ifc.ifc_buf = iter->buf; + if (ioctl(iter->socket, SIOCGIFCONF, (char *) &iter->ifc) >= 0) + break; + if (errno != EINVAL) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "get interface configuration: %s", + strerror(errno)); + result = ISC_R_UNEXPECTED; + goto ioctl_failure; + } + + if (iter->bufsize >= IFCONF_BUFSIZE_MAX) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "get interface configuration: " + "maximum buffer size exceeded"); + result = ISC_R_UNEXPECTED; + goto ioctl_failure; + } + isc_mem_put(mctx, iter->buf, iter->bufsize); + + iter->bufsize *= 2; + } + + /* + * A newly created iterator has an undefined position + * until isc_interfaceiter_first() is called. + */ + iter->pos = (unsigned int) -1; + iter->result = ISC_R_FAILURE; + + iter->magic = IFITER_MAGIC; + *iterp = iter; + return (ISC_R_SUCCESS); + + ioctl_failure: + isc_mem_put(mctx, iter->buf, iter->bufsize); + + alloc_failure: + (void) close(iter->socket); + + socket_failure: + isc_mem_put(mctx, iter, sizeof *iter); + return (result); +} + +/* + * Get information about the current interface to iter->current. + * If successful, return ISC_R_SUCCESS. + * If the interface has an unsupported address family, + * return ISC_R_FAILURE. In case of other failure, + * return ISC_R_UNEXPECTED. + */ + +static isc_result_t +internal_current(isc_interfaceiter_t *iter) { + struct ifreq *ifrp; + struct ifreq ifreq; + int family; + + REQUIRE(VALID_IFITER(iter)); + REQUIRE (iter->pos < (unsigned int) iter->ifc.ifc_len); + + ifrp = (struct ifreq *)((char *) iter->ifc.ifc_req + iter->pos); + + memcpy(&ifreq, ifrp, sizeof ifreq); + + family = ifreq.ifr_addr.sa_family; + if (family != AF_INET) /* XXX IPv6 */ + return (ISC_R_FAILURE); + + memset(&iter->current, 0, sizeof(iter->current)); + + INSIST(sizeof(ifreq.ifr_name) <= sizeof(iter->current.name)); + memcpy(iter->current.name, ifreq.ifr_name, sizeof(ifreq.ifr_name)); + + get_addr(family, &iter->current.address, &ifreq.ifr_addr); + + /* Get interface flags. */ + + iter->current.flags = 0; + + if (ioctl(iter->socket, SIOCGIFFLAGS, (char *) &ifreq) < 0) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "%s: getting interface flags: %s", + ifreq.ifr_name, + strerror(errno)); + return (ISC_R_UNEXPECTED); + } + + if ((ifreq.ifr_flags & IFF_UP) != 0) + iter->current.flags |= INTERFACE_F_UP; + + if ((ifreq.ifr_flags & IFF_POINTOPOINT) != 0) + iter->current.flags |= INTERFACE_F_POINTTOPOINT; + + if ((ifreq.ifr_flags & IFF_LOOPBACK) != 0) + iter->current.flags |= INTERFACE_F_LOOPBACK; + + /* If the interface is point-to-point, get the destination address. */ + if ((iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) { + if (ioctl(iter->socket, SIOCGIFDSTADDR, (char *) &ifreq) < 0) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "%s: getting destination address: %s", + ifreq.ifr_name, + strerror(errno)); + return (ISC_R_UNEXPECTED); + } + get_addr(family, &iter->current.dstaddress, + &ifreq.ifr_dstaddr); + } + + /* Get the network mask. */ + if (ioctl(iter->socket, SIOCGIFNETMASK, (char *) &ifreq) < 0) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "%s: getting netmask: %s", + ifreq.ifr_name, + strerror(errno)); + return (ISC_R_UNEXPECTED); + } + get_addr(family, &iter->current.netmask, + &ifreq.ifr_addr); + + return (ISC_R_SUCCESS); +} + +/* + * Step the iterator to the next interface. Unlike + * isc_interfaceiter_next(), this may leave the iterator + * positioned on an interface that will ultimately + * be ignored. Return ISC_R_NOMORE if there are no more + * interfaces, otherwise ISC_R_SUCCESS. + */ +static isc_result_t +internal_next(isc_interfaceiter_t *iter) { + struct ifreq *ifrp; + + REQUIRE (iter->pos < (unsigned int) iter->ifc.ifc_len); + + ifrp = (struct ifreq *)((char *) iter->ifc.ifc_req + iter->pos); + +#ifdef ISC_NET_HAVESALEN + if (ifrp->ifr_addr.sa_len > sizeof(struct sockaddr)) + iter->pos += sizeof(ifrp->ifr_name) + ifrp->ifr_addr.sa_len; + else +#endif + iter->pos += sizeof *ifrp; + + if (iter->pos >= (unsigned int) iter->ifc.ifc_len) + return (ISC_R_NOMORE); + + return (ISC_R_SUCCESS); +} + +static void +internal_destroy(isc_interfaceiter_t *iter) { + (void) close(iter->socket); +} diff --git a/lib/isc/unix/ifiter_sysctl.c b/lib/isc/unix/ifiter_sysctl.c new file mode 100644 index 0000000000..7cd6bd9dd7 --- /dev/null +++ b/lib/isc/unix/ifiter_sysctl.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 1999 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Obtain the list of network interfaces using sysctl. + * See TCP/IP Illustrated Volume 2, sections 19.8, 19.14, + * and 19.16. + */ + +#include +#include + +#include +#include + +/* XXX what about Alpha? */ +#ifdef sgi +#define ROUNDUP(a) ((a) > 0 ? \ + (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) : \ + sizeof(__uint64_t)) +#else +#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ + : sizeof(long)) +#endif + +#define IFITER_MAGIC 0x49464953U /* IFIS. */ +#define VALID_IFITER(t) ((t) != NULL && (t)->magic == IFITER_MAGIC) + +struct isc_interfaceiter { + unsigned int magic; /* Magic number. */ + isc_mem_t *mctx; + void *buf; /* Buffer for sysctl data. */ + unsigned int bufsize; /* Bytes allocated. */ + unsigned int bufused; /* Bytes used. */ + unsigned int pos; /* Current offset in + sysctl data. */ + isc_interface_t current; /* Current interface data. */ + isc_result_t result; /* Last result code. */ +}; + +static int mib[6] = { + CTL_NET, + PF_ROUTE, + 0, + 0, /* Any address family. */ + NET_RT_IFLIST, + 0 /* Flags. */ +}; + +isc_result_t +isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) +{ + isc_interfaceiter_t *iter; + isc_result_t result; + REQUIRE(mctx != NULL); + REQUIRE(iterp != NULL); + REQUIRE(*iterp == NULL); + + iter = isc_mem_get(mctx, sizeof(*iter)); + if (iter == NULL) + return (ISC_R_NOMEMORY); + + iter->mctx = mctx; + iter->buf = 0; + + /* Determine the amount of memory needed. */ + if (sysctl(mib, 6, 0, &iter->bufsize, 0, 0) < 0) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "getting interface list size: sysctl: %s", + strerror(errno)); + result = ISC_R_UNEXPECTED; + goto failure; + } + + iter->buf = isc_mem_get(iter->mctx, iter->bufsize); + if (iter->buf == NULL) { + result = ISC_R_NOMEMORY; + goto failure; + } + + iter->bufused = iter->bufsize; + if (sysctl(mib, 6, iter->buf, &iter->bufused, 0, 0) < 0) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "getting interface list: sysctl: %s", + strerror(errno)); + result = ISC_R_UNEXPECTED; + goto failure; + } + INSIST(iter->bufused <= iter->bufsize); + + /* + * A newly created iterator has an undefined position + * until isc_interfaceiter_first() is called. + */ + iter->pos = (unsigned int) -1; + iter->result = ISC_R_FAILURE; + + iter->magic = IFITER_MAGIC; + *iterp = iter; + return (ISC_R_SUCCESS); + + failure: + if (iter->buf != NULL) + isc_mem_put(mctx, iter->buf, iter->bufsize); + isc_mem_put(mctx, iter, sizeof *iter); + return (result); +} + +/* + * Get information about the current interface to iter->current. + * If successful, return ISC_R_SUCCESS. + * If the interface has an unsupported address family, + * return ISC_R_FAILURE. In case of other failure, + * return ISC_R_UNEXPECTED. + */ + +static isc_result_t +internal_current(isc_interfaceiter_t *iter) { + struct ifa_msghdr *ifam, *ifam_end; + + REQUIRE(VALID_IFITER(iter)); + REQUIRE (iter->pos < (unsigned int) iter->bufused); + + ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos); + ifam_end = (struct ifa_msghdr *) ((char *) iter->buf + iter->bufused); + + if (ifam->ifam_type == RTM_IFINFO) { + struct if_msghdr *ifm = (struct if_msghdr *) ifam; + struct sockaddr_dl *sdl = (struct sockaddr_dl *) (ifm + 1); + unsigned int namelen; + + memset(&iter->current, 0, sizeof(iter->current)); + + namelen = sdl->sdl_nlen; + if (namelen > sizeof(iter->current.name) - 1) + namelen = sizeof(iter->current.name) - 1; + + memcpy(iter->current.name, sdl->sdl_data, namelen); + + iter->current.flags = 0; + + if ((ifam->ifam_flags & IFF_UP) != 0) + iter->current.flags |= INTERFACE_F_UP; + + if ((ifam->ifam_flags & IFF_POINTOPOINT) != 0) + iter->current.flags |= INTERFACE_F_POINTTOPOINT; + + if ((ifam->ifam_flags & IFF_LOOPBACK) != 0) + iter->current.flags |= INTERFACE_F_LOOPBACK; + + /* + * This is not an interface address. + * Force another iteration. + */ + return (ISC_R_FAILURE); + } else if (ifam->ifam_type == RTM_NEWADDR) { + int i; + int family; + struct sockaddr *mask_sa = NULL; + struct sockaddr *addr_sa = NULL; + struct sockaddr *dst_sa = NULL; + + struct sockaddr *sa = (struct sockaddr *)(ifam + 1); + family = sa->sa_family; + + for (i = 0; i < RTAX_MAX; i++) + { + if ((ifam->ifam_addrs & (1 << i)) == 0) + continue; + + INSIST(sa < (struct sockaddr *) ifam_end); + + switch (i) { + case RTAX_NETMASK: /* Netmask */ + mask_sa = sa; + break; + case RTAX_IFA: /* Interface address */ + addr_sa = sa; + break; + case RTAX_BRD: /* Broadcast or destination address */ + dst_sa = sa; + break; + } +#ifdef ISC_NET_HAVESALEN + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(sa->sa_len)); +#else +#ifdef sgi + /* Do as the contributed SGI code does. */ + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(_FAKE_SA_LEN_DST(sa))); +#else + /* XXX untested. */ + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(sizeof(struct sockaddr))); +#endif +#endif + } + + if (addr_sa == NULL) + return (ISC_R_FAILURE); + + family = addr_sa->sa_family; + if (family != AF_INET) /* XXX IP6 */ + return (ISC_R_FAILURE); + + get_addr(family, &iter->current.address, addr_sa); + + if (mask_sa != NULL) + get_addr(family, &iter->current.netmask, mask_sa); + + if (dst_sa != NULL && + (iter->current.flags & IFF_POINTOPOINT) != 0) + get_addr(family, &iter->current.dstaddress, dst_sa); + + return (ISC_R_SUCCESS); + } else { + printf("warning: unexpected interface list message type\n"); + return (ISC_R_FAILURE); + } +} + +/* + * Step the iterator to the next interface. Unlike + * isc_interfaceiter_next(), this may leave the iterator + * positioned on an interface that will ultimately + * be ignored. Return ISC_R_NOMORE if there are no more + * interfaces, otherwise ISC_R_SUCCESS. + */ +static isc_result_t +internal_next(isc_interfaceiter_t *iter) { + struct ifa_msghdr *ifam; + REQUIRE (iter->pos < (unsigned int) iter->bufused); + + ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos); + + iter->pos += ifam->ifam_msglen; + + if (iter->pos >= iter->bufused) + return (ISC_R_NOMORE); + + return (ISC_R_SUCCESS); +} + +static void +internal_destroy(isc_interfaceiter_t *iter) { + iter = iter; /* Unused. */ + /* Do nothing. */ +} + diff --git a/lib/isc/unix/interfaceiter.c b/lib/isc/unix/interfaceiter.c index eb1c2ce1bf..1b83b5054c 100644 --- a/lib/isc/unix/interfaceiter.c +++ b/lib/isc/unix/interfaceiter.c @@ -40,112 +40,7 @@ #include #include -#define IFITER_MAGIC 0x49464954U /* IFIT. */ -#define VALID_IFITER(t) ((t) != NULL && (t)->magic == IFITER_MAGIC) - -struct isc_interfaceiter { - unsigned int magic; /* Magic number. */ - isc_mem_t *mctx; - int socket; - struct ifconf ifc; - unsigned int bufsize; - unsigned int pos; /* Current offset in - SIOGCONF data */ - isc_interface_t current; /* Current interface data. */ - isc_result_t result; /* Last result code. */ -}; - - -/* - * Size of buffer for SIOCGIFCONF, in bytes. We assume no sane system - * will have more than a megabyte of interface configuration data. - */ -#define IFCONF_BUFSIZE_INITIAL 4096 -#define IFCONF_BUFSIZE_MAX 1048576 - -isc_result_t -isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) -{ - isc_interfaceiter_t *iter; - isc_result_t result; - char *buf; - REQUIRE(mctx != NULL); - REQUIRE(iterp != NULL); - REQUIRE(*iterp == NULL); - - iter = isc_mem_get(mctx, sizeof(*iter)); - if (iter == NULL) - return (ISC_R_NOMEMORY); - - iter->mctx = mctx; - - /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */ - if ((iter->socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "making interface scan socket: %s", - strerror(errno)); - result = ISC_R_UNEXPECTED; - goto socket_failure; - } - - /* - * Get the interface configuration, allocating more memory if - * necessary. - */ - iter->bufsize = IFCONF_BUFSIZE_INITIAL; - - for (;;) { - buf = isc_mem_get(mctx, iter->bufsize); - if (buf == NULL) { - result = ISC_R_NOMEMORY; - goto alloc_failure; - } - - iter->ifc.ifc_len = iter->bufsize; - iter->ifc.ifc_buf = buf; - if (ioctl(iter->socket, SIOCGIFCONF, (char *) &iter->ifc) >= 0) - break; - if (errno != EINVAL) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "get interface configuration: %s", - strerror(errno)); - result = ISC_R_UNEXPECTED; - goto ioctl_failure; - } - - if (iter->bufsize >= IFCONF_BUFSIZE_MAX) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "get interface configuration: " - "maximum buffer size exceeded"); - result = ISC_R_UNEXPECTED; - goto ioctl_failure; - } - isc_mem_put(mctx, buf, iter->bufsize); - - iter->bufsize *= 2; - } - - /* - * A newly created iterator has an undefined position - * until isc_interfaceiter_first() is called. - */ - iter->pos = (unsigned int) -1; - iter->result = ISC_R_FAILURE; - - iter->magic = IFITER_MAGIC; - *iterp = iter; - return (ISC_R_SUCCESS); - - ioctl_failure: - isc_mem_put(mctx, buf, iter->bufsize); - - alloc_failure: - (void) close(iter->socket); - - socket_failure: - isc_mem_put(mctx, iter, sizeof *iter); - return (result); -} +/* Common utility functions */ /* * Extract the network address part from a "struct sockaddr". @@ -170,117 +65,20 @@ get_addr(int family, isc_netaddr_t *dst, struct sockaddr *src) { break; } } - -/* - * Get information about the current interface to iter->current. - * If successful, return ISC_R_SUCCESS. - * If the interface has an unsupported address family, - * return ISC_R_FAILURE. In case of other failure, - * return ISC_R_UNEXPECTED. - */ - -static isc_result_t -internal_current(isc_interfaceiter_t *iter) { - struct ifreq *ifrp; - struct ifreq ifreq; - int family; - - REQUIRE(VALID_IFITER(iter)); - REQUIRE (iter->pos < (unsigned int) iter->ifc.ifc_len); - - ifrp = (struct ifreq *)((char *) iter->ifc.ifc_req + iter->pos); - - memcpy(&ifreq, ifrp, sizeof ifreq); - - family = ifreq.ifr_addr.sa_family; - if (family != AF_INET) /* XXX IPv6 */ - return (ISC_R_FAILURE); - - memset(&iter->current, 0, sizeof(iter->current)); - - INSIST(sizeof(ifreq.ifr_name) <= sizeof(iter->current.name)); - memcpy(iter->current.name, ifreq.ifr_name, sizeof(ifreq.ifr_name)); - - get_addr(family, &iter->current.address, &ifreq.ifr_addr); - - /* Get interface flags. */ - - iter->current.flags = 0; - - if (ioctl(iter->socket, SIOCGIFFLAGS, (char *) &ifreq) < 0) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "%s: getting interface flags: %s", - ifreq.ifr_name, - strerror(errno)); - return (ISC_R_UNEXPECTED); - } - - if ((ifreq.ifr_flags & IFF_UP) != 0) - iter->current.flags |= INTERFACE_F_UP; - - if ((ifreq.ifr_flags & IFF_POINTOPOINT) != 0) - iter->current.flags |= INTERFACE_F_POINTTOPOINT; - - if ((ifreq.ifr_flags & IFF_LOOPBACK) != 0) - iter->current.flags |= INTERFACE_F_LOOPBACK; - - /* - * If point-to-point, get the destination address; - * otherwise, get the network mask. - */ - if ((iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) { - if (ioctl(iter->socket, SIOCGIFDSTADDR, (char *) &ifreq) < 0) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "%s: getting destination address: %s", - ifreq.ifr_name, - strerror(errno)); - return (ISC_R_UNEXPECTED); - } - get_addr(family, &iter->current.dstaddress, - &ifreq.ifr_dstaddr); - } else { - if (ioctl(iter->socket, SIOCGIFNETMASK, (char *) &ifreq) < 0) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "%s: getting netmask: %s", - ifreq.ifr_name, - strerror(errno)); - return (ISC_R_UNEXPECTED); - } - get_addr(family, &iter->current.netmask, - &ifreq.ifr_addr); - } - - return (ISC_R_SUCCESS); -} /* - * Step the iterator to the next interface. Unlike - * isc_interfaceiter_next(), this may leave the iterator - * positioned on an interface that will ultimately - * be ignored. Return ISC_R_NOMORE if there are no more - * interfaces, otherwise ISC_R_SUCCESS. + * Include system-dependent code. */ -static isc_result_t -internal_next(isc_interfaceiter_t *iter) { - struct ifreq *ifrp; - REQUIRE (iter->pos < (unsigned int) iter->ifc.ifc_len); - - ifrp = (struct ifreq *)((char *) iter->ifc.ifc_req + iter->pos); - -#ifdef ISC_NET_HAVESALEN - if (ifrp->ifr_addr.sa_len > sizeof(struct sockaddr)) - iter->pos += sizeof(ifrp->ifr_name) + ifrp->ifr_addr.sa_len; - else +#if HAVE_IFLIST_SYSCTL +#include "ifiter_sysctl.c" +#else +#include "ifiter_ioctl.c" #endif - iter->pos += sizeof *ifrp; - - if (iter->pos >= (unsigned int) iter->ifc.ifc_len) - return (ISC_R_NOMORE); - - return (ISC_R_SUCCESS); -} +/* + * The remaining code is common to the sysctl and ioctl case. + */ isc_result_t isc_interfaceiter_current(isc_interfaceiter_t *iter, @@ -291,9 +89,6 @@ isc_interfaceiter_current(isc_interfaceiter_t *iter, return (ISC_R_SUCCESS); } -/* - * Position the iterator on the first user-visible interface. - */ isc_result_t isc_interfaceiter_first(isc_interfaceiter_t *iter) { isc_result_t result; @@ -305,6 +100,7 @@ isc_interfaceiter_first(isc_interfaceiter_t *iter) { result = internal_current(iter); if (result == ISC_R_SUCCESS) break; + INSIST(result == ISC_R_FAILURE); result = internal_next(iter); if (result == ISC_R_NOMORE) break; @@ -314,9 +110,6 @@ isc_interfaceiter_first(isc_interfaceiter_t *iter) { return (result); } -/* - * Position the iterator on the next user-visible interface. - */ isc_result_t isc_interfaceiter_next(isc_interfaceiter_t *iter) { isc_result_t result; @@ -332,6 +125,7 @@ isc_interfaceiter_next(isc_interfaceiter_t *iter) { result = internal_current(iter); if (result == ISC_R_SUCCESS) break; + INSIST(result == ISC_R_FAILURE); } iter->result = result; return (result); @@ -345,8 +139,8 @@ isc_interfaceiter_destroy(isc_interfaceiter_t **iterp) iter = *iterp; REQUIRE(VALID_IFITER(iter)); - (void) close(iter->socket); - isc_mem_put(iter->mctx, iter->ifc.ifc_buf, iter->bufsize); + internal_destroy(iter); + isc_mem_put(iter->mctx, iter->buf, iter->bufsize); iter->magic = 0; isc_mem_put(iter->mctx, iter, sizeof *iter);