getifaddrs.c revision 1.7.2.1 1 /* $NetBSD: getifaddrs.c,v 1.7.2.1 2001/10/08 20:20:10 nathanw 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.7.2.1 2001/10/08 20:20:10 nathanw 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 #ifdef NET_RT_IFLIST
39 #include <sys/param.h>
40 #include <net/route.h>
41 #include <sys/sysctl.h>
42 #include <net/if_dl.h>
43 #endif
44
45 #include <assert.h>
46 #include <errno.h>
47 #include <ifaddrs.h>
48 #include <stdlib.h>
49 #include <string.h>
50
51 #ifdef __weak_alias
52 __weak_alias(getifaddrs,_getifaddrs)
53 __weak_alias(freeifaddrs,_freeifaddrs)
54 #endif
55
56 #if !defined(AF_LINK)
57 #define SA_LEN(sa) sizeof(struct sockaddr)
58 #endif
59
60 #if !defined(SA_LEN)
61 #define SA_LEN(sa) (sa)->sa_len
62 #endif
63
64 #define SALIGN (sizeof(long) - 1)
65 #define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
66
67 #ifndef ALIGNBYTES
68 /*
69 * On systems with a routing socket, ALIGNBYTES should match the value
70 * that the kernel uses when building the messages.
71 */
72 #define ALIGNBYTES XXX
73 #endif
74 #ifndef ALIGN
75 #define ALIGN(p) (((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
76 #endif
77
78 #if _BSDI_VERSION >= 199701
79 #define HAVE_IFM_DATA
80 #endif
81
82 #if _BSDI_VERSION >= 199802
83 /* ifam_data is very specific to recent versions of bsdi */
84 #define HAVE_IFAM_DATA
85 #endif
86
87 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
88 #define HAVE_IFM_DATA
89 #endif
90
91 int
92 getifaddrs(struct ifaddrs **pif)
93 {
94 int icnt = 1;
95 int dcnt = 0;
96 int ncnt = 0;
97 #ifdef NET_RT_IFLIST
98 int mib[6];
99 size_t needed;
100 char *buf;
101 char *next;
102 struct ifaddrs *cif = 0;
103 char *p, *p0;
104 struct rt_msghdr *rtm;
105 struct if_msghdr *ifm;
106 struct ifa_msghdr *ifam;
107 struct sockaddr_dl *dl;
108 struct sockaddr *sa;
109 struct ifaddrs *ifa, *ift;
110 u_short idx = 0;
111 #else /* NET_RT_IFLIST */
112 char buf[1024];
113 int m, sock;
114 struct ifconf ifc;
115 struct ifreq *ifr;
116 struct ifreq *lifr;
117 #endif /* NET_RT_IFLIST */
118 int i;
119 size_t len, alen;
120 char *data;
121 char *names;
122
123 _DIAGASSERT(pif != NULL);
124
125 #ifdef NET_RT_IFLIST
126 mib[0] = CTL_NET;
127 mib[1] = PF_ROUTE;
128 mib[2] = 0; /* protocol */
129 mib[3] = 0; /* wildcard address family */
130 mib[4] = NET_RT_IFLIST;
131 mib[5] = 0; /* no flags */
132 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
133 return (-1);
134 if ((buf = malloc(needed)) == NULL)
135 return (-1);
136 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
137 free(buf);
138 return (-1);
139 }
140
141 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
142 rtm = (struct rt_msghdr *)(void *)next;
143 if (rtm->rtm_version != RTM_VERSION)
144 continue;
145 switch (rtm->rtm_type) {
146 case RTM_IFINFO:
147 ifm = (struct if_msghdr *)(void *)rtm;
148 if (ifm->ifm_addrs & RTA_IFP) {
149 idx = ifm->ifm_index;
150 ++icnt;
151 dl = (struct sockaddr_dl *)(void *)(ifm + 1);
152 dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
153 ALIGNBYTES;
154 #ifdef HAVE_IFM_DATA
155 dcnt += sizeof(ifm->ifm_data);
156 #endif /* HAVE_IFM_DATA */
157 ncnt += dl->sdl_nlen + 1;
158 } else
159 idx = 0;
160 break;
161
162 case RTM_NEWADDR:
163 ifam = (struct ifa_msghdr *)(void *)rtm;
164 if (idx && ifam->ifam_index != idx)
165 abort(); /* this cannot happen */
166
167 #define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD)
168 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
169 break;
170 p = (char *)(void *)(ifam + 1);
171 ++icnt;
172 #ifdef HAVE_IFAM_DATA
173 dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES;
174 #endif /* HAVE_IFAM_DATA */
175 /* Scan to look for length of address */
176 alen = 0;
177 for (p0 = p, i = 0; i < RTAX_MAX; i++) {
178 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
179 == 0)
180 continue;
181 sa = (struct sockaddr *)(void *)p;
182 len = SA_RLEN(sa);
183 if (i == RTAX_IFA) {
184 alen = len;
185 break;
186 }
187 p += len;
188 }
189 for (p = p0, i = 0; i < RTAX_MAX; i++) {
190 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
191 == 0)
192 continue;
193 sa = (struct sockaddr *)(void *)p;
194 len = SA_RLEN(sa);
195 if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
196 dcnt += alen;
197 else
198 dcnt += len;
199 p += len;
200 }
201 break;
202 }
203 }
204 #else /* NET_RT_IFLIST */
205 ifc.ifc_buf = buf;
206 ifc.ifc_len = sizeof(buf);
207
208 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
209 return (-1);
210 i = ioctl(sock, SIOCGIFCONF, (char *)&ifc);
211 close(sock);
212 if (i < 0)
213 return (-1);
214
215 ifr = ifc.ifc_req;
216 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
217
218 while (ifr < lifr) {
219 struct sockaddr *sa;
220
221 sa = &ifr->ifr_addr;
222 ++icnt;
223 dcnt += SA_RLEN(sa);
224 ncnt += sizeof(ifr->ifr_name) + 1;
225
226 if (SA_LEN(sa) < sizeof(*sa))
227 ifr = (struct ifreq *)(((char *)sa) + sizeof(*sa));
228 else
229 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
230 }
231 #endif /* NET_RT_IFLIST */
232
233 if (icnt + dcnt + ncnt == 1) {
234 *pif = NULL;
235 free(buf);
236 return (0);
237 }
238 data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
239 if (data == NULL) {
240 free(buf);
241 return(-1);
242 }
243
244 ifa = (struct ifaddrs *)(void *)data;
245 data += sizeof(struct ifaddrs) * icnt;
246 names = data + dcnt;
247
248 memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
249 ift = ifa;
250
251 #ifdef NET_RT_IFLIST
252 idx = 0;
253 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
254 rtm = (struct rt_msghdr *)(void *)next;
255 if (rtm->rtm_version != RTM_VERSION)
256 continue;
257 switch (rtm->rtm_type) {
258 case RTM_IFINFO:
259 ifm = (struct if_msghdr *)(void *)rtm;
260 if (ifm->ifm_addrs & RTA_IFP) {
261 idx = ifm->ifm_index;
262 dl = (struct sockaddr_dl *)(void *)(ifm + 1);
263
264 cif = ift;
265 ift->ifa_name = names;
266 ift->ifa_flags = (int)ifm->ifm_flags;
267 memcpy(names, dl->sdl_data,
268 (size_t)dl->sdl_nlen);
269 names[dl->sdl_nlen] = 0;
270 names += dl->sdl_nlen + 1;
271
272 ift->ifa_addr = (struct sockaddr *)(void *)data;
273 memcpy(data, dl,
274 (size_t)SA_LEN((struct sockaddr *)
275 (void *)dl));
276 data += SA_RLEN((struct sockaddr *)(void *)dl);
277
278 #ifdef HAVE_IFM_DATA
279 /* ifm_data needs to be aligned */
280 ift->ifa_data = data = (void *)ALIGN(data);
281 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
282 data += sizeof(ifm->ifm_data);
283 #else /* HAVE_IFM_DATA */
284 ift->ifa_data = NULL;
285 #endif /* HAVE_IFM_DATA */
286
287 ift = (ift->ifa_next = ift + 1);
288 } else
289 idx = 0;
290 break;
291
292 case RTM_NEWADDR:
293 ifam = (struct ifa_msghdr *)(void *)rtm;
294 if (idx && ifam->ifam_index != idx)
295 abort(); /* this cannot happen */
296
297 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
298 break;
299 ift->ifa_name = cif->ifa_name;
300 ift->ifa_flags = cif->ifa_flags;
301 ift->ifa_data = NULL;
302 p = (char *)(void *)(ifam + 1);
303 /* Scan to look for length of address */
304 alen = 0;
305 for (p0 = p, i = 0; i < RTAX_MAX; i++) {
306 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
307 == 0)
308 continue;
309 sa = (struct sockaddr *)(void *)p;
310 len = SA_RLEN(sa);
311 if (i == RTAX_IFA) {
312 alen = len;
313 break;
314 }
315 p += len;
316 }
317 for (p = p0, i = 0; i < RTAX_MAX; i++) {
318 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
319 == 0)
320 continue;
321 sa = (struct sockaddr *)(void *)p;
322 len = SA_RLEN(sa);
323 switch (i) {
324 case RTAX_IFA:
325 ift->ifa_addr =
326 (struct sockaddr *)(void *)data;
327 memcpy(data, p, len);
328 data += len;
329 break;
330
331 case RTAX_NETMASK:
332 ift->ifa_netmask =
333 (struct sockaddr *)(void *)data;
334 if (SA_LEN(sa) == 0) {
335 memset(data, 0, alen);
336 data += alen;
337 break;
338 }
339 memcpy(data, p, len);
340 data += len;
341 break;
342
343 case RTAX_BRD:
344 ift->ifa_broadaddr =
345 (struct sockaddr *)(void *)data;
346 memcpy(data, p, len);
347 data += len;
348 break;
349 }
350 p += len;
351 }
352
353 #ifdef HAVE_IFAM_DATA
354 /* ifam_data needs to be aligned */
355 ift->ifa_data = data = (void *)ALIGN(data);
356 memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data));
357 data += sizeof(ifam->ifam_data);
358 #endif /* HAVE_IFAM_DATA */
359
360 ift = (ift->ifa_next = ift + 1);
361 break;
362 }
363 }
364
365 free(buf);
366 #else /* NET_RT_IFLIST */
367 ifr = ifc.ifc_req;
368 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
369
370 while (ifr < lifr) {
371 struct sockaddr *sa;
372
373 ift->ifa_name = names;
374 names[sizeof(ifr->ifr_name)] = 0;
375 strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name));
376 while (*names++)
377 ;
378
379 ift->ifa_addr = (struct sockaddr *)data;
380 sa = &ifr->ifr_addr;
381 memcpy(data, sa, SA_LEN(sa));
382 data += SA_RLEN(sa);
383
384 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
385 ift = (ift->ifa_next = ift + 1);
386 }
387 #endif /* NET_RT_IFLIST */
388 if (--ift >= ifa) {
389 ift->ifa_next = NULL;
390 *pif = ifa;
391 } else {
392 *pif = NULL;
393 free(ifa);
394 }
395 return (0);
396 }
397
398 void
399 freeifaddrs(struct ifaddrs *ifp)
400 {
401
402 _DIAGASSERT(ifp != NULL);
403
404 free(ifp);
405 }
406