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__FBSDID("$FreeBSD$");
34
35#include "namespace.h"
36#include <sys/types.h>
37#include <sys/ioctl.h>
38#include <sys/socket.h>
39#include <net/if.h>
40#ifdef	NET_RT_IFLIST
41#include <sys/param.h>
42#include <net/route.h>
43#include <sys/sysctl.h>
44#include <net/if_dl.h>
45#endif
46
47#include <errno.h>
48#include <ifaddrs.h>
49#include <stdlib.h>
50#include <string.h>
51#include "un-namespace.h"
52
53#if !defined(AF_LINK)
54#define	SA_LEN(sa)	sizeof(struct sockaddr)
55#endif
56
57#if !defined(SA_LEN)
58#define	SA_LEN(sa)	(sa)->sa_len
59#endif
60
61#define	SALIGN	(sizeof(long) - 1)
62#define	SA_RLEN(sa)	((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
63
64#ifndef	ALIGNBYTES
65/*
66 * On systems with a routing socket, ALIGNBYTES should match the value
67 * that the kernel uses when building the messages.
68 */
69#define	ALIGNBYTES	XXX
70#endif
71#ifndef	ALIGN
72#define	ALIGN(p)	(((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
73#endif
74
75#define MAX_SYSCTL_TRY 5
76
77int
78getifaddrs(struct ifaddrs **pif)
79{
80	int icnt = 1;
81	int dcnt = 0;
82	int ncnt = 0;
83	int ntry = 0;
84	int mib[6];
85	size_t needed;
86	char *buf;
87	char *next;
88	struct ifaddrs *cif;
89	char *p, *p0;
90	struct rt_msghdr *rtm;
91	struct if_msghdrl *ifm;
92	struct ifa_msghdrl *ifam;
93	struct sockaddr_dl *dl;
94	struct sockaddr *sa;
95	struct ifaddrs *ifa, *ift;
96	struct if_data *if_data;
97	u_short idx = 0;
98	int i;
99	size_t len, alen;
100	char *data;
101	char *names;
102
103	mib[0] = CTL_NET;
104	mib[1] = PF_ROUTE;
105	mib[2] = 0;             /* protocol */
106	mib[3] = 0;             /* wildcard address family */
107	mib[4] = NET_RT_IFLISTL;/* extra fields for extensible msghdr structs */
108	mib[5] = 0;             /* no flags */
109	do {
110		/*
111		 * We'll try to get addresses several times in case that
112		 * the number of addresses is unexpectedly increased during
113		 * the two sysctl calls.  This should rarely happen, but we'll
114		 * try to do our best for applications that assume success of
115		 * this library (which should usually be the case).
116		 * Portability note: since FreeBSD does not add margin of
117		 * memory at the first sysctl, the possibility of failure on
118		 * the second sysctl call is a bit higher.
119		 */
120
121		if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
122			return (-1);
123		if ((buf = malloc(needed)) == NULL)
124			return (-1);
125		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
126			if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
127				free(buf);
128				return (-1);
129			}
130			free(buf);
131			buf = NULL;
132		}
133	} while (buf == NULL);
134
135	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
136		rtm = (struct rt_msghdr *)(void *)next;
137		if (rtm->rtm_version != RTM_VERSION)
138			continue;
139		switch (rtm->rtm_type) {
140		case RTM_IFINFO:
141			ifm = (struct if_msghdrl *)(void *)rtm;
142			if (ifm->ifm_addrs & RTA_IFP) {
143				idx = ifm->ifm_index;
144				++icnt;
145				if_data = IF_MSGHDRL_IFM_DATA(ifm);
146				dcnt += if_data->ifi_datalen;
147				dl = (struct sockaddr_dl *)IF_MSGHDRL_RTA(ifm);
148				dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
149				    ALIGNBYTES;
150				ncnt += dl->sdl_nlen + 1;
151			} else
152				idx = 0;
153			break;
154
155		case RTM_NEWADDR:
156			ifam = (struct ifa_msghdrl *)(void *)rtm;
157			if (idx && ifam->ifam_index != idx)
158				abort();	/* this cannot happen */
159
160#define	RTA_MASKS	(RTA_NETMASK | RTA_IFA | RTA_BRD)
161			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
162				break;
163			p = (char *)IFA_MSGHDRL_RTA(ifam);
164			++icnt;
165			if_data = IFA_MSGHDRL_IFAM_DATA(ifam);
166			dcnt += if_data->ifi_datalen + ALIGNBYTES;
167
168			/* Scan to look for length of address */
169			alen = 0;
170			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
171				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
172				    == 0)
173					continue;
174				sa = (struct sockaddr *)(void *)p;
175				len = SA_RLEN(sa);
176				if (i == RTAX_IFA) {
177					alen = len;
178					break;
179				}
180				p += len;
181			}
182			for (p = p0, i = 0; i < RTAX_MAX; i++) {
183				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
184				    == 0)
185					continue;
186				sa = (struct sockaddr *)(void *)p;
187				len = SA_RLEN(sa);
188				if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
189					dcnt += alen;
190				else
191					dcnt += len;
192				p += len;
193			}
194			break;
195		}
196	}
197
198	if (icnt + dcnt + ncnt == 1) {
199		*pif = NULL;
200		free(buf);
201		return (0);
202	}
203	data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
204	if (data == NULL) {
205		free(buf);
206		return(-1);
207	}
208
209	ifa = (struct ifaddrs *)(void *)data;
210	data += sizeof(struct ifaddrs) * icnt;
211	names = data + dcnt;
212
213	memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
214	ift = ifa;
215
216	idx = 0;
217	cif = NULL;
218	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
219		rtm = (struct rt_msghdr *)(void *)next;
220		if (rtm->rtm_version != RTM_VERSION)
221			continue;
222		switch (rtm->rtm_type) {
223		case RTM_IFINFO:
224			ifm = (struct if_msghdrl *)(void *)rtm;
225			if ((ifm->ifm_addrs & RTA_IFP) == 0) {
226				idx = 0;
227				break;
228			}
229
230			idx = ifm->ifm_index;
231			dl = (struct sockaddr_dl *)IF_MSGHDRL_RTA(ifm);
232
233			cif = ift;
234			ift->ifa_name = names;
235			ift->ifa_flags = (int)ifm->ifm_flags;
236			memcpy(names, dl->sdl_data, (size_t)dl->sdl_nlen);
237			names[dl->sdl_nlen] = 0;
238			names += dl->sdl_nlen + 1;
239
240			ift->ifa_addr = (struct sockaddr *)(void *)data;
241			memcpy(data, dl, (size_t)SA_LEN((struct sockaddr *)
242			    (void *)dl));
243			data += SA_RLEN((struct sockaddr *)(void *)dl);
244
245			if_data = IF_MSGHDRL_IFM_DATA(ifm);
246			/* ifm_data needs to be aligned */
247			ift->ifa_data = data = (void *)ALIGN(data);
248			memcpy(data, if_data, if_data->ifi_datalen);
249			data += if_data->ifi_datalen;
250
251			ift = (ift->ifa_next = ift + 1);
252			break;
253
254		case RTM_NEWADDR:
255			ifam = (struct ifa_msghdrl *)(void *)rtm;
256			if (idx && ifam->ifam_index != idx)
257				abort();	/* this cannot happen */
258
259			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
260				break;
261			ift->ifa_name = cif->ifa_name;
262			ift->ifa_flags = cif->ifa_flags;
263			ift->ifa_data = NULL;
264
265			p = (char *)IFA_MSGHDRL_RTA(ifam);
266			/* Scan to look for length of address */
267			alen = 0;
268			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
269				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
270				    == 0)
271					continue;
272				sa = (struct sockaddr *)(void *)p;
273				len = SA_RLEN(sa);
274				if (i == RTAX_IFA) {
275					alen = len;
276					break;
277				}
278				p += len;
279			}
280			for (p = p0, i = 0; i < RTAX_MAX; i++) {
281				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
282				    == 0)
283					continue;
284				sa = (struct sockaddr *)(void *)p;
285				len = SA_RLEN(sa);
286				switch (i) {
287				case RTAX_IFA:
288					ift->ifa_addr =
289					    (struct sockaddr *)(void *)data;
290					memcpy(data, p, len);
291					data += len;
292					break;
293
294				case RTAX_NETMASK:
295					ift->ifa_netmask =
296					    (struct sockaddr *)(void *)data;
297					if (SA_LEN(sa) == 0) {
298						memset(data, 0, alen);
299						data += alen;
300						break;
301					}
302					memcpy(data, p, len);
303					data += len;
304					break;
305
306				case RTAX_BRD:
307					ift->ifa_broadaddr =
308					    (struct sockaddr *)(void *)data;
309					memcpy(data, p, len);
310					data += len;
311					break;
312				}
313				p += len;
314			}
315
316			if_data = IFA_MSGHDRL_IFAM_DATA(ifam);
317			/* ifam_data needs to be aligned */
318			ift->ifa_data = data = (void *)ALIGN(data);
319			memcpy(data, if_data, if_data->ifi_datalen);
320			data += if_data->ifi_datalen;
321
322			ift = (ift->ifa_next = ift + 1);
323			break;
324		}
325	}
326
327	free(buf);
328
329	if (--ift >= ifa) {
330		ift->ifa_next = NULL;
331		*pif = ifa;
332	} else {
333		*pif = NULL;
334		free(ifa);
335	}
336	return (0);
337}
338
339void
340freeifaddrs(struct ifaddrs *ifp)
341{
342
343	free(ifp);
344}
345