1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2026 Oxide Computer Company
25 */
26
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <inet/common.h>
35 #include <net/if.h>
36 #include <netinet/in.h>
37 #include <sys/sockio.h>
38 #include <sys/ioctl.h>
39 #include <unistd.h>
40 #include <errno.h>
41
42 #define IPIF_SEPARATOR_CHAR ":"
43
44 /*
45 * Given an interface name, this function retrives the associated
46 * index value. Returns index value if successful, zero otherwise.
47 * The length of the supplied interface name must be at most
48 * IF_NAMESIZE-1 bytes
49 */
50 uint32_t
if_nametoindex(const char * ifname)51 if_nametoindex(const char *ifname)
52 {
53 int s;
54 struct lifreq lifr;
55 int save_err;
56 size_t size;
57
58
59 /* Make sure the given name is not NULL */
60 if (ifname == NULL || *ifname == '\0') {
61 errno = ENXIO;
62 return (0);
63 }
64
65 /*
66 * Fill up the interface name in the ioctl
67 * request message. Make sure that the length of
68 * the given interface name <= (IF_NAMESIZE-1)
69 */
70 size = strlen(ifname);
71 if (size > (IF_NAMESIZE - 1)) {
72 errno = EINVAL;
73 return (0);
74 }
75
76 (void) memcpy(lifr.lifr_name, ifname, size + 1);
77
78 /* Check the v4 interfaces first */
79 s = socket(AF_INET, SOCK_DGRAM, 0);
80 if (s >= 0) {
81 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) {
82 (void) close(s);
83 return (lifr.lifr_index);
84 }
85 (void) close(s);
86 }
87
88 /* Check the v6 interface list */
89 s = socket(AF_INET6, SOCK_DGRAM, 0);
90 if (s < 0)
91 return (0);
92
93 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) < 0)
94 lifr.lifr_index = 0;
95
96 save_err = errno;
97 (void) close(s);
98 errno = save_err;
99 return (lifr.lifr_index);
100 }
101
102 /*
103 * Given an index, this function returns the associated interface
104 * name in the supplied buffer ifname.
105 * Returns physical interface name if successful, NULL otherwise.
106 * The interface name returned will be at most IF_NAMESIZE-1 bytes.
107 */
108 char *
if_indextoname(uint32_t ifindex,char * ifname)109 if_indextoname(uint32_t ifindex, char *ifname)
110 {
111 int n;
112 int s;
113 char *buf;
114 uint32_t index;
115 struct lifnum lifn;
116 struct lifconf lifc;
117 struct lifreq *lifrp;
118 int numifs;
119 size_t bufsize;
120 boolean_t found;
121 uint_t flags;
122
123 flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES | LIFC_UNDER_IPMP;
124
125 /* A interface index of 0 is invalid */
126 if (ifindex == 0) {
127 errno = ENXIO;
128 return (NULL);
129 }
130
131 s = socket(AF_INET6, SOCK_DGRAM, 0);
132 if (s < 0) {
133 s = socket(AF_INET, SOCK_DGRAM, 0);
134 if (s < 0) {
135 return (NULL);
136 }
137 }
138
139 /* Prepare to send a SIOCGLIFNUM request message */
140 lifn.lifn_family = AF_UNSPEC;
141 lifn.lifn_flags = flags;
142 if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) {
143 int save_err = errno;
144 (void) close(s);
145 errno = save_err;
146 return (NULL);
147 }
148
149 /*
150 * NOTE: "+ 10" sleaze mitigates new IP interfaces showing up between
151 * the SIOCGLIFNUM and the SIOCGLIFCONF.
152 */
153 numifs = lifn.lifn_count + 10;
154
155 /*
156 * Provide enough buffer to obtain the interface
157 * list from the kernel as response to a SIOCGLIFCONF
158 * request
159 */
160
161 bufsize = numifs * sizeof (struct lifreq);
162 buf = malloc(bufsize);
163 if (buf == NULL) {
164 int save_err = errno;
165 (void) close(s);
166 errno = save_err;
167 return (NULL);
168 }
169 lifc.lifc_family = AF_UNSPEC;
170 lifc.lifc_flags = flags;
171 lifc.lifc_len = bufsize;
172 lifc.lifc_buf = buf;
173 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
174 int save_err = errno;
175 (void) close(s);
176 errno = save_err;
177 free(buf);
178 return (NULL);
179 }
180
181 lifrp = lifc.lifc_req;
182 found = B_FALSE;
183 for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) {
184 /*
185 * Obtain the index value of each interface, and
186 * match to see if the retrived index value matches
187 * the given one. If so we return the corresponding
188 * device name of that interface.
189 */
190 size_t size;
191
192 index = if_nametoindex(lifrp->lifr_name);
193 if (index == 0)
194 /* Oops the interface just disappeared */
195 continue;
196 if (index == ifindex) {
197 size = strcspn(lifrp->lifr_name,
198 (char *)IPIF_SEPARATOR_CHAR);
199 lifrp->lifr_name[size] = '\0';
200 found = B_TRUE;
201 (void) strncpy(ifname, lifrp->lifr_name, size + 1);
202 break;
203 }
204 }
205 (void) close(s);
206 free(buf);
207 if (!found) {
208 errno = ENXIO;
209 return (NULL);
210 }
211 return (ifname);
212 }
213
214 /*
215 * This function returns all the interface names and indexes
216 */
217 struct if_nameindex *
if_nameindex(void)218 if_nameindex(void)
219 {
220 int n;
221 int s;
222 boolean_t found;
223 char *buf;
224 struct lifnum lifn;
225 struct lifconf lifc;
226 struct lifreq *lifrp;
227 int numifs;
228 int i;
229 int physinterf_num;
230 size_t bufsize;
231 struct if_nameindex *interface_list;
232 struct if_nameindex *interface_entry;
233
234 s = socket(AF_INET6, SOCK_DGRAM, 0);
235 if (s < 0) {
236 s = socket(AF_INET, SOCK_DGRAM, 0);
237 if (s < 0)
238 return (NULL);
239 }
240
241 lifn.lifn_family = AF_UNSPEC;
242 lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
243 if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0)
244 return (NULL);
245 numifs = lifn.lifn_count;
246
247 bufsize = numifs * sizeof (struct lifreq);
248 buf = malloc(bufsize);
249 if (buf == NULL) {
250 int save_err = errno;
251 (void) close(s);
252 errno = save_err;
253 return (NULL);
254 }
255 lifc.lifc_family = AF_UNSPEC;
256 lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
257 lifc.lifc_len = bufsize;
258 lifc.lifc_buf = buf;
259 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
260 int save_err = errno;
261 (void) close(s);
262 errno = save_err;
263 free(buf);
264 return (NULL);
265 }
266
267 lifrp = lifc.lifc_req;
268 (void) close(s);
269
270 /* Allocate the array of if_nameindex structure */
271 interface_list = malloc((numifs + 1) * sizeof (struct if_nameindex));
272 if (!interface_list) {
273 int save_err = errno;
274 free(buf);
275 errno = save_err;
276 return (NULL);
277 }
278 /*
279 * Make sure that terminator structure automatically
280 * happens to be all zeroes.
281 */
282 bzero(interface_list, ((numifs + 1) * sizeof (struct if_nameindex)));
283 interface_entry = interface_list;
284 physinterf_num = 0;
285 for (n = numifs; n > 0; n--, lifrp++) {
286 size_t size;
287
288 size = strcspn(lifrp->lifr_name, (char *)IPIF_SEPARATOR_CHAR);
289 found = B_FALSE;
290 /*
291 * Search the current array to see if this interface
292 * already exists. Only compare the physical name.
293 */
294 for (i = 0; i < physinterf_num; i++) {
295 if (strncmp(interface_entry[i].if_name,
296 lifrp->lifr_name, size) == 0) {
297 found = B_TRUE;
298 break;
299 }
300 }
301
302 /* New one. Allocate an array element and fill it */
303 if (!found) {
304 /*
305 * Obtain the index value for the interface
306 */
307 interface_entry[physinterf_num].if_index =
308 if_nametoindex(lifrp->lifr_name);
309
310 if (interface_entry[physinterf_num].if_index == 0) {
311 /* The interface went away. Skip this entry. */
312 continue;
313 }
314
315 /*
316 * Truncate the name to ensure that it represents
317 * a physical interface.
318 */
319 lifrp->lifr_name[size] = '\0';
320 if ((interface_entry[physinterf_num].if_name =
321 strdup(lifrp->lifr_name)) == NULL) {
322 int save_err;
323
324 if_freenameindex(interface_list);
325 save_err = errno;
326 free(buf);
327 errno = save_err;
328 return (NULL);
329 }
330
331 physinterf_num++;
332 }
333 }
334
335 /* Create the last one of the array */
336 interface_entry[physinterf_num].if_name = NULL;
337 interface_entry[physinterf_num].if_index = 0;
338
339 /* Free up the excess array space */
340 free(buf);
341 interface_list = realloc(interface_list, ((physinterf_num + 1) *
342 sizeof (struct if_nameindex)));
343
344 return (interface_list);
345 }
346
347 /*
348 * This function frees the the array that is created while
349 * the if_nameindex function.
350 */
351 void
if_freenameindex(struct if_nameindex * ptr)352 if_freenameindex(struct if_nameindex *ptr)
353 {
354 struct if_nameindex *p;
355
356 if (ptr == NULL)
357 return;
358
359 /* First free the if_name member in each array element */
360 for (p = ptr; p->if_name != NULL; p++)
361 free(p->if_name);
362
363 /* Now free up the array space */
364 free(ptr);
365 }
366