getifaddrs.c revision 1.11.20.3 1 /* $NetBSD: getifaddrs.c,v 1.11.20.3 2010/05/13 05:38:16 matt 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/cdefs.h>
29 #if defined(LIBC_SCCS) && !defined(lint)
30 __RCSID("$NetBSD: getifaddrs.c,v 1.11.20.3 2010/05/13 05:38:16 matt Exp $");
31 #endif /* LIBC_SCCS and not lint */
32
33 #include "namespace.h"
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
37 #include <net/if.h>
38 #include <sys/param.h>
39 #include <net/route.h>
40 #include <sys/sysctl.h>
41 #include <net/if_dl.h>
42
43 #include <assert.h>
44 #include <errno.h>
45 #include <ifaddrs.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 #ifdef RTDEBUG
50 #include <stdio.h>
51 #endif
52
53 #ifdef __weak_alias
54 __weak_alias(getifaddrs,_getifaddrs)
55 __weak_alias(freeifaddrs,_freeifaddrs)
56 #endif
57
58 #define SA_RLEN(sa) RT_ROUNDUP((sa)->sa_len)
59
60 int
61 getifaddrs(struct ifaddrs **pif)
62 {
63 int icnt = 1;
64 int dcnt = 0;
65 int ncnt = 0;
66 int mib[6];
67 size_t needed;
68 char *buf;
69 char *next;
70 struct ifaddrs cif;
71 char *p, *p0;
72 struct rt_msghdr *rtm;
73 struct if_msghdr *ifm;
74 struct ifa_msghdr *ifam;
75 struct sockaddr *sa;
76 struct ifaddrs *ifa, *ift;
77 u_short idx = 0;
78 int i;
79 size_t len, alen;
80 char *data;
81 char *names;
82
83 #ifdef RTDEBUG
84 static const char rtax_names[][8] = RTAX_NAMES;
85 #endif
86
87 _DIAGASSERT(pif != NULL);
88
89 mib[0] = CTL_NET;
90 mib[1] = PF_ROUTE;
91 mib[2] = 0; /* protocol */
92 mib[3] = 0; /* wildcard address family */
93 mib[4] = NET_RT_IFLIST;
94 mib[5] = 0; /* no flags */
95 if (sysctl(mib, __arraycount(mib), NULL, &needed, NULL, 0) < 0)
96 return (-1);
97 if ((buf = malloc(needed)) == NULL)
98 return (-1);
99 if (sysctl(mib, __arraycount(mib), buf, &needed, NULL, 0) < 0) {
100 free(buf);
101 return (-1);
102 }
103
104 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
105 rtm = (struct rt_msghdr *)(void *)next;
106 if (rtm->rtm_version != RTM_VERSION)
107 continue;
108 #ifdef RTDEBUG
109 printf("%d: %zd rtm len=%d type=%d\n", __LINE__,
110 (char *)rtm - buf,
111 rtm->rtm_msglen, rtm->rtm_type);
112 #endif
113 switch (rtm->rtm_type) {
114 case RTM_IFINFO:
115 ifm = (struct if_msghdr *)(void *)rtm;
116 if (ifm->ifm_addrs & RTA_IFP) {
117 const struct sockaddr_dl *dl;
118
119 idx = ifm->ifm_index;
120 ++icnt;
121 dl = (struct sockaddr_dl *)
122 ((uintptr_t)ifm + RT_ROUNDUP(sizeof(*ifm)));
123 dcnt += SA_RLEN((const struct sockaddr *)(const void *)dl);
124 dcnt += RT_ROUNDUP(sizeof(ifm->ifm_data));
125 ncnt += dl->sdl_nlen + 1;
126 #ifdef RTDEBUG
127 printf("%d: %zd sdl idx=%d dcnt=%#x flags=%#x name=",
128 __LINE__, (const char *)dl - buf, idx,
129 dcnt, ifm->ifm_flags);
130 fwrite(dl->sdl_data, 1, dl->sdl_nlen, stdout);
131 putchar('\n');
132 #endif
133 } else
134 idx = 0;
135 break;
136
137 case RTM_NEWADDR:
138 ifam = (struct ifa_msghdr *)(void *)rtm;
139 if (idx && ifam->ifam_index != idx)
140 abort(); /* this cannot happen */
141
142 #define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD)
143 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
144 break;
145 p = (char *)
146 ((uintptr_t)ifam + RT_ROUNDUP(sizeof(*ifam)));
147 ++icnt;
148 #ifdef RTDEBUG
149 printf("%d: %zd sa index=%d addrs=%#x\n",
150 __LINE__, p - buf,
151 ifam->ifam_index, ifam->ifam_addrs);
152 #endif
153 /* Scan to look for length of address */
154 alen = 0;
155 for (p0 = p, i = 0; i < RTAX_MAX; i++) {
156 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
157 == 0)
158 continue;
159 sa = (struct sockaddr *)(void *)p;
160 len = SA_RLEN(sa);
161 #ifdef RTDEBUG
162 printf("%d: %zd sa %s len=%d family=%d\n",
163 __LINE__, p - buf, rtax_names[i],
164 sa->sa_len, sa->sa_family);
165 #endif
166 if (i == RTAX_IFA) {
167 alen = len;
168 break;
169 }
170 p += len;
171 }
172 for (p = p0, i = 0; i < RTAX_MAX; i++) {
173 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
174 == 0)
175 continue;
176 sa = (struct sockaddr *)(void *)p;
177 len = SA_RLEN(sa);
178 if (i == RTAX_NETMASK && sa->sa_len == 0)
179 dcnt += alen;
180 else
181 dcnt += len;
182 #ifdef RTDEBUG
183 printf("%d: %zd sa %s dcnt=%#x, len=%d family=%d\n",
184 __LINE__, p - buf, rtax_names[i], dcnt,
185 sa->sa_len, sa->sa_family);
186 #endif
187 p += len;
188 }
189 break;
190 }
191 }
192
193 if (icnt + dcnt + ncnt == 1) {
194 *pif = NULL;
195 free(buf);
196 return (0);
197 }
198 data = malloc(RT_ROUNDUP(sizeof(struct ifaddrs) * icnt) + dcnt + ncnt);
199 if (data == NULL) {
200 free(buf);
201 return(-1);
202 }
203
204 ifa = (struct ifaddrs *)(void *)data;
205 data += RT_ROUNDUP(sizeof(struct ifaddrs) * icnt);
206 names = data + dcnt;
207
208 memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
209 ift = ifa;
210 #ifdef RTDEBUG
211 printf("ifa=%p data=%p names=%p end=%p\n",
212 ifa, data, names, names + ncnt);
213 #endif
214
215 idx = 0;
216 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
217 rtm = (struct rt_msghdr *)(void *)next;
218 if (rtm->rtm_version != RTM_VERSION)
219 continue;
220 #ifdef RTDEBUG
221 printf("%d: %zd rtm len=%d type=%d\n", __LINE__,
222 (char *)rtm - buf,
223 rtm->rtm_msglen, rtm->rtm_type);
224 #endif
225 switch (rtm->rtm_type) {
226 case RTM_IFINFO:
227 ifm = (struct if_msghdr *)(void *)rtm;
228 if (ifm->ifm_addrs & RTA_IFP) {
229 const struct sockaddr_dl *dl;
230
231 idx = ifm->ifm_index;
232 dl = (struct sockaddr_dl *)
233 ((uintptr_t)ifm + RT_ROUNDUP(sizeof(*ifm)));
234
235 memset(&cif, 0, sizeof(cif));
236
237 cif.ifa_name = names;
238 cif.ifa_flags = (int)ifm->ifm_flags;
239 memcpy(names, dl->sdl_data,
240 (size_t)dl->sdl_nlen);
241 names[dl->sdl_nlen] = 0;
242
243 #ifdef RTDEBUG
244 printf("%d: %zd sdl idx=%d flags=%#x name=%s\n",
245 __LINE__, (const char *)dl - buf, idx,
246 ifm->ifm_flags, names);
247 #endif
248 names += dl->sdl_nlen + 1;
249
250 cif.ifa_addr = (struct sockaddr *)(void *)data;
251 memcpy(data, dl, (size_t)dl->sdl_len);
252 data += SA_RLEN((const struct sockaddr *)(const void *)dl);
253
254 /* ifm_data needs to be aligned */
255 cif.ifa_data = data = (void *)ALIGN(data);
256 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
257 data += sizeof(ifm->ifm_data);
258 } else
259 idx = 0;
260 break;
261
262 case RTM_NEWADDR:
263 ifam = (struct ifa_msghdr *)(void *)rtm;
264 if (idx && ifam->ifam_index != idx)
265 abort(); /* this cannot happen */
266
267 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
268 break;
269 ift->ifa_name = cif.ifa_name;
270 ift->ifa_flags = cif.ifa_flags;
271 ift->ifa_data = NULL;
272 p = (char *)
273 ((uintptr_t)ifam + RT_ROUNDUP(sizeof(*ifam)));
274 #ifdef RTDEBUG
275 printf("%d: %zd sa index=%d addrs=%#x name=%s\n",
276 __LINE__, p - buf,
277 ifam->ifam_index, ifam->ifam_addrs, ifa->ifa_name);
278 #endif
279 /* Scan to look for length of address */
280 alen = 0;
281 for (p0 = p, i = 0; i < RTAX_MAX; i++) {
282 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
283 == 0)
284 continue;
285 sa = (struct sockaddr *)(void *)p;
286 len = SA_RLEN(sa);
287 #ifdef RTDEBUG
288 printf("%d: %zd sa %s len=%d family=%d\n",
289 __LINE__, p - buf, rtax_names[i],
290 sa->sa_len, sa->sa_family);
291 #endif
292 if (i == RTAX_IFA) {
293 alen = len;
294 break;
295 }
296 p += len;
297 }
298 for (p = p0, i = 0; i < RTAX_MAX; i++) {
299 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
300 == 0)
301 continue;
302 sa = (struct sockaddr *)(void *)p;
303 len = SA_RLEN(sa);
304 #ifdef RTDEBUG
305 printf("%d: %zd sa %s len=%d family=%d\n",
306 __LINE__, p - buf, rtax_names[i],
307 sa->sa_len, sa->sa_family);
308 #endif
309 switch (i) {
310 case RTAX_IFA:
311 ift->ifa_addr =
312 (struct sockaddr *)(void *)data;
313 memcpy(data, p, len);
314 data += len;
315 if (ift->ifa_addr->sa_family == AF_LINK)
316 ift->ifa_data = cif.ifa_data;
317 break;
318
319 case RTAX_NETMASK:
320 ift->ifa_netmask =
321 (struct sockaddr *)(void *)data;
322 if (sa->sa_len == 0) {
323 memset(data, 0, alen);
324 data += alen;
325 break;
326 }
327 memcpy(data, p, len);
328 data += len;
329 break;
330
331 case RTAX_BRD:
332 ift->ifa_broadaddr =
333 (struct sockaddr *)(void *)data;
334 memcpy(data, p, len);
335 data += len;
336 break;
337 }
338 p += len;
339 }
340
341 #ifdef RTDEBUG
342 if (ift->ifa_name == NULL || ift->ifa_name[0] == 0)
343 printf("%p: ifa_name == NULL!\n", ift);
344 else
345 printf("%p: ifa_name=%s\n", ift, ift->ifa_name);
346 #endif
347 ift = (ift->ifa_next = ift + 1);
348 break;
349 }
350 }
351
352 #ifdef RTDEBUG
353 printf("ifa=%p data=%p names=%p\n",
354 ifa, data, names);
355 #endif
356
357 free(buf);
358 if (--ift >= ifa) {
359 ift->ifa_next = NULL;
360 *pif = ifa;
361 } else {
362 *pif = NULL;
363 free(ifa);
364 }
365 return (0);
366 }
367
368 void
369 freeifaddrs(struct ifaddrs *ifp)
370 {
371
372 _DIAGASSERT(ifp != NULL);
373
374 free(ifp);
375 }
376