xref: /illumos-gate/usr/src/lib/libsocket/inet/getifaddrs.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2017 RackTop Systems.
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 	/*
107 	 * Initialize ifap to NULL so we can safely call freeifaddrs
108 	 * on it in case of error.
109 	 */
110 	if (ifap == NULL)
111 		return (EINVAL);
112 	*ifap = NULL;
113 
114 	if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
115 		return (-1);
116 	if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
117 		err = errno;
118 		close(sock4);
119 		errno = err;
120 		return (-1);
121 	}
122 
123 retry:
124 	/* Get all interfaces from SIOCGLIFCONF */
125 	ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED));
126 	if (ret != 0)
127 		goto fail;
128 
129 	/*
130 	 * Loop through the interfaces obtained from SIOCGLIFCOMF
131 	 * and retrieve the addresses, netmask and flags.
132 	 */
133 	prev = NULL;
134 	lifrp = buf;
135 	for (n = 0; n < numifs; n++, lifrp++) {
136 
137 		/* Prepare for the ioctl call */
138 		(void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
139 		    sizeof (lifrl.lifr_name));
140 		lifr_af = lifrp->lifr_addr.ss_family;
141 		if (af != AF_UNSPEC && lifr_af != af)
142 			continue;
143 
144 		s = (lifr_af == AF_INET ? sock4 : sock6);
145 
146 		if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
147 			goto fail;
148 		if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP))
149 			continue;
150 
151 		/*
152 		 * Allocate the current list node. Each node contains data
153 		 * for one ifaddrs structure.
154 		 */
155 		curr = calloc(1, sizeof (struct ifaddrs));
156 		if (curr == NULL)
157 			goto fail;
158 
159 		if (prev != NULL) {
160 			prev->ifa_next = curr;
161 		} else {
162 			/* First node in the linked list */
163 			*ifap = curr;
164 		}
165 		prev = curr;
166 
167 		curr->ifa_flags = lifrl.lifr_flags;
168 		if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL)
169 			goto fail;
170 
171 		curr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
172 		if (curr->ifa_addr == NULL)
173 			goto fail;
174 		(void) memcpy(curr->ifa_addr, &lifrp->lifr_addr,
175 		    sizeof (struct sockaddr_storage));
176 
177 		/* Get the netmask */
178 		if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0)
179 			goto fail;
180 		curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
181 		if (curr->ifa_netmask == NULL)
182 			goto fail;
183 		(void) memcpy(curr->ifa_netmask, &lifrl.lifr_addr,
184 		    sizeof (struct sockaddr_storage));
185 
186 		/* Get the destination for a pt-pt interface */
187 		if (curr->ifa_flags & IFF_POINTOPOINT) {
188 			if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0)
189 				goto fail;
190 			curr->ifa_dstaddr = malloc(
191 			    sizeof (struct sockaddr_storage));
192 			if (curr->ifa_dstaddr == NULL)
193 				goto fail;
194 			(void) memcpy(curr->ifa_dstaddr, &lifrl.lifr_addr,
195 			    sizeof (struct sockaddr_storage));
196 		} else if (curr->ifa_flags & IFF_BROADCAST) {
197 			if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0)
198 				goto fail;
199 			curr->ifa_broadaddr = malloc(
200 			    sizeof (struct sockaddr_storage));
201 			if (curr->ifa_broadaddr == NULL)
202 				goto fail;
203 			(void) memcpy(curr->ifa_broadaddr, &lifrl.lifr_addr,
204 			    sizeof (struct sockaddr_storage));
205 		}
206 
207 	}
208 	free(buf);
209 	close(sock4);
210 	close(sock6);
211 	return (0);
212 fail:
213 	err = errno;
214 	free(buf);
215 	freeifaddrs(*ifap);
216 	*ifap = NULL;
217 	if (err == ENXIO)
218 		goto retry;
219 	close(sock4);
220 	close(sock6);
221 	errno = err;
222 	return (-1);
223 }
224 
225 /*
226  * Do a SIOCGLIFCONF and store all the interfaces in `buf'.
227  */
228 int
229 getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs,
230     int64_t lifc_flags)
231 {
232 	struct lifnum lifn;
233 	struct lifconf lifc;
234 	size_t bufsize;
235 	char *tmp;
236 	caddr_t *buf = (caddr_t *)lifr;
237 
238 	lifn.lifn_family = af;
239 	lifn.lifn_flags = lifc_flags;
240 
241 	*buf = NULL;
242 retry:
243 	if (ioctl(s, SIOCGLIFNUM, &lifn) < 0)
244 		goto fail;
245 
246 	/*
247 	 * When calculating the buffer size needed, add a small number
248 	 * of interfaces to those we counted.  We do this to capture
249 	 * the interface status of potential interfaces which may have
250 	 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
251 	 */
252 	bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq);
253 
254 	if ((tmp = realloc(*buf, bufsize)) == NULL)
255 		goto fail;
256 
257 	*buf = tmp;
258 	lifc.lifc_family = af;
259 	lifc.lifc_flags = lifc_flags;
260 	lifc.lifc_len = bufsize;
261 	lifc.lifc_buf = *buf;
262 	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
263 		goto fail;
264 
265 	*numifs = lifc.lifc_len / sizeof (struct lifreq);
266 	if (*numifs >= (lifn.lifn_count + 4)) {
267 		/*
268 		 * If every entry was filled, there are probably
269 		 * more interfaces than (lifn.lifn_count + 4).
270 		 * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to
271 		 * get all the interfaces.
272 		 */
273 		goto retry;
274 	}
275 	return (0);
276 fail:
277 	free(*buf);
278 	*buf = NULL;
279 	return (-1);
280 }
281