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