ifiter_sysctl.c revision 258945
129881Swollman/*
229881Swollman * Copyright (C) 2004, 2005, 2007  Internet Systems Consortium, Inc. ("ISC")
329881Swollman * Copyright (C) 1999-2003  Internet Software Consortium.
429881Swollman *
529881Swollman * Permission to use, copy, modify, and/or distribute this software for any
629881Swollman * purpose with or without fee is hereby granted, provided that the above
729881Swollman * copyright notice and this permission notice appear in all copies.
829881Swollman *
929881Swollman * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
1029881Swollman * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1129881Swollman * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
1229881Swollman * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1329881Swollman * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
1429881Swollman * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1529881Swollman * PERFORMANCE OF THIS SOFTWARE.
1629881Swollman */
1729881Swollman
1829881Swollman/* $Id: ifiter_sysctl.c,v 1.25 2007/06/19 23:47:18 tbox Exp $ */
1929881Swollman
2029881Swollman/*! \file
2129881Swollman * \brief
2229881Swollman * Obtain the list of network interfaces using sysctl.
2329881Swollman * See TCP/IP Illustrated Volume 2, sections 19.8, 19.14,
2429881Swollman * and 19.16.
2529881Swollman */
2629881Swollman
2729881Swollman#include <sys/param.h>
2829881Swollman#include <sys/sysctl.h>
2929881Swollman
3029881Swollman#include <net/route.h>
3129881Swollman#include <net/if_dl.h>
3229881Swollman
3329881Swollman/* XXX what about Alpha? */
34126229Sbde#ifdef sgi
35126229Sbde#define ROUNDUP(a) ((a) > 0 ? \
36126229Sbde		(1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) : \
37126229Sbde		sizeof(__uint64_t))
38126229Sbde#else
39126229Sbde#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \
40126229Sbde                    : sizeof(long))
41126229Sbde#endif
42126229Sbde
4387715Smarkm#define IFITER_MAGIC		ISC_MAGIC('I', 'F', 'I', 'S')
4487715Smarkm#define VALID_IFITER(t)		ISC_MAGIC_VALID(t, IFITER_MAGIC)
4587715Smarkm
4629881Swollmanstruct isc_interfaceiter {
4729881Swollman	unsigned int		magic;		/* Magic number. */
4829881Swollman	isc_mem_t		*mctx;
4929881Swollman	void			*buf;		/* Buffer for sysctl data. */
5029881Swollman	unsigned int		bufsize;	/* Bytes allocated. */
5129881Swollman	unsigned int		bufused;	/* Bytes used. */
5229881Swollman	unsigned int		pos;		/* Current offset in
5329881Swollman						   sysctl data. */
5429881Swollman	isc_interface_t		current;	/* Current interface data. */
5529881Swollman	isc_result_t		result;		/* Last result code. */
5629881Swollman};
5729881Swollman
5829881Swollmanstatic int mib[6] = {
5929881Swollman	CTL_NET,
6029881Swollman	PF_ROUTE,
6129881Swollman        0,
6229881Swollman	0, 			/* Any address family. */
6387715Smarkm        NET_RT_IFLIST,
6429881Swollman	0 			/* Flags. */
6529881Swollman};
6629881Swollman
6729881Swollmanisc_result_t
6829881Swollmanisc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) {
6929881Swollman	isc_interfaceiter_t *iter;
7029881Swollman	isc_result_t result;
7129881Swollman	size_t bufsize;
7229881Swollman	size_t bufused;
73158160Sbde	char strbuf[ISC_STRERRORSIZE];
74158160Sbde
75158160Sbde	REQUIRE(mctx != NULL);
76170780Sjhb	REQUIRE(iterp != NULL);
77170780Sjhb	REQUIRE(*iterp == NULL);
78170780Sjhb
79170780Sjhb	iter = isc_mem_get(mctx, sizeof(*iter));
80170780Sjhb	if (iter == NULL)
81170780Sjhb		return (ISC_R_NOMEMORY);
82170780Sjhb
83170780Sjhb	iter->mctx = mctx;
84170780Sjhb	iter->buf = 0;
85170780Sjhb
86170780Sjhb	/*
87170780Sjhb	 * Determine the amount of memory needed.
88170780Sjhb	 */
89170780Sjhb	bufsize = 0;
90170780Sjhb	if (sysctl(mib, 6, NULL, &bufsize, NULL, (size_t) 0) < 0) {
91170780Sjhb		isc__strerror(errno, strbuf, sizeof(strbuf));
92170780Sjhb		UNEXPECTED_ERROR(__FILE__, __LINE__,
93170780Sjhb				 isc_msgcat_get(isc_msgcat,
9429881Swollman						ISC_MSGSET_IFITERSYSCTL,
9529881Swollman						ISC_MSG_GETIFLISTSIZE,
9629881Swollman						"getting interface "
9729881Swollman						"list size: sysctl: %s"),
9829881Swollman				 strbuf);
9929881Swollman		result = ISC_R_UNEXPECTED;
10029881Swollman		goto failure;
101158160Sbde	}
10229881Swollman	iter->bufsize = bufsize;
10329881Swollman
10429881Swollman	iter->buf = isc_mem_get(iter->mctx, iter->bufsize);
10529881Swollman	if (iter->buf == NULL) {
10629881Swollman		result = ISC_R_NOMEMORY;
10729881Swollman		goto failure;
10829881Swollman	}
10929881Swollman
11029881Swollman	bufused = bufsize;
11129881Swollman	if (sysctl(mib, 6, iter->buf, &bufused, NULL, (size_t) 0) < 0) {
112158160Sbde		isc__strerror(errno, strbuf, sizeof(strbuf));
11329881Swollman		UNEXPECTED_ERROR(__FILE__, __LINE__,
11429881Swollman				 isc_msgcat_get(isc_msgcat,
11529881Swollman						ISC_MSGSET_IFITERSYSCTL,
11629881Swollman						ISC_MSG_GETIFLIST,
11729881Swollman						"getting interface list: "
11829881Swollman						"sysctl: %s"),
11929881Swollman				 strbuf);
12029881Swollman		result = ISC_R_UNEXPECTED;
121158160Sbde		goto failure;
122158160Sbde	}
123158160Sbde	iter->bufused = bufused;
124170780Sjhb	INSIST(iter->bufused <= iter->bufsize);
125170780Sjhb
126170780Sjhb	/*
127170780Sjhb	 * A newly created iterator has an undefined position
128170780Sjhb	 * until isc_interfaceiter_first() is called.
129170780Sjhb	 */
130170780Sjhb	iter->pos = (unsigned int) -1;
131170780Sjhb	iter->result = ISC_R_FAILURE;
132170780Sjhb
133170780Sjhb	iter->magic = IFITER_MAGIC;
134170780Sjhb	*iterp = iter;
135170780Sjhb	return (ISC_R_SUCCESS);
136170780Sjhb
137170780Sjhb failure:
138170780Sjhb	if (iter->buf != NULL)
139170780Sjhb		isc_mem_put(mctx, iter->buf, iter->bufsize);
140170780Sjhb	isc_mem_put(mctx, iter, sizeof(*iter));
141170780Sjhb	return (result);
14229881Swollman}
14329881Swollman
14429881Swollman/*
14529881Swollman * Get information about the current interface to iter->current.
14629881Swollman * If successful, return ISC_R_SUCCESS.
14729881Swollman * If the interface has an unsupported address family,
14829881Swollman * return ISC_R_IGNORE.  In case of other failure,
14929881Swollman * return ISC_R_UNEXPECTED.
15040060Sobrien */
15129881Swollman
15229881Swollmanstatic isc_result_t
15329881Swollmaninternal_current(isc_interfaceiter_t *iter) {
15429881Swollman	struct ifa_msghdr *ifam, *ifam_end;
15529881Swollman
15629881Swollman	REQUIRE(VALID_IFITER(iter));
15729881Swollman	REQUIRE (iter->pos < (unsigned int) iter->bufused);
15829881Swollman
15929881Swollman	ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos);
16029881Swollman	ifam_end = (struct ifa_msghdr *) ((char *) iter->buf + iter->bufused);
16129881Swollman
16229881Swollman	if (ifam->ifam_type == RTM_IFINFO) {
16329881Swollman		struct if_msghdr *ifm = (struct if_msghdr *) ifam;
16429881Swollman		struct sockaddr_dl *sdl = (struct sockaddr_dl *) (ifm + 1);
16529881Swollman		unsigned int namelen;
16629881Swollman
16729881Swollman		memset(&iter->current, 0, sizeof(iter->current));
16829881Swollman
16929881Swollman		iter->current.ifindex = sdl->sdl_index;
17029881Swollman		namelen = sdl->sdl_nlen;
17129881Swollman		if (namelen > sizeof(iter->current.name) - 1)
17229881Swollman			namelen = sizeof(iter->current.name) - 1;
17329881Swollman
17429881Swollman		memset(iter->current.name, 0, sizeof(iter->current.name));
17529881Swollman		memcpy(iter->current.name, sdl->sdl_data, namelen);
17629881Swollman
17729881Swollman		iter->current.flags = 0;
17829881Swollman
17929881Swollman		if ((ifam->ifam_flags & IFF_UP) != 0)
18029881Swollman			iter->current.flags |= INTERFACE_F_UP;
18129881Swollman
18229881Swollman		if ((ifam->ifam_flags & IFF_POINTOPOINT) != 0)
18329881Swollman			iter->current.flags |= INTERFACE_F_POINTTOPOINT;
18429881Swollman
18529881Swollman		if ((ifam->ifam_flags & IFF_LOOPBACK) != 0)
18629881Swollman			iter->current.flags |= INTERFACE_F_LOOPBACK;
18729881Swollman
188170780Sjhb		if ((ifam->ifam_flags & IFF_BROADCAST) != 0)
18929881Swollman			iter->current.flags |= INTERFACE_F_BROADCAST;
19029881Swollman
19129881Swollman#ifdef IFF_MULTICAST
19229881Swollman		if ((ifam->ifam_flags & IFF_MULTICAST) != 0)
19329881Swollman			iter->current.flags |= INTERFACE_F_MULTICAST;
19429881Swollman#endif
19529881Swollman
19629881Swollman		/*
19729881Swollman		 * This is not an interface address.
19829881Swollman		 * Force another iteration.
19929881Swollman		 */
20029881Swollman		return (ISC_R_IGNORE);
20129881Swollman	} else if (ifam->ifam_type == RTM_NEWADDR) {
20229881Swollman		int i;
20329881Swollman		int family;
20429881Swollman		struct sockaddr *mask_sa = NULL;
20529881Swollman		struct sockaddr *addr_sa = NULL;
20629881Swollman		struct sockaddr *dst_sa = NULL;
20729881Swollman
20829881Swollman		struct sockaddr *sa = (struct sockaddr *)(ifam + 1);
20929881Swollman		family = sa->sa_family;
21029881Swollman
21129881Swollman		for (i = 0; i < RTAX_MAX; i++)
21229881Swollman		{
21329881Swollman			if ((ifam->ifam_addrs & (1 << i)) == 0)
21429881Swollman				continue;
21529881Swollman
21629881Swollman			INSIST(sa < (struct sockaddr *) ifam_end);
21729881Swollman
21829881Swollman			switch (i) {
21929881Swollman			case RTAX_NETMASK: /* Netmask */
22029881Swollman				mask_sa = sa;
22129881Swollman				break;
22229881Swollman			case RTAX_IFA: /* Interface address */
22329881Swollman				addr_sa = sa;
22429881Swollman				break;
22529881Swollman			case RTAX_BRD: /* Broadcast or destination address */
22629881Swollman				dst_sa = sa;
22729881Swollman				break;
22829881Swollman			}
22929881Swollman#ifdef ISC_PLATFORM_HAVESALEN
23029881Swollman			sa = (struct sockaddr *)((char*)(sa)
23129881Swollman					 + ROUNDUP(sa->sa_len));
232158161Sbde#else
23329881Swollman#ifdef sgi
23429881Swollman			/*
23529881Swollman			 * Do as the contributed SGI code does.
23629881Swollman			 */
23729881Swollman			sa = (struct sockaddr *)((char*)(sa)
23829881Swollman					 + ROUNDUP(_FAKE_SA_LEN_DST(sa)));
23929881Swollman#else
24029881Swollman			/* XXX untested. */
24129881Swollman			sa = (struct sockaddr *)((char*)(sa)
24229881Swollman					 + ROUNDUP(sizeof(struct sockaddr)));
24329881Swollman#endif
24429881Swollman#endif
245158160Sbde		}
246158160Sbde
247158160Sbde		if (addr_sa == NULL)
248170780Sjhb			return (ISC_R_IGNORE);
249170780Sjhb
250170780Sjhb		family = addr_sa->sa_family;
251170780Sjhb		if (family != AF_INET && family != AF_INET6)
252170780Sjhb			return (ISC_R_IGNORE);
253170780Sjhb
254170780Sjhb		iter->current.af = family;
255170780Sjhb
256170780Sjhb		get_addr(family, &iter->current.address, addr_sa,
257170780Sjhb			 iter->current.name);
258170780Sjhb
259170780Sjhb		if (mask_sa != NULL)
260170780Sjhb			get_addr(family, &iter->current.netmask, mask_sa,
261170780Sjhb				 iter->current.name);
262170780Sjhb
263170780Sjhb		if (dst_sa != NULL &&
264170780Sjhb		    (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0)
26529881Swollman			get_addr(family, &iter->current.dstaddress, dst_sa,
26629881Swollman				 iter->current.name);
26729881Swollman
26829881Swollman		if (dst_sa != NULL &&
26929881Swollman		    (iter->current.flags & INTERFACE_F_BROADCAST) != 0)
27029881Swollman			get_addr(family, &iter->current.broadcast, dst_sa,
27129881Swollman				 iter->current.name);
27229881Swollman
27329881Swollman		return (ISC_R_SUCCESS);
27429881Swollman	} else {
27529881Swollman		printf(isc_msgcat_get(isc_msgcat, ISC_MSGSET_IFITERSYSCTL,
27629881Swollman				      ISC_MSG_UNEXPECTEDTYPE,
27729881Swollman				      "warning: unexpected interface list "
27829881Swollman				      "message type\n"));
27929881Swollman		return (ISC_R_IGNORE);
28029881Swollman	}
28129881Swollman}
28229881Swollman
28329881Swollman/*
28429881Swollman * Step the iterator to the next interface.  Unlike
28529881Swollman * isc_interfaceiter_next(), this may leave the iterator
28629881Swollman * positioned on an interface that will ultimately
28729881Swollman * be ignored.  Return ISC_R_NOMORE if there are no more
28829881Swollman * interfaces, otherwise ISC_R_SUCCESS.
28929881Swollman */
29029881Swollmanstatic isc_result_t
29129881Swollmaninternal_next(isc_interfaceiter_t *iter) {
29229881Swollman	struct ifa_msghdr *ifam;
29329881Swollman	REQUIRE (iter->pos < (unsigned int) iter->bufused);
29429881Swollman
29529881Swollman	ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos);
29629881Swollman
29729881Swollman	iter->pos += ifam->ifam_msglen;
29829881Swollman
29929881Swollman	if (iter->pos >= iter->bufused)
30029881Swollman		return (ISC_R_NOMORE);
30129881Swollman
30229881Swollman	return (ISC_R_SUCCESS);
30329881Swollman}
30429881Swollman
30529881Swollmanstatic void
30629881Swollmaninternal_destroy(isc_interfaceiter_t *iter) {
30729881Swollman	UNUSED(iter); /* Unused. */
30829881Swollman	/*
30929881Swollman	 * Do nothing.
31029881Swollman	 */
31129881Swollman}
31229881Swollman
31329881Swollmanstatic
31429881Swollmanvoid internal_first(isc_interfaceiter_t *iter) {
31529881Swollman	iter->pos = 0;
31629881Swollman}
31729881Swollman