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