xref: /freebsd/lib/libc/net/getifaddrs.c (revision a220d00e74dd245b4fca59c5eca0c53963686325)
1 /*	$FreeBSD$	*/
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 <ifaddrs.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include "un-namespace.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 int
85 getifaddrs(struct ifaddrs **pif)
86 {
87 	int icnt = 1;
88 	int dcnt = 0;
89 	int ncnt = 0;
90 #ifdef	NET_RT_IFLIST
91 	int mib[6];
92 	size_t needed;
93 	char *buf;
94 	char *next;
95 	struct ifaddrs *cif = 0;
96 	char *p, *p0;
97 	struct rt_msghdr *rtm;
98 	struct if_msghdr *ifm;
99 	struct ifa_msghdr *ifam;
100 	struct sockaddr_dl *dl;
101 	struct sockaddr *sa;
102 	struct ifaddrs *ifa, *ift;
103 	u_short idx = 0;
104 #else	/* NET_RT_IFLIST */
105 	char buf[1024];
106 	int m, sock;
107 	struct ifconf ifc;
108 	struct ifreq *ifr;
109 	struct ifreq *lifr;
110 #endif	/* NET_RT_IFLIST */
111 	int i;
112 	size_t len, alen;
113 	char *data;
114 	char *names;
115 
116 #ifdef	NET_RT_IFLIST
117 	mib[0] = CTL_NET;
118 	mib[1] = PF_ROUTE;
119 	mib[2] = 0;             /* protocol */
120 	mib[3] = 0;             /* wildcard address family */
121 	mib[4] = NET_RT_IFLIST;
122 	mib[5] = 0;             /* no flags */
123 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
124 		return (-1);
125 	if ((buf = malloc(needed)) == NULL)
126 		return (-1);
127 	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
128 		free(buf);
129 		return (-1);
130 	}
131 
132 	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
133 		rtm = (struct rt_msghdr *)(void *)next;
134 		if (rtm->rtm_version != RTM_VERSION)
135 			continue;
136 		switch (rtm->rtm_type) {
137 		case RTM_IFINFO:
138 			ifm = (struct if_msghdr *)(void *)rtm;
139 			if (ifm->ifm_addrs & RTA_IFP) {
140 				idx = ifm->ifm_index;
141 				++icnt;
142 				dl = (struct sockaddr_dl *)(void *)(ifm + 1);
143 				dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
144 				    ALIGNBYTES;
145 #ifdef	HAVE_IFM_DATA
146 				dcnt += sizeof(ifm->ifm_data);
147 #endif	/* HAVE_IFM_DATA */
148 				ncnt += dl->sdl_nlen + 1;
149 			} else
150 				idx = 0;
151 			break;
152 
153 		case RTM_NEWADDR:
154 			ifam = (struct ifa_msghdr *)(void *)rtm;
155 			if (idx && ifam->ifam_index != idx)
156 				abort();	/* this cannot happen */
157 
158 #define	RTA_MASKS	(RTA_NETMASK | RTA_IFA | RTA_BRD)
159 			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
160 				break;
161 			p = (char *)(void *)(ifam + 1);
162 			++icnt;
163 #ifdef	HAVE_IFAM_DATA
164 			dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES;
165 #endif	/* HAVE_IFAM_DATA */
166 			/* Scan to look for length of address */
167 			alen = 0;
168 			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
169 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
170 				    == 0)
171 					continue;
172 				sa = (struct sockaddr *)(void *)p;
173 				len = SA_RLEN(sa);
174 				if (i == RTAX_IFA) {
175 					alen = len;
176 					break;
177 				}
178 				p += len;
179 			}
180 			for (p = p0, i = 0; i < RTAX_MAX; i++) {
181 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
182 				    == 0)
183 					continue;
184 				sa = (struct sockaddr *)(void *)p;
185 				len = SA_RLEN(sa);
186 				if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
187 					dcnt += alen;
188 				else
189 					dcnt += len;
190 				p += len;
191 			}
192 			break;
193 		}
194 	}
195 #else	/* NET_RT_IFLIST */
196 	ifc.ifc_buf = buf;
197 	ifc.ifc_len = sizeof(buf);
198 
199 	if ((sock = _socket(AF_INET, SOCK_STREAM, 0)) < 0)
200 		return (-1);
201 	i =  _ioctl(sock, SIOCGIFCONF, (char *)&ifc);
202 	_close(sock);
203 	if (i < 0)
204 		return (-1);
205 
206 	ifr = ifc.ifc_req;
207 	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
208 
209 	while (ifr < lifr) {
210 		struct sockaddr *sa;
211 
212 		sa = &ifr->ifr_addr;
213 		++icnt;
214 		dcnt += SA_RLEN(sa);
215 		ncnt += sizeof(ifr->ifr_name) + 1;
216 
217 		if (SA_LEN(sa) < sizeof(*sa))
218 			ifr = (struct ifreq *)(((char *)sa) + sizeof(*sa));
219 		else
220 			ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
221 	}
222 #endif	/* NET_RT_IFLIST */
223 
224 	if (icnt + dcnt + ncnt == 1) {
225 		*pif = NULL;
226 		free(buf);
227 		return (0);
228 	}
229 	data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
230 	if (data == NULL) {
231 		free(buf);
232 		return(-1);
233 	}
234 
235 	ifa = (struct ifaddrs *)(void *)data;
236 	data += sizeof(struct ifaddrs) * icnt;
237 	names = data + dcnt;
238 
239 	memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
240 	ift = ifa;
241 
242 #ifdef	NET_RT_IFLIST
243 	idx = 0;
244 	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
245 		rtm = (struct rt_msghdr *)(void *)next;
246 		if (rtm->rtm_version != RTM_VERSION)
247 			continue;
248 		switch (rtm->rtm_type) {
249 		case RTM_IFINFO:
250 			ifm = (struct if_msghdr *)(void *)rtm;
251 			if (ifm->ifm_addrs & RTA_IFP) {
252 				idx = ifm->ifm_index;
253 				dl = (struct sockaddr_dl *)(void *)(ifm + 1);
254 
255 				cif = ift;
256 				ift->ifa_name = names;
257 				ift->ifa_flags = (int)ifm->ifm_flags;
258 				memcpy(names, dl->sdl_data,
259 				    (size_t)dl->sdl_nlen);
260 				names[dl->sdl_nlen] = 0;
261 				names += dl->sdl_nlen + 1;
262 
263 				ift->ifa_addr = (struct sockaddr *)(void *)data;
264 				memcpy(data, dl,
265 				    (size_t)SA_LEN((struct sockaddr *)
266 				    (void *)dl));
267 				data += SA_RLEN((struct sockaddr *)(void *)dl);
268 
269 #ifdef	HAVE_IFM_DATA
270 				/* ifm_data needs to be aligned */
271 				ift->ifa_data = data = (void *)ALIGN(data);
272 				memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
273  				data += sizeof(ifm->ifm_data);
274 #else	/* HAVE_IFM_DATA */
275 				ift->ifa_data = NULL;
276 #endif	/* HAVE_IFM_DATA */
277 
278 				ift = (ift->ifa_next = ift + 1);
279 			} else
280 				idx = 0;
281 			break;
282 
283 		case RTM_NEWADDR:
284 			ifam = (struct ifa_msghdr *)(void *)rtm;
285 			if (idx && ifam->ifam_index != idx)
286 				abort();	/* this cannot happen */
287 
288 			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
289 				break;
290 			ift->ifa_name = cif->ifa_name;
291 			ift->ifa_flags = cif->ifa_flags;
292 			ift->ifa_data = NULL;
293 			p = (char *)(void *)(ifam + 1);
294 			/* Scan to look for length of address */
295 			alen = 0;
296 			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
297 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
298 				    == 0)
299 					continue;
300 				sa = (struct sockaddr *)(void *)p;
301 				len = SA_RLEN(sa);
302 				if (i == RTAX_IFA) {
303 					alen = len;
304 					break;
305 				}
306 				p += len;
307 			}
308 			for (p = p0, i = 0; i < RTAX_MAX; i++) {
309 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
310 				    == 0)
311 					continue;
312 				sa = (struct sockaddr *)(void *)p;
313 				len = SA_RLEN(sa);
314 				switch (i) {
315 				case RTAX_IFA:
316 					ift->ifa_addr =
317 					    (struct sockaddr *)(void *)data;
318 					memcpy(data, p, len);
319 					data += len;
320 					break;
321 
322 				case RTAX_NETMASK:
323 					ift->ifa_netmask =
324 					    (struct sockaddr *)(void *)data;
325 					if (SA_LEN(sa) == 0) {
326 						memset(data, 0, alen);
327 						data += alen;
328 						break;
329 					}
330 					memcpy(data, p, len);
331 					data += len;
332 					break;
333 
334 				case RTAX_BRD:
335 					ift->ifa_broadaddr =
336 					    (struct sockaddr *)(void *)data;
337 					memcpy(data, p, len);
338 					data += len;
339 					break;
340 				}
341 				p += len;
342 			}
343 
344 #ifdef	HAVE_IFAM_DATA
345 			/* ifam_data needs to be aligned */
346 			ift->ifa_data = data = (void *)ALIGN(data);
347 			memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data));
348 			data += sizeof(ifam->ifam_data);
349 #endif	/* HAVE_IFAM_DATA */
350 
351 			ift = (ift->ifa_next = ift + 1);
352 			break;
353 		}
354 	}
355 
356 	free(buf);
357 #else	/* NET_RT_IFLIST */
358 	ifr = ifc.ifc_req;
359 	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
360 
361 	while (ifr < lifr) {
362 		struct sockaddr *sa;
363 
364 		ift->ifa_name = names;
365 		names[sizeof(ifr->ifr_name)] = 0;
366 		strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name));
367 		while (*names++)
368 			;
369 
370 		ift->ifa_addr = (struct sockaddr *)data;
371 		sa = &ifr->ifr_addr;
372 		memcpy(data, sa, SA_LEN(sa));
373 		data += SA_RLEN(sa);
374 
375 		ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
376 		ift = (ift->ifa_next = ift + 1);
377 	}
378 #endif	/* NET_RT_IFLIST */
379 	if (--ift >= ifa) {
380 		ift->ifa_next = NULL;
381 		*pif = ifa;
382 	} else {
383 		*pif = NULL;
384 		free(ifa);
385 	}
386 	return (0);
387 }
388 
389 void
390 freeifaddrs(struct ifaddrs *ifp)
391 {
392 
393 	free(ifp);
394 }
395