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