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