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