Home | History | Annotate | Line # | Download | only in isc
      1 /*	$NetBSD: interfaceiter.c,v 1.4 2025/01/26 16:25:37 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 
     18 #include <sys/ioctl.h>
     19 #include <sys/types.h>
     20 #ifdef HAVE_SYS_SOCKIO_H
     21 #include <sys/sockio.h> /* Required for ifiter_ioctl.c. */
     22 #endif			/* ifdef HAVE_SYS_SOCKIO_H */
     23 
     24 #include <errno.h>
     25 #include <ifaddrs.h>
     26 #include <inttypes.h>
     27 #include <stdbool.h>
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <unistd.h>
     31 
     32 #include <isc/interfaceiter.h>
     33 #include <isc/log.h>
     34 #include <isc/magic.h>
     35 #include <isc/mem.h>
     36 #include <isc/net.h>
     37 #include <isc/result.h>
     38 #include <isc/strerr.h>
     39 #include <isc/string.h>
     40 #include <isc/types.h>
     41 #include <isc/util.h>
     42 
     43 /* Must follow <isc/net.h>. */
     44 #ifdef HAVE_NET_IF6_H
     45 #include <net/if6.h>
     46 #endif /* ifdef HAVE_NET_IF6_H */
     47 #include <net/if.h>
     48 
     49 /* Common utility functions */
     50 
     51 /*%
     52  * Extract the network address part from a "struct sockaddr".
     53  * \brief
     54  * The address family is given explicitly
     55  * instead of using src->sa_family, because the latter does not work
     56  * for copying a network mask obtained by SIOCGIFNETMASK (it does
     57  * not have a valid address family).
     58  */
     59 
     60 static void
     61 get_addr(unsigned int family, isc_netaddr_t *dst, struct sockaddr *src,
     62 	 char *ifname) {
     63 	struct sockaddr_in6 *sa6;
     64 
     65 #if !defined(HAVE_IF_NAMETOINDEX)
     66 	UNUSED(ifname);
     67 #endif /* if !defined(HAVE_IF_NAMETOINDEX) */
     68 
     69 	/* clear any remaining value for safety */
     70 	memset(dst, 0, sizeof(*dst));
     71 
     72 	dst->family = family;
     73 	switch (family) {
     74 	case AF_INET:
     75 		memmove(&dst->type.in, &((struct sockaddr_in *)src)->sin_addr,
     76 			sizeof(struct in_addr));
     77 		break;
     78 	case AF_INET6:
     79 		sa6 = (struct sockaddr_in6 *)src;
     80 		memmove(&dst->type.in6, &sa6->sin6_addr,
     81 			sizeof(struct in6_addr));
     82 		if (sa6->sin6_scope_id != 0) {
     83 			isc_netaddr_setzone(dst, sa6->sin6_scope_id);
     84 		} else {
     85 			/*
     86 			 * BSD variants embed scope zone IDs in the 128bit
     87 			 * address as a kernel internal form.  Unfortunately,
     88 			 * the embedded IDs are not hidden from applications
     89 			 * when getting access to them by sysctl or ioctl.
     90 			 * We convert the internal format to the pure address
     91 			 * part and the zone ID part.
     92 			 * Since multicast addresses should not appear here
     93 			 * and they cannot be distinguished from netmasks,
     94 			 * we only consider unicast link-local addresses.
     95 			 */
     96 			if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
     97 				uint16_t zone16;
     98 
     99 				memmove(&zone16, &sa6->sin6_addr.s6_addr[2],
    100 					sizeof(zone16));
    101 				zone16 = ntohs(zone16);
    102 				if (zone16 != 0) {
    103 					/* the zone ID is embedded */
    104 					isc_netaddr_setzone(dst,
    105 							    (uint32_t)zone16);
    106 					dst->type.in6.s6_addr[2] = 0;
    107 					dst->type.in6.s6_addr[3] = 0;
    108 #ifdef HAVE_IF_NAMETOINDEX
    109 				} else if (ifname != NULL) {
    110 					unsigned int zone;
    111 
    112 					/*
    113 					 * sin6_scope_id is still not provided,
    114 					 * but the corresponding interface name
    115 					 * is know.  Use the interface ID as
    116 					 * the link ID.
    117 					 */
    118 					zone = if_nametoindex(ifname);
    119 					if (zone != 0) {
    120 						isc_netaddr_setzone(
    121 							dst, (uint32_t)zone);
    122 					}
    123 #endif /* ifdef HAVE_IF_NAMETOINDEX */
    124 				}
    125 			}
    126 		}
    127 		break;
    128 	default:
    129 		UNREACHABLE();
    130 	}
    131 }
    132 
    133 /*
    134  * Include system-dependent code.
    135  */
    136 
    137 /*% Iterator Magic */
    138 #define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'G')
    139 /*% Valid Iterator */
    140 #define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC)
    141 
    142 /*% Iterator structure */
    143 struct isc_interfaceiter {
    144 	unsigned int magic; /*%< Magic number. */
    145 	isc_mem_t *mctx;
    146 	void *buf;		 /*%< (unused) */
    147 	unsigned int bufsize;	 /*%< (always 0) */
    148 	struct ifaddrs *ifaddrs; /*%< List of ifaddrs */
    149 	struct ifaddrs *pos;	 /*%< Ptr to current ifaddr */
    150 	isc_interface_t current; /*%< Current interface data. */
    151 	isc_result_t result;	 /*%< Last result code. */
    152 };
    153 
    154 isc_result_t
    155 isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) {
    156 	isc_interfaceiter_t *iter;
    157 	isc_result_t result;
    158 	char strbuf[ISC_STRERRORSIZE];
    159 
    160 	REQUIRE(mctx != NULL);
    161 	REQUIRE(iterp != NULL);
    162 	REQUIRE(*iterp == NULL);
    163 
    164 	iter = isc_mem_get(mctx, sizeof(*iter));
    165 
    166 	iter->mctx = mctx;
    167 	iter->buf = NULL;
    168 	iter->bufsize = 0;
    169 	iter->ifaddrs = NULL;
    170 
    171 	if (getifaddrs(&iter->ifaddrs) < 0) {
    172 		strerror_r(errno, strbuf, sizeof(strbuf));
    173 		UNEXPECTED_ERROR("getting interface addresses: getifaddrs: %s",
    174 				 strbuf);
    175 		result = ISC_R_UNEXPECTED;
    176 		goto failure;
    177 	}
    178 
    179 	/*
    180 	 * A newly created iterator has an undefined position
    181 	 * until isc_interfaceiter_first() is called.
    182 	 */
    183 	iter->pos = NULL;
    184 	iter->result = ISC_R_FAILURE;
    185 
    186 	iter->magic = IFITER_MAGIC;
    187 	*iterp = iter;
    188 	return ISC_R_SUCCESS;
    189 
    190 failure:
    191 	if (iter->ifaddrs != NULL) { /* just in case */
    192 		freeifaddrs(iter->ifaddrs);
    193 	}
    194 	isc_mem_put(mctx, iter, sizeof(*iter));
    195 	return result;
    196 }
    197 
    198 /*
    199  * Get information about the current interface to iter->current.
    200  * If successful, return ISC_R_SUCCESS.
    201  * If the interface has an unsupported address family,
    202  * return ISC_R_IGNORE.
    203  */
    204 
    205 static isc_result_t
    206 internal_current(isc_interfaceiter_t *iter) {
    207 	struct ifaddrs *ifa;
    208 	int family;
    209 	unsigned int namelen;
    210 
    211 	REQUIRE(VALID_IFITER(iter));
    212 
    213 	ifa = iter->pos;
    214 
    215 	INSIST(ifa != NULL);
    216 	INSIST(ifa->ifa_name != NULL);
    217 
    218 	if (ifa->ifa_addr == NULL) {
    219 		return ISC_R_IGNORE;
    220 	}
    221 
    222 	family = ifa->ifa_addr->sa_family;
    223 	if (family != AF_INET && family != AF_INET6) {
    224 		return ISC_R_IGNORE;
    225 	}
    226 
    227 	memset(&iter->current, 0, sizeof(iter->current));
    228 
    229 	namelen = strlen(ifa->ifa_name);
    230 	if (namelen > sizeof(iter->current.name) - 1) {
    231 		namelen = sizeof(iter->current.name) - 1;
    232 	}
    233 
    234 	memset(iter->current.name, 0, sizeof(iter->current.name));
    235 	memmove(iter->current.name, ifa->ifa_name, namelen);
    236 
    237 	iter->current.flags = 0;
    238 
    239 	if ((ifa->ifa_flags & IFF_UP) != 0) {
    240 		iter->current.flags |= INTERFACE_F_UP;
    241 	}
    242 
    243 	if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) {
    244 		iter->current.flags |= INTERFACE_F_POINTTOPOINT;
    245 	}
    246 
    247 	if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) {
    248 		iter->current.flags |= INTERFACE_F_LOOPBACK;
    249 	}
    250 
    251 	iter->current.af = family;
    252 
    253 	get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name);
    254 
    255 	if (ifa->ifa_netmask != NULL) {
    256 		get_addr(family, &iter->current.netmask, ifa->ifa_netmask,
    257 			 ifa->ifa_name);
    258 	}
    259 
    260 	if (ifa->ifa_dstaddr != NULL &&
    261 	    (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0)
    262 	{
    263 		get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr,
    264 			 ifa->ifa_name);
    265 	}
    266 
    267 	return ISC_R_SUCCESS;
    268 }
    269 
    270 /*
    271  * Step the iterator to the next interface.  Unlike
    272  * isc_interfaceiter_next(), this may leave the iterator
    273  * positioned on an interface that will ultimately
    274  * be ignored.  Return ISC_R_NOMORE if there are no more
    275  * interfaces, otherwise ISC_R_SUCCESS.
    276  */
    277 static isc_result_t
    278 internal_next(isc_interfaceiter_t *iter) {
    279 	if (iter->pos != NULL) {
    280 		iter->pos = iter->pos->ifa_next;
    281 	}
    282 	if (iter->pos == NULL) {
    283 		return ISC_R_NOMORE;
    284 	}
    285 
    286 	return ISC_R_SUCCESS;
    287 }
    288 
    289 static void
    290 internal_destroy(isc_interfaceiter_t *iter) {
    291 	if (iter->ifaddrs) {
    292 		freeifaddrs(iter->ifaddrs);
    293 	}
    294 	iter->ifaddrs = NULL;
    295 }
    296 
    297 static void
    298 internal_first(isc_interfaceiter_t *iter) {
    299 	iter->pos = iter->ifaddrs;
    300 }
    301 
    302 /*
    303  * The remaining code is common to the sysctl and ioctl case.
    304  */
    305 
    306 isc_result_t
    307 isc_interfaceiter_current(isc_interfaceiter_t *iter, isc_interface_t *ifdata) {
    308 	REQUIRE(iter->result == ISC_R_SUCCESS);
    309 	memmove(ifdata, &iter->current, sizeof(*ifdata));
    310 	return ISC_R_SUCCESS;
    311 }
    312 
    313 isc_result_t
    314 isc_interfaceiter_first(isc_interfaceiter_t *iter) {
    315 	isc_result_t result;
    316 
    317 	REQUIRE(VALID_IFITER(iter));
    318 
    319 	internal_first(iter);
    320 	for (;;) {
    321 		result = internal_current(iter);
    322 		if (result != ISC_R_IGNORE) {
    323 			break;
    324 		}
    325 		result = internal_next(iter);
    326 		if (result != ISC_R_SUCCESS) {
    327 			break;
    328 		}
    329 	}
    330 	iter->result = result;
    331 	return result;
    332 }
    333 
    334 isc_result_t
    335 isc_interfaceiter_next(isc_interfaceiter_t *iter) {
    336 	isc_result_t result;
    337 
    338 	REQUIRE(VALID_IFITER(iter));
    339 	REQUIRE(iter->result == ISC_R_SUCCESS);
    340 
    341 	for (;;) {
    342 		result = internal_next(iter);
    343 		if (result != ISC_R_SUCCESS) {
    344 			break;
    345 		}
    346 		result = internal_current(iter);
    347 		if (result != ISC_R_IGNORE) {
    348 			break;
    349 		}
    350 	}
    351 	iter->result = result;
    352 	return result;
    353 }
    354 
    355 void
    356 isc_interfaceiter_destroy(isc_interfaceiter_t **iterp) {
    357 	isc_interfaceiter_t *iter;
    358 	REQUIRE(iterp != NULL);
    359 	iter = *iterp;
    360 	*iterp = NULL;
    361 	REQUIRE(VALID_IFITER(iter));
    362 
    363 	internal_destroy(iter);
    364 	if (iter->buf != NULL) {
    365 		isc_mem_put(iter->mctx, iter->buf, iter->bufsize);
    366 	}
    367 
    368 	iter->magic = 0;
    369 	isc_mem_put(iter->mctx, iter, sizeof(*iter));
    370 }
    371