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 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
22 * Use is subject to license terms.
23 */
24
25#include <errno.h>
26#include <libinetutil.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <sys/socket.h>
32#include <sys/sockio.h>
33
34/*
35 * Create a list of the addresses on physical interface `ifname' with at least
36 * one of the flags in `set' set and all of the flags in `clear' clear.
37 * Return the number of items in the list, or -1 on failure.
38 */
39int
40ifaddrlistx(const char *ifname, uint64_t set, uint64_t clear,
41    ifaddrlistx_t **ifaddrsp)
42{
43	struct lifconf	lifc;
44	struct lifnum	lifn;
45	struct lifreq	*lifrp;
46	ifaddrlistx_t	*ifaddrp, *ifaddrs = NULL;
47	int		i, nlifr, naddr = 0;
48	char		*cp;
49	uint_t		flags;
50	int		s4, s6 = -1;
51	boolean_t	isv6;
52	int		save_errno;
53	struct sockaddr_storage addr;
54
55	(void) memset(&lifc, 0, sizeof (lifc));
56	flags = LIFC_NOXMIT | LIFC_ALLZONES | LIFC_TEMPORARY | LIFC_UNDER_IPMP;
57
58	/*
59	 * We need both IPv4 and IPv6 sockets to query both IPv4 and IPv6
60	 * interfaces below.
61	 */
62	if ((s4 = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ||
63	    (s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
64		goto fail;
65	}
66
67	/*
68	 * Get the number of network interfaces of type `family'.
69	 */
70	lifn.lifn_family = AF_UNSPEC;
71	lifn.lifn_flags = flags;
72again:
73	if (ioctl(s4, SIOCGLIFNUM, &lifn) == -1)
74		goto fail;
75
76	/*
77	 * Pad the interface count to detect when additional interfaces have
78	 * been configured between SIOCGLIFNUM and SIOCGLIFCONF.
79	 */
80	lifn.lifn_count += 4;
81
82	lifc.lifc_flags = flags;
83	lifc.lifc_family = AF_UNSPEC;
84	lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
85	if ((lifc.lifc_buf = realloc(lifc.lifc_buf, lifc.lifc_len)) == NULL)
86		goto fail;
87
88	if (ioctl(s4, SIOCGLIFCONF, &lifc) == -1)
89		goto fail;
90
91	/*
92	 * If every lifr_req slot is taken, then additional interfaces must
93	 * have been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
94	 * Recalculate to make sure we didn't miss any interfaces.
95	 */
96	nlifr = lifc.lifc_len / sizeof (struct lifreq);
97	if (nlifr >= lifn.lifn_count)
98		goto again;
99
100	/*
101	 * Populate the ifaddrlistx by querying each matching interface.  If a
102	 * query ioctl returns ENXIO, then the interface must have been
103	 * removed after the SIOCGLIFCONF completed -- so we just ignore it.
104	 */
105	for (lifrp = lifc.lifc_req, i = 0; i < nlifr; i++, lifrp++) {
106		if ((cp = strchr(lifrp->lifr_name, ':')) != NULL)
107			*cp = '\0';
108
109		if (strcmp(lifrp->lifr_name, ifname) != 0)
110			continue;
111
112		if (cp != NULL)
113			*cp = ':';
114
115		addr = lifrp->lifr_addr;
116		isv6 = addr.ss_family == AF_INET6;
117		if (ioctl(isv6 ? s6 : s4, SIOCGLIFFLAGS, lifrp) == -1) {
118			if (errno == ENXIO)
119				continue;
120			goto fail;
121		}
122
123		if (set != 0 && ((lifrp->lifr_flags & set) == 0) ||
124		    (lifrp->lifr_flags & clear) != 0)
125			continue;
126
127		/*
128		 * We've got a match; allocate a new record.
129		 */
130		if ((ifaddrp = malloc(sizeof (ifaddrlistx_t))) == NULL)
131			goto fail;
132
133		(void) strlcpy(ifaddrp->ia_name, lifrp->lifr_name, LIFNAMSIZ);
134		ifaddrp->ia_flags = lifrp->lifr_flags;
135		ifaddrp->ia_addr = addr;
136		ifaddrp->ia_next = ifaddrs;
137		ifaddrs = ifaddrp;
138		naddr++;
139	}
140
141	(void) close(s4);
142	(void) close(s6);
143	free(lifc.lifc_buf);
144	*ifaddrsp = ifaddrs;
145	return (naddr);
146fail:
147	save_errno = errno;
148	(void) close(s4);
149	(void) close(s6);
150	free(lifc.lifc_buf);
151	ifaddrlistx_free(ifaddrs);
152	errno = save_errno;
153	return (-1);
154}
155
156/*
157 * Free the provided ifaddrlistx_t.
158 */
159void
160ifaddrlistx_free(ifaddrlistx_t *ifaddrp)
161{
162	ifaddrlistx_t *next_ifaddrp;
163
164	for (; ifaddrp != NULL; ifaddrp = next_ifaddrp) {
165		next_ifaddrp = ifaddrp->ia_next;
166		free(ifaddrp);
167	}
168}
169