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