xref: /netbsd-src/external/mpl/bind/dist/lib/isc/interfaceiter.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
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