xref: /illumos-gate/usr/src/lib/libinetutil/common/ifaddrlist.c (revision a07094369b21309434206d9b3601d162693466fc)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1997
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the Computer Systems
21  *	Engineering Group at Lawrence Berkeley Laboratory.
22  * 4. Neither the name of the University nor of the Laboratory may be used
23  *    to endorse or promote products derived from this software without
24  *    specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * @(#) $Header: ifaddrlist.c,v 1.2 97/04/22 13:31:05 leres Exp $ (LBL)
39  */
40 
41 #pragma ident	"%Z%%M%	%I%	%E% SMI"
42 
43 #include <alloca.h>
44 #include <errno.h>
45 #include <libinetutil.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <sys/socket.h>
51 #include <sys/sockio.h>
52 
53 /*
54  * See <libinetutil.h> for a description of the programming interface.
55  */
56 int
57 ifaddrlist(struct ifaddrlist **ipaddrp, int family, char *errbuf)
58 {
59 	struct ifaddrlist	*ifaddrlist, *al;
60 	struct sockaddr_in	*sin;
61 	struct sockaddr_in6	*sin6;
62 	struct lifconf		lifc;
63 	struct lifnum		lifn;
64 	struct lifreq		*lifrp;
65 	int			i, count, nlifr;
66 	int			fd;
67 	const char		*iocstr;
68 
69 	if (family != AF_INET && family != AF_INET6) {
70 		(void) strlcpy(errbuf, "invalid address family", ERRBUFSIZE);
71 		return (-1);
72 	}
73 
74 	fd = socket(family, SOCK_DGRAM, 0);
75 	if (fd == -1) {
76 		(void) snprintf(errbuf, ERRBUFSIZE, "socket: %s",
77 		    strerror(errno));
78 		return (-1);
79 	}
80 
81 	/*
82 	 * Get the number of network interfaces of type `family'.
83 	 */
84 	lifn.lifn_family = family;
85 	lifn.lifn_flags = 0;
86 again:
87 	if (ioctl(fd, SIOCGLIFNUM, &lifn) == -1) {
88 		(void) snprintf(errbuf, ERRBUFSIZE, "SIOCGLIFNUM: %s",
89 		    strerror(errno));
90 		(void) close(fd);
91 		return (-1);
92 	}
93 
94 	/*
95 	 * Pad the interface count to detect when additional interfaces have
96 	 * been configured between SIOCGLIFNUM and SIOCGLIFCONF.
97 	 */
98 	lifn.lifn_count += 4;
99 
100 	lifc.lifc_family = family;
101 	lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
102 	lifc.lifc_buf = alloca(lifc.lifc_len);
103 	lifc.lifc_flags = 0;
104 
105 	if (ioctl(fd, SIOCGLIFCONF, &lifc) == -1) {
106 		(void) snprintf(errbuf, ERRBUFSIZE, "SIOCGLIFCONF: %s",
107 		    strerror(errno));
108 		(void) close(fd);
109 		return (-1);
110 	}
111 
112 	/*
113 	 * If every lifr_req slot is taken, then additional interfaces must
114 	 * have been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
115 	 * Recalculate to make sure we didn't miss any interfaces.
116 	 */
117 	nlifr = lifc.lifc_len / sizeof (struct lifreq);
118 	if (nlifr >= lifn.lifn_count)
119 		goto again;
120 
121 	/*
122 	 * Allocate the address list to return.
123 	 */
124 	ifaddrlist = calloc(nlifr, sizeof (struct ifaddrlist));
125 	if (ifaddrlist == NULL) {
126 		(void) snprintf(errbuf, ERRBUFSIZE, "calloc: %s",
127 		    strerror(errno));
128 		(void) close(fd);
129 		return (-1);
130 	}
131 
132 	/*
133 	 * Populate the address list by querying each underlying interface.
134 	 * If a query ioctl returns ENXIO, then the interface must have been
135 	 * removed after the SIOCGLIFCONF completed -- so we just ignore it.
136 	 */
137 	al = ifaddrlist;
138 	count = 0;
139 	for (lifrp = lifc.lifc_req, i = 0; i < nlifr; i++, lifrp++) {
140 		(void) strlcpy(al->device, lifrp->lifr_name, LIFNAMSIZ);
141 
142 		if (ioctl(fd, SIOCGLIFFLAGS, lifrp) == -1) {
143 			if (errno == ENXIO)
144 				continue;
145 			iocstr = "SIOCGLIFFLAGS";
146 			goto fail;
147 		}
148 		al->flags = lifrp->lifr_flags;
149 
150 		if (ioctl(fd, SIOCGLIFINDEX, lifrp) == -1) {
151 			if (errno == ENXIO)
152 				continue;
153 			iocstr = "SIOCGLIFINDEX";
154 			goto fail;
155 		}
156 		al->index = lifrp->lifr_index;
157 
158 		if (ioctl(fd, SIOCGLIFADDR, lifrp) == -1) {
159 			if (errno == ENXIO)
160 				continue;
161 			iocstr = "SIOCGLIFADDR";
162 			goto fail;
163 		}
164 
165 		if (family == AF_INET) {
166 			sin = (struct sockaddr_in *)&lifrp->lifr_addr;
167 			al->addr.addr = sin->sin_addr;
168 		} else {
169 			sin6 = (struct sockaddr_in6 *)&lifrp->lifr_addr;
170 			al->addr.addr6 = sin6->sin6_addr;
171 		}
172 		al++;
173 		count++;
174 	}
175 
176 	(void) close(fd);
177 	if (count == 0) {
178 		free(ifaddrlist);
179 		*ipaddrp = NULL;
180 		return (0);
181 	}
182 
183 	*ipaddrp = ifaddrlist;
184 	return (count);
185 fail:
186 	(void) snprintf(errbuf, ERRBUFSIZE, "%s: %s: %s", iocstr, al->device,
187 	    strerror(errno));
188 
189 	free(ifaddrlist);
190 	(void) close(fd);
191 	return (-1);
192 }
193