xref: /titanic_52/usr/src/lib/libsocket/inet/getifaddrs.c (revision 6e91bba0d6c6bdabbba62cefae583715a4a58e2a)
1*6e91bba0SGirish Moodalbail /*
2*6e91bba0SGirish Moodalbail  * CDDL HEADER START
3*6e91bba0SGirish Moodalbail  *
4*6e91bba0SGirish Moodalbail  * The contents of this file are subject to the terms of the
5*6e91bba0SGirish Moodalbail  * Common Development and Distribution License (the "License").
6*6e91bba0SGirish Moodalbail  * You may not use this file except in compliance with the License.
7*6e91bba0SGirish Moodalbail  *
8*6e91bba0SGirish Moodalbail  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*6e91bba0SGirish Moodalbail  * or http://www.opensolaris.org/os/licensing.
10*6e91bba0SGirish Moodalbail  * See the License for the specific language governing permissions
11*6e91bba0SGirish Moodalbail  * and limitations under the License.
12*6e91bba0SGirish Moodalbail  *
13*6e91bba0SGirish Moodalbail  * When distributing Covered Code, include this CDDL HEADER in each
14*6e91bba0SGirish Moodalbail  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*6e91bba0SGirish Moodalbail  * If applicable, add the following below this CDDL HEADER, with the
16*6e91bba0SGirish Moodalbail  * fields enclosed by brackets "[]" replaced with your own identifying
17*6e91bba0SGirish Moodalbail  * information: Portions Copyright [yyyy] [name of copyright owner]
18*6e91bba0SGirish Moodalbail  *
19*6e91bba0SGirish Moodalbail  * CDDL HEADER END
20*6e91bba0SGirish Moodalbail  */
21*6e91bba0SGirish Moodalbail 
22*6e91bba0SGirish Moodalbail /*
23*6e91bba0SGirish Moodalbail  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24*6e91bba0SGirish Moodalbail  * Use is subject to license terms.
25*6e91bba0SGirish Moodalbail  */
26*6e91bba0SGirish Moodalbail 
27*6e91bba0SGirish Moodalbail #include <netdb.h>
28*6e91bba0SGirish Moodalbail #include <nss_dbdefs.h>
29*6e91bba0SGirish Moodalbail #include <netinet/in.h>
30*6e91bba0SGirish Moodalbail #include <sys/socket.h>
31*6e91bba0SGirish Moodalbail #include <string.h>
32*6e91bba0SGirish Moodalbail #include <stdio.h>
33*6e91bba0SGirish Moodalbail #include <sys/sockio.h>
34*6e91bba0SGirish Moodalbail #include <sys/types.h>
35*6e91bba0SGirish Moodalbail #include <stdlib.h>
36*6e91bba0SGirish Moodalbail #include <net/if.h>
37*6e91bba0SGirish Moodalbail #include <ifaddrs.h>
38*6e91bba0SGirish Moodalbail #include <libsocket_priv.h>
39*6e91bba0SGirish Moodalbail 
40*6e91bba0SGirish Moodalbail /*
41*6e91bba0SGirish Moodalbail  * Create a linked list of `struct ifaddrs' structures, one for each
42*6e91bba0SGirish Moodalbail  * address that is UP. If successful, store the list in *ifap and
43*6e91bba0SGirish Moodalbail  * return 0.  On errors, return -1 and set `errno'.
44*6e91bba0SGirish Moodalbail  *
45*6e91bba0SGirish Moodalbail  * The storage returned in *ifap is allocated dynamically and can
46*6e91bba0SGirish Moodalbail  * only be properly freed by passing it to `freeifaddrs'.
47*6e91bba0SGirish Moodalbail  */
48*6e91bba0SGirish Moodalbail int
49*6e91bba0SGirish Moodalbail getifaddrs(struct ifaddrs **ifap)
50*6e91bba0SGirish Moodalbail {
51*6e91bba0SGirish Moodalbail 	int		err;
52*6e91bba0SGirish Moodalbail 	char		*cp;
53*6e91bba0SGirish Moodalbail 	struct ifaddrs	*curr;
54*6e91bba0SGirish Moodalbail 
55*6e91bba0SGirish Moodalbail 	if (ifap == NULL) {
56*6e91bba0SGirish Moodalbail 		errno = EINVAL;
57*6e91bba0SGirish Moodalbail 		return (-1);
58*6e91bba0SGirish Moodalbail 	}
59*6e91bba0SGirish Moodalbail 	*ifap = NULL;
60*6e91bba0SGirish Moodalbail 	err = getallifaddrs(AF_UNSPEC, ifap, LIFC_ENABLED);
61*6e91bba0SGirish Moodalbail 	if (err == 0) {
62*6e91bba0SGirish Moodalbail 		for (curr = *ifap; curr != NULL; curr = curr->ifa_next) {
63*6e91bba0SGirish Moodalbail 			if ((cp = strchr(curr->ifa_name, ':')) != NULL)
64*6e91bba0SGirish Moodalbail 				*cp = '\0';
65*6e91bba0SGirish Moodalbail 		}
66*6e91bba0SGirish Moodalbail 	}
67*6e91bba0SGirish Moodalbail 	return (err);
68*6e91bba0SGirish Moodalbail }
69*6e91bba0SGirish Moodalbail 
70*6e91bba0SGirish Moodalbail void
71*6e91bba0SGirish Moodalbail freeifaddrs(struct ifaddrs *ifa)
72*6e91bba0SGirish Moodalbail {
73*6e91bba0SGirish Moodalbail 	struct ifaddrs *curr;
74*6e91bba0SGirish Moodalbail 
75*6e91bba0SGirish Moodalbail 	while (ifa != NULL) {
76*6e91bba0SGirish Moodalbail 		curr = ifa;
77*6e91bba0SGirish Moodalbail 		ifa = ifa->ifa_next;
78*6e91bba0SGirish Moodalbail 		free(curr->ifa_name);
79*6e91bba0SGirish Moodalbail 		free(curr->ifa_addr);
80*6e91bba0SGirish Moodalbail 		free(curr->ifa_netmask);
81*6e91bba0SGirish Moodalbail 		free(curr->ifa_dstaddr);
82*6e91bba0SGirish Moodalbail 		free(curr);
83*6e91bba0SGirish Moodalbail 	}
84*6e91bba0SGirish Moodalbail }
85*6e91bba0SGirish Moodalbail 
86*6e91bba0SGirish Moodalbail /*
87*6e91bba0SGirish Moodalbail  * Returns all addresses configured on the system. If flags contain
88*6e91bba0SGirish Moodalbail  * LIFC_ENABLED, only the addresses that are UP are returned.
89*6e91bba0SGirish Moodalbail  * Address list that is returned by this function must be freed
90*6e91bba0SGirish Moodalbail  * using freeifaddrs().
91*6e91bba0SGirish Moodalbail  */
92*6e91bba0SGirish Moodalbail int
93*6e91bba0SGirish Moodalbail getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags)
94*6e91bba0SGirish Moodalbail {
95*6e91bba0SGirish Moodalbail 	struct lifreq *buf = NULL;
96*6e91bba0SGirish Moodalbail 	struct lifreq *lifrp;
97*6e91bba0SGirish Moodalbail 	struct lifreq lifrl;
98*6e91bba0SGirish Moodalbail 	int ret;
99*6e91bba0SGirish Moodalbail 	int s, n, numifs;
100*6e91bba0SGirish Moodalbail 	struct ifaddrs *curr, *prev;
101*6e91bba0SGirish Moodalbail 	sa_family_t lifr_af;
102*6e91bba0SGirish Moodalbail 	int sock4;
103*6e91bba0SGirish Moodalbail 	int sock6;
104*6e91bba0SGirish Moodalbail 	int err;
105*6e91bba0SGirish Moodalbail 
106*6e91bba0SGirish Moodalbail 	if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
107*6e91bba0SGirish Moodalbail 		return (-1);
108*6e91bba0SGirish Moodalbail 	if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
109*6e91bba0SGirish Moodalbail 		err = errno;
110*6e91bba0SGirish Moodalbail 		close(sock4);
111*6e91bba0SGirish Moodalbail 		errno = err;
112*6e91bba0SGirish Moodalbail 		return (-1);
113*6e91bba0SGirish Moodalbail 	}
114*6e91bba0SGirish Moodalbail 
115*6e91bba0SGirish Moodalbail retry:
116*6e91bba0SGirish Moodalbail 	/* Get all interfaces from SIOCGLIFCONF */
117*6e91bba0SGirish Moodalbail 	ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED));
118*6e91bba0SGirish Moodalbail 	if (ret != 0)
119*6e91bba0SGirish Moodalbail 		goto fail;
120*6e91bba0SGirish Moodalbail 
121*6e91bba0SGirish Moodalbail 	/*
122*6e91bba0SGirish Moodalbail 	 * Loop through the interfaces obtained from SIOCGLIFCOMF
123*6e91bba0SGirish Moodalbail 	 * and retrieve the addresses, netmask and flags.
124*6e91bba0SGirish Moodalbail 	 */
125*6e91bba0SGirish Moodalbail 	prev = NULL;
126*6e91bba0SGirish Moodalbail 	lifrp = buf;
127*6e91bba0SGirish Moodalbail 	*ifap = NULL;
128*6e91bba0SGirish Moodalbail 	for (n = 0; n < numifs; n++, lifrp++) {
129*6e91bba0SGirish Moodalbail 
130*6e91bba0SGirish Moodalbail 		/* Prepare for the ioctl call */
131*6e91bba0SGirish Moodalbail 		(void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
132*6e91bba0SGirish Moodalbail 		    sizeof (lifrl.lifr_name));
133*6e91bba0SGirish Moodalbail 		lifr_af = lifrp->lifr_addr.ss_family;
134*6e91bba0SGirish Moodalbail 		if (af != AF_UNSPEC && lifr_af != af)
135*6e91bba0SGirish Moodalbail 			continue;
136*6e91bba0SGirish Moodalbail 
137*6e91bba0SGirish Moodalbail 		s = (lifr_af == AF_INET ? sock4 : sock6);
138*6e91bba0SGirish Moodalbail 
139*6e91bba0SGirish Moodalbail 		if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
140*6e91bba0SGirish Moodalbail 			goto fail;
141*6e91bba0SGirish Moodalbail 		if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP))
142*6e91bba0SGirish Moodalbail 			continue;
143*6e91bba0SGirish Moodalbail 
144*6e91bba0SGirish Moodalbail 		/*
145*6e91bba0SGirish Moodalbail 		 * Allocate the current list node. Each node contains data
146*6e91bba0SGirish Moodalbail 		 * for one ifaddrs structure.
147*6e91bba0SGirish Moodalbail 		 */
148*6e91bba0SGirish Moodalbail 		curr = calloc(1, sizeof (struct ifaddrs));
149*6e91bba0SGirish Moodalbail 		if (curr == NULL)
150*6e91bba0SGirish Moodalbail 			goto fail;
151*6e91bba0SGirish Moodalbail 
152*6e91bba0SGirish Moodalbail 		if (prev != NULL) {
153*6e91bba0SGirish Moodalbail 			prev->ifa_next = curr;
154*6e91bba0SGirish Moodalbail 		} else {
155*6e91bba0SGirish Moodalbail 			/* First node in the linked list */
156*6e91bba0SGirish Moodalbail 			*ifap = curr;
157*6e91bba0SGirish Moodalbail 		}
158*6e91bba0SGirish Moodalbail 		prev = curr;
159*6e91bba0SGirish Moodalbail 
160*6e91bba0SGirish Moodalbail 		curr->ifa_flags = lifrl.lifr_flags;
161*6e91bba0SGirish Moodalbail 		if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL)
162*6e91bba0SGirish Moodalbail 			goto fail;
163*6e91bba0SGirish Moodalbail 
164*6e91bba0SGirish Moodalbail 		curr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
165*6e91bba0SGirish Moodalbail 		if (curr->ifa_addr == NULL)
166*6e91bba0SGirish Moodalbail 			goto fail;
167*6e91bba0SGirish Moodalbail 		*curr->ifa_addr = lifrp->lifr_addr;
168*6e91bba0SGirish Moodalbail 
169*6e91bba0SGirish Moodalbail 		/* Get the netmask */
170*6e91bba0SGirish Moodalbail 		if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0)
171*6e91bba0SGirish Moodalbail 			goto fail;
172*6e91bba0SGirish Moodalbail 		curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
173*6e91bba0SGirish Moodalbail 		if (curr->ifa_netmask == NULL)
174*6e91bba0SGirish Moodalbail 			goto fail;
175*6e91bba0SGirish Moodalbail 		*curr->ifa_netmask = lifrl.lifr_addr;
176*6e91bba0SGirish Moodalbail 
177*6e91bba0SGirish Moodalbail 		/* Get the destination for a pt-pt interface */
178*6e91bba0SGirish Moodalbail 		if (curr->ifa_flags & IFF_POINTOPOINT) {
179*6e91bba0SGirish Moodalbail 			if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0)
180*6e91bba0SGirish Moodalbail 				goto fail;
181*6e91bba0SGirish Moodalbail 			curr->ifa_dstaddr = malloc(
182*6e91bba0SGirish Moodalbail 			    sizeof (struct sockaddr_storage));
183*6e91bba0SGirish Moodalbail 			if (curr->ifa_dstaddr == NULL)
184*6e91bba0SGirish Moodalbail 				goto fail;
185*6e91bba0SGirish Moodalbail 			*curr->ifa_dstaddr = lifrl.lifr_addr;
186*6e91bba0SGirish Moodalbail 		} else if (curr->ifa_flags & IFF_BROADCAST) {
187*6e91bba0SGirish Moodalbail 			if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0)
188*6e91bba0SGirish Moodalbail 				goto fail;
189*6e91bba0SGirish Moodalbail 			curr->ifa_broadaddr = malloc(
190*6e91bba0SGirish Moodalbail 			    sizeof (struct sockaddr_storage));
191*6e91bba0SGirish Moodalbail 			if (curr->ifa_broadaddr == NULL)
192*6e91bba0SGirish Moodalbail 				goto fail;
193*6e91bba0SGirish Moodalbail 			*curr->ifa_broadaddr = lifrl.lifr_addr;
194*6e91bba0SGirish Moodalbail 		}
195*6e91bba0SGirish Moodalbail 
196*6e91bba0SGirish Moodalbail 	}
197*6e91bba0SGirish Moodalbail 	free(buf);
198*6e91bba0SGirish Moodalbail 	close(sock4);
199*6e91bba0SGirish Moodalbail 	close(sock6);
200*6e91bba0SGirish Moodalbail 	return (0);
201*6e91bba0SGirish Moodalbail fail:
202*6e91bba0SGirish Moodalbail 	err = errno;
203*6e91bba0SGirish Moodalbail 	free(buf);
204*6e91bba0SGirish Moodalbail 	freeifaddrs(*ifap);
205*6e91bba0SGirish Moodalbail 	*ifap = NULL;
206*6e91bba0SGirish Moodalbail 	if (err == ENXIO)
207*6e91bba0SGirish Moodalbail 		goto retry;
208*6e91bba0SGirish Moodalbail 	close(sock4);
209*6e91bba0SGirish Moodalbail 	close(sock6);
210*6e91bba0SGirish Moodalbail 	errno = err;
211*6e91bba0SGirish Moodalbail 	return (-1);
212*6e91bba0SGirish Moodalbail }
213*6e91bba0SGirish Moodalbail 
214*6e91bba0SGirish Moodalbail /*
215*6e91bba0SGirish Moodalbail  * Do a SIOCGLIFCONF and store all the interfaces in `buf'.
216*6e91bba0SGirish Moodalbail  */
217*6e91bba0SGirish Moodalbail int
218*6e91bba0SGirish Moodalbail getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs,
219*6e91bba0SGirish Moodalbail     int64_t lifc_flags)
220*6e91bba0SGirish Moodalbail {
221*6e91bba0SGirish Moodalbail 	struct lifnum lifn;
222*6e91bba0SGirish Moodalbail 	struct lifconf lifc;
223*6e91bba0SGirish Moodalbail 	size_t bufsize;
224*6e91bba0SGirish Moodalbail 	char *tmp;
225*6e91bba0SGirish Moodalbail 	caddr_t *buf = (caddr_t *)lifr;
226*6e91bba0SGirish Moodalbail 
227*6e91bba0SGirish Moodalbail 	lifn.lifn_family = af;
228*6e91bba0SGirish Moodalbail 	lifn.lifn_flags = lifc_flags;
229*6e91bba0SGirish Moodalbail 
230*6e91bba0SGirish Moodalbail 	*buf = NULL;
231*6e91bba0SGirish Moodalbail retry:
232*6e91bba0SGirish Moodalbail 	if (ioctl(s, SIOCGLIFNUM, &lifn) < 0)
233*6e91bba0SGirish Moodalbail 		goto fail;
234*6e91bba0SGirish Moodalbail 
235*6e91bba0SGirish Moodalbail 	/*
236*6e91bba0SGirish Moodalbail 	 * When calculating the buffer size needed, add a small number
237*6e91bba0SGirish Moodalbail 	 * of interfaces to those we counted.  We do this to capture
238*6e91bba0SGirish Moodalbail 	 * the interface status of potential interfaces which may have
239*6e91bba0SGirish Moodalbail 	 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
240*6e91bba0SGirish Moodalbail 	 */
241*6e91bba0SGirish Moodalbail 	bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq);
242*6e91bba0SGirish Moodalbail 
243*6e91bba0SGirish Moodalbail 	if ((tmp = realloc(*buf, bufsize)) == NULL)
244*6e91bba0SGirish Moodalbail 		goto fail;
245*6e91bba0SGirish Moodalbail 
246*6e91bba0SGirish Moodalbail 	*buf = tmp;
247*6e91bba0SGirish Moodalbail 	lifc.lifc_family = af;
248*6e91bba0SGirish Moodalbail 	lifc.lifc_flags = lifc_flags;
249*6e91bba0SGirish Moodalbail 	lifc.lifc_len = bufsize;
250*6e91bba0SGirish Moodalbail 	lifc.lifc_buf = *buf;
251*6e91bba0SGirish Moodalbail 	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
252*6e91bba0SGirish Moodalbail 		goto fail;
253*6e91bba0SGirish Moodalbail 
254*6e91bba0SGirish Moodalbail 	*numifs = lifc.lifc_len / sizeof (struct lifreq);
255*6e91bba0SGirish Moodalbail 	if (*numifs >= (lifn.lifn_count + 4)) {
256*6e91bba0SGirish Moodalbail 		/*
257*6e91bba0SGirish Moodalbail 		 * If every entry was filled, there are probably
258*6e91bba0SGirish Moodalbail 		 * more interfaces than (lifn.lifn_count + 4).
259*6e91bba0SGirish Moodalbail 		 * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to
260*6e91bba0SGirish Moodalbail 		 * get all the interfaces.
261*6e91bba0SGirish Moodalbail 		 */
262*6e91bba0SGirish Moodalbail 		goto retry;
263*6e91bba0SGirish Moodalbail 	}
264*6e91bba0SGirish Moodalbail 	return (0);
265*6e91bba0SGirish Moodalbail fail:
266*6e91bba0SGirish Moodalbail 	free(*buf);
267*6e91bba0SGirish Moodalbail 	*buf = NULL;
268*6e91bba0SGirish Moodalbail 	return (-1);
269*6e91bba0SGirish Moodalbail }
270