1 /* $NetBSD: ifiter_getifaddrs.c,v 1.1 2024/02/18 20:57:57 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /*! \file 17 * \brief 18 * Obtain the list of network interfaces using the getifaddrs(3) library. 19 */ 20 21 #include <ifaddrs.h> 22 #include <stdbool.h> 23 24 #include <isc/strerr.h> 25 26 /*% Iterator Magic */ 27 #define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'G') 28 /*% Valid Iterator */ 29 #define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) 30 31 #ifdef __linux 32 static bool seenv6 = false; 33 #endif /* ifdef __linux */ 34 35 /*% Iterator structure */ 36 struct isc_interfaceiter { 37 unsigned int magic; /*%< Magic number. */ 38 isc_mem_t *mctx; 39 void *buf; /*%< (unused) */ 40 unsigned int bufsize; /*%< (always 0) */ 41 struct ifaddrs *ifaddrs; /*%< List of ifaddrs */ 42 struct ifaddrs *pos; /*%< Ptr to current ifaddr */ 43 isc_interface_t current; /*%< Current interface data. */ 44 isc_result_t result; /*%< Last result code. */ 45 #ifdef __linux 46 FILE *proc; 47 char entry[ISC_IF_INET6_SZ]; 48 isc_result_t valid; 49 #endif /* ifdef __linux */ 50 }; 51 52 isc_result_t 53 isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { 54 isc_interfaceiter_t *iter; 55 isc_result_t result; 56 char strbuf[ISC_STRERRORSIZE]; 57 58 REQUIRE(mctx != NULL); 59 REQUIRE(iterp != NULL); 60 REQUIRE(*iterp == NULL); 61 62 iter = isc_mem_get(mctx, sizeof(*iter)); 63 64 iter->mctx = mctx; 65 iter->buf = NULL; 66 iter->bufsize = 0; 67 iter->ifaddrs = NULL; 68 #ifdef __linux 69 /* 70 * Only open "/proc/net/if_inet6" if we have never seen a IPv6 71 * address returned by getifaddrs(). 72 */ 73 if (!seenv6) { 74 iter->proc = fopen("/proc/net/if_inet6", "r"); 75 } else { 76 iter->proc = NULL; 77 } 78 iter->valid = ISC_R_FAILURE; 79 #endif /* ifdef __linux */ 80 81 if (getifaddrs(&iter->ifaddrs) < 0) { 82 strerror_r(errno, strbuf, sizeof(strbuf)); 83 UNEXPECTED_ERROR(__FILE__, __LINE__, 84 "getting interface addresses: getifaddrs: %s", 85 strbuf); 86 result = ISC_R_UNEXPECTED; 87 goto failure; 88 } 89 90 /* 91 * A newly created iterator has an undefined position 92 * until isc_interfaceiter_first() is called. 93 */ 94 iter->pos = NULL; 95 iter->result = ISC_R_FAILURE; 96 97 iter->magic = IFITER_MAGIC; 98 *iterp = iter; 99 return (ISC_R_SUCCESS); 100 101 failure: 102 #ifdef __linux 103 if (iter->proc != NULL) { 104 fclose(iter->proc); 105 } 106 #endif /* ifdef __linux */ 107 if (iter->ifaddrs != NULL) { /* just in case */ 108 freeifaddrs(iter->ifaddrs); 109 } 110 isc_mem_put(mctx, iter, sizeof(*iter)); 111 return (result); 112 } 113 114 /* 115 * Get information about the current interface to iter->current. 116 * If successful, return ISC_R_SUCCESS. 117 * If the interface has an unsupported address family, 118 * return ISC_R_IGNORE. 119 */ 120 121 static isc_result_t 122 internal_current(isc_interfaceiter_t *iter) { 123 struct ifaddrs *ifa; 124 int family; 125 unsigned int namelen; 126 127 REQUIRE(VALID_IFITER(iter)); 128 129 ifa = iter->pos; 130 131 #ifdef __linux 132 if (iter->pos == NULL) { 133 return (linux_if_inet6_current(iter)); 134 } 135 #endif /* ifdef __linux */ 136 137 INSIST(ifa != NULL); 138 INSIST(ifa->ifa_name != NULL); 139 140 if (ifa->ifa_addr == NULL) { 141 return (ISC_R_IGNORE); 142 } 143 144 family = ifa->ifa_addr->sa_family; 145 if (family != AF_INET && family != AF_INET6) { 146 return (ISC_R_IGNORE); 147 } 148 149 #ifdef __linux 150 if (family == AF_INET6) { 151 seenv6 = true; 152 } 153 #endif /* ifdef __linux */ 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 162 memset(iter->current.name, 0, sizeof(iter->current.name)); 163 memmove(iter->current.name, ifa->ifa_name, namelen); 164 165 iter->current.flags = 0; 166 167 if ((ifa->ifa_flags & IFF_UP) != 0) { 168 iter->current.flags |= INTERFACE_F_UP; 169 } 170 171 if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) { 172 iter->current.flags |= INTERFACE_F_POINTTOPOINT; 173 } 174 175 if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) { 176 iter->current.flags |= INTERFACE_F_LOOPBACK; 177 } 178 179 iter->current.af = family; 180 181 get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name); 182 183 if (ifa->ifa_netmask != NULL) { 184 get_addr(family, &iter->current.netmask, ifa->ifa_netmask, 185 ifa->ifa_name); 186 } 187 188 if (ifa->ifa_dstaddr != NULL && 189 (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) 190 { 191 get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr, 192 ifa->ifa_name); 193 } 194 195 return (ISC_R_SUCCESS); 196 } 197 198 /* 199 * Step the iterator to the next interface. Unlike 200 * isc_interfaceiter_next(), this may leave the iterator 201 * positioned on an interface that will ultimately 202 * be ignored. Return ISC_R_NOMORE if there are no more 203 * interfaces, otherwise ISC_R_SUCCESS. 204 */ 205 static isc_result_t 206 internal_next(isc_interfaceiter_t *iter) { 207 if (iter->pos != NULL) { 208 iter->pos = iter->pos->ifa_next; 209 } 210 if (iter->pos == NULL) { 211 #ifdef __linux 212 if (!seenv6) { 213 return (linux_if_inet6_next(iter)); 214 } 215 #endif /* ifdef __linux */ 216 return (ISC_R_NOMORE); 217 } 218 219 return (ISC_R_SUCCESS); 220 } 221 222 static void 223 internal_destroy(isc_interfaceiter_t *iter) { 224 #ifdef __linux 225 if (iter->proc != NULL) { 226 fclose(iter->proc); 227 } 228 iter->proc = NULL; 229 #endif /* ifdef __linux */ 230 if (iter->ifaddrs) { 231 freeifaddrs(iter->ifaddrs); 232 } 233 iter->ifaddrs = NULL; 234 } 235 236 static void 237 internal_first(isc_interfaceiter_t *iter) { 238 #ifdef __linux 239 linux_if_inet6_first(iter); 240 #endif /* ifdef __linux */ 241 iter->pos = iter->ifaddrs; 242 } 243