xref: /dragonfly/lib/libc/net/getifaddrs.c (revision c03f08f3)
1 /*	$FreeBSD: src/lib/libc/net/getifaddrs.c,v 1.1.2.4 2002/08/01 19:31:06 ume Exp $	*/
2 /*	$DragonFly: src/lib/libc/net/getifaddrs.c,v 1.5 2005/01/31 22:29:33 dillon Exp $	*/
3 /*	$KAME: getifaddrs.c,v 1.9 2001/08/20 02:31:20 itojun Exp $	*/
4 
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 #include "namespace.h"
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
37 #include <net/if.h>
38 #ifdef	NET_RT_IFLIST
39 #include <sys/param.h>
40 #include <net/route.h>
41 #include <sys/sysctl.h>
42 #include <net/if_dl.h>
43 #endif
44 
45 #include <errno.h>
46 #include <ifaddrs.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include "un-namespace.h"
50 
51 #if !defined(AF_LINK)
52 #define	SA_LEN(sa)	sizeof(struct sockaddr)
53 #endif
54 
55 #if !defined(SA_LEN)
56 #define	SA_LEN(sa)	(sa)->sa_len
57 #endif
58 
59 #define	SALIGN	(sizeof(long) - 1)
60 #define	SA_RLEN(sa)	((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
61 
62 #ifndef	ALIGNBYTES
63 /*
64  * On systems with a routing socket, ALIGNBYTES should match the value
65  * that the kernel uses when building the messages.
66  */
67 #define	ALIGNBYTES	XXX
68 #endif
69 #ifndef	ALIGN
70 #define	ALIGN(p)	(((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
71 #endif
72 
73 #if	_BSDI_VERSION >= 199701
74 #define	HAVE_IFM_DATA
75 #endif
76 
77 #if	_BSDI_VERSION >= 199802
78 /* ifam_data is very specific to recent versions of bsdi */
79 #define	HAVE_IFAM_DATA
80 #endif
81 
82 #if defined(__NetBSD__) || defined(__OpenBSD__) || \
83     defined(__FreeBSD__) || defined(__DragonFly__)
84 #define	HAVE_IFM_DATA
85 #endif
86 
87 #define MAX_SYSCTL_TRY 5
88 
89 int
90 getifaddrs(struct ifaddrs **pif)
91 {
92 	int icnt = 1;
93 	int dcnt = 0;
94 	int ncnt = 0;
95 #ifdef	NET_RT_IFLIST
96 	int ntry = 0;
97 	int mib[6];
98 	size_t needed;
99 	char *buf;
100 	char *next;
101 	struct ifaddrs *cif = 0;
102 	char *p, *p0;
103 	struct rt_msghdr *rtm;
104 	struct if_msghdr *ifm;
105 	struct ifa_msghdr *ifam;
106 	struct sockaddr_dl *dl;
107 	struct sockaddr *sa;
108 	struct ifaddrs *ifa, *ift;
109 	u_short idx = 0;
110 #else	/* NET_RT_IFLIST */
111 	char buf[1024];
112 	int m, sock;
113 	struct ifconf ifc;
114 	struct ifreq *ifr;
115 	struct ifreq *lifr;
116 #endif	/* NET_RT_IFLIST */
117 	int i;
118 	size_t len, alen;
119 	char *data;
120 	char *names;
121 
122 #ifdef	NET_RT_IFLIST
123 	mib[0] = CTL_NET;
124 	mib[1] = PF_ROUTE;
125 	mib[2] = 0;             /* protocol */
126 	mib[3] = 0;             /* wildcard address family */
127 	mib[4] = NET_RT_IFLIST;
128 	mib[5] = 0;             /* no flags */
129 	do {
130 		/*
131 		 * We'll try to get addresses several times in case that
132 		 * the number of addresses is unexpectedly increased during
133 		 * the two sysctl calls.  This should rarely happen, but we'll
134 		 * try to do our best for applications that assume success of
135 		 * this library (which should usually be the case).
136 		 * Portability note: since FreeBSD does not add margin of
137 		 * memory at the first sysctl, the possibility of failure on
138 		 * the second sysctl call is a bit higher.
139 		 */
140 
141 		if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
142 			return (-1);
143 		if ((buf = malloc(needed)) == NULL)
144 			return (-1);
145 		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
146 			if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
147 				free(buf);
148 				return (-1);
149 			}
150 			free(buf);
151 			buf = NULL;
152 		}
153 	} while (buf == NULL);
154 
155 	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
156 		rtm = (struct rt_msghdr *)(void *)next;
157 		if (rtm->rtm_version != RTM_VERSION)
158 			continue;
159 		switch (rtm->rtm_type) {
160 		case RTM_IFINFO:
161 			ifm = (struct if_msghdr *)(void *)rtm;
162 			if (ifm->ifm_addrs & RTA_IFP) {
163 				idx = ifm->ifm_index;
164 				++icnt;
165 				dl = (struct sockaddr_dl *)(void *)(ifm + 1);
166 				dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
167 				    ALIGNBYTES;
168 #ifdef	HAVE_IFM_DATA
169 				dcnt += sizeof(ifm->ifm_data);
170 #endif	/* HAVE_IFM_DATA */
171 				ncnt += dl->sdl_nlen + 1;
172 			} else
173 				idx = 0;
174 			break;
175 
176 		case RTM_NEWADDR:
177 			ifam = (struct ifa_msghdr *)(void *)rtm;
178 			if (idx && ifam->ifam_index != idx)
179 				abort();	/* this cannot happen */
180 
181 #define	RTA_MASKS	(RTA_NETMASK | RTA_IFA | RTA_BRD)
182 			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
183 				break;
184 			p = (char *)(void *)(ifam + 1);
185 			++icnt;
186 #ifdef	HAVE_IFAM_DATA
187 			dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES;
188 #endif	/* HAVE_IFAM_DATA */
189 			/* Scan to look for length of address */
190 			alen = 0;
191 			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
192 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
193 				    == 0)
194 					continue;
195 				sa = (struct sockaddr *)(void *)p;
196 				len = SA_RLEN(sa);
197 				if (i == RTAX_IFA) {
198 					alen = len;
199 					break;
200 				}
201 				p += len;
202 			}
203 			for (p = p0, i = 0; i < RTAX_MAX; i++) {
204 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
205 				    == 0)
206 					continue;
207 				sa = (struct sockaddr *)(void *)p;
208 				len = SA_RLEN(sa);
209 				if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
210 					dcnt += alen;
211 				else
212 					dcnt += len;
213 				p += len;
214 			}
215 			break;
216 		}
217 	}
218 #else	/* NET_RT_IFLIST */
219 	ifc.ifc_buf = buf;
220 	ifc.ifc_len = sizeof(buf);
221 
222 	if ((sock = _socket(AF_INET, SOCK_STREAM, 0)) < 0)
223 		return (-1);
224 	i =  _ioctl(sock, SIOCGIFCONF, (char *)&ifc);
225 	_close(sock);
226 	if (i < 0)
227 		return (-1);
228 
229 	ifr = ifc.ifc_req;
230 	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
231 
232 	while (ifr < lifr) {
233 		struct sockaddr *sa;
234 
235 		sa = &ifr->ifr_addr;
236 		++icnt;
237 		dcnt += SA_RLEN(sa);
238 		ncnt += sizeof(ifr->ifr_name) + 1;
239 
240 		if (SA_LEN(sa) < sizeof(*sa))
241 			ifr = (struct ifreq *)(((char *)sa) + sizeof(*sa));
242 		else
243 			ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
244 	}
245 #endif	/* NET_RT_IFLIST */
246 
247 	if (icnt + dcnt + ncnt == 1) {
248 		*pif = NULL;
249 		free(buf);
250 		return (0);
251 	}
252 	data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
253 	if (data == NULL) {
254 		free(buf);
255 		return(-1);
256 	}
257 
258 	ifa = (struct ifaddrs *)(void *)data;
259 	data += sizeof(struct ifaddrs) * icnt;
260 	names = data + dcnt;
261 
262 	memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
263 	ift = ifa;
264 
265 #ifdef	NET_RT_IFLIST
266 	idx = 0;
267 	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
268 		rtm = (struct rt_msghdr *)(void *)next;
269 		if (rtm->rtm_version != RTM_VERSION)
270 			continue;
271 		switch (rtm->rtm_type) {
272 		case RTM_IFINFO:
273 			ifm = (struct if_msghdr *)(void *)rtm;
274 			if (ifm->ifm_addrs & RTA_IFP) {
275 				idx = ifm->ifm_index;
276 				dl = (struct sockaddr_dl *)(void *)(ifm + 1);
277 
278 				cif = ift;
279 				ift->ifa_name = names;
280 				ift->ifa_flags = (int)ifm->ifm_flags;
281 				memcpy(names, dl->sdl_data,
282 				    (size_t)dl->sdl_nlen);
283 				names[dl->sdl_nlen] = 0;
284 				names += dl->sdl_nlen + 1;
285 
286 				ift->ifa_addr = (struct sockaddr *)(void *)data;
287 				memcpy(data, dl,
288 				    (size_t)SA_LEN((struct sockaddr *)
289 				    (void *)dl));
290 				data += SA_RLEN((struct sockaddr *)(void *)dl);
291 
292 #ifdef	HAVE_IFM_DATA
293 				/* ifm_data needs to be aligned */
294 				ift->ifa_data = data = (void *)ALIGN(data);
295 				memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
296  				data += sizeof(ifm->ifm_data);
297 #else	/* HAVE_IFM_DATA */
298 				ift->ifa_data = NULL;
299 #endif	/* HAVE_IFM_DATA */
300 
301 				ift = (ift->ifa_next = ift + 1);
302 			} else
303 				idx = 0;
304 			break;
305 
306 		case RTM_NEWADDR:
307 			ifam = (struct ifa_msghdr *)(void *)rtm;
308 			if (idx && ifam->ifam_index != idx)
309 				abort();	/* this cannot happen */
310 
311 			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
312 				break;
313 			ift->ifa_name = cif->ifa_name;
314 			ift->ifa_flags = cif->ifa_flags;
315 			ift->ifa_data = NULL;
316 			p = (char *)(void *)(ifam + 1);
317 			/* Scan to look for length of address */
318 			alen = 0;
319 			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
320 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
321 				    == 0)
322 					continue;
323 				sa = (struct sockaddr *)(void *)p;
324 				len = SA_RLEN(sa);
325 				if (i == RTAX_IFA) {
326 					alen = len;
327 					break;
328 				}
329 				p += len;
330 			}
331 			for (p = p0, i = 0; i < RTAX_MAX; i++) {
332 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
333 				    == 0)
334 					continue;
335 				sa = (struct sockaddr *)(void *)p;
336 				len = SA_RLEN(sa);
337 				switch (i) {
338 				case RTAX_IFA:
339 					ift->ifa_addr =
340 					    (struct sockaddr *)(void *)data;
341 					memcpy(data, p, len);
342 					data += len;
343 					break;
344 
345 				case RTAX_NETMASK:
346 					ift->ifa_netmask =
347 					    (struct sockaddr *)(void *)data;
348 					if (SA_LEN(sa) == 0) {
349 						memset(data, 0, alen);
350 						data += alen;
351 						break;
352 					}
353 					memcpy(data, p, len);
354 					data += len;
355 					break;
356 
357 				case RTAX_BRD:
358 					ift->ifa_broadaddr =
359 					    (struct sockaddr *)(void *)data;
360 					memcpy(data, p, len);
361 					data += len;
362 					break;
363 				}
364 				p += len;
365 			}
366 
367 #ifdef	HAVE_IFAM_DATA
368 			/* ifam_data needs to be aligned */
369 			ift->ifa_data = data = (void *)ALIGN(data);
370 			memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data));
371 			data += sizeof(ifam->ifam_data);
372 #endif	/* HAVE_IFAM_DATA */
373 
374 			ift = (ift->ifa_next = ift + 1);
375 			break;
376 		}
377 	}
378 
379 	free(buf);
380 #else	/* NET_RT_IFLIST */
381 	ifr = ifc.ifc_req;
382 	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
383 
384 	while (ifr < lifr) {
385 		struct sockaddr *sa;
386 
387 		ift->ifa_name = names;
388 		names[sizeof(ifr->ifr_name)] = 0;
389 		strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name));
390 		while (*names++)
391 			;
392 
393 		ift->ifa_addr = (struct sockaddr *)data;
394 		sa = &ifr->ifr_addr;
395 		memcpy(data, sa, SA_LEN(sa));
396 		data += SA_RLEN(sa);
397 
398 		ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
399 		ift = (ift->ifa_next = ift + 1);
400 	}
401 #endif	/* NET_RT_IFLIST */
402 	if (--ift >= ifa) {
403 		ift->ifa_next = NULL;
404 		*pif = ifa;
405 	} else {
406 		*pif = NULL;
407 		free(ifa);
408 	}
409 	return (0);
410 }
411 
412 void
413 freeifaddrs(struct ifaddrs *ifp)
414 {
415 
416 	free(ifp);
417 }
418