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