getifaddrs.c revision 1.11 1 /* $NetBSD: getifaddrs.c,v 1.11 2007/12/06 22:51:57 dyoung 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 #include <sys/cdefs.h>
29 #if defined(LIBC_SCCS) && !defined(lint)
30 __RCSID("$NetBSD: getifaddrs.c,v 1.11 2007/12/06 22:51:57 dyoung Exp $");
31 #endif /* LIBC_SCCS and not lint */
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 #include <sys/param.h>
39 #include <net/route.h>
40 #include <sys/sysctl.h>
41 #include <net/if_dl.h>
42
43 #include <assert.h>
44 #include <errno.h>
45 #include <ifaddrs.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 #ifdef __weak_alias
50 __weak_alias(getifaddrs,_getifaddrs)
51 __weak_alias(freeifaddrs,_freeifaddrs)
52 #endif
53
54 #define SALIGN (sizeof(long) - 1)
55 #define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
56
57 int
58 getifaddrs(struct ifaddrs **pif)
59 {
60 int icnt = 1;
61 int dcnt = 0;
62 int ncnt = 0;
63 int mib[6];
64 size_t needed;
65 char *buf;
66 char *next;
67 struct ifaddrs *cif = 0;
68 char *p, *p0;
69 struct rt_msghdr *rtm;
70 struct if_msghdr *ifm;
71 struct ifa_msghdr *ifam;
72 struct sockaddr_dl *dl;
73 struct sockaddr *sa;
74 struct ifaddrs *ifa, *ift;
75 u_short idx = 0;
76 int i;
77 size_t len, alen;
78 char *data;
79 char *names;
80
81 _DIAGASSERT(pif != NULL);
82
83 mib[0] = CTL_NET;
84 mib[1] = PF_ROUTE;
85 mib[2] = 0; /* protocol */
86 mib[3] = 0; /* wildcard address family */
87 mib[4] = NET_RT_IFLIST;
88 mib[5] = 0; /* no flags */
89 if (sysctl(mib, __arraycount(mib), NULL, &needed, NULL, 0) < 0)
90 return (-1);
91 if ((buf = malloc(needed)) == NULL)
92 return (-1);
93 if (sysctl(mib, __arraycount(mib), buf, &needed, NULL, 0) < 0) {
94 free(buf);
95 return (-1);
96 }
97
98 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
99 rtm = (struct rt_msghdr *)(void *)next;
100 if (rtm->rtm_version != RTM_VERSION)
101 continue;
102 switch (rtm->rtm_type) {
103 case RTM_IFINFO:
104 ifm = (struct if_msghdr *)(void *)rtm;
105 if (ifm->ifm_addrs & RTA_IFP) {
106 idx = ifm->ifm_index;
107 ++icnt;
108 dl = (struct sockaddr_dl *)(void *)(ifm + 1);
109 dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
110 ALIGNBYTES;
111 dcnt += sizeof(ifm->ifm_data);
112 ncnt += dl->sdl_nlen + 1;
113 } else
114 idx = 0;
115 break;
116
117 case RTM_NEWADDR:
118 ifam = (struct ifa_msghdr *)(void *)rtm;
119 if (idx && ifam->ifam_index != idx)
120 abort(); /* this cannot happen */
121
122 #define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD)
123 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
124 break;
125 p = (char *)(void *)(ifam + 1);
126 ++icnt;
127 /* Scan to look for length of address */
128 alen = 0;
129 for (p0 = p, i = 0; i < RTAX_MAX; i++) {
130 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
131 == 0)
132 continue;
133 sa = (struct sockaddr *)(void *)p;
134 len = SA_RLEN(sa);
135 if (i == RTAX_IFA) {
136 alen = len;
137 break;
138 }
139 p += len;
140 }
141 for (p = p0, i = 0; i < RTAX_MAX; i++) {
142 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
143 == 0)
144 continue;
145 sa = (struct sockaddr *)(void *)p;
146 len = SA_RLEN(sa);
147 if (i == RTAX_NETMASK && sa->sa_len == 0)
148 dcnt += alen;
149 else
150 dcnt += len;
151 p += len;
152 }
153 break;
154 }
155 }
156
157 if (icnt + dcnt + ncnt == 1) {
158 *pif = NULL;
159 free(buf);
160 return (0);
161 }
162 data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
163 if (data == NULL) {
164 free(buf);
165 return(-1);
166 }
167
168 ifa = (struct ifaddrs *)(void *)data;
169 data += sizeof(struct ifaddrs) * icnt;
170 names = data + dcnt;
171
172 memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
173 ift = ifa;
174
175 idx = 0;
176 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
177 rtm = (struct rt_msghdr *)(void *)next;
178 if (rtm->rtm_version != RTM_VERSION)
179 continue;
180 switch (rtm->rtm_type) {
181 case RTM_IFINFO:
182 ifm = (struct if_msghdr *)(void *)rtm;
183 if (ifm->ifm_addrs & RTA_IFP) {
184 idx = ifm->ifm_index;
185 dl = (struct sockaddr_dl *)(void *)(ifm + 1);
186
187 cif = ift;
188 ift->ifa_name = names;
189 ift->ifa_flags = (int)ifm->ifm_flags;
190 memcpy(names, dl->sdl_data,
191 (size_t)dl->sdl_nlen);
192 names[dl->sdl_nlen] = 0;
193 names += dl->sdl_nlen + 1;
194
195 ift->ifa_addr = (struct sockaddr *)(void *)data;
196 memcpy(data, dl, (size_t)((struct sockaddr *)
197 (void *)dl)->sa_len);
198 data += SA_RLEN((struct sockaddr *)(void *)dl);
199
200 /* ifm_data needs to be aligned */
201 ift->ifa_data = data = (void *)ALIGN(data);
202 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
203 data += sizeof(ifm->ifm_data);
204
205 ift = (ift->ifa_next = ift + 1);
206 } else
207 idx = 0;
208 break;
209
210 case RTM_NEWADDR:
211 ifam = (struct ifa_msghdr *)(void *)rtm;
212 if (idx && ifam->ifam_index != idx)
213 abort(); /* this cannot happen */
214
215 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
216 break;
217 ift->ifa_name = cif->ifa_name;
218 ift->ifa_flags = cif->ifa_flags;
219 ift->ifa_data = NULL;
220 p = (char *)(void *)(ifam + 1);
221 /* Scan to look for length of address */
222 alen = 0;
223 for (p0 = p, i = 0; i < RTAX_MAX; i++) {
224 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
225 == 0)
226 continue;
227 sa = (struct sockaddr *)(void *)p;
228 len = SA_RLEN(sa);
229 if (i == RTAX_IFA) {
230 alen = len;
231 break;
232 }
233 p += len;
234 }
235 for (p = p0, i = 0; i < RTAX_MAX; i++) {
236 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
237 == 0)
238 continue;
239 sa = (struct sockaddr *)(void *)p;
240 len = SA_RLEN(sa);
241 switch (i) {
242 case RTAX_IFA:
243 ift->ifa_addr =
244 (struct sockaddr *)(void *)data;
245 memcpy(data, p, len);
246 data += len;
247 break;
248
249 case RTAX_NETMASK:
250 ift->ifa_netmask =
251 (struct sockaddr *)(void *)data;
252 if (sa->sa_len == 0) {
253 memset(data, 0, alen);
254 data += alen;
255 break;
256 }
257 memcpy(data, p, len);
258 data += len;
259 break;
260
261 case RTAX_BRD:
262 ift->ifa_broadaddr =
263 (struct sockaddr *)(void *)data;
264 memcpy(data, p, len);
265 data += len;
266 break;
267 }
268 p += len;
269 }
270
271
272 ift = (ift->ifa_next = ift + 1);
273 break;
274 }
275 }
276
277 free(buf);
278 if (--ift >= ifa) {
279 ift->ifa_next = NULL;
280 *pif = ifa;
281 } else {
282 *pif = NULL;
283 free(ifa);
284 }
285 return (0);
286 }
287
288 void
289 freeifaddrs(struct ifaddrs *ifp)
290 {
291
292 _DIAGASSERT(ifp != NULL);
293
294 free(ifp);
295 }
296