1 #include "config.h"
2 #ifndef HAVE_GETIFADDRS
3 /*
4  * CDDL HEADER START
5  *
6  * The contents of this file are subject to the terms of the
7  * Common Development and Distribution License (the "License").
8  * You may not use this file except in compliance with the License.
9  *
10  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11  * or http://www.opensolaris.org/os/licensing.
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 
24 /*
25  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
26  */
27 
28 #include <netdb.h>
29 #if !defined (_AIX)
30 #include <nss_dbdefs.h>
31 #endif
32 
33 #include <netinet/in.h>
34 #include <sys/socket.h>
35 #if defined (_AIX)
36 #include <sys/errno.h>
37 #include <sys/ioctl.h>
38 #endif
39 #include <string.h>
40 #include <stdio.h>
41 #if defined (_AIX)
42 #include <netdb.h>
43 #endif
44 #if !defined (_AIX)
45 #include <sys/sockio.h>
46 #endif
47 #include <sys/types.h>
48 #include <stdlib.h>
49 #include <net/if.h>
50 #include <ifaddrs.h>
51 #if defined (_AIX)
52 #include <netinet/in6_var.h>
53 #endif
54 
55 /* Normally this is defined in <net/if.h> but was new for Solaris 11 */
56 #ifndef LIFC_ENABLED
57 #define LIFC_ENABLED    0x20
58 #endif
59 
60 #if defined (_AIX) /* Use ifaddrs_rsys instead of ifaddrs and ifreq instead of lifreq */
61 int getallifaddrs(sa_family_t af, struct ifaddrs_rsys **ifap, int64_t flags);
62 int getallifs(int s, sa_family_t af, struct ifreq **ifr, int *numifs,
63 	int64_t ifc_flags);
64 #else
65 int getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags);
66 int getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs,
67 	int64_t lifc_flags);
68 #endif
69 
70 /*
71  * Create a linked list of `struct ifaddrs_rsys' structures, one for each
72  * address that is UP. If successful, store the list in *ifap and
73  * return 0.  On errors, return -1 and set `errno'.
74  *
75  * The storage returned in *ifap is allocated dynamically and can
76  * only be properly freed by passing it to `freeifaddrs'.
77  */
78 int
79 #if defined (_AIX)
getifaddrs(struct ifaddrs_rsys ** ifap)80 getifaddrs(struct ifaddrs_rsys **ifap)
81 #else
82 getifaddrs(struct ifaddrs **ifap)
83 #endif
84 {
85 	int		err;
86 	char		*cp;
87 #if defined (_AIX)
88 	struct ifaddrs_rsys	*curr;
89 #else
90 	struct ifaddrs  *curr;
91 #endif
92 
93 	if (ifap == NULL) {
94 		errno = EINVAL;
95 		return (-1);
96 	}
97 	*ifap = NULL;
98 	err = getallifaddrs(AF_UNSPEC, ifap, LIFC_ENABLED);
99 	if (err == 0) {
100 		for (curr = *ifap; curr != NULL; curr = curr->ifa_next) {
101 			if ((cp = strchr(curr->ifa_name, ':')) != NULL)
102 				*cp = '\0';
103 		}
104 	}
105 	return (err);
106 }
107 
108 void
109 #if defined (_AIX)
freeifaddrs(struct ifaddrs_rsys * ifa)110 freeifaddrs(struct ifaddrs_rsys *ifa)
111 #else
112 freeifaddrs(struct ifaddrs *ifa)
113 #endif
114 {
115 #if defined (_AIX)
116 	struct ifaddrs_rsys *curr;
117 #else
118 	struct ifaddrs *curr;
119 #endif
120 
121 	while (ifa != NULL) {
122 		curr = ifa;
123 		ifa = ifa->ifa_next;
124 		free(curr->ifa_name);
125 		free(curr->ifa_addr);
126 		free(curr->ifa_netmask);
127 		free(curr->ifa_dstaddr);
128 		free(curr);
129 	}
130 }
131 
132 /*
133  * Returns all addresses configured on the system. If flags contain
134  * LIFC_ENABLED, only the addresses that are UP are returned.
135  * Address list that is returned by this function must be freed
136  * using freeifaddrs().
137  */
138 #if defined (_AIX)
139 int
getallifaddrs(sa_family_t af,struct ifaddrs_rsys ** ifap,int64_t flags)140 getallifaddrs(sa_family_t af, struct ifaddrs_rsys **ifap, int64_t flags)
141 {
142 	struct ifreq *buf = NULL;
143 	struct ifreq *ifrp;
144 	struct ifreq ifrl;
145 	struct in6_ifreq ifrl6;
146 	int ret;
147 	int s, n, iflen;
148 	struct ifaddrs_rsys *curr, *prev;
149 	sa_family_t ifr_af;
150 	int sock4;
151 	int sock6;
152 	int err;
153 	int ifsize;
154 	char *s_ifrp, *e_ifrp;
155 	int flag;
156 
157 	if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
158 		return (-1);
159 	if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
160 		err = errno;
161 		close(sock4);
162 		errno = err;
163 		return (-1);
164 	}
165 
166 retry:
167 	/* Get all interfaces from SIOCGIFCONF */
168 	ret = getallifs(sock4, af, &buf, &iflen, (flags & ~LIFC_ENABLED));
169 	if (ret != 0)
170 		goto fail;
171 
172 	/*
173 	 * Loop through the interfaces obtained from SIOCGIFCOMF
174 	 * and retrieve the addresses, netmask and flags.
175 	 */
176 	prev = NULL;
177 	s_ifrp = (char *)buf;
178 	e_ifrp = (char *)buf + iflen;
179 	*ifap = NULL;
180 	while (s_ifrp < e_ifrp)
181 	{
182 		ifrp = (struct ifreq *)s_ifrp;
183 		ifsize = sizeof(struct ifreq);
184 
185 		if (ifrp->ifr_addr.sa_len > sizeof(ifrp->ifr_ifru)) {
186 			ifsize += ifrp->ifr_addr.sa_len - sizeof(ifrp->ifr_ifru);
187 		}
188 
189 		/* Prepare for the ioctl call */
190 		(void) strncpy(ifrl.ifr_name, ifrp->ifr_name,
191 		    sizeof (ifrl.ifr_name));
192 		(void) strncpy(ifrl6.ifr_name, ifrp->ifr_name,
193 		    sizeof (ifrl.ifr_name));
194 		ifr_af = ifrp->ifr_addr.sa_family;
195 
196 		if (ifr_af != AF_INET && ifr_af != AF_INET6)
197 			goto next;
198 
199 		s = (ifr_af == AF_INET ? sock4 : sock6);
200 
201 		if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifrl) < 0)
202 			goto fail;
203 
204 		if ((flags & LIFC_ENABLED) && !(ifrl.ifr_flags & IFF_UP)) {
205 			goto next;
206 		}
207 
208 		/*
209 		 * Allocate the current list node. Each node contains data
210 		 * for one ifaddrs structure.
211 		 */
212 		curr = calloc(1, sizeof (struct ifaddrs_rsys));
213 		if (curr == NULL)
214 			goto fail;
215 
216 		if (prev != NULL) {
217 			prev->ifa_next = curr;
218 		} else {
219 			/* First node in the linked list */
220 			*ifap = curr;
221 		}
222 		prev = curr;
223 
224 /* AIXPORT :  ifreq field names used instead of linux lifreq field names */
225 		curr->ifa_flags = ifrl.ifr_flags;
226 		if ((curr->ifa_name = strdup(ifrp->ifr_name)) == NULL)
227 			goto fail;
228 
229 		curr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
230 		if (curr->ifa_addr == NULL)
231 			goto fail;
232 		(void) memcpy(curr->ifa_addr, &ifrp->ifr_addr,
233 		    sizeof (struct sockaddr_storage));
234 
235 		/* Get the netmask */
236 		if (ifr_af == AF_INET) {
237 			if (ioctl(s, SIOCGIFNETMASK, (caddr_t)&ifrl) < 0) {
238 				goto fail;
239 			}
240 			curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
241 			if (curr->ifa_netmask == NULL)
242 				goto fail;
243 			(void) memcpy(curr->ifa_netmask, &ifrl.ifr_addr,
244 			    sizeof (struct sockaddr_storage));
245 		} else {
246 			if (ioctl(s, SIOCGIFNETMASK6, (caddr_t)&ifrl6) < 0) {
247 				goto fail;
248 			}
249 			curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
250 			if (curr->ifa_netmask == NULL)
251 				goto fail;
252 			(void) memcpy(curr->ifa_netmask, &ifrl6.ifr_Addr,
253 			    sizeof (struct sockaddr_storage));
254 		}
255 
256 		/* Get the destination for a pt-pt interface */
257 		if (curr->ifa_flags & IFF_POINTOPOINT) {
258 			if (ifr_af == AF_INET) {
259 				if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)&ifrl) < 0)
260 					goto fail;
261 				curr->ifa_dstaddr = malloc(
262 				    sizeof (struct sockaddr_storage));
263 				if (curr->ifa_dstaddr == NULL)
264 					goto fail;
265 				(void) memcpy(curr->ifa_dstaddr, &ifrl.ifr_addr,
266 				    sizeof (struct sockaddr_storage));
267 			} else {
268 				if (ioctl(s, SIOCGIFDSTADDR6, (caddr_t)&ifrl6) < 0)
269 					goto fail;
270 				curr->ifa_dstaddr = malloc(
271 				    sizeof (struct sockaddr_storage));
272 				if (curr->ifa_dstaddr == NULL)
273 					goto fail;
274 				(void) memcpy(curr->ifa_dstaddr, &ifrl6.ifr_Addr,
275 				    sizeof (struct sockaddr_storage));
276 			}
277 			/* Do not get broadcast address for IPv6 */
278 		} else if ((curr->ifa_flags & IFF_BROADCAST) && (ifr_af == AF_INET)) {
279 			if (ioctl(s, SIOCGIFBRDADDR, (caddr_t)&ifrl) < 0)
280 				goto fail;
281 			curr->ifa_broadaddr = malloc(
282 			    sizeof (struct sockaddr_storage));
283 			if (curr->ifa_broadaddr == NULL)
284 				goto fail;
285 			(void) memcpy(curr->ifa_broadaddr, &ifrl.ifr_addr,
286 			    sizeof (struct sockaddr_storage));
287 		}
288 next:
289 		s_ifrp += ifsize;
290 	}
291 	free(buf);
292 	close(sock4);
293 	close(sock6);
294 	return (0);
295 fail:
296 	err = errno;
297 	free(buf);
298 	freeifaddrs(*ifap);
299 	*ifap = NULL;
300 	if (err == ENXIO)
301 		goto retry;
302 	close(sock4);
303 	close(sock6);
304 	errno = err;
305 	return (-1);
306 }
307 
308 /*
309  * Do a SIOCGIFCONF and store all the interfaces in `buf'.
310  */
311 int
getallifs(int s,sa_family_t af,struct ifreq ** ifr,int * iflen,int64_t ifc_flags)312 getallifs(int s, sa_family_t af, struct ifreq **ifr, int *iflen,
313 		int64_t ifc_flags)
314 {
315 	int ifsize;
316 	struct ifconf ifc;
317 	size_t bufsize;
318 	char *tmp;
319 	caddr_t *buf = (caddr_t *)ifr;
320 
321 	*buf = NULL;
322 retry:
323 	if (ioctl(s, SIOCGSIZIFCONF, &ifsize) < 0)
324 		goto fail;
325 
326 	/*
327 	 * When calculating the buffer size needed, add a small number
328 	 * of interfaces to those we counted.  We do this to capture
329 	 * the interface status of potential interfaces which may have
330 	 * been plumbed between the SIOCGSIZIFCONF and the SIOCGIFCONF.
331 	 */
332 	bufsize = ifsize + (4 * sizeof (struct in6_ifreq));
333 
334 	if ((tmp = realloc(*buf, bufsize)) == NULL)
335 		goto fail;
336 
337 	*buf = tmp;
338 	ifc.ifc_buf = *buf;
339 	ifc.ifc_len = bufsize;
340 	if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0)
341 		goto fail;
342 
343 	*iflen = ifc.ifc_len;
344 	if (*iflen >= bufsize) {
345 		/*
346 		 * If every entry was filled, there are probably
347 		 * more interfaces than (ifn + 4)
348 		 * Redo the ioctls SIOCGSIZIFCONF and SIOCGIFCONF to
349 		 * get all the interfaces.
350 		 */
351 		goto retry;
352 	}
353 	return (0);
354 fail:
355 	free(*buf);
356 	*buf = NULL;
357 	return (-1);
358 }
359 #else /* _AIX */
360 int
getallifaddrs(sa_family_t af,struct ifaddrs ** ifap,int64_t flags)361 getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags)
362 {
363 	struct lifreq *buf = NULL;
364 	struct lifreq *lifrp;
365 	struct lifreq lifrl;
366 	int ret;
367 	int s, n, numifs;
368 	struct ifaddrs *curr, *prev;
369 	sa_family_t lifr_af;
370 	int sock4;
371 	int sock6;
372 	int err;
373 
374 	if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
375 		return (-1);
376 	if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
377 		err = errno;
378 		close(sock4);
379 		errno = err;
380 		return (-1);
381 	}
382 
383 retry:
384 	/* Get all interfaces from SIOCGLIFCONF */
385 	ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED));
386 	if (ret != 0)
387 		goto fail;
388 
389 	/*
390 	 * Loop through the interfaces obtained from SIOCGLIFCOMF
391 	 * and retrieve the addresses, netmask and flags.
392 	 */
393 	prev = NULL;
394 	lifrp = buf;
395 	*ifap = NULL;
396 	for (n = 0; n < numifs; n++, lifrp++) {
397 
398 		/* Prepare for the ioctl call */
399 		(void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
400 		    sizeof (lifrl.lifr_name));
401 		lifr_af = lifrp->lifr_addr.ss_family;
402 		if (af != AF_UNSPEC && lifr_af != af)
403 			continue;
404 
405 		s = (lifr_af == AF_INET ? sock4 : sock6);
406 
407 		if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
408 			goto fail;
409 		if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP))
410 			continue;
411 
412 		/*
413 		 * Allocate the current list node. Each node contains data
414 		 * for one ifaddrs structure.
415 		 */
416 		curr = calloc(1, sizeof (struct ifaddrs));
417 		if (curr == NULL)
418 			goto fail;
419 
420 		if (prev != NULL) {
421 			prev->ifa_next = curr;
422 		} else {
423 			/* First node in the linked list */
424 			*ifap = curr;
425 		}
426 		prev = curr;
427 
428 		curr->ifa_flags = lifrl.lifr_flags;
429 		if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL)
430 			goto fail;
431 
432 		curr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
433 		if (curr->ifa_addr == NULL)
434 			goto fail;
435 		(void) memcpy(curr->ifa_addr, &lifrp->lifr_addr,
436 		    sizeof (struct sockaddr_storage));
437 
438 		/* Get the netmask */
439 		if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0)
440 			goto fail;
441 		curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
442 		if (curr->ifa_netmask == NULL)
443 			goto fail;
444 		(void) memcpy(curr->ifa_netmask, &lifrl.lifr_addr,
445 		    sizeof (struct sockaddr_storage));
446 
447 		/* Get the destination for a pt-pt interface */
448 		if (curr->ifa_flags & IFF_POINTOPOINT) {
449 			if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0)
450 				goto fail;
451 			curr->ifa_dstaddr = malloc(
452 			    sizeof (struct sockaddr_storage));
453 			if (curr->ifa_dstaddr == NULL)
454 				goto fail;
455 			(void) memcpy(curr->ifa_dstaddr, &lifrl.lifr_addr,
456 			    sizeof (struct sockaddr_storage));
457 		} else if (curr->ifa_flags & IFF_BROADCAST) {
458 			if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0)
459 				goto fail;
460 			curr->ifa_broadaddr = malloc(
461 			    sizeof (struct sockaddr_storage));
462 			if (curr->ifa_broadaddr == NULL)
463 				goto fail;
464 			(void) memcpy(curr->ifa_broadaddr, &lifrl.lifr_addr,
465 			    sizeof (struct sockaddr_storage));
466 		}
467 
468 	}
469 	free(buf);
470 	close(sock4);
471 	close(sock6);
472 	return (0);
473 fail:
474 	err = errno;
475 	free(buf);
476 	freeifaddrs(*ifap);
477 	*ifap = NULL;
478 	if (err == ENXIO)
479 		goto retry;
480 	close(sock4);
481 	close(sock6);
482 	errno = err;
483 	return (-1);
484 }
485 
486 /*
487  * Do a SIOCGLIFCONF and store all the interfaces in `buf'.
488  */
489 int
getallifs(int s,sa_family_t af,struct lifreq ** lifr,int * numifs,int64_t lifc_flags)490 getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs,
491 		int64_t lifc_flags)
492 {
493 	struct lifnum lifn;
494 	struct lifconf lifc;
495 	size_t bufsize;
496 	char *tmp;
497 	caddr_t *buf = (caddr_t *)lifr;
498 
499 	lifn.lifn_family = af;
500 	lifn.lifn_flags = lifc_flags;
501 
502 	*buf = NULL;
503 retry:
504 	if (ioctl(s, SIOCGLIFNUM, &lifn) < 0)
505 		goto fail;
506 
507 	/*
508 	 * When calculating the buffer size needed, add a small number
509 	 * of interfaces to those we counted.  We do this to capture
510 	 * the interface status of potential interfaces which may have
511 	 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
512 	 */
513 	bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq);
514 
515 	if ((tmp = realloc(*buf, bufsize)) == NULL)
516 		goto fail;
517 
518 	*buf = tmp;
519 	lifc.lifc_family = af;
520 	lifc.lifc_flags = lifc_flags;
521 	lifc.lifc_len = bufsize;
522 	lifc.lifc_buf = *buf;
523 	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
524 		goto fail;
525 
526 	*numifs = lifc.lifc_len / sizeof (struct lifreq);
527 	if (*numifs >= (lifn.lifn_count + 4)) {
528 		/*
529 		 * If every entry was filled, there are probably
530 		 * more interfaces than (lifn.lifn_count + 4).
531 		 * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to
532 		 * get all the interfaces.
533 		 */
534 		goto retry;
535 	}
536 	return (0);
537 fail:
538 	free(*buf);
539 	*buf = NULL;
540 	return (-1);
541 }
542 #endif /* _AIX */
543 #endif /* HAVE_GETIFADDRS */
544