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