xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/isc/unix/ifiter_getifaddrs.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
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
isc_interfaceiter_create(isc_mem_t * mctx,isc_interfaceiter_t ** iterp)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
internal_current(isc_interfaceiter_t * iter)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
internal_next(isc_interfaceiter_t * iter)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
internal_destroy(isc_interfaceiter_t * iter)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
internal_first(isc_interfaceiter_t * iter)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