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