1/*	$NetBSD: interfaceiter.c,v 1.2 2024/08/18 20:47:16 christos Exp $	*/
2
3/*
4 * Copyright (C) 2004, 2005, 2007, 2008  Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-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: interfaceiter.c,v 1.45 2008/12/01 03:51:47 marka Exp  */
21
22/*! \file */
23
24#include <config.h>
25
26#include <sys/types.h>
27#include <sys/ioctl.h>
28#ifdef HAVE_SYS_SOCKIO_H
29#include <sys/sockio.h>		/* Required for ifiter_ioctl.c. */
30#endif
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <errno.h>
36
37#include <isc/interfaceiter.h>
38#include <isc/log.h>
39#include <isc/magic.h>
40#include <isc/mem.h>
41#include <isc/msgs.h>
42#include <isc/net.h>
43#include <isc/print.h>
44#include <isc/result.h>
45#include <isc/strerror.h>
46#include <isc/string.h>
47#include <isc/types.h>
48#include <isc/util.h>
49
50/* Must follow <isc/net.h>. */
51#ifdef HAVE_NET_IF6_H
52#include <net/if6.h>
53#endif
54#include <net/if.h>
55
56#ifdef HAVE_LINUX_IF_ADDR_H
57# include <linux/if_addr.h>
58#endif
59
60/* Common utility functions */
61
62/*%
63 * Extract the network address part from a "struct sockaddr".
64 * \brief
65 * The address family is given explicitly
66 * instead of using src->sa_family, because the latter does not work
67 * for copying a network mask obtained by SIOCGIFNETMASK (it does
68 * not have a valid address family).
69 */
70
71static void
72get_addr(unsigned int family, isc_netaddr_t *dst, struct sockaddr *src,
73	 char *ifname)
74{
75	struct sockaddr_in6 *sa6;
76
77#if !defined(ISC_PLATFORM_HAVEIFNAMETOINDEX) || \
78    !defined(ISC_PLATFORM_HAVESCOPEID)
79	UNUSED(ifname);
80#endif
81
82	/* clear any remaining value for safety */
83	memset(dst, 0, sizeof(*dst));
84
85	dst->family = family;
86	switch (family) {
87	case AF_INET:
88		memcpy(&dst->type.in,
89		       &((struct sockaddr_in *)(void *)src)->sin_addr,
90		       sizeof(struct in_addr));
91		break;
92	case AF_INET6:
93		sa6 = (struct sockaddr_in6 *)(void *)src;
94		memcpy(&dst->type.in6, &sa6->sin6_addr,
95		       sizeof(struct in6_addr));
96#ifdef ISC_PLATFORM_HAVESCOPEID
97		if (sa6->sin6_scope_id != 0)
98			isc_netaddr_setzone(dst, sa6->sin6_scope_id);
99		else {
100			/*
101			 * BSD variants embed scope zone IDs in the 128bit
102			 * address as a kernel internal form.  Unfortunately,
103			 * the embedded IDs are not hidden from applications
104			 * when getting access to them by sysctl or ioctl.
105			 * We convert the internal format to the pure address
106			 * part and the zone ID part.
107			 * Since multicast addresses should not appear here
108			 * and they cannot be distinguished from netmasks,
109			 * we only consider unicast link-local addresses.
110			 */
111			if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
112				isc_uint16_t zone16;
113
114				memcpy(&zone16, &sa6->sin6_addr.s6_addr[2],
115				       sizeof(zone16));
116				zone16 = ntohs(zone16);
117				if (zone16 != 0) {
118					/* the zone ID is embedded */
119					isc_netaddr_setzone(dst,
120							    (isc_uint32_t)zone16);
121					dst->type.in6.s6_addr[2] = 0;
122					dst->type.in6.s6_addr[3] = 0;
123#ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX
124				} else if (ifname != NULL) {
125					unsigned int zone;
126
127					/*
128					 * sin6_scope_id is still not provided,
129					 * but the corresponding interface name
130					 * is know.  Use the interface ID as
131					 * the link ID.
132					 */
133					zone = if_nametoindex(ifname);
134					if (zone != 0) {
135						isc_netaddr_setzone(dst,
136								    (isc_uint32_t)zone);
137					}
138#endif
139				}
140			}
141		}
142#endif
143		break;
144	default:
145		INSIST(0);
146		break;
147	}
148}
149
150/*
151 * Include system-dependent code.
152 */
153
154#ifdef __linux
155#define ISC_IF_INET6_SZ \
156    sizeof("00000000000000000000000000000001 01 80 10 80 XXXXXXloXXXXXXXX\n")
157static isc_result_t linux_if_inet6_next(isc_interfaceiter_t *);
158static isc_result_t linux_if_inet6_current(isc_interfaceiter_t *);
159static void linux_if_inet6_first(isc_interfaceiter_t *iter);
160#endif
161
162#if HAVE_GETIFADDRS
163#include "ifiter_getifaddrs.c"
164#elif HAVE_IFLIST_SYSCTL
165#include "ifiter_sysctl.c"
166#else
167#include "ifiter_ioctl.c"
168#endif
169
170#ifdef __linux
171static void
172linux_if_inet6_first(isc_interfaceiter_t *iter) {
173	if (iter->proc != NULL) {
174		rewind(iter->proc);
175		(void)linux_if_inet6_next(iter);
176	} else
177		iter->valid = ISC_R_NOMORE;
178}
179
180static isc_result_t
181linux_if_inet6_next(isc_interfaceiter_t *iter) {
182	if (iter->proc != NULL &&
183	    fgets(iter->entry, sizeof(iter->entry), iter->proc) != NULL)
184		iter->valid = ISC_R_SUCCESS;
185	else
186		iter->valid = ISC_R_NOMORE;
187	return (iter->valid);
188}
189
190static isc_result_t
191linux_if_inet6_current(isc_interfaceiter_t *iter) {
192	char address[33];
193	char name[IF_NAMESIZE+1];
194	struct in6_addr addr6;
195	unsigned int ifindex;
196	int prefix, scope, flags;
197	int res;
198	unsigned int i;
199
200	if (iter->valid != ISC_R_SUCCESS)
201		return (iter->valid);
202	if (iter->proc == NULL) {
203		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
204			      ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
205			      "/proc/net/if_inet6:iter->proc == NULL");
206		return (ISC_R_FAILURE);
207	}
208
209	res = sscanf(iter->entry, "%32[a-f0-9] %x %x %x %x %16s\n",
210		     address, &ifindex, &prefix, &scope, &flags, name);
211	if (res != 6) {
212		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
213			      ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
214			      "/proc/net/if_inet6:sscanf() -> %d (expected 6)",
215			      res);
216		return (ISC_R_FAILURE);
217	}
218	if (strlen(address) != 32) {
219		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
220			      ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
221			      "/proc/net/if_inet6:strlen(%s) != 32", address);
222		return (ISC_R_FAILURE);
223	}
224	/*
225	** Ignore DAD addresses --
226	** we can't bind to them until they are resolved
227	*/
228#ifdef IFA_F_TENTATIVE
229	if (flags & IFA_F_TENTATIVE)
230		return (ISC_R_IGNORE);
231#endif
232
233	for (i = 0; i < 16; i++) {
234		unsigned char byte;
235		static const char hex[] = "0123456789abcdef";
236		byte = ((strchr(hex, address[i * 2]) - hex) << 4) |
237		       (strchr(hex, address[i * 2 + 1]) - hex);
238		addr6.s6_addr[i] = byte;
239	}
240	iter->current.af = AF_INET6;
241	iter->current.flags = INTERFACE_F_UP;
242	isc_netaddr_fromin6(&iter->current.address, &addr6);
243	iter->current.ifindex = ifindex;
244	if (isc_netaddr_islinklocal(&iter->current.address)) {
245		isc_netaddr_setzone(&iter->current.address,
246				    (isc_uint32_t)ifindex);
247	}
248	for (i = 0; i < 16; i++) {
249		if (prefix > 8) {
250			addr6.s6_addr[i] = 0xff;
251			prefix -= 8;
252		} else {
253			addr6.s6_addr[i] = (0xff << (8 - prefix)) & 0xff;
254			prefix = 0;
255		}
256	}
257	isc_netaddr_fromin6(&iter->current.netmask, &addr6);
258	strncpy(iter->current.name, name, sizeof(iter->current.name));
259	return (ISC_R_SUCCESS);
260}
261#endif
262
263/*
264 * The remaining code is common to the sysctl and ioctl case.
265 */
266
267isc_result_t
268isc_interfaceiter_current(isc_interfaceiter_t *iter,
269			  isc_interface_t *ifdata)
270{
271	REQUIRE(iter->result == ISC_R_SUCCESS);
272	memcpy(ifdata, &iter->current, sizeof(*ifdata));
273	return (ISC_R_SUCCESS);
274}
275
276isc_result_t
277isc_interfaceiter_first(isc_interfaceiter_t *iter) {
278	isc_result_t result;
279
280	REQUIRE(VALID_IFITER(iter));
281
282	internal_first(iter);
283	for (;;) {
284		result = internal_current(iter);
285		if (result != ISC_R_IGNORE)
286			break;
287		result = internal_next(iter);
288		if (result != ISC_R_SUCCESS)
289			break;
290	}
291	iter->result = result;
292	return (result);
293}
294
295isc_result_t
296isc_interfaceiter_next(isc_interfaceiter_t *iter) {
297	isc_result_t result;
298
299	REQUIRE(VALID_IFITER(iter));
300	REQUIRE(iter->result == ISC_R_SUCCESS);
301
302	for (;;) {
303		result = internal_next(iter);
304		if (result != ISC_R_SUCCESS)
305			break;
306		result = internal_current(iter);
307		if (result != ISC_R_IGNORE)
308			break;
309	}
310	iter->result = result;
311	return (result);
312}
313
314void
315isc_interfaceiter_destroy(isc_interfaceiter_t **iterp)
316{
317	isc_interfaceiter_t *iter;
318	REQUIRE(iterp != NULL);
319	iter = *iterp;
320	REQUIRE(VALID_IFITER(iter));
321
322	internal_destroy(iter);
323	if (iter->buf != NULL)
324		isc_mem_put(iter->mctx, iter->buf, iter->bufsize);
325
326	iter->magic = 0;
327	isc_mem_put(iter->mctx, iter, sizeof(*iter));
328	*iterp = NULL;
329}
330