1 /* $NetBSD: ifiter_getifaddrs.c,v 1.2 2024/08/18 20:47:16 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2004, 2005, 2007-2009 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 int trys, ret; 61 62 REQUIRE(mctx != NULL); 63 REQUIRE(iterp != NULL); 64 REQUIRE(*iterp == NULL); 65 66 iter = isc_mem_get(mctx, sizeof(*iter)); 67 if (iter == NULL) 68 return (ISC_R_NOMEMORY); 69 70 iter->mctx = mctx; 71 iter->buf = NULL; 72 iter->bufsize = 0; 73 iter->ifaddrs = NULL; 74 #ifdef __linux 75 /* 76 * Only open "/proc/net/if_inet6" if we have never seen a IPv6 77 * address returned by getifaddrs(). 78 */ 79 if (!seenv6) { 80 iter->proc = fopen("/proc/net/if_inet6", "r"); 81 if (iter->proc == NULL) { 82 isc__strerror(errno, strbuf, sizeof(strbuf)); 83 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 84 ISC_LOGMODULE_SOCKET, ISC_LOG_WARNING, 85 "failed to open /proc/net/if_inet6"); 86 } 87 } else 88 iter->proc = NULL; 89 iter->valid = ISC_R_FAILURE; 90 #endif 91 92 /* If interrupted, try again */ 93 for (trys = 0; trys < 3; trys++) { 94 if ((ret = getifaddrs(&iter->ifaddrs)) >= 0) 95 break; 96 if (errno != EINTR) 97 break; 98 } 99 if (ret < 0) { 100 isc__strerror(errno, strbuf, sizeof(strbuf)); 101 UNEXPECTED_ERROR(__FILE__, __LINE__, 102 "getting interface addresses: %s: %s", 103 isc_msgcat_get(isc_msgcat, 104 ISC_MSGSET_IFITERGETIFADDRS, 105 ISC_MSG_GETIFADDRS, 106 "getifaddrs"), 107 strbuf); 108 result = ISC_R_UNEXPECTED; 109 goto failure; 110 } 111 112 /* 113 * A newly created iterator has an undefined position 114 * until isc_interfaceiter_first() is called. 115 */ 116 iter->pos = NULL; 117 iter->result = ISC_R_FAILURE; 118 119 iter->magic = IFITER_MAGIC; 120 *iterp = iter; 121 return (ISC_R_SUCCESS); 122 123 failure: 124 #ifdef __linux 125 if (iter->proc != NULL) 126 fclose(iter->proc); 127 #endif 128 if (iter->ifaddrs != NULL) /* just in case */ 129 freeifaddrs(iter->ifaddrs); 130 isc_mem_put(mctx, iter, sizeof(*iter)); 131 return (result); 132 } 133 134 /* 135 * Get information about the current interface to iter->current. 136 * If successful, return ISC_R_SUCCESS. 137 * If the interface has an unsupported address family, 138 * return ISC_R_IGNORE. 139 */ 140 141 static isc_result_t 142 internal_current(isc_interfaceiter_t *iter) { 143 struct ifaddrs *ifa; 144 int family; 145 unsigned int namelen; 146 147 REQUIRE(VALID_IFITER(iter)); 148 149 ifa = iter->pos; 150 151 #ifdef __linux 152 /* 153 * [Bug 2792] 154 * burnicki: iter->pos is usually never NULL here (anymore?), 155 * so linux_if_inet6_current(iter) is never called here. 156 * However, that routine would check (under Linux), if the 157 * interface is in a tentative state, e.g. if there's no link 158 * yet but an IPv6 address has already be assigned. 159 */ 160 if (iter->pos == NULL) 161 return (linux_if_inet6_current(iter)); 162 #endif 163 164 INSIST(ifa != NULL); 165 INSIST(ifa->ifa_name != NULL); 166 167 168 #ifdef IFF_RUNNING 169 /* 170 * [Bug 2792] 171 * burnicki: if the interface is not running then 172 * it may be in a tentative state. See above. 173 */ 174 if ((ifa->ifa_flags & IFF_RUNNING) == 0) 175 return (ISC_R_IGNORE); 176 #endif 177 178 if (ifa->ifa_addr == NULL) 179 return (ISC_R_IGNORE); 180 181 family = ifa->ifa_addr->sa_family; 182 if (family != AF_INET && family != AF_INET6) 183 return (ISC_R_IGNORE); 184 185 #ifdef __linux 186 if (family == AF_INET6) 187 seenv6 = ISC_TRUE; 188 #endif 189 190 memset(&iter->current, 0, sizeof(iter->current)); 191 192 namelen = strlen(ifa->ifa_name); 193 if (namelen > sizeof(iter->current.name) - 1) 194 namelen = sizeof(iter->current.name) - 1; 195 196 memset(iter->current.name, 0, sizeof(iter->current.name)); 197 memcpy(iter->current.name, ifa->ifa_name, namelen); 198 199 iter->current.flags = 0; 200 201 if ((ifa->ifa_flags & IFF_UP) != 0) 202 iter->current.flags |= INTERFACE_F_UP; 203 204 if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) 205 iter->current.flags |= INTERFACE_F_POINTTOPOINT; 206 207 if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) 208 iter->current.flags |= INTERFACE_F_LOOPBACK; 209 210 if ((ifa->ifa_flags & IFF_BROADCAST) != 0) 211 iter->current.flags |= INTERFACE_F_BROADCAST; 212 213 #ifdef IFF_MULTICAST 214 if ((ifa->ifa_flags & IFF_MULTICAST) != 0) 215 iter->current.flags |= INTERFACE_F_MULTICAST; 216 #endif 217 218 iter->current.af = family; 219 220 get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name); 221 222 if (ifa->ifa_netmask != NULL) 223 get_addr(family, &iter->current.netmask, ifa->ifa_netmask, 224 ifa->ifa_name); 225 226 if (ifa->ifa_dstaddr != NULL && 227 (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) 228 get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr, 229 ifa->ifa_name); 230 231 if (ifa->ifa_broadaddr != NULL && 232 (iter->current.flags & INTERFACE_F_BROADCAST) != 0) 233 get_addr(family, &iter->current.broadcast, ifa->ifa_broadaddr, 234 ifa->ifa_name); 235 236 #ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX 237 iter->current.ifindex = if_nametoindex(iter->current.name); 238 #endif 239 return (ISC_R_SUCCESS); 240 } 241 242 /* 243 * Step the iterator to the next interface. Unlike 244 * isc_interfaceiter_next(), this may leave the iterator 245 * positioned on an interface that will ultimately 246 * be ignored. Return ISC_R_NOMORE if there are no more 247 * interfaces, otherwise ISC_R_SUCCESS. 248 */ 249 static isc_result_t 250 internal_next(isc_interfaceiter_t *iter) { 251 252 if (iter->pos != NULL) 253 iter->pos = iter->pos->ifa_next; 254 if (iter->pos == NULL) { 255 #ifdef __linux 256 if (!seenv6) 257 return (linux_if_inet6_next(iter)); 258 #endif 259 return (ISC_R_NOMORE); 260 } 261 262 return (ISC_R_SUCCESS); 263 } 264 265 static void 266 internal_destroy(isc_interfaceiter_t *iter) { 267 268 #ifdef __linux 269 if (iter->proc != NULL) 270 fclose(iter->proc); 271 iter->proc = NULL; 272 #endif 273 if (iter->ifaddrs) 274 freeifaddrs(iter->ifaddrs); 275 iter->ifaddrs = NULL; 276 } 277 278 static 279 void internal_first(isc_interfaceiter_t *iter) { 280 281 #ifdef __linux 282 linux_if_inet6_first(iter); 283 #endif 284 iter->pos = iter->ifaddrs; 285 } 286