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