]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/net/getifaddrs.c
Import libxo-1.3.0:
[FreeBSD/FreeBSD.git] / lib / libc / net / getifaddrs.c
1 /*      $KAME: getifaddrs.c,v 1.9 2001/08/20 02:31:20 itojun Exp $      */
2
3 /*-
4  * SPDX-License-Identifier: BSD-1-Clause
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
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include "namespace.h"
38 #include <sys/types.h>
39 #include <sys/ioctl.h>
40 #include <sys/socket.h>
41 #include <net/if.h>
42 #ifdef  NET_RT_IFLIST
43 #include <sys/param.h>
44 #include <net/route.h>
45 #include <sys/sysctl.h>
46 #include <net/if_dl.h>
47 #endif
48
49 #include <errno.h>
50 #include <ifaddrs.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include "un-namespace.h"
54
55 #if !defined(AF_LINK)
56 #define SA_LEN(sa)      sizeof(struct sockaddr)
57 #endif
58
59 #if !defined(SA_LEN)
60 #define SA_LEN(sa)      (sa)->sa_len
61 #endif
62
63 #define SALIGN  (sizeof(long) - 1)
64 #define SA_RLEN(sa)     ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
65
66 #ifndef ALIGNBYTES
67 /*
68  * On systems with a routing socket, ALIGNBYTES should match the value
69  * that the kernel uses when building the messages.
70  */
71 #define ALIGNBYTES      XXX
72 #endif
73 #ifndef ALIGN
74 #define ALIGN(p)        (((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
75 #endif
76
77 #define MAX_SYSCTL_TRY 5
78
79 int
80 getifaddrs(struct ifaddrs **pif)
81 {
82         int icnt = 1;
83         int dcnt = 0;
84         int ncnt = 0;
85         int ntry = 0;
86         int mib[6];
87         size_t needed;
88         char *buf;
89         char *next;
90         struct ifaddrs *cif;
91         char *p, *p0;
92         struct rt_msghdr *rtm;
93         struct if_msghdrl *ifm;
94         struct ifa_msghdrl *ifam;
95         struct sockaddr_dl *dl;
96         struct sockaddr *sa;
97         struct ifaddrs *ifa, *ift;
98         struct if_data *if_data;
99         u_short idx = 0;
100         int i;
101         size_t len, alen;
102         char *data;
103         char *names;
104
105         mib[0] = CTL_NET;
106         mib[1] = PF_ROUTE;
107         mib[2] = 0;             /* protocol */
108         mib[3] = 0;             /* wildcard address family */
109         mib[4] = NET_RT_IFLISTL;/* extra fields for extensible msghdr structs */
110         mib[5] = 0;             /* no flags */
111         do {
112                 /*
113                  * We'll try to get addresses several times in case that
114                  * the number of addresses is unexpectedly increased during
115                  * the two sysctl calls.  This should rarely happen, but we'll
116                  * try to do our best for applications that assume success of
117                  * this library (which should usually be the case).
118                  * Portability note: since FreeBSD does not add margin of
119                  * memory at the first sysctl, the possibility of failure on
120                  * the second sysctl call is a bit higher.
121                  */
122
123                 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
124                         return (-1);
125                 if ((buf = malloc(needed)) == NULL)
126                         return (-1);
127                 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
128                         if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
129                                 free(buf);
130                                 return (-1);
131                         }
132                         free(buf);
133                         buf = NULL;
134                 } 
135         } while (buf == NULL);
136
137         for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
138                 rtm = (struct rt_msghdr *)(void *)next;
139                 if (rtm->rtm_version != RTM_VERSION)
140                         continue;
141                 switch (rtm->rtm_type) {
142                 case RTM_IFINFO:
143                         ifm = (struct if_msghdrl *)(void *)rtm;
144                         if (ifm->ifm_addrs & RTA_IFP) {
145                                 idx = ifm->ifm_index;
146                                 ++icnt;
147                                 if_data = IF_MSGHDRL_IFM_DATA(ifm);
148                                 dcnt += if_data->ifi_datalen;
149                                 dl = (struct sockaddr_dl *)IF_MSGHDRL_RTA(ifm);
150                                 dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
151                                     ALIGNBYTES;
152                                 ncnt += dl->sdl_nlen + 1;
153                         } else
154                                 idx = 0;
155                         break;
156
157                 case RTM_NEWADDR:
158                         ifam = (struct ifa_msghdrl *)(void *)rtm;
159                         if (idx && ifam->ifam_index != idx)
160                                 abort();        /* this cannot happen */
161
162 #define RTA_MASKS       (RTA_NETMASK | RTA_IFA | RTA_BRD)
163                         if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
164                                 break;
165                         p = (char *)IFA_MSGHDRL_RTA(ifam);
166                         ++icnt;
167                         if_data = IFA_MSGHDRL_IFAM_DATA(ifam);
168                         dcnt += if_data->ifi_datalen + ALIGNBYTES;
169
170                         /* Scan to look for length of address */
171                         alen = 0;
172                         for (p0 = p, i = 0; i < RTAX_MAX; i++) {
173                                 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
174                                     == 0)
175                                         continue;
176                                 sa = (struct sockaddr *)(void *)p;
177                                 len = SA_RLEN(sa);
178                                 if (i == RTAX_IFA) {
179                                         alen = len;
180                                         break;
181                                 }
182                                 p += len;
183                         }
184                         for (p = p0, i = 0; i < RTAX_MAX; i++) {
185                                 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
186                                     == 0)
187                                         continue;
188                                 sa = (struct sockaddr *)(void *)p;
189                                 len = SA_RLEN(sa);
190                                 if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
191                                         dcnt += alen;
192                                 else
193                                         dcnt += len;
194                                 p += len;
195                         }
196                         break;
197                 }
198         }
199
200         if (icnt + dcnt + ncnt == 1) {
201                 *pif = NULL;
202                 free(buf);
203                 return (0);
204         }
205         data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
206         if (data == NULL) {
207                 free(buf);
208                 return(-1);
209         }
210
211         ifa = (struct ifaddrs *)(void *)data;
212         data += sizeof(struct ifaddrs) * icnt;
213         names = data + dcnt;
214
215         memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
216         ift = ifa;
217
218         idx = 0;
219         cif = NULL;
220         for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
221                 rtm = (struct rt_msghdr *)(void *)next;
222                 if (rtm->rtm_version != RTM_VERSION)
223                         continue;
224                 switch (rtm->rtm_type) {
225                 case RTM_IFINFO:
226                         ifm = (struct if_msghdrl *)(void *)rtm;
227                         if ((ifm->ifm_addrs & RTA_IFP) == 0) {
228                                 idx = 0;
229                                 break;
230                         }
231
232                         idx = ifm->ifm_index;
233                         dl = (struct sockaddr_dl *)IF_MSGHDRL_RTA(ifm);
234
235                         cif = ift;
236                         ift->ifa_name = names;
237                         ift->ifa_flags = (int)ifm->ifm_flags;
238                         memcpy(names, dl->sdl_data, (size_t)dl->sdl_nlen);
239                         names[dl->sdl_nlen] = 0;
240                         names += dl->sdl_nlen + 1;
241
242                         ift->ifa_addr = (struct sockaddr *)(void *)data;
243                         memcpy(data, dl, (size_t)SA_LEN((struct sockaddr *)
244                             (void *)dl));
245                         data += SA_RLEN((struct sockaddr *)(void *)dl);
246
247                         if_data = IF_MSGHDRL_IFM_DATA(ifm);
248                         /* ifm_data needs to be aligned */
249                         ift->ifa_data = data = (void *)ALIGN(data);
250                         memcpy(data, if_data, if_data->ifi_datalen);
251                         data += if_data->ifi_datalen;
252
253                         ift = (ift->ifa_next = ift + 1);
254                         break;
255
256                 case RTM_NEWADDR:
257                         ifam = (struct ifa_msghdrl *)(void *)rtm;
258                         if (idx && ifam->ifam_index != idx)
259                                 abort();        /* this cannot happen */
260
261                         if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
262                                 break;
263                         ift->ifa_name = cif->ifa_name;
264                         ift->ifa_flags = cif->ifa_flags;
265                         ift->ifa_data = NULL;
266
267                         p = (char *)IFA_MSGHDRL_RTA(ifam);
268                         /* Scan to look for length of address */
269                         alen = 0;
270                         for (p0 = p, i = 0; i < RTAX_MAX; i++) {
271                                 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
272                                     == 0)
273                                         continue;
274                                 sa = (struct sockaddr *)(void *)p;
275                                 len = SA_RLEN(sa);
276                                 if (i == RTAX_IFA) {
277                                         alen = len;
278                                         break;
279                                 }
280                                 p += len;
281                         }
282                         for (p = p0, i = 0; i < RTAX_MAX; i++) {
283                                 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
284                                     == 0)
285                                         continue;
286                                 sa = (struct sockaddr *)(void *)p;
287                                 len = SA_RLEN(sa);
288                                 switch (i) {
289                                 case RTAX_IFA:
290                                         ift->ifa_addr =
291                                             (struct sockaddr *)(void *)data;
292                                         memcpy(data, p, len);
293                                         data += len;
294                                         break;
295
296                                 case RTAX_NETMASK:
297                                         ift->ifa_netmask =
298                                             (struct sockaddr *)(void *)data;
299                                         if (SA_LEN(sa) == 0) {
300                                                 memset(data, 0, alen);
301                                                 data += alen;
302                                                 break;
303                                         }
304                                         memcpy(data, p, len);
305                                         data += len;
306                                         break;
307
308                                 case RTAX_BRD:
309                                         ift->ifa_broadaddr =
310                                             (struct sockaddr *)(void *)data;
311                                         memcpy(data, p, len);
312                                         data += len;
313                                         break;
314                                 }
315                                 p += len;
316                         }
317
318                         if_data = IFA_MSGHDRL_IFAM_DATA(ifam);
319                         /* ifam_data needs to be aligned */
320                         ift->ifa_data = data = (void *)ALIGN(data);
321                         memcpy(data, if_data, if_data->ifi_datalen);
322                         data += if_data->ifi_datalen;
323
324                         ift = (ift->ifa_next = ift + 1);
325                         break;
326                 }
327         }
328
329         free(buf);
330
331         if (--ift >= ifa) {
332                 ift->ifa_next = NULL;
333                 *pif = ifa;
334         } else {
335                 *pif = NULL;
336                 free(ifa);
337         }
338         return (0);
339 }
340
341 void
342 freeifaddrs(struct ifaddrs *ifp)
343 {
344
345         free(ifp);
346 }