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 */
49 uint32_t
if_nametoindex(const char * ifname)50 if_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 */
107 char *
if_indextoname(uint32_t ifindex,char * ifname)108 if_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 */
216 struct if_nameindex *
if_nameindex(void)217 if_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 */
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