ifiter_getifaddrs.c revision 285169
133180Sjdp/* 233180Sjdp * Copyright (C) 2004, 2005, 2007-2009 Internet Systems Consortium, Inc. ("ISC") 333180Sjdp * Copyright (C) 2003 Internet Software Consortium. 433180Sjdp * 533180Sjdp * Permission to use, copy, modify, and/or distribute this software for any 633180Sjdp * purpose with or without fee is hereby granted, provided that the above 733180Sjdp * copyright notice and this permission notice appear in all copies. 833180Sjdp * 933180Sjdp * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 1033180Sjdp * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 1133180Sjdp * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 1233180Sjdp * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 1333180Sjdp * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 1433180Sjdp * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 1533180Sjdp * PERFORMANCE OF THIS SOFTWARE. 1633180Sjdp */ 1767967Sasmodai 1833180Sjdp/* $Id: ifiter_getifaddrs.c,v 1.13 2009/09/24 23:48:13 tbox Exp $ */ 1967967Sasmodai 2033180Sjdp/*! \file 2133180Sjdp * \brief 2233180Sjdp * Obtain the list of network interfaces using the getifaddrs(3) library. 2333180Sjdp */ 2433180Sjdp 2533180Sjdp#include <ifaddrs.h> 2633180Sjdp 2733180Sjdp/*% Iterator Magic */ 2833180Sjdp#define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'G') 2933180Sjdp/*% Valid Iterator */ 3033180Sjdp#define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) 3133180Sjdp 3233180Sjdp#ifdef __linux 3350476Speterstatic isc_boolean_t seenv6 = ISC_FALSE; 3448794Snik#endif 35230410Skib 36206622Suqs/*% Iterator structure */ 3779531Srustruct isc_interfaceiter { 3833180Sjdp unsigned int magic; /*%< Magic number. */ 39110854Sphantom isc_mem_t *mctx; 40230410Skib void *buf; /*%< (unused) */ 41110854Sphantom unsigned int bufsize; /*%< (always 0) */ 42110854Sphantom struct ifaddrs *ifaddrs; /*%< List of ifaddrs */ 43110854Sphantom struct ifaddrs *pos; /*%< Ptr to current ifaddr */ 44110854Sphantom isc_interface_t current; /*%< Current interface data. */ 4533180Sjdp isc_result_t result; /*%< Last result code. */ 4659460Sphantom#ifdef __linux 4759460Sphantom FILE * proc; 4833180Sjdp char entry[ISC_IF_INET6_SZ]; 4984306Sru isc_result_t valid; 5033180Sjdp#endif 5133180Sjdp}; 5233180Sjdp 53230410Skibisc_result_t 54230410Skibisc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { 55103213Smike isc_interfaceiter_t *iter; 5697509Swollman isc_result_t result; 57103213Smike char strbuf[ISC_STRERRORSIZE]; 58205606Sgahr int trys, ret; 5933180Sjdp 6033180Sjdp REQUIRE(mctx != NULL); 6133180Sjdp REQUIRE(iterp != NULL); 6233180Sjdp REQUIRE(*iterp == NULL); 6333180Sjdp 6433180Sjdp iter = isc_mem_get(mctx, sizeof(*iter)); 6533180Sjdp if (iter == NULL) 6633180Sjdp return (ISC_R_NOMEMORY); 6733180Sjdp 6833180Sjdp iter->mctx = mctx; 6933180Sjdp iter->buf = NULL; 70108030Sru iter->bufsize = 0; 7133180Sjdp iter->ifaddrs = NULL; 72108030Sru#ifdef __linux 7367967Sasmodai /* 7433180Sjdp * Only open "/proc/net/if_inet6" if we have never seen a IPv6 7533180Sjdp * address returned by getifaddrs(). 7667967Sasmodai */ 7733180Sjdp if (!seenv6) { 7833180Sjdp iter->proc = fopen("/proc/net/if_inet6", "r"); 7933180Sjdp if (iter->proc == NULL) { 8033180Sjdp isc__strerror(errno, strbuf, sizeof(strbuf)); 8133180Sjdp isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 8233180Sjdp ISC_LOGMODULE_SOCKET, ISC_LOG_WARNING, 8333180Sjdp "failed to open /proc/net/if_inet6"); 8433180Sjdp } 8533180Sjdp } else 8633180Sjdp iter->proc = NULL; 8733180Sjdp iter->valid = ISC_R_FAILURE; 8833180Sjdp#endif 8933180Sjdp 9033180Sjdp /* If interrupted, try again */ 9133180Sjdp for (trys = 0; trys < 3; trys++) { 9267967Sasmodai if ((ret = getifaddrs(&iter->ifaddrs)) >= 0) 9367967Sasmodai break; 9433180Sjdp if (errno != EINTR) 9533180Sjdp break; 9633180Sjdp } 9733180Sjdp if (ret < 0) { 9867967Sasmodai isc__strerror(errno, strbuf, sizeof(strbuf)); 9933180Sjdp UNEXPECTED_ERROR(__FILE__, __LINE__, 10033180Sjdp "getting interface addresses: %s: %s", 10133180Sjdp isc_msgcat_get(isc_msgcat, 102108087Sru ISC_MSGSET_IFITERGETIFADDRS, 10333180Sjdp ISC_MSG_GETIFADDRS, 104108087Sru "getifaddrs"), 10533180Sjdp strbuf); 10633180Sjdp result = ISC_R_UNEXPECTED; 10766054Sjdp goto failure; 10866054Sjdp } 10933180Sjdp 11033180Sjdp /* 11133180Sjdp * A newly created iterator has an undefined position 11233180Sjdp * until isc_interfaceiter_first() is called. 11333180Sjdp */ 11433180Sjdp iter->pos = NULL; 11533180Sjdp iter->result = ISC_R_FAILURE; 11633180Sjdp 11733180Sjdp iter->magic = IFITER_MAGIC; 11833180Sjdp *iterp = iter; 11933180Sjdp return (ISC_R_SUCCESS); 12033180Sjdp 12133180Sjdp failure: 12233180Sjdp#ifdef __linux 12333180Sjdp if (iter->proc != NULL) 12433180Sjdp fclose(iter->proc); 12566054Sjdp#endif 12666054Sjdp if (iter->ifaddrs != NULL) /* just in case */ 12766054Sjdp freeifaddrs(iter->ifaddrs); 12866054Sjdp isc_mem_put(mctx, iter, sizeof(*iter)); 129190624Skib return (result); 13066054Sjdp} 13166054Sjdp 13266054Sjdp/* 13366054Sjdp * Get information about the current interface to iter->current. 13466054Sjdp * If successful, return ISC_R_SUCCESS. 13566054Sjdp * If the interface has an unsupported address family, 13666054Sjdp * return ISC_R_IGNORE. 137101574Sru */ 138101574Sru 13966054Sjdpstatic isc_result_t 14090172Ssobomaxinternal_current(isc_interfaceiter_t *iter) { 14190172Ssobomax struct ifaddrs *ifa; 14290172Ssobomax int family; 14390172Ssobomax unsigned int namelen; 14490172Ssobomax 14590172Ssobomax REQUIRE(VALID_IFITER(iter)); 14690172Ssobomax 147190624Skib ifa = iter->pos; 148190624Skib 149190624Skib#ifdef __linux 150190624Skib /* 151190624Skib * [Bug 2792] 152190624Skib * burnicki: iter->pos is usually never NULL here (anymore?), 153190624Skib * so linux_if_inet6_current(iter) is never called here. 154195745Skib * However, that routine would check (under Linux), if the 155211397Sjoel * interface is in a tentative state, e.g. if there's no link 156195745Skib * yet but an IPv6 address has already be assigned. 157195745Skib */ 158195745Skib if (iter->pos == NULL) 159195745Skib return (linux_if_inet6_current(iter)); 160195745Skib#endif 16166054Sjdp 16266054Sjdp INSIST(ifa != NULL); 16367967Sasmodai INSIST(ifa->ifa_name != NULL); 16433180Sjdp 16533180Sjdp 16633180Sjdp#ifdef IFF_RUNNING 16733180Sjdp /* 16833180Sjdp * [Bug 2792] 169108030Sru * burnicki: if the interface is not running then 170230410Skib * it may be in a tentative state. See above. 171230410Skib */ 172230410Skib if ((ifa->ifa_flags & IFF_RUNNING) == 0) 173230410Skib return (ISC_R_IGNORE); 174230410Skib#endif 175230410Skib 176230410Skib if (ifa->ifa_addr == NULL) 177230410Skib return (ISC_R_IGNORE); 178230410Skib 179230410Skib family = ifa->ifa_addr->sa_family; 180230410Skib if (family != AF_INET && family != AF_INET6) 181230410Skib return (ISC_R_IGNORE); 182230410Skib 183230410Skib#ifdef __linux 184230410Skib if (family == AF_INET6) 185230410Skib seenv6 = ISC_TRUE; 186230410Skib#endif 187230410Skib 188230410Skib memset(&iter->current, 0, sizeof(iter->current)); 189230410Skib 190230410Skib namelen = strlen(ifa->ifa_name); 191230410Skib if (namelen > sizeof(iter->current.name) - 1) 192230410Skib namelen = sizeof(iter->current.name) - 1; 193230410Skib 194230410Skib memset(iter->current.name, 0, sizeof(iter->current.name)); 195230410Skib memcpy(iter->current.name, ifa->ifa_name, namelen); 196230410Skib 197230410Skib iter->current.flags = 0; 198230410Skib 199230410Skib if ((ifa->ifa_flags & IFF_UP) != 0) 20033180Sjdp iter->current.flags |= INTERFACE_F_UP; 201108030Sru 20233180Sjdp if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) 20333180Sjdp iter->current.flags |= INTERFACE_F_POINTTOPOINT; 20433180Sjdp 20533180Sjdp if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) 20633180Sjdp iter->current.flags |= INTERFACE_F_LOOPBACK; 20767967Sasmodai 20833180Sjdp if ((ifa->ifa_flags & IFF_BROADCAST) != 0) 20933180Sjdp iter->current.flags |= INTERFACE_F_BROADCAST; 21033180Sjdp 21133180Sjdp#ifdef IFF_MULTICAST 21233180Sjdp if ((ifa->ifa_flags & IFF_MULTICAST) != 0) 21333180Sjdp iter->current.flags |= INTERFACE_F_MULTICAST; 21466057Sjdp#endif 21566057Sjdp 21666057Sjdp iter->current.af = family; 21766057Sjdp 21833180Sjdp get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name); 21966057Sjdp 22066057Sjdp if (ifa->ifa_netmask != NULL) 22166057Sjdp get_addr(family, &iter->current.netmask, ifa->ifa_netmask, 222101574Sru ifa->ifa_name); 223101574Sru 22466057Sjdp if (ifa->ifa_dstaddr != NULL && 22566057Sjdp (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) 22633180Sjdp get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr, 22766057Sjdp ifa->ifa_name); 22866057Sjdp 22966057Sjdp if (ifa->ifa_broadaddr != NULL && 23066057Sjdp (iter->current.flags & INTERFACE_F_BROADCAST) != 0) 231101574Sru get_addr(family, &iter->current.broadcast, ifa->ifa_broadaddr, 232101574Sru ifa->ifa_name); 23366057Sjdp 23466057Sjdp#ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX 23566057Sjdp iter->current.ifindex = if_nametoindex(iter->current.name); 23666057Sjdp#endif 23766057Sjdp return (ISC_R_SUCCESS); 23866057Sjdp} 23966057Sjdp 24066057Sjdp/* 24166057Sjdp * Step the iterator to the next interface. Unlike 24266057Sjdp * isc_interfaceiter_next(), this may leave the iterator 24366057Sjdp * positioned on an interface that will ultimately 24466057Sjdp * be ignored. Return ISC_R_NOMORE if there are no more 24566057Sjdp * interfaces, otherwise ISC_R_SUCCESS. 24666057Sjdp */ 24766057Sjdpstatic isc_result_t 24866057Sjdpinternal_next(isc_interfaceiter_t *iter) { 24966057Sjdp 25066057Sjdp if (iter->pos != NULL) 25166057Sjdp iter->pos = iter->pos->ifa_next; 252130027Sroam if (iter->pos == NULL) { 253130027Sroam#ifdef __linux 254130027Sroam if (!seenv6) 255130027Sroam return (linux_if_inet6_next(iter)); 25666057Sjdp#endif 25733180Sjdp return (ISC_R_NOMORE); 25833180Sjdp } 25933180Sjdp 26033180Sjdp return (ISC_R_SUCCESS); 26133180Sjdp} 26233180Sjdp 26333180Sjdpstatic void 26433180Sjdpinternal_destroy(isc_interfaceiter_t *iter) { 26533180Sjdp 26633180Sjdp#ifdef __linux 26733180Sjdp if (iter->proc != NULL) 26833180Sjdp fclose(iter->proc); 26933180Sjdp iter->proc = NULL; 27033180Sjdp#endif 27133180Sjdp if (iter->ifaddrs) 27233180Sjdp freeifaddrs(iter->ifaddrs); 27333180Sjdp iter->ifaddrs = NULL; 27433180Sjdp} 27533180Sjdp 27633180Sjdpstatic 27733180Sjdpvoid internal_first(isc_interfaceiter_t *iter) { 27852802Sjoerg 27997475Swollman#ifdef __linux 28097475Swollman linux_if_inet6_first(iter); 28197475Swollman#endif 28297475Swollman iter->pos = iter->ifaddrs; 28397475Swollman} 28433180Sjdp