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$");
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
7562606Sitojun#if	_BSDI_VERSION >= 199701
7662606Sitojun#define	HAVE_IFM_DATA
7762606Sitojun#endif
7862606Sitojun
7962606Sitojun#if	_BSDI_VERSION >= 199802
8067800Sume/* ifam_data is very specific to recent versions of bsdi */
8162606Sitojun#define	HAVE_IFAM_DATA
8262606Sitojun#endif
8362606Sitojun
8467800Sume#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
8567800Sume#define	HAVE_IFM_DATA
8667800Sume#endif
8767800Sume
88100657Sume#define MAX_SYSCTL_TRY 5
89100657Sume
9062606Sitojunint
9162606Sitojungetifaddrs(struct ifaddrs **pif)
9262606Sitojun{
9362606Sitojun	int icnt = 1;
9462606Sitojun	int dcnt = 0;
9562606Sitojun	int ncnt = 0;
9662606Sitojun#ifdef	NET_RT_IFLIST
97100657Sume	int ntry = 0;
9862606Sitojun	int mib[6];
9962606Sitojun	size_t needed;
10062606Sitojun	char *buf;
10162606Sitojun	char *next;
10262606Sitojun	struct ifaddrs *cif = 0;
10362606Sitojun	char *p, *p0;
10462606Sitojun	struct rt_msghdr *rtm;
10562606Sitojun	struct if_msghdr *ifm;
10662606Sitojun	struct ifa_msghdr *ifam;
10762606Sitojun	struct sockaddr_dl *dl;
10862606Sitojun	struct sockaddr *sa;
10962606Sitojun	struct ifaddrs *ifa, *ift;
11084372Sume	u_short idx = 0;
11162606Sitojun#else	/* NET_RT_IFLIST */
11262606Sitojun	char buf[1024];
11362606Sitojun	int m, sock;
11462606Sitojun	struct ifconf ifc;
11562606Sitojun	struct ifreq *ifr;
11662606Sitojun	struct ifreq *lifr;
11762606Sitojun#endif	/* NET_RT_IFLIST */
11862606Sitojun	int i;
11962606Sitojun	size_t len, alen;
12062606Sitojun	char *data;
12162606Sitojun	char *names;
12262606Sitojun
12362606Sitojun#ifdef	NET_RT_IFLIST
12462606Sitojun	mib[0] = CTL_NET;
12562606Sitojun	mib[1] = PF_ROUTE;
12662606Sitojun	mib[2] = 0;             /* protocol */
12762606Sitojun	mib[3] = 0;             /* wildcard address family */
12862606Sitojun	mib[4] = NET_RT_IFLIST;
12962606Sitojun	mib[5] = 0;             /* no flags */
130100657Sume	do {
131100657Sume		/*
132100657Sume		 * We'll try to get addresses several times in case that
133100657Sume		 * the number of addresses is unexpectedly increased during
134100657Sume		 * the two sysctl calls.  This should rarely happen, but we'll
135100657Sume		 * try to do our best for applications that assume success of
136100657Sume		 * this library (which should usually be the case).
137100657Sume		 * Portability note: since FreeBSD does not add margin of
138100657Sume		 * memory at the first sysctl, the possibility of failure on
139100657Sume		 * the second sysctl call is a bit higher.
140100657Sume		 */
14162606Sitojun
142100657Sume		if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
143100657Sume			return (-1);
144100657Sume		if ((buf = malloc(needed)) == NULL)
145100657Sume			return (-1);
146100657Sume		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
147100657Sume			if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
148100657Sume				free(buf);
149100657Sume				return (-1);
150100657Sume			}
151100657Sume			free(buf);
152100657Sume			buf = NULL;
153100657Sume		}
154100657Sume	} while (buf == NULL);
155100657Sume
15662606Sitojun	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
15784372Sume		rtm = (struct rt_msghdr *)(void *)next;
15862606Sitojun		if (rtm->rtm_version != RTM_VERSION)
15962606Sitojun			continue;
16062606Sitojun		switch (rtm->rtm_type) {
16162606Sitojun		case RTM_IFINFO:
16284372Sume			ifm = (struct if_msghdr *)(void *)rtm;
16362606Sitojun			if (ifm->ifm_addrs & RTA_IFP) {
16484372Sume				idx = ifm->ifm_index;
16562606Sitojun				++icnt;
16684372Sume				dl = (struct sockaddr_dl *)(void *)(ifm + 1);
16784372Sume				dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
16884372Sume				    ALIGNBYTES;
16962606Sitojun#ifdef	HAVE_IFM_DATA
17062606Sitojun				dcnt += sizeof(ifm->ifm_data);
17162606Sitojun#endif	/* HAVE_IFM_DATA */
17262606Sitojun				ncnt += dl->sdl_nlen + 1;
17362606Sitojun			} else
17484372Sume				idx = 0;
17562606Sitojun			break;
17662606Sitojun
17762606Sitojun		case RTM_NEWADDR:
17884372Sume			ifam = (struct ifa_msghdr *)(void *)rtm;
17984372Sume			if (idx && ifam->ifam_index != idx)
18062606Sitojun				abort();	/* this cannot happen */
18162606Sitojun
18262606Sitojun#define	RTA_MASKS	(RTA_NETMASK | RTA_IFA | RTA_BRD)
18384372Sume			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
18462606Sitojun				break;
18584372Sume			p = (char *)(void *)(ifam + 1);
18662606Sitojun			++icnt;
18762606Sitojun#ifdef	HAVE_IFAM_DATA
18862606Sitojun			dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES;
18962606Sitojun#endif	/* HAVE_IFAM_DATA */
19062606Sitojun			/* Scan to look for length of address */
19162606Sitojun			alen = 0;
19262606Sitojun			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
19362606Sitojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
19462606Sitojun				    == 0)
19562606Sitojun					continue;
19684372Sume				sa = (struct sockaddr *)(void *)p;
19762606Sitojun				len = SA_RLEN(sa);
19862606Sitojun				if (i == RTAX_IFA) {
19962606Sitojun					alen = len;
20062606Sitojun					break;
20162606Sitojun				}
20262606Sitojun				p += len;
20362606Sitojun			}
20462606Sitojun			for (p = p0, i = 0; i < RTAX_MAX; i++) {
20562606Sitojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
20662606Sitojun				    == 0)
20762606Sitojun					continue;
20884372Sume				sa = (struct sockaddr *)(void *)p;
20962606Sitojun				len = SA_RLEN(sa);
21062606Sitojun				if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
21162606Sitojun					dcnt += alen;
21262606Sitojun				else
21362606Sitojun					dcnt += len;
21462606Sitojun				p += len;
21562606Sitojun			}
21662606Sitojun			break;
21762606Sitojun		}
21862606Sitojun	}
21962606Sitojun#else	/* NET_RT_IFLIST */
22062606Sitojun	ifc.ifc_buf = buf;
22162606Sitojun	ifc.ifc_len = sizeof(buf);
22262606Sitojun
22371579Sdeischen	if ((sock = _socket(AF_INET, SOCK_STREAM, 0)) < 0)
22462606Sitojun		return (-1);
22571579Sdeischen	i =  _ioctl(sock, SIOCGIFCONF, (char *)&ifc);
22671579Sdeischen	_close(sock);
22762606Sitojun	if (i < 0)
22862606Sitojun		return (-1);
22962606Sitojun
23062606Sitojun	ifr = ifc.ifc_req;
23162606Sitojun	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
23262606Sitojun
23362606Sitojun	while (ifr < lifr) {
23462606Sitojun		struct sockaddr *sa;
23562606Sitojun
23662606Sitojun		sa = &ifr->ifr_addr;
23762606Sitojun		++icnt;
23862606Sitojun		dcnt += SA_RLEN(sa);
23962606Sitojun		ncnt += sizeof(ifr->ifr_name) + 1;
24062606Sitojun
24184372Sume		if (SA_LEN(sa) < sizeof(*sa))
24284372Sume			ifr = (struct ifreq *)(((char *)sa) + sizeof(*sa));
24384372Sume		else
24484372Sume			ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
24562606Sitojun	}
24662606Sitojun#endif	/* NET_RT_IFLIST */
24762606Sitojun
24862606Sitojun	if (icnt + dcnt + ncnt == 1) {
24962606Sitojun		*pif = NULL;
25062606Sitojun		free(buf);
25162606Sitojun		return (0);
25262606Sitojun	}
25362606Sitojun	data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
25462606Sitojun	if (data == NULL) {
25562606Sitojun		free(buf);
25662606Sitojun		return(-1);
25762606Sitojun	}
25862606Sitojun
25984372Sume	ifa = (struct ifaddrs *)(void *)data;
26062606Sitojun	data += sizeof(struct ifaddrs) * icnt;
26162606Sitojun	names = data + dcnt;
26262606Sitojun
26362606Sitojun	memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
26462606Sitojun	ift = ifa;
26562606Sitojun
26662606Sitojun#ifdef	NET_RT_IFLIST
26784372Sume	idx = 0;
26862606Sitojun	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
26984372Sume		rtm = (struct rt_msghdr *)(void *)next;
27062606Sitojun		if (rtm->rtm_version != RTM_VERSION)
27162606Sitojun			continue;
27262606Sitojun		switch (rtm->rtm_type) {
27362606Sitojun		case RTM_IFINFO:
27484372Sume			ifm = (struct if_msghdr *)(void *)rtm;
27562606Sitojun			if (ifm->ifm_addrs & RTA_IFP) {
27684372Sume				idx = ifm->ifm_index;
27784372Sume				dl = (struct sockaddr_dl *)(void *)(ifm + 1);
27862606Sitojun
27962606Sitojun				cif = ift;
28062606Sitojun				ift->ifa_name = names;
28162606Sitojun				ift->ifa_flags = (int)ifm->ifm_flags;
28284372Sume				memcpy(names, dl->sdl_data,
28384372Sume				    (size_t)dl->sdl_nlen);
28462606Sitojun				names[dl->sdl_nlen] = 0;
28562606Sitojun				names += dl->sdl_nlen + 1;
28662606Sitojun
28784372Sume				ift->ifa_addr = (struct sockaddr *)(void *)data;
28884372Sume				memcpy(data, dl,
28984372Sume				    (size_t)SA_LEN((struct sockaddr *)
29084372Sume				    (void *)dl));
29184372Sume				data += SA_RLEN((struct sockaddr *)(void *)dl);
29262606Sitojun
29362606Sitojun#ifdef	HAVE_IFM_DATA
29462606Sitojun				/* ifm_data needs to be aligned */
29562606Sitojun				ift->ifa_data = data = (void *)ALIGN(data);
29662606Sitojun				memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
29762606Sitojun 				data += sizeof(ifm->ifm_data);
29862606Sitojun#else	/* HAVE_IFM_DATA */
29962606Sitojun				ift->ifa_data = NULL;
30062606Sitojun#endif	/* HAVE_IFM_DATA */
30162606Sitojun
30262606Sitojun				ift = (ift->ifa_next = ift + 1);
30362606Sitojun			} else
30484372Sume				idx = 0;
30562606Sitojun			break;
30662606Sitojun
30762606Sitojun		case RTM_NEWADDR:
30884372Sume			ifam = (struct ifa_msghdr *)(void *)rtm;
30984372Sume			if (idx && ifam->ifam_index != idx)
31062606Sitojun				abort();	/* this cannot happen */
31162606Sitojun
31284372Sume			if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
31362606Sitojun				break;
31462606Sitojun			ift->ifa_name = cif->ifa_name;
31562606Sitojun			ift->ifa_flags = cif->ifa_flags;
31662606Sitojun			ift->ifa_data = NULL;
31784372Sume			p = (char *)(void *)(ifam + 1);
31862606Sitojun			/* Scan to look for length of address */
31962606Sitojun			alen = 0;
32062606Sitojun			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
32162606Sitojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
32262606Sitojun				    == 0)
32362606Sitojun					continue;
32484372Sume				sa = (struct sockaddr *)(void *)p;
32562606Sitojun				len = SA_RLEN(sa);
32662606Sitojun				if (i == RTAX_IFA) {
32762606Sitojun					alen = len;
32862606Sitojun					break;
32962606Sitojun				}
33062606Sitojun				p += len;
33162606Sitojun			}
33262606Sitojun			for (p = p0, i = 0; i < RTAX_MAX; i++) {
33362606Sitojun				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
33462606Sitojun				    == 0)
33562606Sitojun					continue;
33684372Sume				sa = (struct sockaddr *)(void *)p;
33762606Sitojun				len = SA_RLEN(sa);
33862606Sitojun				switch (i) {
33962606Sitojun				case RTAX_IFA:
34084372Sume					ift->ifa_addr =
34184372Sume					    (struct sockaddr *)(void *)data;
34262606Sitojun					memcpy(data, p, len);
34362606Sitojun					data += len;
34462606Sitojun					break;
34562606Sitojun
34662606Sitojun				case RTAX_NETMASK:
34762606Sitojun					ift->ifa_netmask =
34884372Sume					    (struct sockaddr *)(void *)data;
34962606Sitojun					if (SA_LEN(sa) == 0) {
35062606Sitojun						memset(data, 0, alen);
35162606Sitojun						data += alen;
35262606Sitojun						break;
35362606Sitojun					}
35462606Sitojun					memcpy(data, p, len);
35562606Sitojun					data += len;
35662606Sitojun					break;
35762606Sitojun
35862606Sitojun				case RTAX_BRD:
35962606Sitojun					ift->ifa_broadaddr =
36084372Sume					    (struct sockaddr *)(void *)data;
36162606Sitojun					memcpy(data, p, len);
36262606Sitojun					data += len;
36362606Sitojun					break;
36462606Sitojun				}
36562606Sitojun				p += len;
36662606Sitojun			}
36762606Sitojun
36862606Sitojun#ifdef	HAVE_IFAM_DATA
36962606Sitojun			/* ifam_data needs to be aligned */
37062606Sitojun			ift->ifa_data = data = (void *)ALIGN(data);
37162606Sitojun			memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data));
37262606Sitojun			data += sizeof(ifam->ifam_data);
37362606Sitojun#endif	/* HAVE_IFAM_DATA */
37462606Sitojun
37562606Sitojun			ift = (ift->ifa_next = ift + 1);
37662606Sitojun			break;
37762606Sitojun		}
37862606Sitojun	}
37962606Sitojun
38062606Sitojun	free(buf);
38162606Sitojun#else	/* NET_RT_IFLIST */
38262606Sitojun	ifr = ifc.ifc_req;
38362606Sitojun	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
38462606Sitojun
38562606Sitojun	while (ifr < lifr) {
38662606Sitojun		struct sockaddr *sa;
38762606Sitojun
38862606Sitojun		ift->ifa_name = names;
38962606Sitojun		names[sizeof(ifr->ifr_name)] = 0;
39062606Sitojun		strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name));
39162606Sitojun		while (*names++)
39262606Sitojun			;
39362606Sitojun
39462606Sitojun		ift->ifa_addr = (struct sockaddr *)data;
39562606Sitojun		sa = &ifr->ifr_addr;
39662606Sitojun		memcpy(data, sa, SA_LEN(sa));
39762606Sitojun		data += SA_RLEN(sa);
39862606Sitojun
39962606Sitojun		ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
40062606Sitojun		ift = (ift->ifa_next = ift + 1);
40162606Sitojun	}
40262606Sitojun#endif	/* NET_RT_IFLIST */
40362606Sitojun	if (--ift >= ifa) {
40462606Sitojun		ift->ifa_next = NULL;
40562606Sitojun		*pif = ifa;
40662606Sitojun	} else {
40762606Sitojun		*pif = NULL;
40862606Sitojun		free(ifa);
40962606Sitojun	}
41062606Sitojun	return (0);
41162606Sitojun}
41262606Sitojun
41362606Sitojunvoid
41462606Sitojunfreeifaddrs(struct ifaddrs *ifp)
41562606Sitojun{
41684372Sume
41762606Sitojun	free(ifp);
41862606Sitojun}
419