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