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