xref: /illumos-gate/usr/src/lib/libsocket/inet/interface_id.c (revision aa1e129cc24759a40c3f60101674aeadbe9287a4)
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
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 *
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 *
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
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