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