xref: /netbsd-src/external/bsd/ntp/dist/libntp/lib/isc/unix/interfaceiter.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: interfaceiter.c,v 1.2 2024/08/18 20:47:16 christos Exp $	*/
2897be3a4Schristos 
3897be3a4Schristos /*
4897be3a4Schristos  * Copyright (C) 2004, 2005, 2007, 2008  Internet Systems Consortium, Inc. ("ISC")
5897be3a4Schristos  * Copyright (C) 1999-2003  Internet Software Consortium.
6897be3a4Schristos  *
7897be3a4Schristos  * Permission to use, copy, modify, and/or distribute this software for any
8897be3a4Schristos  * purpose with or without fee is hereby granted, provided that the above
9897be3a4Schristos  * copyright notice and this permission notice appear in all copies.
10897be3a4Schristos  *
11897be3a4Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12897be3a4Schristos  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13897be3a4Schristos  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14897be3a4Schristos  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15897be3a4Schristos  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16897be3a4Schristos  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17897be3a4Schristos  * PERFORMANCE OF THIS SOFTWARE.
18897be3a4Schristos  */
19897be3a4Schristos 
20897be3a4Schristos /* Id: interfaceiter.c,v 1.45 2008/12/01 03:51:47 marka Exp  */
21897be3a4Schristos 
22897be3a4Schristos /*! \file */
23897be3a4Schristos 
24897be3a4Schristos #include <config.h>
25897be3a4Schristos 
26897be3a4Schristos #include <sys/types.h>
27897be3a4Schristos #include <sys/ioctl.h>
28897be3a4Schristos #ifdef HAVE_SYS_SOCKIO_H
29897be3a4Schristos #include <sys/sockio.h>		/* Required for ifiter_ioctl.c. */
30897be3a4Schristos #endif
31897be3a4Schristos 
32897be3a4Schristos #include <stdio.h>
33897be3a4Schristos #include <stdlib.h>
34897be3a4Schristos #include <unistd.h>
35897be3a4Schristos #include <errno.h>
36897be3a4Schristos 
37897be3a4Schristos #include <isc/interfaceiter.h>
38897be3a4Schristos #include <isc/log.h>
39897be3a4Schristos #include <isc/magic.h>
40897be3a4Schristos #include <isc/mem.h>
41897be3a4Schristos #include <isc/msgs.h>
42897be3a4Schristos #include <isc/net.h>
43897be3a4Schristos #include <isc/print.h>
44897be3a4Schristos #include <isc/result.h>
45897be3a4Schristos #include <isc/strerror.h>
46897be3a4Schristos #include <isc/string.h>
47897be3a4Schristos #include <isc/types.h>
48897be3a4Schristos #include <isc/util.h>
49897be3a4Schristos 
50897be3a4Schristos /* Must follow <isc/net.h>. */
51897be3a4Schristos #ifdef HAVE_NET_IF6_H
52897be3a4Schristos #include <net/if6.h>
53897be3a4Schristos #endif
54897be3a4Schristos #include <net/if.h>
55897be3a4Schristos 
56897be3a4Schristos #ifdef HAVE_LINUX_IF_ADDR_H
57897be3a4Schristos # include <linux/if_addr.h>
58897be3a4Schristos #endif
59897be3a4Schristos 
60897be3a4Schristos /* Common utility functions */
61897be3a4Schristos 
62897be3a4Schristos /*%
63897be3a4Schristos  * Extract the network address part from a "struct sockaddr".
64897be3a4Schristos  * \brief
65897be3a4Schristos  * The address family is given explicitly
66897be3a4Schristos  * instead of using src->sa_family, because the latter does not work
67897be3a4Schristos  * for copying a network mask obtained by SIOCGIFNETMASK (it does
68897be3a4Schristos  * not have a valid address family).
69897be3a4Schristos  */
70897be3a4Schristos 
71897be3a4Schristos static void
72897be3a4Schristos get_addr(unsigned int family, isc_netaddr_t *dst, struct sockaddr *src,
73897be3a4Schristos 	 char *ifname)
74897be3a4Schristos {
75897be3a4Schristos 	struct sockaddr_in6 *sa6;
76897be3a4Schristos 
77897be3a4Schristos #if !defined(ISC_PLATFORM_HAVEIFNAMETOINDEX) || \
78897be3a4Schristos     !defined(ISC_PLATFORM_HAVESCOPEID)
79897be3a4Schristos 	UNUSED(ifname);
80897be3a4Schristos #endif
81897be3a4Schristos 
82897be3a4Schristos 	/* clear any remaining value for safety */
83897be3a4Schristos 	memset(dst, 0, sizeof(*dst));
84897be3a4Schristos 
85897be3a4Schristos 	dst->family = family;
86897be3a4Schristos 	switch (family) {
87897be3a4Schristos 	case AF_INET:
88897be3a4Schristos 		memcpy(&dst->type.in,
89897be3a4Schristos 		       &((struct sockaddr_in *)(void *)src)->sin_addr,
90897be3a4Schristos 		       sizeof(struct in_addr));
91897be3a4Schristos 		break;
92897be3a4Schristos 	case AF_INET6:
93897be3a4Schristos 		sa6 = (struct sockaddr_in6 *)(void *)src;
94897be3a4Schristos 		memcpy(&dst->type.in6, &sa6->sin6_addr,
95897be3a4Schristos 		       sizeof(struct in6_addr));
96897be3a4Schristos #ifdef ISC_PLATFORM_HAVESCOPEID
97897be3a4Schristos 		if (sa6->sin6_scope_id != 0)
98897be3a4Schristos 			isc_netaddr_setzone(dst, sa6->sin6_scope_id);
99897be3a4Schristos 		else {
100897be3a4Schristos 			/*
101897be3a4Schristos 			 * BSD variants embed scope zone IDs in the 128bit
102897be3a4Schristos 			 * address as a kernel internal form.  Unfortunately,
103897be3a4Schristos 			 * the embedded IDs are not hidden from applications
104897be3a4Schristos 			 * when getting access to them by sysctl or ioctl.
105897be3a4Schristos 			 * We convert the internal format to the pure address
106897be3a4Schristos 			 * part and the zone ID part.
107897be3a4Schristos 			 * Since multicast addresses should not appear here
108897be3a4Schristos 			 * and they cannot be distinguished from netmasks,
109897be3a4Schristos 			 * we only consider unicast link-local addresses.
110897be3a4Schristos 			 */
111897be3a4Schristos 			if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
112897be3a4Schristos 				isc_uint16_t zone16;
113897be3a4Schristos 
114897be3a4Schristos 				memcpy(&zone16, &sa6->sin6_addr.s6_addr[2],
115897be3a4Schristos 				       sizeof(zone16));
116897be3a4Schristos 				zone16 = ntohs(zone16);
117897be3a4Schristos 				if (zone16 != 0) {
118897be3a4Schristos 					/* the zone ID is embedded */
119897be3a4Schristos 					isc_netaddr_setzone(dst,
120897be3a4Schristos 							    (isc_uint32_t)zone16);
121897be3a4Schristos 					dst->type.in6.s6_addr[2] = 0;
122897be3a4Schristos 					dst->type.in6.s6_addr[3] = 0;
123897be3a4Schristos #ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX
124897be3a4Schristos 				} else if (ifname != NULL) {
125897be3a4Schristos 					unsigned int zone;
126897be3a4Schristos 
127897be3a4Schristos 					/*
128897be3a4Schristos 					 * sin6_scope_id is still not provided,
129897be3a4Schristos 					 * but the corresponding interface name
130897be3a4Schristos 					 * is know.  Use the interface ID as
131897be3a4Schristos 					 * the link ID.
132897be3a4Schristos 					 */
133897be3a4Schristos 					zone = if_nametoindex(ifname);
134897be3a4Schristos 					if (zone != 0) {
135897be3a4Schristos 						isc_netaddr_setzone(dst,
136897be3a4Schristos 								    (isc_uint32_t)zone);
137897be3a4Schristos 					}
138897be3a4Schristos #endif
139897be3a4Schristos 				}
140897be3a4Schristos 			}
141897be3a4Schristos 		}
142897be3a4Schristos #endif
143897be3a4Schristos 		break;
144897be3a4Schristos 	default:
145897be3a4Schristos 		INSIST(0);
146897be3a4Schristos 		break;
147897be3a4Schristos 	}
148897be3a4Schristos }
149897be3a4Schristos 
150897be3a4Schristos /*
151897be3a4Schristos  * Include system-dependent code.
152897be3a4Schristos  */
153897be3a4Schristos 
154897be3a4Schristos #ifdef __linux
155897be3a4Schristos #define ISC_IF_INET6_SZ \
156897be3a4Schristos     sizeof("00000000000000000000000000000001 01 80 10 80 XXXXXXloXXXXXXXX\n")
157897be3a4Schristos static isc_result_t linux_if_inet6_next(isc_interfaceiter_t *);
158897be3a4Schristos static isc_result_t linux_if_inet6_current(isc_interfaceiter_t *);
159897be3a4Schristos static void linux_if_inet6_first(isc_interfaceiter_t *iter);
160897be3a4Schristos #endif
161897be3a4Schristos 
162897be3a4Schristos #if HAVE_GETIFADDRS
163897be3a4Schristos #include "ifiter_getifaddrs.c"
164897be3a4Schristos #elif HAVE_IFLIST_SYSCTL
165897be3a4Schristos #include "ifiter_sysctl.c"
166897be3a4Schristos #else
167897be3a4Schristos #include "ifiter_ioctl.c"
168897be3a4Schristos #endif
169897be3a4Schristos 
170897be3a4Schristos #ifdef __linux
171897be3a4Schristos static void
172897be3a4Schristos linux_if_inet6_first(isc_interfaceiter_t *iter) {
173897be3a4Schristos 	if (iter->proc != NULL) {
174897be3a4Schristos 		rewind(iter->proc);
175897be3a4Schristos 		(void)linux_if_inet6_next(iter);
176897be3a4Schristos 	} else
177897be3a4Schristos 		iter->valid = ISC_R_NOMORE;
178897be3a4Schristos }
179897be3a4Schristos 
180897be3a4Schristos static isc_result_t
181897be3a4Schristos linux_if_inet6_next(isc_interfaceiter_t *iter) {
182897be3a4Schristos 	if (iter->proc != NULL &&
183897be3a4Schristos 	    fgets(iter->entry, sizeof(iter->entry), iter->proc) != NULL)
184897be3a4Schristos 		iter->valid = ISC_R_SUCCESS;
185897be3a4Schristos 	else
186897be3a4Schristos 		iter->valid = ISC_R_NOMORE;
187897be3a4Schristos 	return (iter->valid);
188897be3a4Schristos }
189897be3a4Schristos 
190897be3a4Schristos static isc_result_t
191897be3a4Schristos linux_if_inet6_current(isc_interfaceiter_t *iter) {
192897be3a4Schristos 	char address[33];
193897be3a4Schristos 	char name[IF_NAMESIZE+1];
194897be3a4Schristos 	struct in6_addr addr6;
195897be3a4Schristos 	unsigned int ifindex;
196897be3a4Schristos 	int prefix, scope, flags;
197897be3a4Schristos 	int res;
198897be3a4Schristos 	unsigned int i;
199897be3a4Schristos 
200897be3a4Schristos 	if (iter->valid != ISC_R_SUCCESS)
201897be3a4Schristos 		return (iter->valid);
202897be3a4Schristos 	if (iter->proc == NULL) {
203897be3a4Schristos 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
204897be3a4Schristos 			      ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
205897be3a4Schristos 			      "/proc/net/if_inet6:iter->proc == NULL");
206897be3a4Schristos 		return (ISC_R_FAILURE);
207897be3a4Schristos 	}
208897be3a4Schristos 
209897be3a4Schristos 	res = sscanf(iter->entry, "%32[a-f0-9] %x %x %x %x %16s\n",
210897be3a4Schristos 		     address, &ifindex, &prefix, &scope, &flags, name);
211897be3a4Schristos 	if (res != 6) {
212897be3a4Schristos 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
213897be3a4Schristos 			      ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
214897be3a4Schristos 			      "/proc/net/if_inet6:sscanf() -> %d (expected 6)",
215897be3a4Schristos 			      res);
216897be3a4Schristos 		return (ISC_R_FAILURE);
217897be3a4Schristos 	}
218897be3a4Schristos 	if (strlen(address) != 32) {
219897be3a4Schristos 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
220897be3a4Schristos 			      ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
221897be3a4Schristos 			      "/proc/net/if_inet6:strlen(%s) != 32", address);
222897be3a4Schristos 		return (ISC_R_FAILURE);
223897be3a4Schristos 	}
224897be3a4Schristos 	/*
225897be3a4Schristos 	** Ignore DAD addresses --
226897be3a4Schristos 	** we can't bind to them until they are resolved
227897be3a4Schristos 	*/
228897be3a4Schristos #ifdef IFA_F_TENTATIVE
229897be3a4Schristos 	if (flags & IFA_F_TENTATIVE)
230897be3a4Schristos 		return (ISC_R_IGNORE);
231897be3a4Schristos #endif
232897be3a4Schristos 
233897be3a4Schristos 	for (i = 0; i < 16; i++) {
234897be3a4Schristos 		unsigned char byte;
235897be3a4Schristos 		static const char hex[] = "0123456789abcdef";
236897be3a4Schristos 		byte = ((strchr(hex, address[i * 2]) - hex) << 4) |
237897be3a4Schristos 		       (strchr(hex, address[i * 2 + 1]) - hex);
238897be3a4Schristos 		addr6.s6_addr[i] = byte;
239897be3a4Schristos 	}
240897be3a4Schristos 	iter->current.af = AF_INET6;
241897be3a4Schristos 	iter->current.flags = INTERFACE_F_UP;
242897be3a4Schristos 	isc_netaddr_fromin6(&iter->current.address, &addr6);
243897be3a4Schristos 	iter->current.ifindex = ifindex;
244897be3a4Schristos 	if (isc_netaddr_islinklocal(&iter->current.address)) {
245897be3a4Schristos 		isc_netaddr_setzone(&iter->current.address,
246897be3a4Schristos 				    (isc_uint32_t)ifindex);
247897be3a4Schristos 	}
248897be3a4Schristos 	for (i = 0; i < 16; i++) {
249897be3a4Schristos 		if (prefix > 8) {
250897be3a4Schristos 			addr6.s6_addr[i] = 0xff;
251897be3a4Schristos 			prefix -= 8;
252897be3a4Schristos 		} else {
253897be3a4Schristos 			addr6.s6_addr[i] = (0xff << (8 - prefix)) & 0xff;
254897be3a4Schristos 			prefix = 0;
255897be3a4Schristos 		}
256897be3a4Schristos 	}
257897be3a4Schristos 	isc_netaddr_fromin6(&iter->current.netmask, &addr6);
258897be3a4Schristos 	strncpy(iter->current.name, name, sizeof(iter->current.name));
259897be3a4Schristos 	return (ISC_R_SUCCESS);
260897be3a4Schristos }
261897be3a4Schristos #endif
262897be3a4Schristos 
263897be3a4Schristos /*
264897be3a4Schristos  * The remaining code is common to the sysctl and ioctl case.
265897be3a4Schristos  */
266897be3a4Schristos 
267897be3a4Schristos isc_result_t
268897be3a4Schristos isc_interfaceiter_current(isc_interfaceiter_t *iter,
269897be3a4Schristos 			  isc_interface_t *ifdata)
270897be3a4Schristos {
271897be3a4Schristos 	REQUIRE(iter->result == ISC_R_SUCCESS);
272897be3a4Schristos 	memcpy(ifdata, &iter->current, sizeof(*ifdata));
273897be3a4Schristos 	return (ISC_R_SUCCESS);
274897be3a4Schristos }
275897be3a4Schristos 
276897be3a4Schristos isc_result_t
277897be3a4Schristos isc_interfaceiter_first(isc_interfaceiter_t *iter) {
278897be3a4Schristos 	isc_result_t result;
279897be3a4Schristos 
280897be3a4Schristos 	REQUIRE(VALID_IFITER(iter));
281897be3a4Schristos 
282897be3a4Schristos 	internal_first(iter);
283897be3a4Schristos 	for (;;) {
284897be3a4Schristos 		result = internal_current(iter);
285897be3a4Schristos 		if (result != ISC_R_IGNORE)
286897be3a4Schristos 			break;
287897be3a4Schristos 		result = internal_next(iter);
288897be3a4Schristos 		if (result != ISC_R_SUCCESS)
289897be3a4Schristos 			break;
290897be3a4Schristos 	}
291897be3a4Schristos 	iter->result = result;
292897be3a4Schristos 	return (result);
293897be3a4Schristos }
294897be3a4Schristos 
295897be3a4Schristos isc_result_t
296897be3a4Schristos isc_interfaceiter_next(isc_interfaceiter_t *iter) {
297897be3a4Schristos 	isc_result_t result;
298897be3a4Schristos 
299897be3a4Schristos 	REQUIRE(VALID_IFITER(iter));
300897be3a4Schristos 	REQUIRE(iter->result == ISC_R_SUCCESS);
301897be3a4Schristos 
302897be3a4Schristos 	for (;;) {
303897be3a4Schristos 		result = internal_next(iter);
304897be3a4Schristos 		if (result != ISC_R_SUCCESS)
305897be3a4Schristos 			break;
306897be3a4Schristos 		result = internal_current(iter);
307897be3a4Schristos 		if (result != ISC_R_IGNORE)
308897be3a4Schristos 			break;
309897be3a4Schristos 	}
310897be3a4Schristos 	iter->result = result;
311897be3a4Schristos 	return (result);
312897be3a4Schristos }
313897be3a4Schristos 
314897be3a4Schristos void
315897be3a4Schristos isc_interfaceiter_destroy(isc_interfaceiter_t **iterp)
316897be3a4Schristos {
317897be3a4Schristos 	isc_interfaceiter_t *iter;
318897be3a4Schristos 	REQUIRE(iterp != NULL);
319897be3a4Schristos 	iter = *iterp;
320897be3a4Schristos 	REQUIRE(VALID_IFITER(iter));
321897be3a4Schristos 
322897be3a4Schristos 	internal_destroy(iter);
323897be3a4Schristos 	if (iter->buf != NULL)
324897be3a4Schristos 		isc_mem_put(iter->mctx, iter->buf, iter->bufsize);
325897be3a4Schristos 
326897be3a4Schristos 	iter->magic = 0;
327897be3a4Schristos 	isc_mem_put(iter->mctx, iter, sizeof(*iter));
328897be3a4Schristos 	*iterp = NULL;
329897be3a4Schristos }
330