1290001Sglebius/* 2290001Sglebius * Copyright (C) 2004, 2005, 2007-2009 Internet Systems Consortium, Inc. ("ISC") 3290001Sglebius * Copyright (C) 2003 Internet Software Consortium. 4290001Sglebius * 5290001Sglebius * Permission to use, copy, modify, and/or distribute this software for any 6290001Sglebius * purpose with or without fee is hereby granted, provided that the above 7290001Sglebius * copyright notice and this permission notice appear in all copies. 8290001Sglebius * 9290001Sglebius * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10290001Sglebius * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11290001Sglebius * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12290001Sglebius * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13290001Sglebius * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14290001Sglebius * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15290001Sglebius * PERFORMANCE OF THIS SOFTWARE. 16290001Sglebius */ 17290001Sglebius 18290001Sglebius/* $Id: ifiter_getifaddrs.c,v 1.13 2009/09/24 23:48:13 tbox Exp $ */ 19290001Sglebius 20290001Sglebius/*! \file 21290001Sglebius * \brief 22290001Sglebius * Obtain the list of network interfaces using the getifaddrs(3) library. 23290001Sglebius */ 24290001Sglebius 25290001Sglebius#include <ifaddrs.h> 26290001Sglebius 27290001Sglebius/*% Iterator Magic */ 28290001Sglebius#define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'G') 29290001Sglebius/*% Valid Iterator */ 30290001Sglebius#define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) 31290001Sglebius 32290001Sglebius#ifdef __linux 33290001Sglebiusstatic isc_boolean_t seenv6 = ISC_FALSE; 34290001Sglebius#endif 35290001Sglebius 36290001Sglebius/*% Iterator structure */ 37290001Sglebiusstruct isc_interfaceiter { 38290001Sglebius unsigned int magic; /*%< Magic number. */ 39290001Sglebius isc_mem_t *mctx; 40290001Sglebius void *buf; /*%< (unused) */ 41290001Sglebius unsigned int bufsize; /*%< (always 0) */ 42290001Sglebius struct ifaddrs *ifaddrs; /*%< List of ifaddrs */ 43290001Sglebius struct ifaddrs *pos; /*%< Ptr to current ifaddr */ 44290001Sglebius isc_interface_t current; /*%< Current interface data. */ 45290001Sglebius isc_result_t result; /*%< Last result code. */ 46290001Sglebius#ifdef __linux 47290001Sglebius FILE * proc; 48290001Sglebius char entry[ISC_IF_INET6_SZ]; 49290001Sglebius isc_result_t valid; 50290001Sglebius#endif 51290001Sglebius}; 52290001Sglebius 53290001Sglebiusisc_result_t 54290001Sglebiusisc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { 55290001Sglebius isc_interfaceiter_t *iter; 56290001Sglebius isc_result_t result; 57290001Sglebius char strbuf[ISC_STRERRORSIZE]; 58290001Sglebius int trys, ret; 59290001Sglebius 60290001Sglebius REQUIRE(mctx != NULL); 61290001Sglebius REQUIRE(iterp != NULL); 62290001Sglebius REQUIRE(*iterp == NULL); 63290001Sglebius 64290001Sglebius iter = isc_mem_get(mctx, sizeof(*iter)); 65290001Sglebius if (iter == NULL) 66290001Sglebius return (ISC_R_NOMEMORY); 67290001Sglebius 68290001Sglebius iter->mctx = mctx; 69290001Sglebius iter->buf = NULL; 70290001Sglebius iter->bufsize = 0; 71290001Sglebius iter->ifaddrs = NULL; 72290001Sglebius#ifdef __linux 73290001Sglebius /* 74290001Sglebius * Only open "/proc/net/if_inet6" if we have never seen a IPv6 75290001Sglebius * address returned by getifaddrs(). 76290001Sglebius */ 77290001Sglebius if (!seenv6) { 78290001Sglebius iter->proc = fopen("/proc/net/if_inet6", "r"); 79290001Sglebius if (iter->proc == NULL) { 80290001Sglebius isc__strerror(errno, strbuf, sizeof(strbuf)); 81290001Sglebius isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 82290001Sglebius ISC_LOGMODULE_SOCKET, ISC_LOG_WARNING, 83290001Sglebius "failed to open /proc/net/if_inet6"); 84290001Sglebius } 85290001Sglebius } else 86290001Sglebius iter->proc = NULL; 87290001Sglebius iter->valid = ISC_R_FAILURE; 88290001Sglebius#endif 89290001Sglebius 90290001Sglebius /* If interrupted, try again */ 91290001Sglebius for (trys = 0; trys < 3; trys++) { 92290001Sglebius if ((ret = getifaddrs(&iter->ifaddrs)) >= 0) 93290001Sglebius break; 94290001Sglebius if (errno != EINTR) 95290001Sglebius break; 96290001Sglebius } 97290001Sglebius if (ret < 0) { 98290001Sglebius isc__strerror(errno, strbuf, sizeof(strbuf)); 99290001Sglebius UNEXPECTED_ERROR(__FILE__, __LINE__, 100290001Sglebius "getting interface addresses: %s: %s", 101290001Sglebius isc_msgcat_get(isc_msgcat, 102290001Sglebius ISC_MSGSET_IFITERGETIFADDRS, 103290001Sglebius ISC_MSG_GETIFADDRS, 104290001Sglebius "getifaddrs"), 105290001Sglebius strbuf); 106290001Sglebius result = ISC_R_UNEXPECTED; 107290001Sglebius goto failure; 108290001Sglebius } 109290001Sglebius 110290001Sglebius /* 111290001Sglebius * A newly created iterator has an undefined position 112290001Sglebius * until isc_interfaceiter_first() is called. 113290001Sglebius */ 114290001Sglebius iter->pos = NULL; 115290001Sglebius iter->result = ISC_R_FAILURE; 116290001Sglebius 117290001Sglebius iter->magic = IFITER_MAGIC; 118290001Sglebius *iterp = iter; 119290001Sglebius return (ISC_R_SUCCESS); 120290001Sglebius 121290001Sglebius failure: 122290001Sglebius#ifdef __linux 123290001Sglebius if (iter->proc != NULL) 124290001Sglebius fclose(iter->proc); 125290001Sglebius#endif 126290001Sglebius if (iter->ifaddrs != NULL) /* just in case */ 127290001Sglebius freeifaddrs(iter->ifaddrs); 128290001Sglebius isc_mem_put(mctx, iter, sizeof(*iter)); 129290001Sglebius return (result); 130290001Sglebius} 131290001Sglebius 132290001Sglebius/* 133290001Sglebius * Get information about the current interface to iter->current. 134290001Sglebius * If successful, return ISC_R_SUCCESS. 135290001Sglebius * If the interface has an unsupported address family, 136290001Sglebius * return ISC_R_IGNORE. 137290001Sglebius */ 138290001Sglebius 139290001Sglebiusstatic isc_result_t 140290001Sglebiusinternal_current(isc_interfaceiter_t *iter) { 141290001Sglebius struct ifaddrs *ifa; 142290001Sglebius int family; 143290001Sglebius unsigned int namelen; 144290001Sglebius 145290001Sglebius REQUIRE(VALID_IFITER(iter)); 146290001Sglebius 147290001Sglebius ifa = iter->pos; 148290001Sglebius 149290001Sglebius#ifdef __linux 150290001Sglebius /* 151290001Sglebius * [Bug 2792] 152290001Sglebius * burnicki: iter->pos is usually never NULL here (anymore?), 153290001Sglebius * so linux_if_inet6_current(iter) is never called here. 154290001Sglebius * However, that routine would check (under Linux), if the 155290001Sglebius * interface is in a tentative state, e.g. if there's no link 156290001Sglebius * yet but an IPv6 address has already be assigned. 157290001Sglebius */ 158290001Sglebius if (iter->pos == NULL) 159290001Sglebius return (linux_if_inet6_current(iter)); 160290001Sglebius#endif 161290001Sglebius 162290001Sglebius INSIST(ifa != NULL); 163290001Sglebius INSIST(ifa->ifa_name != NULL); 164290001Sglebius 165290001Sglebius 166290001Sglebius#ifdef IFF_RUNNING 167290001Sglebius /* 168290001Sglebius * [Bug 2792] 169290001Sglebius * burnicki: if the interface is not running then 170290001Sglebius * it may be in a tentative state. See above. 171290001Sglebius */ 172290001Sglebius if ((ifa->ifa_flags & IFF_RUNNING) == 0) 173290001Sglebius return (ISC_R_IGNORE); 174290001Sglebius#endif 175290001Sglebius 176290001Sglebius if (ifa->ifa_addr == NULL) 177290001Sglebius return (ISC_R_IGNORE); 178290001Sglebius 179290001Sglebius family = ifa->ifa_addr->sa_family; 180290001Sglebius if (family != AF_INET && family != AF_INET6) 181290001Sglebius return (ISC_R_IGNORE); 182290001Sglebius 183290001Sglebius#ifdef __linux 184290001Sglebius if (family == AF_INET6) 185290001Sglebius seenv6 = ISC_TRUE; 186290001Sglebius#endif 187290001Sglebius 188290001Sglebius memset(&iter->current, 0, sizeof(iter->current)); 189290001Sglebius 190290001Sglebius namelen = strlen(ifa->ifa_name); 191290001Sglebius if (namelen > sizeof(iter->current.name) - 1) 192290001Sglebius namelen = sizeof(iter->current.name) - 1; 193290001Sglebius 194290001Sglebius memset(iter->current.name, 0, sizeof(iter->current.name)); 195290001Sglebius memcpy(iter->current.name, ifa->ifa_name, namelen); 196290001Sglebius 197290001Sglebius iter->current.flags = 0; 198290001Sglebius 199290001Sglebius if ((ifa->ifa_flags & IFF_UP) != 0) 200290001Sglebius iter->current.flags |= INTERFACE_F_UP; 201290001Sglebius 202290001Sglebius if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) 203290001Sglebius iter->current.flags |= INTERFACE_F_POINTTOPOINT; 204290001Sglebius 205290001Sglebius if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) 206290001Sglebius iter->current.flags |= INTERFACE_F_LOOPBACK; 207290001Sglebius 208290001Sglebius if ((ifa->ifa_flags & IFF_BROADCAST) != 0) 209290001Sglebius iter->current.flags |= INTERFACE_F_BROADCAST; 210290001Sglebius 211290001Sglebius#ifdef IFF_MULTICAST 212290001Sglebius if ((ifa->ifa_flags & IFF_MULTICAST) != 0) 213290001Sglebius iter->current.flags |= INTERFACE_F_MULTICAST; 214290001Sglebius#endif 215290001Sglebius 216290001Sglebius iter->current.af = family; 217290001Sglebius 218290001Sglebius get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name); 219290001Sglebius 220290001Sglebius if (ifa->ifa_netmask != NULL) 221290001Sglebius get_addr(family, &iter->current.netmask, ifa->ifa_netmask, 222290001Sglebius ifa->ifa_name); 223290001Sglebius 224290001Sglebius if (ifa->ifa_dstaddr != NULL && 225290001Sglebius (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) 226290001Sglebius get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr, 227290001Sglebius ifa->ifa_name); 228290001Sglebius 229290001Sglebius if (ifa->ifa_broadaddr != NULL && 230290001Sglebius (iter->current.flags & INTERFACE_F_BROADCAST) != 0) 231290001Sglebius get_addr(family, &iter->current.broadcast, ifa->ifa_broadaddr, 232290001Sglebius ifa->ifa_name); 233290001Sglebius 234290001Sglebius#ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX 235290001Sglebius iter->current.ifindex = if_nametoindex(iter->current.name); 236290001Sglebius#endif 237290001Sglebius return (ISC_R_SUCCESS); 238290001Sglebius} 239290001Sglebius 240290001Sglebius/* 241290001Sglebius * Step the iterator to the next interface. Unlike 242290001Sglebius * isc_interfaceiter_next(), this may leave the iterator 243290001Sglebius * positioned on an interface that will ultimately 244290001Sglebius * be ignored. Return ISC_R_NOMORE if there are no more 245290001Sglebius * interfaces, otherwise ISC_R_SUCCESS. 246290001Sglebius */ 247290001Sglebiusstatic isc_result_t 248290001Sglebiusinternal_next(isc_interfaceiter_t *iter) { 249290001Sglebius 250290001Sglebius if (iter->pos != NULL) 251290001Sglebius iter->pos = iter->pos->ifa_next; 252290001Sglebius if (iter->pos == NULL) { 253290001Sglebius#ifdef __linux 254290001Sglebius if (!seenv6) 255290001Sglebius return (linux_if_inet6_next(iter)); 256290001Sglebius#endif 257290001Sglebius return (ISC_R_NOMORE); 258290001Sglebius } 259290001Sglebius 260290001Sglebius return (ISC_R_SUCCESS); 261290001Sglebius} 262290001Sglebius 263290001Sglebiusstatic void 264290001Sglebiusinternal_destroy(isc_interfaceiter_t *iter) { 265290001Sglebius 266290001Sglebius#ifdef __linux 267290001Sglebius if (iter->proc != NULL) 268290001Sglebius fclose(iter->proc); 269290001Sglebius iter->proc = NULL; 270290001Sglebius#endif 271290001Sglebius if (iter->ifaddrs) 272290001Sglebius freeifaddrs(iter->ifaddrs); 273290001Sglebius iter->ifaddrs = NULL; 274290001Sglebius} 275290001Sglebius 276290001Sglebiusstatic 277290001Sglebiusvoid internal_first(isc_interfaceiter_t *iter) { 278290001Sglebius 279290001Sglebius#ifdef __linux 280290001Sglebius linux_if_inet6_first(iter); 281290001Sglebius#endif 282290001Sglebius iter->pos = iter->ifaddrs; 283290001Sglebius} 284