1b348544ume/*	$KAME: getifaddrs.c,v 1.9 2001/08/20 02:31:20 itojun Exp $	*/
209cb034itojun
3b036d1bpfg/*-
4b036d1bpfg * SPDX-License-Identifier: BSD-1-Clause
5b036d1bpfg *
609cb034itojun * Copyright (c) 1995, 1999
709cb034itojun *	Berkeley Software Design, Inc.  All rights reserved.
809cb034itojun *
909cb034itojun * Redistribution and use in source and binary forms, with or without
1009cb034itojun * modification, are permitted provided that the following conditions
1109cb034itojun * are met:
1209cb034itojun * 1. Redistributions of source code must retain the above copyright
1309cb034itojun *    notice, this list of conditions and the following disclaimer.
1409cb034itojun *
1509cb034itojun * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
1609cb034itojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1709cb034itojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1809cb034itojun * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
1909cb034itojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2009cb034itojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2109cb034itojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2209cb034itojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2309cb034itojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2409cb034itojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2509cb034itojun * SUCH DAMAGE.
2609cb034itojun *
2709cb034itojun *	BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp
2809cb034itojun */
2909cb034itojun/*
3009cb034itojun * NOTE: SIOCGIFCONF case is not LP64 friendly.  it also does not perform
3109cb034itojun * try-and-error for region size.
3209cb034itojun */
33d90536eobrien
34d90536eobrien#include <sys/cdefs.h>
35d90536eobrien__FBSDID("$FreeBSD$");
36d90536eobrien
371635c22deischen#include "namespace.h"
3809cb034itojun#include <sys/types.h>
3909cb034itojun#include <sys/ioctl.h>
4009cb034itojun#include <sys/socket.h>
4109cb034itojun#include <net/if.h>
4209cb034itojun#ifdef	NET_RT_IFLIST
4309cb034itojun#include <sys/param.h>
4409cb034itojun#include <net/route.h>
4509cb034itojun#include <sys/sysctl.h>
4609cb034itojun#include <net/if_dl.h>
4709cb034itojun#endif
4809cb034itojun
494b45c00ume#include <errno.h>
5009cb034itojun#include <ifaddrs.h>
5109cb034itojun#include <stdlib.h>
5209cb034itojun#include <string.h>
531635c22deischen#include "un-namespace.h"
5409cb034itojun
5509cb034itojun#if !defined(AF_LINK)
5609cb034itojun#define	SA_LEN(sa)	sizeof(struct sockaddr)
5709cb034itojun#endif
5809cb034itojun
5909cb034itojun#if !defined(SA_LEN)
6009cb034itojun#define	SA_LEN(sa)	(sa)->sa_len
6109cb034itojun#endif
6209cb034itojun
6309cb034itojun#define	SALIGN	(sizeof(long) - 1)
6409cb034itojun#define	SA_RLEN(sa)	((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
6509cb034itojun
6609cb034itojun#ifndef	ALIGNBYTES
6709cb034itojun/*
6809cb034itojun * On systems with a routing socket, ALIGNBYTES should match the value
6909cb034itojun * that the kernel uses when building the messages.
7009cb034itojun */
7109cb034itojun#define	ALIGNBYTES	XXX
7209cb034itojun#endif
7309cb034itojun#ifndef	ALIGN
7409cb034itojun#define	ALIGN(p)	(((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
7509cb034itojun#endif
7609cb034itojun
774b45c00ume#define MAX_SYSCTL_TRY 5
784b45c00ume
7909cb034itojunint
8009cb034itojungetifaddrs(struct ifaddrs **pif)
8109cb034itojun{
8209cb034itojun	int icnt = 1;
8309cb034itojun	int dcnt = 0;
8409cb034itojun	int ncnt = 0;
854b45c00ume	int ntry = 0;
8609cb034itojun	int mib[6];
8709cb034itojun	size_t needed;
8809cb034itojun	char *buf;
8909cb034itojun	char *next;
906e91d78pfg	struct ifaddrs *cif;
9109cb034itojun	char *p, *p0;
9209cb034itojun	struct rt_msghdr *rtm;
93891142ebz	struct if_msghdrl *ifm;
94891142ebz	struct ifa_msghdrl *ifam;
9509cb034itojun	struct sockaddr_dl *dl;
9609cb034itojun	struct sockaddr *sa;
9709cb034itojun	struct ifaddrs *ifa, *ift;
98891142ebz	struct if_data *if_data;
99b348544ume	u_short idx = 0;
10009cb034itojun	int i;
10109cb034itojun	size_t len, alen;
10209cb034itojun	char *data;
10309cb034itojun	char *names;
10409cb034itojun
10509cb034itojun	mib[0] = CTL_NET;
10609cb034itojun	mib[1] = PF_ROUTE;
10709cb034itojun	mib[2] = 0;             /* protocol */
10809cb034itojun	mib[3] = 0;             /* wildcard address family */
109891142ebz	mib[4] = NET_RT_IFLISTL;/* extra fields for extensible msghdr structs */
11009cb034itojun	mib[5] = 0;             /* no flags */
1114b45c00ume	do {
1124b45c00ume		/*
1134b45c00ume		 * We'll try to get addresses several times in case that
1144b45c00ume		 * the number of addresses is unexpectedly increased during
1154b45c00ume		 * the two sysctl calls.  This should rarely happen, but we'll
1164b45c00ume		 * try to do our best for applications that assume success of
1174b45c00ume		 * this library (which should usually be the case).
1184b45c00ume		 * Portability note: since FreeBSD does not add margin of
1194b45c00ume		 * memory at the first sysctl, the possibility of failure on
1204b45c00ume		 * the second sysctl call is a bit higher.
1214b45c00ume		 */
1224b45c00ume
1234b45c00ume		if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
1244b45c00ume			return (-1);
1254b45c00ume		if ((buf = malloc(needed)) == NULL)
1264b45c00ume			return (-1);
1274b45c00ume		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
1284b45c00ume			if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
1294b45c00ume				free(buf);
1304b45c00ume				return (-1);
1314b45c00ume			}
1324b45c00ume			free(buf);
1334b45c00ume			buf = NULL;
1344b45c00ume		}
1354b45c00ume	} while (buf == NULL);
13609cb034itojun
13709cb034itojun	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
138b348544ume		rtm = (struct rt_msghdr *)(void *)next;
13909cb034itojun		if (rtm->rtm_version != RTM_VERSION)
14009cb034itojun			continue;
14109cb034itojun		switch (rtm->rtm_type) {
14209cb034itojun		case RTM_IFINFO:
143891142ebz			ifm = (struct if_msghdrl *)(void *)rtm;
14409cb034itojun			if (ifm->ifm_addrs & RTA_IFP) {
145b348544ume				idx = ifm->ifm_index;
14609cb034itojun				++icnt;
147891142ebz				if_data = IF_MSGHDRL_IFM_DATA(ifm);
148891142ebz				dcnt += if_data->ifi_datalen;
149891142ebz				dl = (struct sockaddr_dl *)IF_MSGHDRL_RTA(ifm);
150b348544ume				dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
151b348544ume				    ALIGNBYTES;
15209cb034itojun				ncnt += dl->sdl_nlen + 1;
15309cb034itojun			} else
154b348544ume				idx = 0;
15509cb034itojun			break;
15609cb034itojun
15709cb034itojun		case RTM_NEWADDR:
158891142ebz			ifam = (struct ifa_msghdrl *)(void *)rtm;
159b348544ume			if (idx && ifam->ifam_index != idx)
16009cb034itojun				abort();	/* this cannot happen */
16109cb034itojun
16209cb034itojun#define	RTA_MASKS	(RTA_NETMASK | RTA_IFA | RTA_BRD)
163b348544ume			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
16409cb034itojun				break;
165891142ebz			p = (char *)IFA_MSGHDRL_RTA(ifam);
16609cb034itojun			++icnt;
167891142ebz			if_data = IFA_MSGHDRL_IFAM_DATA(ifam);
168891142ebz			dcnt += if_data->ifi_datalen + ALIGNBYTES;
169891142ebz
17009cb034itojun			/* Scan to look for length of address */
17109cb034itojun			alen = 0;
17209cb034itojun			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
17309cb034itojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
17409cb034itojun				    == 0)
17509cb034itojun					continue;
176b348544ume				sa = (struct sockaddr *)(void *)p;
17709cb034itojun				len = SA_RLEN(sa);
17809cb034itojun				if (i == RTAX_IFA) {
17909cb034itojun					alen = len;
18009cb034itojun					break;
18109cb034itojun				}
18209cb034itojun				p += len;
18309cb034itojun			}
18409cb034itojun			for (p = p0, i = 0; i < RTAX_MAX; i++) {
18509cb034itojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
18609cb034itojun				    == 0)
18709cb034itojun					continue;
188b348544ume				sa = (struct sockaddr *)(void *)p;
18909cb034itojun				len = SA_RLEN(sa);
19009cb034itojun				if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
19109cb034itojun					dcnt += alen;
19209cb034itojun				else
19309cb034itojun					dcnt += len;
19409cb034itojun				p += len;
19509cb034itojun			}
19609cb034itojun			break;
19709cb034itojun		}
19809cb034itojun	}
19909cb034itojun
20009cb034itojun	if (icnt + dcnt + ncnt == 1) {
20109cb034itojun		*pif = NULL;
20209cb034itojun		free(buf);
20309cb034itojun		return (0);
20409cb034itojun	}
20509cb034itojun	data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
20609cb034itojun	if (data == NULL) {
20709cb034itojun		free(buf);
20809cb034itojun		return(-1);
20909cb034itojun	}
21009cb034itojun
211b348544ume	ifa = (struct ifaddrs *)(void *)data;
21209cb034itojun	data += sizeof(struct ifaddrs) * icnt;
21309cb034itojun	names = data + dcnt;
21409cb034itojun
21509cb034itojun	memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
21609cb034itojun	ift = ifa;
21709cb034itojun
218b348544ume	idx = 0;
2196e91d78pfg	cif = NULL;
22009cb034itojun	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
221b348544ume		rtm = (struct rt_msghdr *)(void *)next;
22209cb034itojun		if (rtm->rtm_version != RTM_VERSION)
22309cb034itojun			continue;
22409cb034itojun		switch (rtm->rtm_type) {
22509cb034itojun		case RTM_IFINFO:
226891142ebz			ifm = (struct if_msghdrl *)(void *)rtm;
227891142ebz			if ((ifm->ifm_addrs & RTA_IFP) == 0) {
228b348544ume				idx = 0;
229891142ebz				break;
230891142ebz			}
231891142ebz
232891142ebz			idx = ifm->ifm_index;
233891142ebz			dl = (struct sockaddr_dl *)IF_MSGHDRL_RTA(ifm);
234891142ebz
235891142ebz			cif = ift;
236891142ebz			ift->ifa_name = names;
237891142ebz			ift->ifa_flags = (int)ifm->ifm_flags;
238891142ebz			memcpy(names, dl->sdl_data, (size_t)dl->sdl_nlen);
239891142ebz			names[dl->sdl_nlen] = 0;
240891142ebz			names += dl->sdl_nlen + 1;
241891142ebz
242891142ebz			ift->ifa_addr = (struct sockaddr *)(void *)data;
243891142ebz			memcpy(data, dl, (size_t)SA_LEN((struct sockaddr *)
244891142ebz			    (void *)dl));
245891142ebz			data += SA_RLEN((struct sockaddr *)(void *)dl);
246891142ebz
247891142ebz			if_data = IF_MSGHDRL_IFM_DATA(ifm);
248891142ebz			/* ifm_data needs to be aligned */
249891142ebz			ift->ifa_data = data = (void *)ALIGN(data);
250891142ebz			memcpy(data, if_data, if_data->ifi_datalen);
251891142ebz			data += if_data->ifi_datalen;
252891142ebz
253891142ebz			ift = (ift->ifa_next = ift + 1);
25409cb034itojun			break;
25509cb034itojun
25609cb034itojun		case RTM_NEWADDR:
257891142ebz			ifam = (struct ifa_msghdrl *)(void *)rtm;
258b348544ume			if (idx && ifam->ifam_index != idx)
25909cb034itojun				abort();	/* this cannot happen */
26009cb034itojun
261b348544ume			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
26209cb034itojun				break;
26309cb034itojun			ift->ifa_name = cif->ifa_name;
26409cb034itojun			ift->ifa_flags = cif->ifa_flags;
26509cb034itojun			ift->ifa_data = NULL;
266891142ebz
267891142ebz			p = (char *)IFA_MSGHDRL_RTA(ifam);
26809cb034itojun			/* Scan to look for length of address */
26909cb034itojun			alen = 0;
27009cb034itojun			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
27109cb034itojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
27209cb034itojun				    == 0)
27309cb034itojun					continue;
274b348544ume				sa = (struct sockaddr *)(void *)p;
27509cb034itojun				len = SA_RLEN(sa);
27609cb034itojun				if (i == RTAX_IFA) {
27709cb034itojun					alen = len;
27809cb034itojun					break;
27909cb034itojun				}
28009cb034itojun				p += len;
28109cb034itojun			}
28209cb034itojun			for (p = p0, i = 0; i < RTAX_MAX; i++) {
28309cb034itojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
28409cb034itojun				    == 0)
28509cb034itojun					continue;
286b348544ume				sa = (struct sockaddr *)(void *)p;
28709cb034itojun				len = SA_RLEN(sa);
28809cb034itojun				switch (i) {
28909cb034itojun				case RTAX_IFA:
290b348544ume					ift->ifa_addr =
291b348544ume					    (struct sockaddr *)(void *)data;
29209cb034itojun					memcpy(data, p, len);
29309cb034itojun					data += len;
29409cb034itojun					break;
29509cb034itojun
29609cb034itojun				case RTAX_NETMASK:
29709cb034itojun					ift->ifa_netmask =
298b348544ume					    (struct sockaddr *)(void *)data;
29909cb034itojun					if (SA_LEN(sa) == 0) {
30009cb034itojun						memset(data, 0, alen);
30109cb034itojun						data += alen;
30209cb034itojun						break;
30309cb034itojun					}
30409cb034itojun					memcpy(data, p, len);
30509cb034itojun					data += len;
30609cb034itojun					break;
30709cb034itojun
30809cb034itojun				case RTAX_BRD:
30909cb034itojun					ift->ifa_broadaddr =
310b348544ume					    (struct sockaddr *)(void *)data;
31109cb034itojun					memcpy(data, p, len);
31209cb034itojun					data += len;
31309cb034itojun					break;
31409cb034itojun				}
31509cb034itojun				p += len;
31609cb034itojun			}
31709cb034itojun
318891142ebz			if_data = IFA_MSGHDRL_IFAM_DATA(ifam);
31909cb034itojun			/* ifam_data needs to be aligned */
32009cb034itojun			ift->ifa_data = data = (void *)ALIGN(data);
321891142ebz			memcpy(data, if_data, if_data->ifi_datalen);
322891142ebz			data += if_data->ifi_datalen;
32309cb034itojun
32409cb034itojun			ift = (ift->ifa_next = ift + 1);
32509cb034itojun			break;
32609cb034itojun		}
32709cb034itojun	}
32809cb034itojun
32909cb034itojun	free(buf);
330891142ebz
33109cb034itojun	if (--ift >= ifa) {
33209cb034itojun		ift->ifa_next = NULL;
33309cb034itojun		*pif = ifa;
33409cb034itojun	} else {
33509cb034itojun		*pif = NULL;
33609cb034itojun		free(ifa);
33709cb034itojun	}
33809cb034itojun	return (0);
33909cb034itojun}
34009cb034itojun
34109cb034itojunvoid
34209cb034itojunfreeifaddrs(struct ifaddrs *ifp)
34309cb034itojun{
344b348544ume
34509cb034itojun	free(ifp);
34609cb034itojun}
347