xref: /illumos-gate/usr/src/lib/libsocket/inet/getifaddrs.c (revision b1d7ec75953cd517f5b7c3d9cb427ff8ec5d7d07)
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  */
48 int
49 getifaddrs(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 
70 void
71 freeifaddrs(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  */
92 int
93 getallifaddrs(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 
115 retry:
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);
201 fail:
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  */
217 int
218 getallifs(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;
231 retry:
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);
265 fail:
266 	free(*buf);
267 	*buf = NULL;
268 	return (-1);
269 }
270