1/* 2 * Copyright (C) 2004, 2005, 2007-2009 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id: ifiter_getifaddrs.c,v 1.13 2009/09/24 23:48:13 tbox Exp $ */ 19 20/*! \file 21 * \brief 22 * Obtain the list of network interfaces using the getifaddrs(3) library. 23 */ 24 25#include <ifaddrs.h> 26 27/*% Iterator Magic */ 28#define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'G') 29/*% Valid Iterator */ 30#define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) 31 32#ifdef __linux 33static isc_boolean_t seenv6 = ISC_FALSE; 34#endif 35 36/*% Iterator structure */ 37struct isc_interfaceiter { 38 unsigned int magic; /*%< Magic number. */ 39 isc_mem_t *mctx; 40 void *buf; /*%< (unused) */ 41 unsigned int bufsize; /*%< (always 0) */ 42 struct ifaddrs *ifaddrs; /*%< List of ifaddrs */ 43 struct ifaddrs *pos; /*%< Ptr to current ifaddr */ 44 isc_interface_t current; /*%< Current interface data. */ 45 isc_result_t result; /*%< Last result code. */ 46#ifdef __linux 47 FILE * proc; 48 char entry[ISC_IF_INET6_SZ]; 49 isc_result_t valid; 50#endif 51}; 52 53isc_result_t 54isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { 55 isc_interfaceiter_t *iter; 56 isc_result_t result; 57 char strbuf[ISC_STRERRORSIZE]; 58 59 REQUIRE(mctx != NULL); 60 REQUIRE(iterp != NULL); 61 REQUIRE(*iterp == NULL); 62 63 iter = isc_mem_get(mctx, sizeof(*iter)); 64 if (iter == NULL) 65 return (ISC_R_NOMEMORY); 66 67 iter->mctx = mctx; 68 iter->buf = NULL; 69 iter->bufsize = 0; 70 iter->ifaddrs = NULL; 71#ifdef __linux 72 /* 73 * Only open "/proc/net/if_inet6" if we have never seen a IPv6 74 * address returned by getifaddrs(). 75 */ 76 if (!seenv6) 77 iter->proc = fopen("/proc/net/if_inet6", "r"); 78 else 79 iter->proc = NULL; 80 iter->valid = ISC_R_FAILURE; 81#endif 82 83 if (getifaddrs(&iter->ifaddrs) < 0) { 84 isc__strerror(errno, strbuf, sizeof(strbuf)); 85 UNEXPECTED_ERROR(__FILE__, __LINE__, 86 isc_msgcat_get(isc_msgcat, 87 ISC_MSGSET_IFITERGETIFADDRS, 88 ISC_MSG_GETIFADDRS, 89 "getting interface " 90 "addresses: getifaddrs: %s"), 91 strbuf); 92 result = ISC_R_UNEXPECTED; 93 goto failure; 94 } 95 96 /* 97 * A newly created iterator has an undefined position 98 * until isc_interfaceiter_first() is called. 99 */ 100 iter->pos = NULL; 101 iter->result = ISC_R_FAILURE; 102 103 iter->magic = IFITER_MAGIC; 104 *iterp = iter; 105 return (ISC_R_SUCCESS); 106 107 failure: 108#ifdef __linux 109 if (iter->proc != NULL) 110 fclose(iter->proc); 111#endif 112 if (iter->ifaddrs != NULL) /* just in case */ 113 freeifaddrs(iter->ifaddrs); 114 isc_mem_put(mctx, iter, sizeof(*iter)); 115 return (result); 116} 117 118/* 119 * Get information about the current interface to iter->current. 120 * If successful, return ISC_R_SUCCESS. 121 * If the interface has an unsupported address family, 122 * return ISC_R_IGNORE. 123 */ 124 125static isc_result_t 126internal_current(isc_interfaceiter_t *iter) { 127 struct ifaddrs *ifa; 128 int family; 129 unsigned int namelen; 130 131 REQUIRE(VALID_IFITER(iter)); 132 133 ifa = iter->pos; 134 135#ifdef __linux 136 if (iter->pos == NULL) 137 return (linux_if_inet6_current(iter)); 138#endif 139 140 INSIST(ifa != NULL); 141 INSIST(ifa->ifa_name != NULL); 142 143 if (ifa->ifa_addr == NULL) 144 return (ISC_R_IGNORE); 145 146 family = ifa->ifa_addr->sa_family; 147 if (family != AF_INET && family != AF_INET6) 148 return (ISC_R_IGNORE); 149 150#ifdef __linux 151 if (family == AF_INET6) 152 seenv6 = ISC_TRUE; 153#endif 154 155 memset(&iter->current, 0, sizeof(iter->current)); 156 157 namelen = strlen(ifa->ifa_name); 158 if (namelen > sizeof(iter->current.name) - 1) 159 namelen = sizeof(iter->current.name) - 1; 160 161 memset(iter->current.name, 0, sizeof(iter->current.name)); 162 memcpy(iter->current.name, ifa->ifa_name, namelen); 163 164 iter->current.flags = 0; 165 166 if ((ifa->ifa_flags & IFF_UP) != 0) 167 iter->current.flags |= INTERFACE_F_UP; 168 169 if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) 170 iter->current.flags |= INTERFACE_F_POINTTOPOINT; 171 172 if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) 173 iter->current.flags |= INTERFACE_F_LOOPBACK; 174 175 iter->current.af = family; 176 177 get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name); 178 179 if (ifa->ifa_netmask != NULL) 180 get_addr(family, &iter->current.netmask, ifa->ifa_netmask, 181 ifa->ifa_name); 182 183 if (ifa->ifa_dstaddr != NULL && 184 (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) 185 get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr, 186 ifa->ifa_name); 187 188 return (ISC_R_SUCCESS); 189} 190 191/* 192 * Step the iterator to the next interface. Unlike 193 * isc_interfaceiter_next(), this may leave the iterator 194 * positioned on an interface that will ultimately 195 * be ignored. Return ISC_R_NOMORE if there are no more 196 * interfaces, otherwise ISC_R_SUCCESS. 197 */ 198static isc_result_t 199internal_next(isc_interfaceiter_t *iter) { 200 201 if (iter->pos != NULL) 202 iter->pos = iter->pos->ifa_next; 203 if (iter->pos == NULL) { 204#ifdef __linux 205 if (!seenv6) 206 return (linux_if_inet6_next(iter)); 207#endif 208 return (ISC_R_NOMORE); 209 } 210 211 return (ISC_R_SUCCESS); 212} 213 214static void 215internal_destroy(isc_interfaceiter_t *iter) { 216 217#ifdef __linux 218 if (iter->proc != NULL) 219 fclose(iter->proc); 220 iter->proc = NULL; 221#endif 222 if (iter->ifaddrs) 223 freeifaddrs(iter->ifaddrs); 224 iter->ifaddrs = NULL; 225} 226 227static 228void internal_first(isc_interfaceiter_t *iter) { 229 230#ifdef __linux 231 linux_if_inet6_first(iter); 232#endif 233 iter->pos = iter->ifaddrs; 234} 235