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