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