xref: /titanic_52/usr/src/lib/libsocket/inet/getifaddrs.c (revision 08ed54f365d20ed6ea7275787e853c359aa9385b)
16e91bba0SGirish Moodalbail /*
26e91bba0SGirish Moodalbail  * CDDL HEADER START
36e91bba0SGirish Moodalbail  *
46e91bba0SGirish Moodalbail  * The contents of this file are subject to the terms of the
56e91bba0SGirish Moodalbail  * Common Development and Distribution License (the "License").
66e91bba0SGirish Moodalbail  * You may not use this file except in compliance with the License.
76e91bba0SGirish Moodalbail  *
86e91bba0SGirish Moodalbail  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96e91bba0SGirish Moodalbail  * or http://www.opensolaris.org/os/licensing.
106e91bba0SGirish Moodalbail  * See the License for the specific language governing permissions
116e91bba0SGirish Moodalbail  * and limitations under the License.
126e91bba0SGirish Moodalbail  *
136e91bba0SGirish Moodalbail  * When distributing Covered Code, include this CDDL HEADER in each
146e91bba0SGirish Moodalbail  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156e91bba0SGirish Moodalbail  * If applicable, add the following below this CDDL HEADER, with the
166e91bba0SGirish Moodalbail  * fields enclosed by brackets "[]" replaced with your own identifying
176e91bba0SGirish Moodalbail  * information: Portions Copyright [yyyy] [name of copyright owner]
186e91bba0SGirish Moodalbail  *
196e91bba0SGirish Moodalbail  * CDDL HEADER END
206e91bba0SGirish Moodalbail  */
216e91bba0SGirish Moodalbail 
226e91bba0SGirish Moodalbail /*
2364639aafSDarren Reed  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
246e91bba0SGirish Moodalbail  */
256e91bba0SGirish Moodalbail 
266e91bba0SGirish Moodalbail #include <netdb.h>
276e91bba0SGirish Moodalbail #include <nss_dbdefs.h>
286e91bba0SGirish Moodalbail #include <netinet/in.h>
296e91bba0SGirish Moodalbail #include <sys/socket.h>
306e91bba0SGirish Moodalbail #include <string.h>
316e91bba0SGirish Moodalbail #include <stdio.h>
326e91bba0SGirish Moodalbail #include <sys/sockio.h>
336e91bba0SGirish Moodalbail #include <sys/types.h>
346e91bba0SGirish Moodalbail #include <stdlib.h>
356e91bba0SGirish Moodalbail #include <net/if.h>
366e91bba0SGirish Moodalbail #include <ifaddrs.h>
376e91bba0SGirish Moodalbail #include <libsocket_priv.h>
386e91bba0SGirish Moodalbail 
396e91bba0SGirish Moodalbail /*
406e91bba0SGirish Moodalbail  * Create a linked list of `struct ifaddrs' structures, one for each
416e91bba0SGirish Moodalbail  * address that is UP. If successful, store the list in *ifap and
426e91bba0SGirish Moodalbail  * return 0.  On errors, return -1 and set `errno'.
436e91bba0SGirish Moodalbail  *
446e91bba0SGirish Moodalbail  * The storage returned in *ifap is allocated dynamically and can
456e91bba0SGirish Moodalbail  * only be properly freed by passing it to `freeifaddrs'.
466e91bba0SGirish Moodalbail  */
476e91bba0SGirish Moodalbail int
486e91bba0SGirish Moodalbail getifaddrs(struct ifaddrs **ifap)
496e91bba0SGirish Moodalbail {
506e91bba0SGirish Moodalbail 	int		err;
516e91bba0SGirish Moodalbail 	char		*cp;
526e91bba0SGirish Moodalbail 	struct ifaddrs	*curr;
536e91bba0SGirish Moodalbail 
546e91bba0SGirish Moodalbail 	if (ifap == NULL) {
556e91bba0SGirish Moodalbail 		errno = EINVAL;
566e91bba0SGirish Moodalbail 		return (-1);
576e91bba0SGirish Moodalbail 	}
586e91bba0SGirish Moodalbail 	*ifap = NULL;
596e91bba0SGirish Moodalbail 	err = getallifaddrs(AF_UNSPEC, ifap, LIFC_ENABLED);
606e91bba0SGirish Moodalbail 	if (err == 0) {
616e91bba0SGirish Moodalbail 		for (curr = *ifap; curr != NULL; curr = curr->ifa_next) {
626e91bba0SGirish Moodalbail 			if ((cp = strchr(curr->ifa_name, ':')) != NULL)
636e91bba0SGirish Moodalbail 				*cp = '\0';
646e91bba0SGirish Moodalbail 		}
656e91bba0SGirish Moodalbail 	}
666e91bba0SGirish Moodalbail 	return (err);
676e91bba0SGirish Moodalbail }
686e91bba0SGirish Moodalbail 
696e91bba0SGirish Moodalbail void
706e91bba0SGirish Moodalbail freeifaddrs(struct ifaddrs *ifa)
716e91bba0SGirish Moodalbail {
726e91bba0SGirish Moodalbail 	struct ifaddrs *curr;
736e91bba0SGirish Moodalbail 
746e91bba0SGirish Moodalbail 	while (ifa != NULL) {
756e91bba0SGirish Moodalbail 		curr = ifa;
766e91bba0SGirish Moodalbail 		ifa = ifa->ifa_next;
776e91bba0SGirish Moodalbail 		free(curr->ifa_name);
786e91bba0SGirish Moodalbail 		free(curr->ifa_addr);
796e91bba0SGirish Moodalbail 		free(curr->ifa_netmask);
806e91bba0SGirish Moodalbail 		free(curr->ifa_dstaddr);
816e91bba0SGirish Moodalbail 		free(curr);
826e91bba0SGirish Moodalbail 	}
836e91bba0SGirish Moodalbail }
846e91bba0SGirish Moodalbail 
856e91bba0SGirish Moodalbail /*
866e91bba0SGirish Moodalbail  * Returns all addresses configured on the system. If flags contain
876e91bba0SGirish Moodalbail  * LIFC_ENABLED, only the addresses that are UP are returned.
886e91bba0SGirish Moodalbail  * Address list that is returned by this function must be freed
896e91bba0SGirish Moodalbail  * using freeifaddrs().
906e91bba0SGirish Moodalbail  */
916e91bba0SGirish Moodalbail int
926e91bba0SGirish Moodalbail getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags)
936e91bba0SGirish Moodalbail {
946e91bba0SGirish Moodalbail 	struct lifreq *buf = NULL;
956e91bba0SGirish Moodalbail 	struct lifreq *lifrp;
966e91bba0SGirish Moodalbail 	struct lifreq lifrl;
976e91bba0SGirish Moodalbail 	int ret;
986e91bba0SGirish Moodalbail 	int s, n, numifs;
996e91bba0SGirish Moodalbail 	struct ifaddrs *curr, *prev;
1006e91bba0SGirish Moodalbail 	sa_family_t lifr_af;
1016e91bba0SGirish Moodalbail 	int sock4;
1026e91bba0SGirish Moodalbail 	int sock6;
1036e91bba0SGirish Moodalbail 	int err;
1046e91bba0SGirish Moodalbail 
1056e91bba0SGirish Moodalbail 	if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1066e91bba0SGirish Moodalbail 		return (-1);
1076e91bba0SGirish Moodalbail 	if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
1086e91bba0SGirish Moodalbail 		err = errno;
1096e91bba0SGirish Moodalbail 		close(sock4);
1106e91bba0SGirish Moodalbail 		errno = err;
1116e91bba0SGirish Moodalbail 		return (-1);
1126e91bba0SGirish Moodalbail 	}
1136e91bba0SGirish Moodalbail 
1146e91bba0SGirish Moodalbail retry:
1156e91bba0SGirish Moodalbail 	/* Get all interfaces from SIOCGLIFCONF */
1166e91bba0SGirish Moodalbail 	ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED));
1176e91bba0SGirish Moodalbail 	if (ret != 0)
1186e91bba0SGirish Moodalbail 		goto fail;
1196e91bba0SGirish Moodalbail 
1206e91bba0SGirish Moodalbail 	/*
1216e91bba0SGirish Moodalbail 	 * Loop through the interfaces obtained from SIOCGLIFCOMF
1226e91bba0SGirish Moodalbail 	 * and retrieve the addresses, netmask and flags.
1236e91bba0SGirish Moodalbail 	 */
1246e91bba0SGirish Moodalbail 	prev = NULL;
1256e91bba0SGirish Moodalbail 	lifrp = buf;
1266e91bba0SGirish Moodalbail 	*ifap = NULL;
1276e91bba0SGirish Moodalbail 	for (n = 0; n < numifs; n++, lifrp++) {
1286e91bba0SGirish Moodalbail 
1296e91bba0SGirish Moodalbail 		/* Prepare for the ioctl call */
1306e91bba0SGirish Moodalbail 		(void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
1316e91bba0SGirish Moodalbail 		    sizeof (lifrl.lifr_name));
1326e91bba0SGirish Moodalbail 		lifr_af = lifrp->lifr_addr.ss_family;
1336e91bba0SGirish Moodalbail 		if (af != AF_UNSPEC && lifr_af != af)
1346e91bba0SGirish Moodalbail 			continue;
1356e91bba0SGirish Moodalbail 
1366e91bba0SGirish Moodalbail 		s = (lifr_af == AF_INET ? sock4 : sock6);
1376e91bba0SGirish Moodalbail 
1386e91bba0SGirish Moodalbail 		if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
1396e91bba0SGirish Moodalbail 			goto fail;
1406e91bba0SGirish Moodalbail 		if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP))
1416e91bba0SGirish Moodalbail 			continue;
1426e91bba0SGirish Moodalbail 
1436e91bba0SGirish Moodalbail 		/*
1446e91bba0SGirish Moodalbail 		 * Allocate the current list node. Each node contains data
1456e91bba0SGirish Moodalbail 		 * for one ifaddrs structure.
1466e91bba0SGirish Moodalbail 		 */
1476e91bba0SGirish Moodalbail 		curr = calloc(1, sizeof (struct ifaddrs));
1486e91bba0SGirish Moodalbail 		if (curr == NULL)
1496e91bba0SGirish Moodalbail 			goto fail;
1506e91bba0SGirish Moodalbail 
1516e91bba0SGirish Moodalbail 		if (prev != NULL) {
1526e91bba0SGirish Moodalbail 			prev->ifa_next = curr;
1536e91bba0SGirish Moodalbail 		} else {
1546e91bba0SGirish Moodalbail 			/* First node in the linked list */
1556e91bba0SGirish Moodalbail 			*ifap = curr;
1566e91bba0SGirish Moodalbail 		}
1576e91bba0SGirish Moodalbail 		prev = curr;
1586e91bba0SGirish Moodalbail 
1596e91bba0SGirish Moodalbail 		curr->ifa_flags = lifrl.lifr_flags;
1606e91bba0SGirish Moodalbail 		if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL)
1616e91bba0SGirish Moodalbail 			goto fail;
1626e91bba0SGirish Moodalbail 
1636e91bba0SGirish Moodalbail 		curr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
1646e91bba0SGirish Moodalbail 		if (curr->ifa_addr == NULL)
1656e91bba0SGirish Moodalbail 			goto fail;
16664639aafSDarren Reed 		(void) memcpy(curr->ifa_addr, &lifrp->lifr_addr,
16764639aafSDarren Reed 		    sizeof (struct sockaddr_storage));
1686e91bba0SGirish Moodalbail 
1696e91bba0SGirish Moodalbail 		/* Get the netmask */
1706e91bba0SGirish Moodalbail 		if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0)
1716e91bba0SGirish Moodalbail 			goto fail;
1726e91bba0SGirish Moodalbail 		curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
1736e91bba0SGirish Moodalbail 		if (curr->ifa_netmask == NULL)
1746e91bba0SGirish Moodalbail 			goto fail;
17564639aafSDarren Reed 		(void) memcpy(curr->ifa_netmask, &lifrl.lifr_addr,
17664639aafSDarren Reed 		    sizeof (struct sockaddr_storage));
1776e91bba0SGirish Moodalbail 
1786e91bba0SGirish Moodalbail 		/* Get the destination for a pt-pt interface */
1796e91bba0SGirish Moodalbail 		if (curr->ifa_flags & IFF_POINTOPOINT) {
1806e91bba0SGirish Moodalbail 			if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0)
1816e91bba0SGirish Moodalbail 				goto fail;
1826e91bba0SGirish Moodalbail 			curr->ifa_dstaddr = malloc(
1836e91bba0SGirish Moodalbail 			    sizeof (struct sockaddr_storage));
1846e91bba0SGirish Moodalbail 			if (curr->ifa_dstaddr == NULL)
1856e91bba0SGirish Moodalbail 				goto fail;
186*08ed54f3SVasumathi Sundaram 			(void) memcpy(curr->ifa_dstaddr, &lifrl.lifr_addr,
18764639aafSDarren Reed 			    sizeof (struct sockaddr_storage));
1886e91bba0SGirish Moodalbail 		} else if (curr->ifa_flags & IFF_BROADCAST) {
1896e91bba0SGirish Moodalbail 			if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0)
1906e91bba0SGirish Moodalbail 				goto fail;
1916e91bba0SGirish Moodalbail 			curr->ifa_broadaddr = malloc(
1926e91bba0SGirish Moodalbail 			    sizeof (struct sockaddr_storage));
1936e91bba0SGirish Moodalbail 			if (curr->ifa_broadaddr == NULL)
1946e91bba0SGirish Moodalbail 				goto fail;
195*08ed54f3SVasumathi Sundaram 			(void) memcpy(curr->ifa_broadaddr, &lifrl.lifr_addr,
19664639aafSDarren Reed 			    sizeof (struct sockaddr_storage));
1976e91bba0SGirish Moodalbail 		}
1986e91bba0SGirish Moodalbail 
1996e91bba0SGirish Moodalbail 	}
2006e91bba0SGirish Moodalbail 	free(buf);
2016e91bba0SGirish Moodalbail 	close(sock4);
2026e91bba0SGirish Moodalbail 	close(sock6);
2036e91bba0SGirish Moodalbail 	return (0);
2046e91bba0SGirish Moodalbail fail:
2056e91bba0SGirish Moodalbail 	err = errno;
2066e91bba0SGirish Moodalbail 	free(buf);
2076e91bba0SGirish Moodalbail 	freeifaddrs(*ifap);
2086e91bba0SGirish Moodalbail 	*ifap = NULL;
2096e91bba0SGirish Moodalbail 	if (err == ENXIO)
2106e91bba0SGirish Moodalbail 		goto retry;
2116e91bba0SGirish Moodalbail 	close(sock4);
2126e91bba0SGirish Moodalbail 	close(sock6);
2136e91bba0SGirish Moodalbail 	errno = err;
2146e91bba0SGirish Moodalbail 	return (-1);
2156e91bba0SGirish Moodalbail }
2166e91bba0SGirish Moodalbail 
2176e91bba0SGirish Moodalbail /*
2186e91bba0SGirish Moodalbail  * Do a SIOCGLIFCONF and store all the interfaces in `buf'.
2196e91bba0SGirish Moodalbail  */
2206e91bba0SGirish Moodalbail int
2216e91bba0SGirish Moodalbail getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs,
2226e91bba0SGirish Moodalbail     int64_t lifc_flags)
2236e91bba0SGirish Moodalbail {
2246e91bba0SGirish Moodalbail 	struct lifnum lifn;
2256e91bba0SGirish Moodalbail 	struct lifconf lifc;
2266e91bba0SGirish Moodalbail 	size_t bufsize;
2276e91bba0SGirish Moodalbail 	char *tmp;
2286e91bba0SGirish Moodalbail 	caddr_t *buf = (caddr_t *)lifr;
2296e91bba0SGirish Moodalbail 
2306e91bba0SGirish Moodalbail 	lifn.lifn_family = af;
2316e91bba0SGirish Moodalbail 	lifn.lifn_flags = lifc_flags;
2326e91bba0SGirish Moodalbail 
2336e91bba0SGirish Moodalbail 	*buf = NULL;
2346e91bba0SGirish Moodalbail retry:
2356e91bba0SGirish Moodalbail 	if (ioctl(s, SIOCGLIFNUM, &lifn) < 0)
2366e91bba0SGirish Moodalbail 		goto fail;
2376e91bba0SGirish Moodalbail 
2386e91bba0SGirish Moodalbail 	/*
2396e91bba0SGirish Moodalbail 	 * When calculating the buffer size needed, add a small number
2406e91bba0SGirish Moodalbail 	 * of interfaces to those we counted.  We do this to capture
2416e91bba0SGirish Moodalbail 	 * the interface status of potential interfaces which may have
2426e91bba0SGirish Moodalbail 	 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
2436e91bba0SGirish Moodalbail 	 */
2446e91bba0SGirish Moodalbail 	bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq);
2456e91bba0SGirish Moodalbail 
2466e91bba0SGirish Moodalbail 	if ((tmp = realloc(*buf, bufsize)) == NULL)
2476e91bba0SGirish Moodalbail 		goto fail;
2486e91bba0SGirish Moodalbail 
2496e91bba0SGirish Moodalbail 	*buf = tmp;
2506e91bba0SGirish Moodalbail 	lifc.lifc_family = af;
2516e91bba0SGirish Moodalbail 	lifc.lifc_flags = lifc_flags;
2526e91bba0SGirish Moodalbail 	lifc.lifc_len = bufsize;
2536e91bba0SGirish Moodalbail 	lifc.lifc_buf = *buf;
2546e91bba0SGirish Moodalbail 	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
2556e91bba0SGirish Moodalbail 		goto fail;
2566e91bba0SGirish Moodalbail 
2576e91bba0SGirish Moodalbail 	*numifs = lifc.lifc_len / sizeof (struct lifreq);
2586e91bba0SGirish Moodalbail 	if (*numifs >= (lifn.lifn_count + 4)) {
2596e91bba0SGirish Moodalbail 		/*
2606e91bba0SGirish Moodalbail 		 * If every entry was filled, there are probably
2616e91bba0SGirish Moodalbail 		 * more interfaces than (lifn.lifn_count + 4).
2626e91bba0SGirish Moodalbail 		 * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to
2636e91bba0SGirish Moodalbail 		 * get all the interfaces.
2646e91bba0SGirish Moodalbail 		 */
2656e91bba0SGirish Moodalbail 		goto retry;
2666e91bba0SGirish Moodalbail 	}
2676e91bba0SGirish Moodalbail 	return (0);
2686e91bba0SGirish Moodalbail fail:
2696e91bba0SGirish Moodalbail 	free(*buf);
2706e91bba0SGirish Moodalbail 	*buf = NULL;
2716e91bba0SGirish Moodalbail 	return (-1);
2726e91bba0SGirish Moodalbail }
273