From f80c797d42f213e026478a0e767a8dbbc2352e7f Mon Sep 17 00:00:00 2001 From: Andreas Gustafsson Date: Thu, 20 May 1999 12:31:30 +0000 Subject: [PATCH] added network interface iterator --- lib/isc/Makefile.in | 2 +- lib/isc/include/isc/interfaceiter.h | 140 +++++++++++ lib/isc/unix/Makefile.in | 2 +- lib/isc/unix/interfaceiter.c | 351 ++++++++++++++++++++++++++++ 4 files changed, 493 insertions(+), 2 deletions(-) create mode 100644 lib/isc/include/isc/interfaceiter.h create mode 100644 lib/isc/unix/interfaceiter.c diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in index 87d5297207..d2d5ea5fd6 100644 --- a/lib/isc/Makefile.in +++ b/lib/isc/Makefile.in @@ -30,7 +30,7 @@ OBJS = @ISC_EXTRA_OBJS@ \ result.o rwlock.o symtab.o str.o event.o task.o timer.o \ version.o \ unix/app.o unix/time.o unix/stdtime.o unix/socket.o \ - pthreads/condition.o + pthreads/condition.o unix/interfaceiter.o SUBDIRS = include unix pthreads TARGETS = timestamp diff --git a/lib/isc/include/isc/interfaceiter.h b/lib/isc/include/isc/interfaceiter.h new file mode 100644 index 0000000000..b55cec1800 --- /dev/null +++ b/lib/isc/include/isc/interfaceiter.h @@ -0,0 +1,140 @@ +/* + * 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. + */ + +#ifndef ISC_INTERFACEITER_H +#define ISC_INTERFACEITER_H 1 + +/***** + ***** Module Info + *****/ + +/* + * Interface iterator + * + * Iterate over the list of network interfaces. + * + * Interfaces whose address family is not supported are ignored and never + * returned by the iterator. Interfaces whose netmask, interface flags, + * or similar cannot be obtained are also ignored, and the failure is logged. + * + * Standards: + * The API for scanning varies greatly among operating systems. + * This module attempts to hide the differences. + */ + +/*** + *** Imports + ***/ + +#include +#include +#include + +/*** + *** Types + ***/ + +typedef struct isc_interfaceiter isc_interfaceiter_t; + +/* + * Public structure describing a network interface. + * + * In 'address', 'netmask', and 'dstaddress', + * only the network address field (for IPv4, type.sin.sin_addr) + * is used. Other fields including the port and address family + * have undefined values. + */ + +typedef struct { + char name[32]; /* Interface name, null-terminated. */ + unsigned int af; /* Address family. */ + isc_sockaddr_t address; /* Local address. */ + isc_sockaddr_t netmask; /* Network mask + (non-point-to-point only). */ + isc_sockaddr_t dstaddress; /* Destination address + (point-to-point only). */ + isc_uint32_t flags; /* Flags; see below. */ +} isc_interface_t; + +/* Interface flags. */ + +#define INTERFACE_F_UP 0x00000001U +#define INTERFACE_F_POINTTOPOINT 0x00000002U +#define INTERFACE_F_LOOPBACK 0x00000004U + +/*** + *** Functions + ***/ + +isc_result_t +isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp); +/* + * Create an iterator for traversing the operating system's list + * of network interfaces. + * + * Returns: + * ISC_R_SUCCESS + * ISC_R_NOMEMORY + * Various network-related errors + */ + +isc_result_t +isc_interfaceiter_first(isc_interfaceiter_t *iter); +/* + * Position the iterator on the first interface. + * + * Returns: + * ISC_R_SUCCESS Success. + * ISC_R_NOMORE There are no interfaces. + */ + +isc_result_t +isc_interfaceiter_current(isc_interfaceiter_t *iter, + isc_interface_t *ifdata); +/* + * Get information about the interface the iterator is currently + * positioned at and store it at *ifdata. + * + * Requires: + * The iterator has been successfully positioned using + * isc_interface_iter_first() / isc_interface_iter_next(). + * + * Returns: + * ISC_R_SUCCESS Success. + */ + +isc_result_t +isc_interfaceiter_next(isc_interfaceiter_t *iter); +/* + * Position the iterator on the next interface. + * + * Requires: + * The iterator has been successfully positioned using + * isc_interface_iter_first() / isc_interface_iter_next(). + * + * Returns: + * ISC_R_SUCCESS Success. + * ISC_R_NOMORE There are no more interfaces. + */ + +void +isc_interfaceiter_destroy(isc_interfaceiter_t **iterp); +/* + * Destroy the iterator. + */ + +#endif /* ISC_INTERFACEITER_H */ diff --git a/lib/isc/unix/Makefile.in b/lib/isc/unix/Makefile.in index 2041d9fa42..7f534e12b6 100644 --- a/lib/isc/unix/Makefile.in +++ b/lib/isc/unix/Makefile.in @@ -24,7 +24,7 @@ CINCLUDES = -I${srcdir}/.. \ CDEFINES = CWARNINGS = -OBJS = app.o time.o stdtime.o socket.o +OBJS = app.o time.o stdtime.o socket.o interfaceiter.o SUBDIRS = include TARGETS = ${OBJS} diff --git a/lib/isc/unix/interfaceiter.c b/lib/isc/unix/interfaceiter.c new file mode 100644 index 0000000000..ac34df3fcf --- /dev/null +++ b/lib/isc/unix/interfaceiter.c @@ -0,0 +1,351 @@ +/* + * 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. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#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); +} + +/* + * Copy a socket address. The address family is given explicity + * instead of using src->sa_family, because the latter does not work + * for copying the network mask as obtained by SIOCGIFNETMASK. + */ +static void +copy_sockaddr(int family, isc_sockaddr_t *dst, struct sockaddr *src) { + switch (family) { + case AF_INET: + dst->type.sin.sin_family = AF_INET; + memcpy(&dst->type.sin.sin_addr, + &((struct sockaddr_in *) src)->sin_addr, + sizeof(struct in_addr)); + break; + /* XXX IPv6 */ + default: + INSIST(0); + 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)); + + copy_sockaddr(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); + } + copy_sockaddr(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); + } + copy_sockaddr(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 HAVE_SA_LEN + 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); +} + + +isc_result_t +isc_interfaceiter_current(isc_interfaceiter_t *iter, + isc_interface_t *ifdata) +{ + REQUIRE(iter->result == ISC_R_SUCCESS); + memcpy(ifdata, &iter->current, sizeof(*ifdata)); + 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; + + REQUIRE(VALID_IFITER(iter)); + + iter->pos = 0; + for (;;) { + result = internal_current(iter); + if (result == ISC_R_SUCCESS) + break; + result = internal_next(iter); + if (result == ISC_R_NOMORE) + break; + INSIST(result == ISC_R_SUCCESS); + } + iter->result = result; + 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; + + REQUIRE(VALID_IFITER(iter)); + REQUIRE(iter->result == ISC_R_SUCCESS); + + for (;;) { + result = internal_next(iter); + if (result == ISC_R_NOMORE) + break; + INSIST(result == ISC_R_SUCCESS); + result = internal_current(iter); + if (result == ISC_R_SUCCESS) + break; + } + iter->result = result; + return (result); +} + +void +isc_interfaceiter_destroy(isc_interfaceiter_t **iterp) +{ + isc_interfaceiter_t *iter; + REQUIRE(iterp != NULL); + iter = *iterp; + REQUIRE(VALID_IFITER(iter)); + + (void) close(iter->socket); + isc_mem_put(iter->mctx, iter->ifc.ifc_buf, iter->bufsize); + + iter->magic = 0; + isc_mem_put(iter->mctx, iter, sizeof *iter); + *iterp = NULL; +}