ifiter_getifaddrs.c revision 285169
133180Sjdp/*
233180Sjdp * Copyright (C) 2004, 2005, 2007-2009  Internet Systems Consortium, Inc. ("ISC")
333180Sjdp * Copyright (C) 2003  Internet Software Consortium.
433180Sjdp *
533180Sjdp * Permission to use, copy, modify, and/or distribute this software for any
633180Sjdp * purpose with or without fee is hereby granted, provided that the above
733180Sjdp * copyright notice and this permission notice appear in all copies.
833180Sjdp *
933180Sjdp * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
1033180Sjdp * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1133180Sjdp * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
1233180Sjdp * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1333180Sjdp * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
1433180Sjdp * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1533180Sjdp * PERFORMANCE OF THIS SOFTWARE.
1633180Sjdp */
1767967Sasmodai
1833180Sjdp/* $Id: ifiter_getifaddrs.c,v 1.13 2009/09/24 23:48:13 tbox Exp $ */
1967967Sasmodai
2033180Sjdp/*! \file
2133180Sjdp * \brief
2233180Sjdp * Obtain the list of network interfaces using the getifaddrs(3) library.
2333180Sjdp */
2433180Sjdp
2533180Sjdp#include <ifaddrs.h>
2633180Sjdp
2733180Sjdp/*% Iterator Magic */
2833180Sjdp#define IFITER_MAGIC		ISC_MAGIC('I', 'F', 'I', 'G')
2933180Sjdp/*% Valid Iterator */
3033180Sjdp#define VALID_IFITER(t)		ISC_MAGIC_VALID(t, IFITER_MAGIC)
3133180Sjdp
3233180Sjdp#ifdef __linux
3350476Speterstatic isc_boolean_t seenv6 = ISC_FALSE;
3448794Snik#endif
35230410Skib
36206622Suqs/*% Iterator structure */
3779531Srustruct isc_interfaceiter {
3833180Sjdp	unsigned int		magic;		/*%< Magic number. */
39110854Sphantom	isc_mem_t		*mctx;
40230410Skib	void			*buf;		/*%< (unused) */
41110854Sphantom	unsigned int		bufsize;	/*%< (always 0) */
42110854Sphantom	struct ifaddrs		*ifaddrs;	/*%< List of ifaddrs */
43110854Sphantom	struct ifaddrs		*pos;		/*%< Ptr to current ifaddr */
44110854Sphantom	isc_interface_t		current;	/*%< Current interface data. */
4533180Sjdp	isc_result_t		result;		/*%< Last result code. */
4659460Sphantom#ifdef  __linux
4759460Sphantom	FILE *                  proc;
4833180Sjdp	char                    entry[ISC_IF_INET6_SZ];
4984306Sru	isc_result_t            valid;
5033180Sjdp#endif
5133180Sjdp};
5233180Sjdp
53230410Skibisc_result_t
54230410Skibisc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) {
55103213Smike	isc_interfaceiter_t *iter;
5697509Swollman	isc_result_t result;
57103213Smike	char strbuf[ISC_STRERRORSIZE];
58205606Sgahr	int trys, ret;
5933180Sjdp
6033180Sjdp	REQUIRE(mctx != NULL);
6133180Sjdp	REQUIRE(iterp != NULL);
6233180Sjdp	REQUIRE(*iterp == NULL);
6333180Sjdp
6433180Sjdp	iter = isc_mem_get(mctx, sizeof(*iter));
6533180Sjdp	if (iter == NULL)
6633180Sjdp		return (ISC_R_NOMEMORY);
6733180Sjdp
6833180Sjdp	iter->mctx = mctx;
6933180Sjdp	iter->buf = NULL;
70108030Sru	iter->bufsize = 0;
7133180Sjdp	iter->ifaddrs = NULL;
72108030Sru#ifdef __linux
7367967Sasmodai	/*
7433180Sjdp	 * Only open "/proc/net/if_inet6" if we have never seen a IPv6
7533180Sjdp	 * address returned by getifaddrs().
7667967Sasmodai	 */
7733180Sjdp	if (!seenv6) {
7833180Sjdp		iter->proc = fopen("/proc/net/if_inet6", "r");
7933180Sjdp		if (iter->proc == NULL) {
8033180Sjdp			isc__strerror(errno, strbuf, sizeof(strbuf));
8133180Sjdp			isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
8233180Sjdp				      ISC_LOGMODULE_SOCKET, ISC_LOG_WARNING,
8333180Sjdp				      "failed to open /proc/net/if_inet6");
8433180Sjdp		}
8533180Sjdp	} else
8633180Sjdp		iter->proc = NULL;
8733180Sjdp	iter->valid = ISC_R_FAILURE;
8833180Sjdp#endif
8933180Sjdp
9033180Sjdp	/* If interrupted, try again */
9133180Sjdp	for (trys = 0; trys < 3; trys++) {
9267967Sasmodai		if ((ret = getifaddrs(&iter->ifaddrs)) >= 0)
9367967Sasmodai			break;
9433180Sjdp		if (errno != EINTR)
9533180Sjdp			break;
9633180Sjdp	}
9733180Sjdp	if (ret < 0) {
9867967Sasmodai		isc__strerror(errno, strbuf, sizeof(strbuf));
9933180Sjdp		UNEXPECTED_ERROR(__FILE__, __LINE__,
10033180Sjdp                		 "getting interface addresses: %s: %s",
10133180Sjdp				 isc_msgcat_get(isc_msgcat,
102108087Sru						ISC_MSGSET_IFITERGETIFADDRS,
10333180Sjdp						ISC_MSG_GETIFADDRS,
104108087Sru						"getifaddrs"),
10533180Sjdp				 strbuf);
10633180Sjdp		result = ISC_R_UNEXPECTED;
10766054Sjdp		goto failure;
10866054Sjdp	}
10933180Sjdp
11033180Sjdp	/*
11133180Sjdp	 * A newly created iterator has an undefined position
11233180Sjdp	 * until isc_interfaceiter_first() is called.
11333180Sjdp	 */
11433180Sjdp	iter->pos = NULL;
11533180Sjdp	iter->result = ISC_R_FAILURE;
11633180Sjdp
11733180Sjdp	iter->magic = IFITER_MAGIC;
11833180Sjdp	*iterp = iter;
11933180Sjdp	return (ISC_R_SUCCESS);
12033180Sjdp
12133180Sjdp failure:
12233180Sjdp#ifdef __linux
12333180Sjdp	if (iter->proc != NULL)
12433180Sjdp		fclose(iter->proc);
12566054Sjdp#endif
12666054Sjdp	if (iter->ifaddrs != NULL) /* just in case */
12766054Sjdp		freeifaddrs(iter->ifaddrs);
12866054Sjdp	isc_mem_put(mctx, iter, sizeof(*iter));
129190624Skib	return (result);
13066054Sjdp}
13166054Sjdp
13266054Sjdp/*
13366054Sjdp * Get information about the current interface to iter->current.
13466054Sjdp * If successful, return ISC_R_SUCCESS.
13566054Sjdp * If the interface has an unsupported address family,
13666054Sjdp * return ISC_R_IGNORE.
137101574Sru */
138101574Sru
13966054Sjdpstatic isc_result_t
14090172Ssobomaxinternal_current(isc_interfaceiter_t *iter) {
14190172Ssobomax	struct ifaddrs *ifa;
14290172Ssobomax	int family;
14390172Ssobomax	unsigned int namelen;
14490172Ssobomax
14590172Ssobomax	REQUIRE(VALID_IFITER(iter));
14690172Ssobomax
147190624Skib	ifa = iter->pos;
148190624Skib
149190624Skib#ifdef __linux
150190624Skib	/*
151190624Skib	 * [Bug 2792]
152190624Skib	 * burnicki: iter->pos is usually never NULL here (anymore?),
153190624Skib	 * so linux_if_inet6_current(iter) is never called here.
154195745Skib	 * However, that routine would check (under Linux), if the
155211397Sjoel	 * interface is in a tentative state, e.g. if there's no link
156195745Skib	 * yet but an IPv6 address has already be assigned.
157195745Skib	 */
158195745Skib	if (iter->pos == NULL)
159195745Skib		return (linux_if_inet6_current(iter));
160195745Skib#endif
16166054Sjdp
16266054Sjdp	INSIST(ifa != NULL);
16367967Sasmodai	INSIST(ifa->ifa_name != NULL);
16433180Sjdp
16533180Sjdp
16633180Sjdp#ifdef IFF_RUNNING
16733180Sjdp	/*
16833180Sjdp	 * [Bug 2792]
169108030Sru	 * burnicki: if the interface is not running then
170230410Skib	 * it may be in a tentative state. See above.
171230410Skib	 */
172230410Skib	if ((ifa->ifa_flags & IFF_RUNNING) == 0)
173230410Skib		return (ISC_R_IGNORE);
174230410Skib#endif
175230410Skib
176230410Skib	if (ifa->ifa_addr == NULL)
177230410Skib		return (ISC_R_IGNORE);
178230410Skib
179230410Skib	family = ifa->ifa_addr->sa_family;
180230410Skib	if (family != AF_INET && family != AF_INET6)
181230410Skib		return (ISC_R_IGNORE);
182230410Skib
183230410Skib#ifdef __linux
184230410Skib	if (family == AF_INET6)
185230410Skib		seenv6 = ISC_TRUE;
186230410Skib#endif
187230410Skib
188230410Skib	memset(&iter->current, 0, sizeof(iter->current));
189230410Skib
190230410Skib	namelen = strlen(ifa->ifa_name);
191230410Skib	if (namelen > sizeof(iter->current.name) - 1)
192230410Skib		namelen = sizeof(iter->current.name) - 1;
193230410Skib
194230410Skib	memset(iter->current.name, 0, sizeof(iter->current.name));
195230410Skib	memcpy(iter->current.name, ifa->ifa_name, namelen);
196230410Skib
197230410Skib	iter->current.flags = 0;
198230410Skib
199230410Skib	if ((ifa->ifa_flags & IFF_UP) != 0)
20033180Sjdp		iter->current.flags |= INTERFACE_F_UP;
201108030Sru
20233180Sjdp	if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
20333180Sjdp		iter->current.flags |= INTERFACE_F_POINTTOPOINT;
20433180Sjdp
20533180Sjdp	if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
20633180Sjdp		iter->current.flags |= INTERFACE_F_LOOPBACK;
20767967Sasmodai
20833180Sjdp	if ((ifa->ifa_flags & IFF_BROADCAST) != 0)
20933180Sjdp		iter->current.flags |= INTERFACE_F_BROADCAST;
21033180Sjdp
21133180Sjdp#ifdef IFF_MULTICAST
21233180Sjdp	if ((ifa->ifa_flags & IFF_MULTICAST) != 0)
21333180Sjdp		iter->current.flags |= INTERFACE_F_MULTICAST;
21466057Sjdp#endif
21566057Sjdp
21666057Sjdp	iter->current.af = family;
21766057Sjdp
21833180Sjdp	get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name);
21966057Sjdp
22066057Sjdp	if (ifa->ifa_netmask != NULL)
22166057Sjdp		get_addr(family, &iter->current.netmask, ifa->ifa_netmask,
222101574Sru			 ifa->ifa_name);
223101574Sru
22466057Sjdp	if (ifa->ifa_dstaddr != NULL &&
22566057Sjdp	    (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0)
22633180Sjdp		get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr,
22766057Sjdp			 ifa->ifa_name);
22866057Sjdp
22966057Sjdp	if (ifa->ifa_broadaddr != NULL &&
23066057Sjdp	    (iter->current.flags & INTERFACE_F_BROADCAST) != 0)
231101574Sru		get_addr(family, &iter->current.broadcast, ifa->ifa_broadaddr,
232101574Sru			 ifa->ifa_name);
23366057Sjdp
23466057Sjdp#ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX
23566057Sjdp	iter->current.ifindex = if_nametoindex(iter->current.name);
23666057Sjdp#endif
23766057Sjdp	return (ISC_R_SUCCESS);
23866057Sjdp}
23966057Sjdp
24066057Sjdp/*
24166057Sjdp * Step the iterator to the next interface.  Unlike
24266057Sjdp * isc_interfaceiter_next(), this may leave the iterator
24366057Sjdp * positioned on an interface that will ultimately
24466057Sjdp * be ignored.  Return ISC_R_NOMORE if there are no more
24566057Sjdp * interfaces, otherwise ISC_R_SUCCESS.
24666057Sjdp */
24766057Sjdpstatic isc_result_t
24866057Sjdpinternal_next(isc_interfaceiter_t *iter) {
24966057Sjdp
25066057Sjdp	if (iter->pos != NULL)
25166057Sjdp		iter->pos = iter->pos->ifa_next;
252130027Sroam	if (iter->pos == NULL) {
253130027Sroam#ifdef __linux
254130027Sroam		if (!seenv6)
255130027Sroam			return (linux_if_inet6_next(iter));
25666057Sjdp#endif
25733180Sjdp		return (ISC_R_NOMORE);
25833180Sjdp	}
25933180Sjdp
26033180Sjdp	return (ISC_R_SUCCESS);
26133180Sjdp}
26233180Sjdp
26333180Sjdpstatic void
26433180Sjdpinternal_destroy(isc_interfaceiter_t *iter) {
26533180Sjdp
26633180Sjdp#ifdef __linux
26733180Sjdp	if (iter->proc != NULL)
26833180Sjdp		fclose(iter->proc);
26933180Sjdp	iter->proc = NULL;
27033180Sjdp#endif
27133180Sjdp	if (iter->ifaddrs)
27233180Sjdp		freeifaddrs(iter->ifaddrs);
27333180Sjdp	iter->ifaddrs = NULL;
27433180Sjdp}
27533180Sjdp
27633180Sjdpstatic
27733180Sjdpvoid internal_first(isc_interfaceiter_t *iter) {
27852802Sjoerg
27997475Swollman#ifdef __linux
28097475Swollman	linux_if_inet6_first(iter);
28197475Swollman#endif
28297475Swollman	iter->pos = iter->ifaddrs;
28397475Swollman}
28433180Sjdp