getifaddrs.c revision 1.7 1 /* $NetBSD: getifaddrs.c,v 1.7 2001/01/04 03:57:58 lukem 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 2001/01/04 03:57:58 lukem 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 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
227 }
228 #endif /* NET_RT_IFLIST */
229
230 if (icnt + dcnt + ncnt == 1) {
231 *pif = NULL;
232 free(buf);
233 return (0);
234 }
235 data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
236 if (data == NULL) {
237 free(buf);
238 return(-1);
239 }
240
241 ifa = (struct ifaddrs *)(void *)data;
242 data += sizeof(struct ifaddrs) * icnt;
243 names = data + dcnt;
244
245 memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
246 ift = ifa;
247
248 #ifdef NET_RT_IFLIST
249 idx = 0;
250 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
251 rtm = (struct rt_msghdr *)(void *)next;
252 if (rtm->rtm_version != RTM_VERSION)
253 continue;
254 switch (rtm->rtm_type) {
255 case RTM_IFINFO:
256 ifm = (struct if_msghdr *)(void *)rtm;
257 if (ifm->ifm_addrs & RTA_IFP) {
258 idx = ifm->ifm_index;
259 dl = (struct sockaddr_dl *)(void *)(ifm + 1);
260
261 cif = ift;
262 ift->ifa_name = names;
263 ift->ifa_flags = (int)ifm->ifm_flags;
264 memcpy(names, dl->sdl_data,
265 (size_t)dl->sdl_nlen);
266 names[dl->sdl_nlen] = 0;
267 names += dl->sdl_nlen + 1;
268
269 ift->ifa_addr = (struct sockaddr *)(void *)data;
270 memcpy(data, dl,
271 (size_t)SA_LEN((struct sockaddr *)
272 (void *)dl));
273 data += SA_RLEN((struct sockaddr *)(void *)dl);
274
275 #ifdef HAVE_IFM_DATA
276 /* ifm_data needs to be aligned */
277 ift->ifa_data = data = (void *)ALIGN(data);
278 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
279 data += sizeof(ifm->ifm_data);
280 #else /* HAVE_IFM_DATA */
281 ift->ifa_data = NULL;
282 #endif /* HAVE_IFM_DATA */
283
284 ift = (ift->ifa_next = ift + 1);
285 } else
286 idx = 0;
287 break;
288
289 case RTM_NEWADDR:
290 ifam = (struct ifa_msghdr *)(void *)rtm;
291 if (idx && ifam->ifam_index != idx)
292 abort(); /* this cannot happen */
293
294 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
295 break;
296 ift->ifa_name = cif->ifa_name;
297 ift->ifa_flags = cif->ifa_flags;
298 ift->ifa_data = NULL;
299 p = (char *)(void *)(ifam + 1);
300 /* Scan to look for length of address */
301 alen = 0;
302 for (p0 = p, i = 0; i < RTAX_MAX; i++) {
303 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
304 == 0)
305 continue;
306 sa = (struct sockaddr *)(void *)p;
307 len = SA_RLEN(sa);
308 if (i == RTAX_IFA) {
309 alen = len;
310 break;
311 }
312 p += len;
313 }
314 for (p = p0, i = 0; i < RTAX_MAX; i++) {
315 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
316 == 0)
317 continue;
318 sa = (struct sockaddr *)(void *)p;
319 len = SA_RLEN(sa);
320 switch (i) {
321 case RTAX_IFA:
322 ift->ifa_addr =
323 (struct sockaddr *)(void *)data;
324 memcpy(data, p, len);
325 data += len;
326 break;
327
328 case RTAX_NETMASK:
329 ift->ifa_netmask =
330 (struct sockaddr *)(void *)data;
331 if (SA_LEN(sa) == 0) {
332 memset(data, 0, alen);
333 data += alen;
334 break;
335 }
336 memcpy(data, p, len);
337 data += len;
338 break;
339
340 case RTAX_BRD:
341 ift->ifa_broadaddr =
342 (struct sockaddr *)(void *)data;
343 memcpy(data, p, len);
344 data += len;
345 break;
346 }
347 p += len;
348 }
349
350 #ifdef HAVE_IFAM_DATA
351 /* ifam_data needs to be aligned */
352 ift->ifa_data = data = (void *)ALIGN(data);
353 memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data));
354 data += sizeof(ifam->ifam_data);
355 #endif /* HAVE_IFAM_DATA */
356
357 ift = (ift->ifa_next = ift + 1);
358 break;
359 }
360 }
361
362 free(buf);
363 #else /* NET_RT_IFLIST */
364 ifr = ifc.ifc_req;
365 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
366
367 while (ifr < lifr) {
368 struct sockaddr *sa;
369
370 ift->ifa_name = names;
371 names[sizeof(ifr->ifr_name)] = 0;
372 strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name));
373 while (*names++)
374 ;
375
376 ift->ifa_addr = (struct sockaddr *)data;
377 sa = &ifr->ifr_addr;
378 memcpy(data, sa, SA_LEN(sa));
379 data += SA_RLEN(sa);
380
381 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
382 ift = (ift->ifa_next = ift + 1);
383 }
384 #endif /* NET_RT_IFLIST */
385 if (--ift >= ifa) {
386 ift->ifa_next = NULL;
387 *pif = ifa;
388 } else {
389 *pif = NULL;
390 free(ifa);
391 }
392 return (0);
393 }
394
395 void
396 freeifaddrs(struct ifaddrs *ifp)
397 {
398
399 _DIAGASSERT(ifp != NULL);
400
401 free(ifp);
402 }
403