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