summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsocket/inet/interface_id.c
blob: 61235437c0e234f13eb8af385d09ec82e84645d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <inet/common.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/sockio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>

#define	IPIF_SEPARATOR_CHAR	":"

/*
 * Given an interface name, this function retrives the associated
 * index value. Returns index value if successful, zero otherwise.
 * The length of the supplied interface name must be at most
 * IF_NAMESIZE-1 bytes
 */
uint32_t
if_nametoindex(const char *ifname)
{
	int		s;
	struct lifreq	lifr;
	int		save_err;
	size_t		size;


	/* Make sure the given name is not NULL */
	if ((ifname == NULL)||(*ifname == '\0')) {
		errno = ENXIO;
		return (0);
	}

	/*
	 * Fill up the interface name in the ioctl
	 * request message. Make sure that the length of
	 * the given interface name <= (IF_NAMESIZE-1)
	 */
	size = strlen(ifname);
	if (size > (IF_NAMESIZE - 1)) {
		errno = EINVAL;
		return (0);
	}

	strncpy(lifr.lifr_name, ifname, size +1);

	/* Check the v4 interfaces first */
	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s >= 0) {
		if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) {
			(void) close(s);
			return (lifr.lifr_index);
		}
		(void) close(s);
	}

	/* Check the v6 interface list */
	s = socket(AF_INET6, SOCK_DGRAM, 0);
	if (s < 0)
		return (0);

	if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) < 0)
		lifr.lifr_index = 0;

	save_err = errno;
	(void) close(s);
	errno = save_err;
	return (lifr.lifr_index);
}

/*
 * Given an index, this function returns the associated interface
 * name in the supplied buffer ifname.
 * Returns physical interface name if successful, NULL otherwise.
 * The interface name returned will be at most IF_NAMESIZE-1 bytes.
 */
char *
if_indextoname(uint32_t ifindex, char *ifname)
{
	int		n;
	int		s;
	char		*buf;
	uint32_t	index;
	struct lifnum	lifn;
	struct lifconf	lifc;
	struct lifreq	*lifrp;
	int		numifs;
	size_t		bufsize;
	boolean_t 	found;
	uint_t		flags;

	flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES | LIFC_UNDER_IPMP;

	/* A interface index of 0 is invalid */
	if (ifindex == 0) {
		errno = ENXIO;
		return (NULL);
	}

	s = socket(AF_INET6, SOCK_DGRAM, 0);
	if (s < 0) {
		s = socket(AF_INET, SOCK_DGRAM, 0);
		if (s < 0) {
			return (NULL);
		}
	}

	/* Prepare to send a SIOCGLIFNUM request message */
	lifn.lifn_family = AF_UNSPEC;
	lifn.lifn_flags = flags;
	if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) {
		int save_err = errno;
		(void) close(s);
		errno = save_err;
		return (NULL);
	}

	/*
	 * NOTE: "+ 10" sleaze mitigates new IP interfaces showing up between
	 * the SIOCGLIFNUM and the SIOCGLIFCONF.
	 */
	numifs = lifn.lifn_count + 10;

	/*
	 * Provide enough buffer to obtain the interface
	 * list from the kernel as response to a SIOCGLIFCONF
	 * request
	 */

	bufsize = numifs * sizeof (struct lifreq);
	buf = malloc(bufsize);
	if (buf == NULL) {
		int save_err = errno;
		(void) close(s);
		errno = save_err;
		return (NULL);
	}
	lifc.lifc_family = AF_UNSPEC;
	lifc.lifc_flags = flags;
	lifc.lifc_len = bufsize;
	lifc.lifc_buf = buf;
	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
		int save_err = errno;
		(void) close(s);
		errno = save_err;
		free(buf);
		return (NULL);
	}

	lifrp = lifc.lifc_req;
	found = B_FALSE;
	for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) {
		/*
		 * Obtain the index value of each interface, and
		 * match to see if the retrived index value matches
		 * the given one. If so we return the corresponding
		 * device name of that interface.
		 */
		size_t	size;

		index = if_nametoindex(lifrp->lifr_name);
		if (index == 0)
			/* Oops the interface just disappeared */
			continue;
		if (index == ifindex) {
			size = strcspn(lifrp->lifr_name,
			    (char *)IPIF_SEPARATOR_CHAR);
			lifrp->lifr_name[size] = '\0';
			found = B_TRUE;
			(void) strncpy(ifname, lifrp->lifr_name, size + 1);
			break;
		}
	}
	(void) close(s);
	free(buf);
	if (!found) {
		errno = ENXIO;
		return (NULL);
	}
	return (ifname);
}

/*
 * This function returns all the interface names and indexes
 */
struct if_nameindex *
if_nameindex(void)
{
	int		n;
	int		s;
	boolean_t	found;
	char		*buf;
	struct lifnum	lifn;
	struct lifconf	lifc;
	struct lifreq	*lifrp;
	int		numifs;
	int		index;
	int		i;
	int 		physinterf_num;
	size_t		bufsize;
	struct if_nameindex	 *interface_list;
	struct if_nameindex	 *interface_entry;

	s = socket(AF_INET6, SOCK_DGRAM, 0);
	if (s < 0) {
		s = socket(AF_INET, SOCK_DGRAM, 0);
		if (s < 0)
			return (NULL);
	}

	lifn.lifn_family = AF_UNSPEC;
	lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
	if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0)
		return (NULL);
	numifs = lifn.lifn_count;

	bufsize = numifs * sizeof (struct lifreq);
	buf = malloc(bufsize);
	if (buf == NULL) {
		int save_err = errno;
		(void) close(s);
		errno = save_err;
		return (NULL);
	}
	lifc.lifc_family = AF_UNSPEC;
	lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
	lifc.lifc_len = bufsize;
	lifc.lifc_buf = buf;
	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
		int save_err = errno;
		(void) close(s);
		errno = save_err;
		free(buf);
		return (NULL);
	}

	lifrp = lifc.lifc_req;
	(void) close(s);

	/* Allocate the array of if_nameindex structure */
	interface_list = malloc((numifs + 1) * sizeof (struct if_nameindex));
	if (!interface_list) {
		int save_err = errno;
		free(buf);
		errno = save_err;
		return (NULL);
	}
	/*
	 * Make sure that terminator structure automatically
	 * happens to be all zeroes.
	 */
	bzero(interface_list, ((numifs + 1) * sizeof (struct if_nameindex)));
	interface_entry = interface_list;
	physinterf_num = 0;
	for (n = numifs; n > 0; n--, lifrp++) {
		size_t	size;

		size = strcspn(lifrp->lifr_name, (char *)IPIF_SEPARATOR_CHAR);
		found = B_FALSE;
		/*
		 * Search the current array to see if this interface
		 * already exists. Only compare the physical name.
		 */
		for (i = 0; i < physinterf_num; i++) {
			if (strncmp(interface_entry[i].if_name,
			    lifrp->lifr_name, size) == 0) {
				found = B_TRUE;
				break;
			}
		}

		/* New one. Allocate an array element and fill it */
		if (!found) {
			/*
			 * Obtain the index value for the interface
			 */
			interface_entry[physinterf_num].if_index =
			    if_nametoindex(lifrp->lifr_name);

			if (interface_entry[physinterf_num].if_index == 0) {
				/* The interface went away. Skip this entry. */
				continue;
			}

			/*
			 * Truncate the name to ensure that it represents
			 * a physical interface.
			 */
			lifrp->lifr_name[size] = '\0';
			if ((interface_entry[physinterf_num].if_name =
			    strdup(lifrp->lifr_name)) == NULL) {
				int save_err;

				if_freenameindex(interface_list);
				save_err = errno;
				free(buf);
				errno = save_err;
				return (NULL);
			}

			physinterf_num++;
		}
	}

	/* Create the last one of the array */
	interface_entry[physinterf_num].if_name = NULL;
	interface_entry[physinterf_num].if_index = 0;

	/* Free up the excess array space */
	free(buf);
	interface_list = realloc(interface_list, ((physinterf_num + 1) *
	    sizeof (struct if_nameindex)));

	return (interface_list);
}

/*
 * This function frees the the array that is created while
 * the if_nameindex function.
 */
void
if_freenameindex(struct if_nameindex *ptr)
{
	struct if_nameindex *p;

	if (ptr == NULL)
		return;

	/* First free the if_name member in each array element */
	for (p = ptr; p->if_name != NULL; p++)
		free(p->if_name);

	/* Now free up the array space */
	free(ptr);
}