1290001Sglebius/*
2290001Sglebius * Copyright (C) 2004, 2005, 2007-2009  Internet Systems Consortium, Inc. ("ISC")
3290001Sglebius * Copyright (C) 2003  Internet Software Consortium.
4290001Sglebius *
5290001Sglebius * Permission to use, copy, modify, and/or distribute this software for any
6290001Sglebius * purpose with or without fee is hereby granted, provided that the above
7290001Sglebius * copyright notice and this permission notice appear in all copies.
8290001Sglebius *
9290001Sglebius * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10290001Sglebius * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11290001Sglebius * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12290001Sglebius * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13290001Sglebius * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14290001Sglebius * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15290001Sglebius * PERFORMANCE OF THIS SOFTWARE.
16290001Sglebius */
17290001Sglebius
18290001Sglebius/* $Id: ifiter_getifaddrs.c,v 1.13 2009/09/24 23:48:13 tbox Exp $ */
19290001Sglebius
20290001Sglebius/*! \file
21290001Sglebius * \brief
22290001Sglebius * Obtain the list of network interfaces using the getifaddrs(3) library.
23290001Sglebius */
24290001Sglebius
25290001Sglebius#include <ifaddrs.h>
26290001Sglebius
27290001Sglebius/*% Iterator Magic */
28290001Sglebius#define IFITER_MAGIC		ISC_MAGIC('I', 'F', 'I', 'G')
29290001Sglebius/*% Valid Iterator */
30290001Sglebius#define VALID_IFITER(t)		ISC_MAGIC_VALID(t, IFITER_MAGIC)
31290001Sglebius
32290001Sglebius#ifdef __linux
33290001Sglebiusstatic isc_boolean_t seenv6 = ISC_FALSE;
34290001Sglebius#endif
35290001Sglebius
36290001Sglebius/*% Iterator structure */
37290001Sglebiusstruct isc_interfaceiter {
38290001Sglebius	unsigned int		magic;		/*%< Magic number. */
39290001Sglebius	isc_mem_t		*mctx;
40290001Sglebius	void			*buf;		/*%< (unused) */
41290001Sglebius	unsigned int		bufsize;	/*%< (always 0) */
42290001Sglebius	struct ifaddrs		*ifaddrs;	/*%< List of ifaddrs */
43290001Sglebius	struct ifaddrs		*pos;		/*%< Ptr to current ifaddr */
44290001Sglebius	isc_interface_t		current;	/*%< Current interface data. */
45290001Sglebius	isc_result_t		result;		/*%< Last result code. */
46290001Sglebius#ifdef  __linux
47290001Sglebius	FILE *                  proc;
48290001Sglebius	char                    entry[ISC_IF_INET6_SZ];
49290001Sglebius	isc_result_t            valid;
50290001Sglebius#endif
51290001Sglebius};
52290001Sglebius
53290001Sglebiusisc_result_t
54290001Sglebiusisc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) {
55290001Sglebius	isc_interfaceiter_t *iter;
56290001Sglebius	isc_result_t result;
57290001Sglebius	char strbuf[ISC_STRERRORSIZE];
58290001Sglebius	int trys, ret;
59290001Sglebius
60290001Sglebius	REQUIRE(mctx != NULL);
61290001Sglebius	REQUIRE(iterp != NULL);
62290001Sglebius	REQUIRE(*iterp == NULL);
63290001Sglebius
64290001Sglebius	iter = isc_mem_get(mctx, sizeof(*iter));
65290001Sglebius	if (iter == NULL)
66290001Sglebius		return (ISC_R_NOMEMORY);
67290001Sglebius
68290001Sglebius	iter->mctx = mctx;
69290001Sglebius	iter->buf = NULL;
70290001Sglebius	iter->bufsize = 0;
71290001Sglebius	iter->ifaddrs = NULL;
72290001Sglebius#ifdef __linux
73290001Sglebius	/*
74290001Sglebius	 * Only open "/proc/net/if_inet6" if we have never seen a IPv6
75290001Sglebius	 * address returned by getifaddrs().
76290001Sglebius	 */
77290001Sglebius	if (!seenv6) {
78290001Sglebius		iter->proc = fopen("/proc/net/if_inet6", "r");
79290001Sglebius		if (iter->proc == NULL) {
80290001Sglebius			isc__strerror(errno, strbuf, sizeof(strbuf));
81290001Sglebius			isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
82290001Sglebius				      ISC_LOGMODULE_SOCKET, ISC_LOG_WARNING,
83290001Sglebius				      "failed to open /proc/net/if_inet6");
84290001Sglebius		}
85290001Sglebius	} else
86290001Sglebius		iter->proc = NULL;
87290001Sglebius	iter->valid = ISC_R_FAILURE;
88290001Sglebius#endif
89290001Sglebius
90290001Sglebius	/* If interrupted, try again */
91290001Sglebius	for (trys = 0; trys < 3; trys++) {
92290001Sglebius		if ((ret = getifaddrs(&iter->ifaddrs)) >= 0)
93290001Sglebius			break;
94290001Sglebius		if (errno != EINTR)
95290001Sglebius			break;
96290001Sglebius	}
97290001Sglebius	if (ret < 0) {
98290001Sglebius		isc__strerror(errno, strbuf, sizeof(strbuf));
99290001Sglebius		UNEXPECTED_ERROR(__FILE__, __LINE__,
100290001Sglebius                		 "getting interface addresses: %s: %s",
101290001Sglebius				 isc_msgcat_get(isc_msgcat,
102290001Sglebius						ISC_MSGSET_IFITERGETIFADDRS,
103290001Sglebius						ISC_MSG_GETIFADDRS,
104290001Sglebius						"getifaddrs"),
105290001Sglebius				 strbuf);
106290001Sglebius		result = ISC_R_UNEXPECTED;
107290001Sglebius		goto failure;
108290001Sglebius	}
109290001Sglebius
110290001Sglebius	/*
111290001Sglebius	 * A newly created iterator has an undefined position
112290001Sglebius	 * until isc_interfaceiter_first() is called.
113290001Sglebius	 */
114290001Sglebius	iter->pos = NULL;
115290001Sglebius	iter->result = ISC_R_FAILURE;
116290001Sglebius
117290001Sglebius	iter->magic = IFITER_MAGIC;
118290001Sglebius	*iterp = iter;
119290001Sglebius	return (ISC_R_SUCCESS);
120290001Sglebius
121290001Sglebius failure:
122290001Sglebius#ifdef __linux
123290001Sglebius	if (iter->proc != NULL)
124290001Sglebius		fclose(iter->proc);
125290001Sglebius#endif
126290001Sglebius	if (iter->ifaddrs != NULL) /* just in case */
127290001Sglebius		freeifaddrs(iter->ifaddrs);
128290001Sglebius	isc_mem_put(mctx, iter, sizeof(*iter));
129290001Sglebius	return (result);
130290001Sglebius}
131290001Sglebius
132290001Sglebius/*
133290001Sglebius * Get information about the current interface to iter->current.
134290001Sglebius * If successful, return ISC_R_SUCCESS.
135290001Sglebius * If the interface has an unsupported address family,
136290001Sglebius * return ISC_R_IGNORE.
137290001Sglebius */
138290001Sglebius
139290001Sglebiusstatic isc_result_t
140290001Sglebiusinternal_current(isc_interfaceiter_t *iter) {
141290001Sglebius	struct ifaddrs *ifa;
142290001Sglebius	int family;
143290001Sglebius	unsigned int namelen;
144290001Sglebius
145290001Sglebius	REQUIRE(VALID_IFITER(iter));
146290001Sglebius
147290001Sglebius	ifa = iter->pos;
148290001Sglebius
149290001Sglebius#ifdef __linux
150290001Sglebius	/*
151290001Sglebius	 * [Bug 2792]
152290001Sglebius	 * burnicki: iter->pos is usually never NULL here (anymore?),
153290001Sglebius	 * so linux_if_inet6_current(iter) is never called here.
154290001Sglebius	 * However, that routine would check (under Linux), if the
155290001Sglebius	 * interface is in a tentative state, e.g. if there's no link
156290001Sglebius	 * yet but an IPv6 address has already be assigned.
157290001Sglebius	 */
158290001Sglebius	if (iter->pos == NULL)
159290001Sglebius		return (linux_if_inet6_current(iter));
160290001Sglebius#endif
161290001Sglebius
162290001Sglebius	INSIST(ifa != NULL);
163290001Sglebius	INSIST(ifa->ifa_name != NULL);
164290001Sglebius
165290001Sglebius
166290001Sglebius#ifdef IFF_RUNNING
167290001Sglebius	/*
168290001Sglebius	 * [Bug 2792]
169290001Sglebius	 * burnicki: if the interface is not running then
170290001Sglebius	 * it may be in a tentative state. See above.
171290001Sglebius	 */
172290001Sglebius	if ((ifa->ifa_flags & IFF_RUNNING) == 0)
173290001Sglebius		return (ISC_R_IGNORE);
174290001Sglebius#endif
175290001Sglebius
176290001Sglebius	if (ifa->ifa_addr == NULL)
177290001Sglebius		return (ISC_R_IGNORE);
178290001Sglebius
179290001Sglebius	family = ifa->ifa_addr->sa_family;
180290001Sglebius	if (family != AF_INET && family != AF_INET6)
181290001Sglebius		return (ISC_R_IGNORE);
182290001Sglebius
183290001Sglebius#ifdef __linux
184290001Sglebius	if (family == AF_INET6)
185290001Sglebius		seenv6 = ISC_TRUE;
186290001Sglebius#endif
187290001Sglebius
188290001Sglebius	memset(&iter->current, 0, sizeof(iter->current));
189290001Sglebius
190290001Sglebius	namelen = strlen(ifa->ifa_name);
191290001Sglebius	if (namelen > sizeof(iter->current.name) - 1)
192290001Sglebius		namelen = sizeof(iter->current.name) - 1;
193290001Sglebius
194290001Sglebius	memset(iter->current.name, 0, sizeof(iter->current.name));
195290001Sglebius	memcpy(iter->current.name, ifa->ifa_name, namelen);
196290001Sglebius
197290001Sglebius	iter->current.flags = 0;
198290001Sglebius
199290001Sglebius	if ((ifa->ifa_flags & IFF_UP) != 0)
200290001Sglebius		iter->current.flags |= INTERFACE_F_UP;
201290001Sglebius
202290001Sglebius	if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
203290001Sglebius		iter->current.flags |= INTERFACE_F_POINTTOPOINT;
204290001Sglebius
205290001Sglebius	if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
206290001Sglebius		iter->current.flags |= INTERFACE_F_LOOPBACK;
207290001Sglebius
208290001Sglebius	if ((ifa->ifa_flags & IFF_BROADCAST) != 0)
209290001Sglebius		iter->current.flags |= INTERFACE_F_BROADCAST;
210290001Sglebius
211290001Sglebius#ifdef IFF_MULTICAST
212290001Sglebius	if ((ifa->ifa_flags & IFF_MULTICAST) != 0)
213290001Sglebius		iter->current.flags |= INTERFACE_F_MULTICAST;
214290001Sglebius#endif
215290001Sglebius
216290001Sglebius	iter->current.af = family;
217290001Sglebius
218290001Sglebius	get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name);
219290001Sglebius
220290001Sglebius	if (ifa->ifa_netmask != NULL)
221290001Sglebius		get_addr(family, &iter->current.netmask, ifa->ifa_netmask,
222290001Sglebius			 ifa->ifa_name);
223290001Sglebius
224290001Sglebius	if (ifa->ifa_dstaddr != NULL &&
225290001Sglebius	    (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0)
226290001Sglebius		get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr,
227290001Sglebius			 ifa->ifa_name);
228290001Sglebius
229290001Sglebius	if (ifa->ifa_broadaddr != NULL &&
230290001Sglebius	    (iter->current.flags & INTERFACE_F_BROADCAST) != 0)
231290001Sglebius		get_addr(family, &iter->current.broadcast, ifa->ifa_broadaddr,
232290001Sglebius			 ifa->ifa_name);
233290001Sglebius
234290001Sglebius#ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX
235290001Sglebius	iter->current.ifindex = if_nametoindex(iter->current.name);
236290001Sglebius#endif
237290001Sglebius	return (ISC_R_SUCCESS);
238290001Sglebius}
239290001Sglebius
240290001Sglebius/*
241290001Sglebius * Step the iterator to the next interface.  Unlike
242290001Sglebius * isc_interfaceiter_next(), this may leave the iterator
243290001Sglebius * positioned on an interface that will ultimately
244290001Sglebius * be ignored.  Return ISC_R_NOMORE if there are no more
245290001Sglebius * interfaces, otherwise ISC_R_SUCCESS.
246290001Sglebius */
247290001Sglebiusstatic isc_result_t
248290001Sglebiusinternal_next(isc_interfaceiter_t *iter) {
249290001Sglebius
250290001Sglebius	if (iter->pos != NULL)
251290001Sglebius		iter->pos = iter->pos->ifa_next;
252290001Sglebius	if (iter->pos == NULL) {
253290001Sglebius#ifdef __linux
254290001Sglebius		if (!seenv6)
255290001Sglebius			return (linux_if_inet6_next(iter));
256290001Sglebius#endif
257290001Sglebius		return (ISC_R_NOMORE);
258290001Sglebius	}
259290001Sglebius
260290001Sglebius	return (ISC_R_SUCCESS);
261290001Sglebius}
262290001Sglebius
263290001Sglebiusstatic void
264290001Sglebiusinternal_destroy(isc_interfaceiter_t *iter) {
265290001Sglebius
266290001Sglebius#ifdef __linux
267290001Sglebius	if (iter->proc != NULL)
268290001Sglebius		fclose(iter->proc);
269290001Sglebius	iter->proc = NULL;
270290001Sglebius#endif
271290001Sglebius	if (iter->ifaddrs)
272290001Sglebius		freeifaddrs(iter->ifaddrs);
273290001Sglebius	iter->ifaddrs = NULL;
274290001Sglebius}
275290001Sglebius
276290001Sglebiusstatic
277290001Sglebiusvoid internal_first(isc_interfaceiter_t *iter) {
278290001Sglebius
279290001Sglebius#ifdef __linux
280290001Sglebius	linux_if_inet6_first(iter);
281290001Sglebius#endif
282290001Sglebius	iter->pos = iter->ifaddrs;
283290001Sglebius}
284