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 */
47int
48getifaddrs(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
69void
70freeifaddrs(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 */
91int
92getallifaddrs(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
114retry:
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);
204fail:
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 */
220int
221getallifs(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;
234retry:
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);
268fail:
269	free(*buf);
270	*buf = NULL;
271	return (-1);
272}
273