184372Sume/*	$KAME: getifaddrs.c,v 1.9 2001/08/20 02:31:20 itojun Exp $	*/
262606Sitojun
362606Sitojun/*
462606Sitojun * Copyright (c) 1995, 1999
562606Sitojun *	Berkeley Software Design, Inc.  All rights reserved.
662606Sitojun *
762606Sitojun * Redistribution and use in source and binary forms, with or without
862606Sitojun * modification, are permitted provided that the following conditions
962606Sitojun * are met:
1062606Sitojun * 1. Redistributions of source code must retain the above copyright
1162606Sitojun *    notice, this list of conditions and the following disclaimer.
1262606Sitojun *
1362606Sitojun * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
1462606Sitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1562606Sitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1662606Sitojun * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
1762606Sitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1862606Sitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1962606Sitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2062606Sitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2162606Sitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2262606Sitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2362606Sitojun * SUCH DAMAGE.
2462606Sitojun *
2562606Sitojun *	BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp
2662606Sitojun */
2762606Sitojun/*
2862606Sitojun * NOTE: SIOCGIFCONF case is not LP64 friendly.  it also does not perform
2962606Sitojun * try-and-error for region size.
3062606Sitojun */
3192986Sobrien
3292986Sobrien#include <sys/cdefs.h>
3392986Sobrien__FBSDID("$FreeBSD: stable/10/lib/libc/net/getifaddrs.c 309485 2016-12-03 17:17:42Z ngie $");
3492986Sobrien
3571579Sdeischen#include "namespace.h"
3662606Sitojun#include <sys/types.h>
3762606Sitojun#include <sys/ioctl.h>
3862606Sitojun#include <sys/socket.h>
3962606Sitojun#include <net/if.h>
4062606Sitojun#ifdef	NET_RT_IFLIST
4162606Sitojun#include <sys/param.h>
4262606Sitojun#include <net/route.h>
4362606Sitojun#include <sys/sysctl.h>
4462606Sitojun#include <net/if_dl.h>
4562606Sitojun#endif
4662606Sitojun
47100657Sume#include <errno.h>
4862606Sitojun#include <ifaddrs.h>
4962606Sitojun#include <stdlib.h>
5062606Sitojun#include <string.h>
5171579Sdeischen#include "un-namespace.h"
5262606Sitojun
5362606Sitojun#if !defined(AF_LINK)
5462606Sitojun#define	SA_LEN(sa)	sizeof(struct sockaddr)
5562606Sitojun#endif
5662606Sitojun
5762606Sitojun#if !defined(SA_LEN)
5862606Sitojun#define	SA_LEN(sa)	(sa)->sa_len
5962606Sitojun#endif
6062606Sitojun
6162606Sitojun#define	SALIGN	(sizeof(long) - 1)
6262606Sitojun#define	SA_RLEN(sa)	((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
6362606Sitojun
6462606Sitojun#ifndef	ALIGNBYTES
6562606Sitojun/*
6662606Sitojun * On systems with a routing socket, ALIGNBYTES should match the value
6762606Sitojun * that the kernel uses when building the messages.
6862606Sitojun */
6962606Sitojun#define	ALIGNBYTES	XXX
7062606Sitojun#endif
7162606Sitojun#ifndef	ALIGN
7262606Sitojun#define	ALIGN(p)	(((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
7362606Sitojun#endif
7462606Sitojun
75100657Sume#define MAX_SYSCTL_TRY 5
76100657Sume
7762606Sitojunint
7862606Sitojungetifaddrs(struct ifaddrs **pif)
7962606Sitojun{
8062606Sitojun	int icnt = 1;
8162606Sitojun	int dcnt = 0;
8262606Sitojun	int ncnt = 0;
83100657Sume	int ntry = 0;
8462606Sitojun	int mib[6];
8562606Sitojun	size_t needed;
8662606Sitojun	char *buf;
8762606Sitojun	char *next;
88309485Sngie	struct ifaddrs *cif;
8962606Sitojun	char *p, *p0;
9062606Sitojun	struct rt_msghdr *rtm;
91231506Sbz	struct if_msghdrl *ifm;
92231506Sbz	struct ifa_msghdrl *ifam;
9362606Sitojun	struct sockaddr_dl *dl;
9462606Sitojun	struct sockaddr *sa;
9562606Sitojun	struct ifaddrs *ifa, *ift;
96231506Sbz	struct if_data *if_data;
9784372Sume	u_short idx = 0;
9862606Sitojun	int i;
9962606Sitojun	size_t len, alen;
10062606Sitojun	char *data;
10162606Sitojun	char *names;
10262606Sitojun
10362606Sitojun	mib[0] = CTL_NET;
10462606Sitojun	mib[1] = PF_ROUTE;
10562606Sitojun	mib[2] = 0;             /* protocol */
10662606Sitojun	mib[3] = 0;             /* wildcard address family */
107231506Sbz	mib[4] = NET_RT_IFLISTL;/* extra fields for extensible msghdr structs */
10862606Sitojun	mib[5] = 0;             /* no flags */
109100657Sume	do {
110100657Sume		/*
111100657Sume		 * We'll try to get addresses several times in case that
112100657Sume		 * the number of addresses is unexpectedly increased during
113100657Sume		 * the two sysctl calls.  This should rarely happen, but we'll
114100657Sume		 * try to do our best for applications that assume success of
115100657Sume		 * this library (which should usually be the case).
116100657Sume		 * Portability note: since FreeBSD does not add margin of
117100657Sume		 * memory at the first sysctl, the possibility of failure on
118100657Sume		 * the second sysctl call is a bit higher.
119100657Sume		 */
12062606Sitojun
121100657Sume		if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
122100657Sume			return (-1);
123100657Sume		if ((buf = malloc(needed)) == NULL)
124100657Sume			return (-1);
125100657Sume		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
126100657Sume			if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
127100657Sume				free(buf);
128100657Sume				return (-1);
129100657Sume			}
130100657Sume			free(buf);
131100657Sume			buf = NULL;
132100657Sume		}
133100657Sume	} while (buf == NULL);
134100657Sume
13562606Sitojun	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
13684372Sume		rtm = (struct rt_msghdr *)(void *)next;
13762606Sitojun		if (rtm->rtm_version != RTM_VERSION)
13862606Sitojun			continue;
13962606Sitojun		switch (rtm->rtm_type) {
14062606Sitojun		case RTM_IFINFO:
141231506Sbz			ifm = (struct if_msghdrl *)(void *)rtm;
14262606Sitojun			if (ifm->ifm_addrs & RTA_IFP) {
14384372Sume				idx = ifm->ifm_index;
14462606Sitojun				++icnt;
145231506Sbz				if_data = IF_MSGHDRL_IFM_DATA(ifm);
146231506Sbz				dcnt += if_data->ifi_datalen;
147231506Sbz				dl = (struct sockaddr_dl *)IF_MSGHDRL_RTA(ifm);
14884372Sume				dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
14984372Sume				    ALIGNBYTES;
15062606Sitojun				ncnt += dl->sdl_nlen + 1;
15162606Sitojun			} else
15284372Sume				idx = 0;
15362606Sitojun			break;
15462606Sitojun
15562606Sitojun		case RTM_NEWADDR:
156231506Sbz			ifam = (struct ifa_msghdrl *)(void *)rtm;
15784372Sume			if (idx && ifam->ifam_index != idx)
15862606Sitojun				abort();	/* this cannot happen */
15962606Sitojun
16062606Sitojun#define	RTA_MASKS	(RTA_NETMASK | RTA_IFA | RTA_BRD)
16184372Sume			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
16262606Sitojun				break;
163231506Sbz			p = (char *)IFA_MSGHDRL_RTA(ifam);
16462606Sitojun			++icnt;
165231506Sbz			if_data = IFA_MSGHDRL_IFAM_DATA(ifam);
166231506Sbz			dcnt += if_data->ifi_datalen + ALIGNBYTES;
167231506Sbz
16862606Sitojun			/* Scan to look for length of address */
16962606Sitojun			alen = 0;
17062606Sitojun			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
17162606Sitojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
17262606Sitojun				    == 0)
17362606Sitojun					continue;
17484372Sume				sa = (struct sockaddr *)(void *)p;
17562606Sitojun				len = SA_RLEN(sa);
17662606Sitojun				if (i == RTAX_IFA) {
17762606Sitojun					alen = len;
17862606Sitojun					break;
17962606Sitojun				}
18062606Sitojun				p += len;
18162606Sitojun			}
18262606Sitojun			for (p = p0, i = 0; i < RTAX_MAX; i++) {
18362606Sitojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
18462606Sitojun				    == 0)
18562606Sitojun					continue;
18684372Sume				sa = (struct sockaddr *)(void *)p;
18762606Sitojun				len = SA_RLEN(sa);
18862606Sitojun				if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
18962606Sitojun					dcnt += alen;
19062606Sitojun				else
19162606Sitojun					dcnt += len;
19262606Sitojun				p += len;
19362606Sitojun			}
19462606Sitojun			break;
19562606Sitojun		}
19662606Sitojun	}
19762606Sitojun
19862606Sitojun	if (icnt + dcnt + ncnt == 1) {
19962606Sitojun		*pif = NULL;
20062606Sitojun		free(buf);
20162606Sitojun		return (0);
20262606Sitojun	}
20362606Sitojun	data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
20462606Sitojun	if (data == NULL) {
20562606Sitojun		free(buf);
20662606Sitojun		return(-1);
20762606Sitojun	}
20862606Sitojun
20984372Sume	ifa = (struct ifaddrs *)(void *)data;
21062606Sitojun	data += sizeof(struct ifaddrs) * icnt;
21162606Sitojun	names = data + dcnt;
21262606Sitojun
21362606Sitojun	memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
21462606Sitojun	ift = ifa;
21562606Sitojun
21684372Sume	idx = 0;
217309485Sngie	cif = NULL;
21862606Sitojun	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
21984372Sume		rtm = (struct rt_msghdr *)(void *)next;
22062606Sitojun		if (rtm->rtm_version != RTM_VERSION)
22162606Sitojun			continue;
22262606Sitojun		switch (rtm->rtm_type) {
22362606Sitojun		case RTM_IFINFO:
224231506Sbz			ifm = (struct if_msghdrl *)(void *)rtm;
225231506Sbz			if ((ifm->ifm_addrs & RTA_IFP) == 0) {
226231506Sbz				idx = 0;
227231506Sbz				break;
228231506Sbz			}
22962606Sitojun
230231506Sbz			idx = ifm->ifm_index;
231231506Sbz			dl = (struct sockaddr_dl *)IF_MSGHDRL_RTA(ifm);
23262606Sitojun
233231506Sbz			cif = ift;
234231506Sbz			ift->ifa_name = names;
235231506Sbz			ift->ifa_flags = (int)ifm->ifm_flags;
236231506Sbz			memcpy(names, dl->sdl_data, (size_t)dl->sdl_nlen);
237231506Sbz			names[dl->sdl_nlen] = 0;
238231506Sbz			names += dl->sdl_nlen + 1;
23962606Sitojun
240231506Sbz			ift->ifa_addr = (struct sockaddr *)(void *)data;
241231506Sbz			memcpy(data, dl, (size_t)SA_LEN((struct sockaddr *)
242231506Sbz			    (void *)dl));
243231506Sbz			data += SA_RLEN((struct sockaddr *)(void *)dl);
24462606Sitojun
245231506Sbz			if_data = IF_MSGHDRL_IFM_DATA(ifm);
246231506Sbz			/* ifm_data needs to be aligned */
247231506Sbz			ift->ifa_data = data = (void *)ALIGN(data);
248231506Sbz			memcpy(data, if_data, if_data->ifi_datalen);
249231506Sbz			data += if_data->ifi_datalen;
250231506Sbz
251231506Sbz			ift = (ift->ifa_next = ift + 1);
25262606Sitojun			break;
25362606Sitojun
25462606Sitojun		case RTM_NEWADDR:
255231506Sbz			ifam = (struct ifa_msghdrl *)(void *)rtm;
25684372Sume			if (idx && ifam->ifam_index != idx)
25762606Sitojun				abort();	/* this cannot happen */
25862606Sitojun
25984372Sume			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
26062606Sitojun				break;
26162606Sitojun			ift->ifa_name = cif->ifa_name;
26262606Sitojun			ift->ifa_flags = cif->ifa_flags;
26362606Sitojun			ift->ifa_data = NULL;
264231506Sbz
265231506Sbz			p = (char *)IFA_MSGHDRL_RTA(ifam);
26662606Sitojun			/* Scan to look for length of address */
26762606Sitojun			alen = 0;
26862606Sitojun			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
26962606Sitojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
27062606Sitojun				    == 0)
27162606Sitojun					continue;
27284372Sume				sa = (struct sockaddr *)(void *)p;
27362606Sitojun				len = SA_RLEN(sa);
27462606Sitojun				if (i == RTAX_IFA) {
27562606Sitojun					alen = len;
27662606Sitojun					break;
27762606Sitojun				}
27862606Sitojun				p += len;
27962606Sitojun			}
28062606Sitojun			for (p = p0, i = 0; i < RTAX_MAX; i++) {
28162606Sitojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
28262606Sitojun				    == 0)
28362606Sitojun					continue;
28484372Sume				sa = (struct sockaddr *)(void *)p;
28562606Sitojun				len = SA_RLEN(sa);
28662606Sitojun				switch (i) {
28762606Sitojun				case RTAX_IFA:
28884372Sume					ift->ifa_addr =
28984372Sume					    (struct sockaddr *)(void *)data;
29062606Sitojun					memcpy(data, p, len);
29162606Sitojun					data += len;
29262606Sitojun					break;
29362606Sitojun
29462606Sitojun				case RTAX_NETMASK:
29562606Sitojun					ift->ifa_netmask =
29684372Sume					    (struct sockaddr *)(void *)data;
29762606Sitojun					if (SA_LEN(sa) == 0) {
29862606Sitojun						memset(data, 0, alen);
29962606Sitojun						data += alen;
30062606Sitojun						break;
30162606Sitojun					}
30262606Sitojun					memcpy(data, p, len);
30362606Sitojun					data += len;
30462606Sitojun					break;
30562606Sitojun
30662606Sitojun				case RTAX_BRD:
30762606Sitojun					ift->ifa_broadaddr =
30884372Sume					    (struct sockaddr *)(void *)data;
30962606Sitojun					memcpy(data, p, len);
31062606Sitojun					data += len;
31162606Sitojun					break;
31262606Sitojun				}
31362606Sitojun				p += len;
31462606Sitojun			}
31562606Sitojun
316231506Sbz			if_data = IFA_MSGHDRL_IFAM_DATA(ifam);
31762606Sitojun			/* ifam_data needs to be aligned */
31862606Sitojun			ift->ifa_data = data = (void *)ALIGN(data);
319231506Sbz			memcpy(data, if_data, if_data->ifi_datalen);
320231506Sbz			data += if_data->ifi_datalen;
32162606Sitojun
32262606Sitojun			ift = (ift->ifa_next = ift + 1);
32362606Sitojun			break;
32462606Sitojun		}
32562606Sitojun	}
32662606Sitojun
32762606Sitojun	free(buf);
32862606Sitojun
32962606Sitojun	if (--ift >= ifa) {
33062606Sitojun		ift->ifa_next = NULL;
33162606Sitojun		*pif = ifa;
33262606Sitojun	} else {
33362606Sitojun		*pif = NULL;
33462606Sitojun		free(ifa);
33562606Sitojun	}
33662606Sitojun	return (0);
33762606Sitojun}
33862606Sitojun
33962606Sitojunvoid
34062606Sitojunfreeifaddrs(struct ifaddrs *ifp)
34162606Sitojun{
34284372Sume
34362606Sitojun	free(ifp);
34462606Sitojun}
345