Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libc / net / getifaddrs.c
1 /*      $FreeBSD: src/lib/libc/net/getifaddrs.c,v 1.1.2.4 2002/08/01 19:31:06 ume Exp $ */
2 /*      $KAME: getifaddrs.c,v 1.9 2001/08/20 02:31:20 itojun Exp $      */
3
4 /*
5  * Copyright (c) 1995, 1999
6  *      Berkeley Software Design, Inc.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *      BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp
27  */
28 /*
29  * NOTE: SIOCGIFCONF case is not LP64 friendly.  it also does not perform
30  * try-and-error for region size.
31  */
32 #include <sys/types.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
35 #include <net/if.h>
36 #ifdef  NET_RT_IFLIST
37 #include <sys/param.h>
38 #include <net/route.h>
39 #include <sys/sysctl.h>
40 #include <net/if_dl.h>
41 #endif
42
43 #include <errno.h>
44 #include <ifaddrs.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #if !defined(AF_LINK)
49 #define SA_LEN(sa)      sizeof(struct sockaddr)
50 #endif
51
52 #if !defined(SA_LEN)
53 #define SA_LEN(sa)      (sa)->sa_len
54 #endif
55
56 #define SALIGN  (sizeof(long) - 1)
57 #define SA_RLEN(sa)     ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
58
59 #ifndef ALIGNBYTES
60 /*
61  * On systems with a routing socket, ALIGNBYTES should match the value
62  * that the kernel uses when building the messages.
63  */
64 #define ALIGNBYTES      XXX
65 #endif
66 #ifndef ALIGN
67 #define ALIGN(p)        (((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
68 #endif
69
70 #if     _BSDI_VERSION >= 199701
71 #define HAVE_IFM_DATA
72 #endif
73
74 #if     _BSDI_VERSION >= 199802
75 /* ifam_data is very specific to recent versions of bsdi */
76 #define HAVE_IFAM_DATA
77 #endif
78
79 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
80 #define HAVE_IFM_DATA
81 #endif
82
83 #define MAX_SYSCTL_TRY 5
84
85 int
86 getifaddrs(struct ifaddrs **pif)
87 {
88         int icnt = 1;
89         int dcnt = 0;
90         int ncnt = 0;
91 #ifdef  NET_RT_IFLIST
92         int ntry = 0;
93         int mib[6];
94         size_t needed;
95         char *buf;
96         char *next;
97         struct ifaddrs *cif = 0;
98         char *p, *p0;
99         struct rt_msghdr *rtm;
100         struct if_msghdr *ifm;
101         struct ifa_msghdr *ifam;
102         struct sockaddr_dl *dl;
103         struct sockaddr *sa;
104         struct ifaddrs *ifa, *ift;
105         u_short idx = 0;
106 #else   /* NET_RT_IFLIST */
107         char buf[1024];
108         int m, sock;
109         struct ifconf ifc;
110         struct ifreq *ifr;
111         struct ifreq *lifr;
112 #endif  /* NET_RT_IFLIST */
113         int i;
114         size_t len, alen;
115         char *data;
116         char *names;
117
118 #ifdef  NET_RT_IFLIST
119         mib[0] = CTL_NET;
120         mib[1] = PF_ROUTE;
121         mib[2] = 0;             /* protocol */
122         mib[3] = 0;             /* wildcard address family */
123         mib[4] = NET_RT_IFLIST;
124         mib[5] = 0;             /* no flags */
125         do {
126                 /*
127                  * We'll try to get addresses several times in case that
128                  * the number of addresses is unexpectedly increased during
129                  * the two sysctl calls.  This should rarely happen, but we'll
130                  * try to do our best for applications that assume success of
131                  * this library (which should usually be the case).
132                  * Portability note: since FreeBSD does not add margin of
133                  * memory at the first sysctl, the possibility of failure on
134                  * the second sysctl call is a bit higher.
135                  */
136
137                 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
138                         return (-1);
139                 if ((buf = malloc(needed)) == NULL)
140                         return (-1);
141                 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
142                         if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
143                                 free(buf);
144                                 return (-1);
145                         }
146                         free(buf);
147                         buf = NULL;
148                 } 
149         } while (buf == NULL);
150
151         for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
152                 rtm = (struct rt_msghdr *)(void *)next;
153                 if (rtm->rtm_version != RTM_VERSION)
154                         continue;
155                 switch (rtm->rtm_type) {
156                 case RTM_IFINFO:
157                         ifm = (struct if_msghdr *)(void *)rtm;
158                         if (ifm->ifm_addrs & RTA_IFP) {
159                                 idx = ifm->ifm_index;
160                                 ++icnt;
161                                 dl = (struct sockaddr_dl *)(void *)(ifm + 1);
162                                 dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
163                                     ALIGNBYTES;
164 #ifdef  HAVE_IFM_DATA
165                                 dcnt += sizeof(ifm->ifm_data);
166 #endif  /* HAVE_IFM_DATA */
167                                 ncnt += dl->sdl_nlen + 1;
168                         } else
169                                 idx = 0;
170                         break;
171
172                 case RTM_NEWADDR:
173                         ifam = (struct ifa_msghdr *)(void *)rtm;
174                         if (idx && ifam->ifam_index != idx)
175                                 abort();        /* this cannot happen */
176
177 #define RTA_MASKS       (RTA_NETMASK | RTA_IFA | RTA_BRD)
178                         if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
179                                 break;
180                         p = (char *)(void *)(ifam + 1);
181                         ++icnt;
182 #ifdef  HAVE_IFAM_DATA
183                         dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES;
184 #endif  /* HAVE_IFAM_DATA */
185                         /* Scan to look for length of address */
186                         alen = 0;
187                         for (p0 = p, 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_IFA) {
194                                         alen = len;
195                                         break;
196                                 }
197                                 p += len;
198                         }
199                         for (p = p0, i = 0; i < RTAX_MAX; i++) {
200                                 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
201                                     == 0)
202                                         continue;
203                                 sa = (struct sockaddr *)(void *)p;
204                                 len = SA_RLEN(sa);
205                                 if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
206                                         dcnt += alen;
207                                 else
208                                         dcnt += len;
209                                 p += len;
210                         }
211                         break;
212                 }
213         }
214 #else   /* NET_RT_IFLIST */
215         ifc.ifc_buf = buf;
216         ifc.ifc_len = sizeof(buf);
217
218         if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
219                 return (-1);
220         i =  ioctl(sock, SIOCGIFCONF, (char *)&ifc);
221         close(sock);
222         if (i < 0)
223                 return (-1);
224
225         ifr = ifc.ifc_req;
226         lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
227
228         while (ifr < lifr) {
229                 struct sockaddr *sa;
230
231                 sa = &ifr->ifr_addr;
232                 ++icnt;
233                 dcnt += SA_RLEN(sa);
234                 ncnt += sizeof(ifr->ifr_name) + 1;
235                 
236                 if (SA_LEN(sa) < sizeof(*sa))
237                         ifr = (struct ifreq *)(((char *)sa) + sizeof(*sa));
238                 else
239                         ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
240         }
241 #endif  /* NET_RT_IFLIST */
242
243         if (icnt + dcnt + ncnt == 1) {
244                 *pif = NULL;
245                 free(buf);
246                 return (0);
247         }
248         data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
249         if (data == NULL) {
250                 free(buf);
251                 return(-1);
252         }
253
254         ifa = (struct ifaddrs *)(void *)data;
255         data += sizeof(struct ifaddrs) * icnt;
256         names = data + dcnt;
257
258         memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
259         ift = ifa;
260
261 #ifdef  NET_RT_IFLIST
262         idx = 0;
263         for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
264                 rtm = (struct rt_msghdr *)(void *)next;
265                 if (rtm->rtm_version != RTM_VERSION)
266                         continue;
267                 switch (rtm->rtm_type) {
268                 case RTM_IFINFO:
269                         ifm = (struct if_msghdr *)(void *)rtm;
270                         if (ifm->ifm_addrs & RTA_IFP) {
271                                 idx = ifm->ifm_index;
272                                 dl = (struct sockaddr_dl *)(void *)(ifm + 1);
273
274                                 cif = ift;
275                                 ift->ifa_name = names;
276                                 ift->ifa_flags = (int)ifm->ifm_flags;
277                                 memcpy(names, dl->sdl_data,
278                                     (size_t)dl->sdl_nlen);
279                                 names[dl->sdl_nlen] = 0;
280                                 names += dl->sdl_nlen + 1;
281
282                                 ift->ifa_addr = (struct sockaddr *)(void *)data;
283                                 memcpy(data, dl,
284                                     (size_t)SA_LEN((struct sockaddr *)
285                                     (void *)dl));
286                                 data += SA_RLEN((struct sockaddr *)(void *)dl);
287
288 #ifdef  HAVE_IFM_DATA
289                                 /* ifm_data needs to be aligned */
290                                 ift->ifa_data = data = (void *)ALIGN(data);
291                                 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
292                                 data += sizeof(ifm->ifm_data);
293 #else   /* HAVE_IFM_DATA */
294                                 ift->ifa_data = NULL;
295 #endif  /* HAVE_IFM_DATA */
296
297                                 ift = (ift->ifa_next = ift + 1);
298                         } else
299                                 idx = 0;
300                         break;
301
302                 case RTM_NEWADDR:
303                         ifam = (struct ifa_msghdr *)(void *)rtm;
304                         if (idx && ifam->ifam_index != idx)
305                                 abort();        /* this cannot happen */
306
307                         if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
308                                 break;
309                         ift->ifa_name = cif->ifa_name;
310                         ift->ifa_flags = cif->ifa_flags;
311                         ift->ifa_data = NULL;
312                         p = (char *)(void *)(ifam + 1);
313                         /* Scan to look for length of address */
314                         alen = 0;
315                         for (p0 = p, 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                                 if (i == RTAX_IFA) {
322                                         alen = len;
323                                         break;
324                                 }
325                                 p += len;
326                         }
327                         for (p = p0, i = 0; i < RTAX_MAX; i++) {
328                                 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
329                                     == 0)
330                                         continue;
331                                 sa = (struct sockaddr *)(void *)p;
332                                 len = SA_RLEN(sa);
333                                 switch (i) {
334                                 case RTAX_IFA:
335                                         ift->ifa_addr =
336                                             (struct sockaddr *)(void *)data;
337                                         memcpy(data, p, len);
338                                         data += len;
339                                         break;
340
341                                 case RTAX_NETMASK:
342                                         ift->ifa_netmask =
343                                             (struct sockaddr *)(void *)data;
344                                         if (SA_LEN(sa) == 0) {
345                                                 memset(data, 0, alen);
346                                                 data += alen;
347                                                 break;
348                                         }
349                                         memcpy(data, p, len);
350                                         data += len;
351                                         break;
352
353                                 case RTAX_BRD:
354                                         ift->ifa_broadaddr =
355                                             (struct sockaddr *)(void *)data;
356                                         memcpy(data, p, len);
357                                         data += len;
358                                         break;
359                                 }
360                                 p += len;
361                         }
362
363 #ifdef  HAVE_IFAM_DATA
364                         /* ifam_data needs to be aligned */
365                         ift->ifa_data = data = (void *)ALIGN(data);
366                         memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data));
367                         data += sizeof(ifam->ifam_data);
368 #endif  /* HAVE_IFAM_DATA */
369
370                         ift = (ift->ifa_next = ift + 1);
371                         break;
372                 }
373         }
374
375         free(buf);
376 #else   /* NET_RT_IFLIST */
377         ifr = ifc.ifc_req;
378         lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
379
380         while (ifr < lifr) {
381                 struct sockaddr *sa;
382
383                 ift->ifa_name = names;
384                 names[sizeof(ifr->ifr_name)] = 0;
385                 strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name));
386                 while (*names++)
387                         ;
388
389                 ift->ifa_addr = (struct sockaddr *)data;
390                 sa = &ifr->ifr_addr;
391                 memcpy(data, sa, SA_LEN(sa));
392                 data += SA_RLEN(sa);
393                 
394                 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
395                 ift = (ift->ifa_next = ift + 1);
396         }
397 #endif  /* NET_RT_IFLIST */
398         if (--ift >= ifa) {
399                 ift->ifa_next = NULL;
400                 *pif = ifa;
401         } else {
402                 *pif = NULL;
403                 free(ifa);
404         }
405         return (0);
406 }
407
408 void
409 freeifaddrs(struct ifaddrs *ifp)
410 {
411
412         free(ifp);
413 }