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 */
25
26#include <stdio.h>
27#include <ctype.h>
28#include <string.h>
29#include <strings.h>
30#include <stdlib.h>
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <inet/common.h>
34#include <net/if.h>
35#include <netinet/in.h>
36#include <sys/sockio.h>
37#include <sys/ioctl.h>
38#include <unistd.h>
39#include <errno.h>
40
41#define	IPIF_SEPARATOR_CHAR	":"
42
43/*
44 * Given an interface name, this function retrives the associated
45 * index value. Returns index value if successful, zero otherwise.
46 * The length of the supplied interface name must be at most
47 * IF_NAMESIZE-1 bytes
48 */
49uint32_t
50if_nametoindex(const char *ifname)
51{
52	int		s;
53	struct lifreq	lifr;
54	int		save_err;
55	size_t		size;
56
57
58	/* Make sure the given name is not NULL */
59	if ((ifname == NULL)||(*ifname == '\0')) {
60		errno = ENXIO;
61		return (0);
62	}
63
64	/*
65	 * Fill up the interface name in the ioctl
66	 * request message. Make sure that the length of
67	 * the given interface name <= (IF_NAMESIZE-1)
68	 */
69	size = strlen(ifname);
70	if (size > (IF_NAMESIZE - 1)) {
71		errno = EINVAL;
72		return (0);
73	}
74
75	strncpy(lifr.lifr_name, ifname, size +1);
76
77	/* Check the v4 interfaces first */
78	s = socket(AF_INET, SOCK_DGRAM, 0);
79	if (s >= 0) {
80		if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) {
81			(void) close(s);
82			return (lifr.lifr_index);
83		}
84		(void) close(s);
85	}
86
87	/* Check the v6 interface list */
88	s = socket(AF_INET6, SOCK_DGRAM, 0);
89	if (s < 0)
90		return (0);
91
92	if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) < 0)
93		lifr.lifr_index = 0;
94
95	save_err = errno;
96	(void) close(s);
97	errno = save_err;
98	return (lifr.lifr_index);
99}
100
101/*
102 * Given an index, this function returns the associated interface
103 * name in the supplied buffer ifname.
104 * Returns physical interface name if successful, NULL otherwise.
105 * The interface name returned will be at most IF_NAMESIZE-1 bytes.
106 */
107char *
108if_indextoname(uint32_t ifindex, char *ifname)
109{
110	int		n;
111	int		s;
112	char		*buf;
113	uint32_t	index;
114	struct lifnum	lifn;
115	struct lifconf	lifc;
116	struct lifreq	*lifrp;
117	int		numifs;
118	size_t		bufsize;
119	boolean_t 	found;
120	uint_t		flags;
121
122	flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES | LIFC_UNDER_IPMP;
123
124	/* A interface index of 0 is invalid */
125	if (ifindex == 0) {
126		errno = ENXIO;
127		return (NULL);
128	}
129
130	s = socket(AF_INET6, SOCK_DGRAM, 0);
131	if (s < 0) {
132		s = socket(AF_INET, SOCK_DGRAM, 0);
133		if (s < 0) {
134			return (NULL);
135		}
136	}
137
138	/* Prepare to send a SIOCGLIFNUM request message */
139	lifn.lifn_family = AF_UNSPEC;
140	lifn.lifn_flags = flags;
141	if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) {
142		int save_err = errno;
143		(void) close(s);
144		errno = save_err;
145		return (NULL);
146	}
147
148	/*
149	 * NOTE: "+ 10" sleaze mitigates new IP interfaces showing up between
150	 * the SIOCGLIFNUM and the SIOCGLIFCONF.
151	 */
152	numifs = lifn.lifn_count + 10;
153
154	/*
155	 * Provide enough buffer to obtain the interface
156	 * list from the kernel as response to a SIOCGLIFCONF
157	 * request
158	 */
159
160	bufsize = numifs * sizeof (struct lifreq);
161	buf = malloc(bufsize);
162	if (buf == NULL) {
163		int save_err = errno;
164		(void) close(s);
165		errno = save_err;
166		return (NULL);
167	}
168	lifc.lifc_family = AF_UNSPEC;
169	lifc.lifc_flags = flags;
170	lifc.lifc_len = bufsize;
171	lifc.lifc_buf = buf;
172	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
173		int save_err = errno;
174		(void) close(s);
175		errno = save_err;
176		free(buf);
177		return (NULL);
178	}
179
180	lifrp = lifc.lifc_req;
181	found = B_FALSE;
182	for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) {
183		/*
184		 * Obtain the index value of each interface, and
185		 * match to see if the retrived index value matches
186		 * the given one. If so we return the corresponding
187		 * device name of that interface.
188		 */
189		size_t	size;
190
191		index = if_nametoindex(lifrp->lifr_name);
192		if (index == 0)
193			/* Oops the interface just disappeared */
194			continue;
195		if (index == ifindex) {
196			size = strcspn(lifrp->lifr_name,
197			    (char *)IPIF_SEPARATOR_CHAR);
198			lifrp->lifr_name[size] = '\0';
199			found = B_TRUE;
200			(void) strncpy(ifname, lifrp->lifr_name, size + 1);
201			break;
202		}
203	}
204	(void) close(s);
205	free(buf);
206	if (!found) {
207		errno = ENXIO;
208		return (NULL);
209	}
210	return (ifname);
211}
212
213/*
214 * This function returns all the interface names and indexes
215 */
216struct if_nameindex *
217if_nameindex(void)
218{
219	int		n;
220	int		s;
221	boolean_t	found;
222	char		*buf;
223	struct lifnum	lifn;
224	struct lifconf	lifc;
225	struct lifreq	*lifrp;
226	int		numifs;
227	int		index;
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 */
351void
352if_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