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