xref: /openbsd-src/lib/libc/net/getifaddrs.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: getifaddrs.c,v 1.10 2008/11/24 20:08:49 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 1995, 1999
5  *	Berkeley Software Design, Inc.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  *	BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp
26  */
27 
28 #include <sys/types.h>
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
31 #include <net/if.h>
32 #include <sys/param.h>
33 #include <net/route.h>
34 #include <sys/sysctl.h>
35 #include <net/if_dl.h>
36 
37 #include <errno.h>
38 #include <ifaddrs.h>
39 #include <stddef.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #define	SALIGN	(sizeof(long) - 1)
45 #define	SA_RLEN(sa)	((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
46 
47 int
48 getifaddrs(struct ifaddrs **pif)
49 {
50 	int icnt = 1;
51 	int dcnt = 0;
52 	int ncnt = 0;
53 	int mib[6];
54 	size_t needed;
55 	char *buf;
56 	char *next;
57 	struct ifaddrs *cif = 0;
58 	char *p, *p0;
59 	struct rt_msghdr *rtm;
60 	struct if_msghdr *ifm;
61 	struct ifa_msghdr *ifam;
62 	struct sockaddr_dl *dl;
63 	struct sockaddr *sa;
64 	u_short index = 0;
65 	size_t len, alen, dlen;
66 	struct ifaddrs *ifa, *ift;
67 	int i;
68 	char *data;
69 	char *names;
70 
71 	mib[0] = CTL_NET;
72 	mib[1] = PF_ROUTE;
73 	mib[2] = 0;             /* protocol */
74 	mib[3] = 0;             /* wildcard address family */
75 	mib[4] = NET_RT_IFLIST;
76 	mib[5] = 0;             /* no flags */
77 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
78 		return (-1);
79 	if ((buf = malloc(needed)) == NULL)
80 		return (-1);
81 	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
82 		free(buf);
83 		return (-1);
84 	}
85 
86 	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
87 		rtm = (struct rt_msghdr *)next;
88 		if (rtm->rtm_version != RTM_VERSION)
89 			continue;
90 		switch (rtm->rtm_type) {
91 		case RTM_IFINFO:
92 			ifm = (struct if_msghdr *)rtm;
93 			if (ifm->ifm_addrs & RTA_IFP) {
94 				index = ifm->ifm_index;
95 				++icnt;
96 				dl = (struct sockaddr_dl *)(next +
97 				    rtm->rtm_hdrlen);
98 				dcnt += SA_RLEN((struct sockaddr *)dl) +
99 					ALIGNBYTES;
100 				dcnt += sizeof(ifm->ifm_data);
101 				ncnt += dl->sdl_nlen + 1;
102 			} else
103 				index = 0;
104 			break;
105 
106 		case RTM_NEWADDR:
107 			ifam = (struct ifa_msghdr *)rtm;
108 			if (index && ifam->ifam_index != index)
109 				abort();	/* XXX abort illegal in library */
110 
111 #define	RTA_MASKS	(RTA_NETMASK | RTA_IFA | RTA_BRD)
112 			if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
113 				break;
114 			p = next + rtm->rtm_hdrlen;
115 			++icnt;
116 			/* Scan to look for length of address */
117 			alen = 0;
118 			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
119 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
120 				    == 0)
121 					continue;
122 				sa = (struct sockaddr *)p;
123 				len = SA_RLEN(sa);
124 				if (i == RTAX_IFA) {
125 					alen = len;
126 					break;
127 				}
128 				p += len;
129 			}
130 			for (p = p0, i = 0; i < RTAX_MAX; i++) {
131 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
132 				    == 0)
133 					continue;
134 				sa = (struct sockaddr *)p;
135 				len = SA_RLEN(sa);
136 				if (i == RTAX_NETMASK && sa->sa_len == 0)
137 					dcnt += alen;
138 				else
139 					dcnt += len;
140 				p += len;
141 			}
142 			break;
143 		}
144 	}
145 
146 	if (icnt + dcnt + ncnt == 1) {
147 		*pif = NULL;
148 		free(buf);
149 		return (0);
150 	}
151 	data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
152 	if (data == NULL) {
153 		free(buf);
154 		return(-1);
155 	}
156 
157 	ifa = (struct ifaddrs *)data;
158 	data += sizeof(struct ifaddrs) * icnt;
159 	names = data + dcnt;
160 
161 	memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
162 	ift = ifa;
163 
164 	index = 0;
165 	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
166 		rtm = (struct rt_msghdr *)next;
167 		if (rtm->rtm_version != RTM_VERSION)
168 			continue;
169 		switch (rtm->rtm_type) {
170 		case RTM_IFINFO:
171 			ifm = (struct if_msghdr *)rtm;
172 			if (ifm->ifm_addrs & RTA_IFP) {
173 				index = ifm->ifm_index;
174 				dl = (struct sockaddr_dl *)(next +
175 				    rtm->rtm_hdrlen);
176 
177 				cif = ift;
178 				ift->ifa_name = names;
179 				ift->ifa_flags = (int)ifm->ifm_flags;
180 				memcpy(names, dl->sdl_data, dl->sdl_nlen);
181 				names[dl->sdl_nlen] = 0;
182 				names += dl->sdl_nlen + 1;
183 
184 				ift->ifa_addr = (struct sockaddr *)data;
185 				memcpy(data, dl,
186 				    ((struct sockaddr *)dl)->sa_len);
187 				data += SA_RLEN((struct sockaddr *)dl);
188 
189 				/* ifm_data needs to be aligned */
190 				ift->ifa_data = data = (void *)ALIGN(data);
191 				dlen = rtm->rtm_hdrlen -
192 				    offsetof(struct if_msghdr, ifm_data);
193 				if (dlen > sizeof(ifm->ifm_data))
194 					dlen = sizeof(ifm->ifm_data);
195 				memcpy(data, &ifm->ifm_data, dlen);
196  				data += sizeof(ifm->ifm_data);
197 
198 				ift = (ift->ifa_next = ift + 1);
199 			} else
200 				index = 0;
201 			break;
202 
203 		case RTM_NEWADDR:
204 			ifam = (struct ifa_msghdr *)rtm;
205 			if (index && ifam->ifam_index != index)
206 				abort();	/* XXX abort illegal in library */
207 
208 			if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
209 				break;
210 			ift->ifa_name = cif->ifa_name;
211 			ift->ifa_flags = cif->ifa_flags;
212 			ift->ifa_data = NULL;
213 			p = next + rtm->rtm_hdrlen;
214 			/* Scan to look for length of address */
215 			alen = 0;
216 			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
217 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
218 				    == 0)
219 					continue;
220 				sa = (struct sockaddr *)p;
221 				len = SA_RLEN(sa);
222 				if (i == RTAX_IFA) {
223 					alen = len;
224 					break;
225 				}
226 				p += len;
227 			}
228 			for (p = p0, i = 0; i < RTAX_MAX; i++) {
229 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
230 				    == 0)
231 					continue;
232 				sa = (struct sockaddr *)p;
233 				len = SA_RLEN(sa);
234 				switch (i) {
235 				case RTAX_IFA:
236 					ift->ifa_addr = (struct sockaddr *)data;
237 					memcpy(data, p, len);
238 					data += len;
239 					break;
240 
241 				case RTAX_NETMASK:
242 					ift->ifa_netmask =
243 					    (struct sockaddr *)data;
244 					if (sa->sa_len == 0) {
245 						memset(data, 0, alen);
246 						data += alen;
247 						break;
248 					}
249 					memcpy(data, p, len);
250 					data += len;
251 					break;
252 
253 				case RTAX_BRD:
254 					ift->ifa_broadaddr =
255 					    (struct sockaddr *)data;
256 					memcpy(data, p, len);
257 					data += len;
258 					break;
259 				}
260 				p += len;
261 			}
262 
263 
264 			ift = (ift->ifa_next = ift + 1);
265 			break;
266 		}
267 	}
268 
269 	free(buf);
270 	if (--ift >= ifa) {
271 		ift->ifa_next = NULL;
272 		*pif = ifa;
273 	} else {
274 		*pif = NULL;
275 		free(ifa);
276 	}
277 	return (0);
278 }
279 
280 void
281 freeifaddrs(struct ifaddrs *ifp)
282 {
283 	free(ifp);
284 }
285