getifaddrs.c revision 1.2 1 /* $NetBSD: getifaddrs.c,v 1.2 2000/02/23 15:29:18 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 #include <sys/types.h>
28 #include <sys/ioctl.h>
29 #include <sys/socket.h>
30 #include <net/if.h>
31 #ifdef NET_RT_IFLIST
32 #include <sys/param.h>
33 #include <net/route.h>
34 #include <sys/sysctl.h>
35 #include <net/if_dl.h>
36 #endif
37
38 #include <errno.h>
39 #include <ifaddrs.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #if !defined(AF_LINK)
44 #define SA_LEN(sa) sizeof(struct sockaddr)
45 #endif
46
47 #if !defined(SA_LEN)
48 #define SA_LEN(sa) (sa)->sa_len
49 #endif
50
51 #define SALIGN (sizeof(long) - 1)
52 #define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
53
54 #ifndef ALIGNBYTES
55 /*
56 * On systems with a routing socket, ALIGNBYTES should match the value
57 * that the kernel uses when building the messages.
58 */
59 #define ALIGNBYTES XXX
60 #endif
61 #ifndef ALIGN
62 #define ALIGN(p) (((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
63 #endif
64
65 #if _BSDI_VERSION >= 199701
66 #define HAVE_IFM_DATA
67 #endif
68
69 #if _BSDI_VERSION >= 199802
70 #define HAVE_IFAM_DATA
71 #endif
72
73 int
74 getifaddrs(struct ifaddrs **pif)
75 {
76 int icnt = 1;
77 int dcnt = 0;
78 int ncnt = 0;
79 #ifdef NET_RT_IFLIST
80 int mib[6];
81 size_t needed;
82 char *buf;
83 char *next;
84 struct ifaddrs *cif = 0;
85 char *p, *p0;
86 struct rt_msghdr *rtm;
87 struct if_msghdr *ifm;
88 struct ifa_msghdr *ifam;
89 struct sockaddr_dl *dl;
90 struct sockaddr *sa;
91 struct ifaddrs *ifa, *ift;
92 u_short index = 0;
93 #else /* NET_RT_IFLIST */
94 char buf[1024];
95 int m, sock;
96 struct ifconf ifc;
97 struct ifreq *ifr;
98 struct ifreq *lifr;
99 #endif /* NET_RT_IFLIST */
100 int i;
101 size_t len, alen;
102 char *data;
103 char *names;
104
105 #ifdef NET_RT_IFLIST
106 mib[0] = CTL_NET;
107 mib[1] = PF_ROUTE;
108 mib[2] = 0; /* protocol */
109 mib[3] = 0; /* wildcard address family */
110 mib[4] = NET_RT_IFLIST;
111 mib[5] = 0; /* no flags */
112 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
113 return (-1);
114 if ((buf = malloc(needed)) == NULL)
115 return (-1);
116 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
117 free(buf);
118 return (-1);
119 }
120
121 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
122 rtm = (struct rt_msghdr *)next;
123 if (rtm->rtm_version != RTM_VERSION)
124 continue;
125 switch (rtm->rtm_type) {
126 case RTM_IFINFO:
127 ifm = (struct if_msghdr *)rtm;
128 if (ifm->ifm_addrs & RTA_IFP) {
129 index = ifm->ifm_index;
130 ++icnt;
131 dl = (struct sockaddr_dl *)(ifm + 1);
132 dcnt += SA_RLEN((struct sockaddr *)dl) +
133 ALIGNBYTES;
134 #ifdef HAVE_IFM_DATA
135 dcnt += sizeof(ifm->ifm_data);
136 #endif /* HAVE_IFM_DATA */
137 ncnt += dl->sdl_nlen + 1;
138 } else
139 index = 0;
140 break;
141
142 case RTM_NEWADDR:
143 ifam = (struct ifa_msghdr *)rtm;
144 if (index && ifam->ifam_index != index)
145 abort(); /* this cannot happen */
146
147 #define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD)
148 if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
149 break;
150 p = (char *)(ifam + 1);
151 ++icnt;
152 #ifdef HAVE_IFAM_DATA
153 dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES;
154 #endif /* HAVE_IFAM_DATA */
155 /* Scan to look for length of address */
156 alen = 0;
157 for (p0 = p, i = 0; i < RTAX_MAX; i++) {
158 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
159 == 0)
160 continue;
161 sa = (struct sockaddr *)p;
162 len = SA_RLEN(sa);
163 if (i == RTAX_IFA) {
164 alen = len;
165 break;
166 }
167 p += len;
168 }
169 for (p = p0, i = 0; i < RTAX_MAX; i++) {
170 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
171 == 0)
172 continue;
173 sa = (struct sockaddr *)p;
174 len = SA_RLEN(sa);
175 if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
176 dcnt += alen;
177 else
178 dcnt += len;
179 p += len;
180 }
181 break;
182 }
183 }
184 #else /* NET_RT_IFLIST */
185 ifc.ifc_buf = buf;
186 ifc.ifc_len = sizeof(buf);
187
188 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
189 return (-1);
190 i = ioctl(sock, SIOCGIFCONF, (char *)&ifc);
191 close(sock);
192 if (i < 0)
193 return (-1);
194
195 ifr = ifc.ifc_req;
196 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
197
198 while (ifr < lifr) {
199 struct sockaddr *sa;
200
201 sa = &ifr->ifr_addr;
202 ++icnt;
203 dcnt += SA_RLEN(sa);
204 ncnt += sizeof(ifr->ifr_name) + 1;
205
206 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
207 }
208 #endif /* NET_RT_IFLIST */
209
210 if (icnt + dcnt + ncnt == 1) {
211 *pif = NULL;
212 free(buf);
213 return (0);
214 }
215 data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
216 if (data == NULL) {
217 free(buf);
218 return(-1);
219 }
220
221 ifa = (struct ifaddrs *)data;
222 data += sizeof(struct ifaddrs) * icnt;
223 names = data + dcnt;
224
225 memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
226 ift = ifa;
227
228 #ifdef NET_RT_IFLIST
229 index = 0;
230 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
231 rtm = (struct rt_msghdr *)next;
232 if (rtm->rtm_version != RTM_VERSION)
233 continue;
234 switch (rtm->rtm_type) {
235 case RTM_IFINFO:
236 ifm = (struct if_msghdr *)rtm;
237 if (ifm->ifm_addrs & RTA_IFP) {
238 index = ifm->ifm_index;
239 dl = (struct sockaddr_dl *)(ifm + 1);
240
241 cif = ift;
242 ift->ifa_name = names;
243 ift->ifa_flags = (int)ifm->ifm_flags;
244 memcpy(names, dl->sdl_data, dl->sdl_nlen);
245 names[dl->sdl_nlen] = 0;
246 names += dl->sdl_nlen + 1;
247
248 ift->ifa_addr = (struct sockaddr *)data;
249 memcpy(data, dl, SA_LEN((struct sockaddr *)dl));
250 data += SA_RLEN((struct sockaddr *)dl);
251
252 #ifdef HAVE_IFM_DATA
253 /* ifm_data needs to be aligned */
254 ift->ifa_data = data = (void *)ALIGN(data);
255 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
256 data += sizeof(ifm->ifm_data);
257 #else /* HAVE_IFM_DATA */
258 ift->ifa_data = NULL;
259 #endif /* HAVE_IFM_DATA */
260
261 ift = (ift->ifa_next = ift + 1);
262 } else
263 index = 0;
264 break;
265
266 case RTM_NEWADDR:
267 ifam = (struct ifa_msghdr *)rtm;
268 if (index && ifam->ifam_index != index)
269 abort(); /* this cannot happen */
270
271 if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
272 break;
273 ift->ifa_name = cif->ifa_name;
274 ift->ifa_flags = cif->ifa_flags;
275 ift->ifa_data = NULL;
276 p = (char *)(ifam + 1);
277 /* Scan to look for length of address */
278 alen = 0;
279 for (p0 = p, i = 0; i < RTAX_MAX; i++) {
280 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
281 == 0)
282 continue;
283 sa = (struct sockaddr *)p;
284 len = SA_RLEN(sa);
285 if (i == RTAX_IFA) {
286 alen = len;
287 break;
288 }
289 p += len;
290 }
291 for (p = p0, i = 0; i < RTAX_MAX; i++) {
292 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
293 == 0)
294 continue;
295 sa = (struct sockaddr *)p;
296 len = SA_RLEN(sa);
297 switch (i) {
298 case RTAX_IFA:
299 ift->ifa_addr = (struct sockaddr *)data;
300 memcpy(data, p, len);
301 data += len;
302 break;
303
304 case RTAX_NETMASK:
305 ift->ifa_netmask =
306 (struct sockaddr *)data;
307 if (SA_LEN(sa) == 0) {
308 memset(data, 0, alen);
309 data += alen;
310 break;
311 }
312 memcpy(data, p, len);
313 data += len;
314 break;
315
316 case RTAX_BRD:
317 ift->ifa_broadaddr =
318 (struct sockaddr *)data;
319 memcpy(data, p, len);
320 data += len;
321 break;
322 }
323 p += len;
324 }
325
326 #ifdef HAVE_IFAM_DATA
327 /* ifam_data needs to be aligned */
328 ift->ifa_data = data = (void *)ALIGN(data);
329 memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data));
330 data += sizeof(ifam->ifam_data);
331 #endif /* HAVE_IFAM_DATA */
332
333 ift = (ift->ifa_next = ift + 1);
334 break;
335 }
336 }
337
338 free(buf);
339 #else /* NET_RT_IFLIST */
340 ifr = ifc.ifc_req;
341 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
342
343 while (ifr < lifr) {
344 struct sockaddr *sa;
345
346 ift->ifa_name = names;
347 names[sizeof(ifr->ifr_name)] = 0;
348 strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name));
349 while (*names++)
350 ;
351
352 ift->ifa_addr = (struct sockaddr *)data;
353 sa = &ifr->ifr_addr;
354 memcpy(data, sa, SA_LEN(sa));
355 data += SA_RLEN(sa);
356
357 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
358 ift = (ift->ifa_next = ift + 1);
359 }
360 #endif /* NET_RT_IFLIST */
361 if (--ift >= ifa) {
362 ift->ifa_next = NULL;
363 *pif = ifa;
364 } else {
365 *pif = NULL;
366 free(ifa);
367 }
368 return (0);
369 }
370
371 void
372 freeifaddrs(struct ifaddrs *ifp)
373 {
374 free(ifp);
375 }
376