1290001Sglebius/*
2290001Sglebius * Copyright (C) 2004, 2005, 2007, 2008  Internet Systems Consortium, Inc. ("ISC")
3290001Sglebius * Copyright (C) 1999-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: interfaceiter.c,v 1.45 2008/12/01 03:51:47 marka Exp $ */
19290001Sglebius
20290001Sglebius/*! \file */
21290001Sglebius
22290001Sglebius#include <config.h>
23290001Sglebius
24290001Sglebius#include <sys/types.h>
25290001Sglebius#include <sys/ioctl.h>
26290001Sglebius#ifdef HAVE_SYS_SOCKIO_H
27290001Sglebius#include <sys/sockio.h>		/* Required for ifiter_ioctl.c. */
28290001Sglebius#endif
29290001Sglebius
30290001Sglebius#include <stdio.h>
31290001Sglebius#include <stdlib.h>
32290001Sglebius#include <unistd.h>
33290001Sglebius#include <errno.h>
34290001Sglebius
35290001Sglebius#include <isc/interfaceiter.h>
36290001Sglebius#include <isc/log.h>
37290001Sglebius#include <isc/magic.h>
38290001Sglebius#include <isc/mem.h>
39290001Sglebius#include <isc/msgs.h>
40290001Sglebius#include <isc/net.h>
41290001Sglebius#include <isc/print.h>
42290001Sglebius#include <isc/result.h>
43290001Sglebius#include <isc/strerror.h>
44290001Sglebius#include <isc/string.h>
45290001Sglebius#include <isc/types.h>
46290001Sglebius#include <isc/util.h>
47290001Sglebius
48290001Sglebius/* Must follow <isc/net.h>. */
49290001Sglebius#ifdef HAVE_NET_IF6_H
50290001Sglebius#include <net/if6.h>
51290001Sglebius#endif
52290001Sglebius#include <net/if.h>
53290001Sglebius
54290001Sglebius#ifdef HAVE_LINUX_IF_ADDR_H
55290001Sglebius# include <linux/if_addr.h>
56290001Sglebius#endif
57290001Sglebius
58290001Sglebius/* Common utility functions */
59290001Sglebius
60290001Sglebius/*%
61290001Sglebius * Extract the network address part from a "struct sockaddr".
62290001Sglebius * \brief
63290001Sglebius * The address family is given explicitly
64290001Sglebius * instead of using src->sa_family, because the latter does not work
65290001Sglebius * for copying a network mask obtained by SIOCGIFNETMASK (it does
66290001Sglebius * not have a valid address family).
67290001Sglebius */
68290001Sglebius
69290001Sglebiusstatic void
70290001Sglebiusget_addr(unsigned int family, isc_netaddr_t *dst, struct sockaddr *src,
71290001Sglebius	 char *ifname)
72290001Sglebius{
73290001Sglebius	struct sockaddr_in6 *sa6;
74290001Sglebius
75290001Sglebius#if !defined(ISC_PLATFORM_HAVEIFNAMETOINDEX) || \
76290001Sglebius    !defined(ISC_PLATFORM_HAVESCOPEID)
77290001Sglebius	UNUSED(ifname);
78290001Sglebius#endif
79290001Sglebius
80290001Sglebius	/* clear any remaining value for safety */
81290001Sglebius	memset(dst, 0, sizeof(*dst));
82290001Sglebius
83290001Sglebius	dst->family = family;
84290001Sglebius	switch (family) {
85290001Sglebius	case AF_INET:
86290001Sglebius		memcpy(&dst->type.in,
87290001Sglebius		       &((struct sockaddr_in *)(void *)src)->sin_addr,
88290001Sglebius		       sizeof(struct in_addr));
89290001Sglebius		break;
90290001Sglebius	case AF_INET6:
91290001Sglebius		sa6 = (struct sockaddr_in6 *)(void *)src;
92290001Sglebius		memcpy(&dst->type.in6, &sa6->sin6_addr,
93290001Sglebius		       sizeof(struct in6_addr));
94290001Sglebius#ifdef ISC_PLATFORM_HAVESCOPEID
95290001Sglebius		if (sa6->sin6_scope_id != 0)
96290001Sglebius			isc_netaddr_setzone(dst, sa6->sin6_scope_id);
97290001Sglebius		else {
98290001Sglebius			/*
99290001Sglebius			 * BSD variants embed scope zone IDs in the 128bit
100290001Sglebius			 * address as a kernel internal form.  Unfortunately,
101290001Sglebius			 * the embedded IDs are not hidden from applications
102290001Sglebius			 * when getting access to them by sysctl or ioctl.
103290001Sglebius			 * We convert the internal format to the pure address
104290001Sglebius			 * part and the zone ID part.
105290001Sglebius			 * Since multicast addresses should not appear here
106290001Sglebius			 * and they cannot be distinguished from netmasks,
107290001Sglebius			 * we only consider unicast link-local addresses.
108290001Sglebius			 */
109290001Sglebius			if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
110290001Sglebius				isc_uint16_t zone16;
111290001Sglebius
112290001Sglebius				memcpy(&zone16, &sa6->sin6_addr.s6_addr[2],
113290001Sglebius				       sizeof(zone16));
114290001Sglebius				zone16 = ntohs(zone16);
115290001Sglebius				if (zone16 != 0) {
116290001Sglebius					/* the zone ID is embedded */
117290001Sglebius					isc_netaddr_setzone(dst,
118290001Sglebius							    (isc_uint32_t)zone16);
119290001Sglebius					dst->type.in6.s6_addr[2] = 0;
120290001Sglebius					dst->type.in6.s6_addr[3] = 0;
121290001Sglebius#ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX
122290001Sglebius				} else if (ifname != NULL) {
123290001Sglebius					unsigned int zone;
124290001Sglebius
125290001Sglebius					/*
126290001Sglebius					 * sin6_scope_id is still not provided,
127290001Sglebius					 * but the corresponding interface name
128290001Sglebius					 * is know.  Use the interface ID as
129290001Sglebius					 * the link ID.
130290001Sglebius					 */
131290001Sglebius					zone = if_nametoindex(ifname);
132290001Sglebius					if (zone != 0) {
133290001Sglebius						isc_netaddr_setzone(dst,
134290001Sglebius								    (isc_uint32_t)zone);
135290001Sglebius					}
136290001Sglebius#endif
137290001Sglebius				}
138290001Sglebius			}
139290001Sglebius		}
140290001Sglebius#endif
141290001Sglebius		break;
142290001Sglebius	default:
143290001Sglebius		INSIST(0);
144290001Sglebius		break;
145290001Sglebius	}
146290001Sglebius}
147290001Sglebius
148290001Sglebius/*
149290001Sglebius * Include system-dependent code.
150290001Sglebius */
151290001Sglebius
152290001Sglebius#ifdef __linux
153290001Sglebius#define ISC_IF_INET6_SZ \
154290001Sglebius    sizeof("00000000000000000000000000000001 01 80 10 80 XXXXXXloXXXXXXXX\n")
155290001Sglebiusstatic isc_result_t linux_if_inet6_next(isc_interfaceiter_t *);
156290001Sglebiusstatic isc_result_t linux_if_inet6_current(isc_interfaceiter_t *);
157290001Sglebiusstatic void linux_if_inet6_first(isc_interfaceiter_t *iter);
158290001Sglebius#endif
159290001Sglebius
160290001Sglebius#if HAVE_GETIFADDRS
161290001Sglebius#include "ifiter_getifaddrs.c"
162290001Sglebius#elif HAVE_IFLIST_SYSCTL
163290001Sglebius#include "ifiter_sysctl.c"
164290001Sglebius#else
165290001Sglebius#include "ifiter_ioctl.c"
166290001Sglebius#endif
167290001Sglebius
168290001Sglebius#ifdef __linux
169290001Sglebiusstatic void
170290001Sglebiuslinux_if_inet6_first(isc_interfaceiter_t *iter) {
171290001Sglebius	if (iter->proc != NULL) {
172290001Sglebius		rewind(iter->proc);
173290001Sglebius		(void)linux_if_inet6_next(iter);
174290001Sglebius	} else
175290001Sglebius		iter->valid = ISC_R_NOMORE;
176290001Sglebius}
177290001Sglebius
178290001Sglebiusstatic isc_result_t
179290001Sglebiuslinux_if_inet6_next(isc_interfaceiter_t *iter) {
180290001Sglebius	if (iter->proc != NULL &&
181290001Sglebius	    fgets(iter->entry, sizeof(iter->entry), iter->proc) != NULL)
182290001Sglebius		iter->valid = ISC_R_SUCCESS;
183290001Sglebius	else
184290001Sglebius		iter->valid = ISC_R_NOMORE;
185290001Sglebius	return (iter->valid);
186290001Sglebius}
187290001Sglebius
188290001Sglebiusstatic isc_result_t
189290001Sglebiuslinux_if_inet6_current(isc_interfaceiter_t *iter) {
190290001Sglebius	char address[33];
191290001Sglebius	char name[IF_NAMESIZE+1];
192290001Sglebius	struct in6_addr addr6;
193290001Sglebius	unsigned int ifindex;
194290001Sglebius	int prefix, scope, flags;
195290001Sglebius	int res;
196290001Sglebius	unsigned int i;
197290001Sglebius
198290001Sglebius	if (iter->valid != ISC_R_SUCCESS)
199290001Sglebius		return (iter->valid);
200290001Sglebius	if (iter->proc == NULL) {
201290001Sglebius		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
202290001Sglebius			      ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
203290001Sglebius			      "/proc/net/if_inet6:iter->proc == NULL");
204290001Sglebius		return (ISC_R_FAILURE);
205290001Sglebius	}
206290001Sglebius
207290001Sglebius	res = sscanf(iter->entry, "%32[a-f0-9] %x %x %x %x %16s\n",
208290001Sglebius		     address, &ifindex, &prefix, &scope, &flags, name);
209290001Sglebius	if (res != 6) {
210290001Sglebius		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
211290001Sglebius			      ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
212290001Sglebius			      "/proc/net/if_inet6:sscanf() -> %d (expected 6)",
213290001Sglebius			      res);
214290001Sglebius		return (ISC_R_FAILURE);
215290001Sglebius	}
216290001Sglebius	if (strlen(address) != 32) {
217290001Sglebius		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
218290001Sglebius			      ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
219290001Sglebius			      "/proc/net/if_inet6:strlen(%s) != 32", address);
220290001Sglebius		return (ISC_R_FAILURE);
221290001Sglebius	}
222290001Sglebius	/*
223290001Sglebius	** Ignore DAD addresses --
224290001Sglebius	** we can't bind to them until they are resolved
225290001Sglebius	*/
226290001Sglebius#ifdef IFA_F_TENTATIVE
227290001Sglebius	if (flags & IFA_F_TENTATIVE)
228290001Sglebius		return (ISC_R_IGNORE);
229290001Sglebius#endif
230290001Sglebius
231290001Sglebius	for (i = 0; i < 16; i++) {
232290001Sglebius		unsigned char byte;
233290001Sglebius		static const char hex[] = "0123456789abcdef";
234290001Sglebius		byte = ((strchr(hex, address[i * 2]) - hex) << 4) |
235290001Sglebius		       (strchr(hex, address[i * 2 + 1]) - hex);
236290001Sglebius		addr6.s6_addr[i] = byte;
237290001Sglebius	}
238290001Sglebius	iter->current.af = AF_INET6;
239290001Sglebius	iter->current.flags = INTERFACE_F_UP;
240290001Sglebius	isc_netaddr_fromin6(&iter->current.address, &addr6);
241290001Sglebius	iter->current.ifindex = ifindex;
242290001Sglebius	if (isc_netaddr_islinklocal(&iter->current.address)) {
243290001Sglebius		isc_netaddr_setzone(&iter->current.address,
244290001Sglebius				    (isc_uint32_t)ifindex);
245290001Sglebius	}
246290001Sglebius	for (i = 0; i < 16; i++) {
247290001Sglebius		if (prefix > 8) {
248290001Sglebius			addr6.s6_addr[i] = 0xff;
249290001Sglebius			prefix -= 8;
250290001Sglebius		} else {
251290001Sglebius			addr6.s6_addr[i] = (0xff << (8 - prefix)) & 0xff;
252290001Sglebius			prefix = 0;
253290001Sglebius		}
254290001Sglebius	}
255290001Sglebius	isc_netaddr_fromin6(&iter->current.netmask, &addr6);
256290001Sglebius	strncpy(iter->current.name, name, sizeof(iter->current.name));
257290001Sglebius	return (ISC_R_SUCCESS);
258290001Sglebius}
259290001Sglebius#endif
260290001Sglebius
261290001Sglebius/*
262290001Sglebius * The remaining code is common to the sysctl and ioctl case.
263290001Sglebius */
264290001Sglebius
265290001Sglebiusisc_result_t
266290001Sglebiusisc_interfaceiter_current(isc_interfaceiter_t *iter,
267290001Sglebius			  isc_interface_t *ifdata)
268290001Sglebius{
269290001Sglebius	REQUIRE(iter->result == ISC_R_SUCCESS);
270290001Sglebius	memcpy(ifdata, &iter->current, sizeof(*ifdata));
271290001Sglebius	return (ISC_R_SUCCESS);
272290001Sglebius}
273290001Sglebius
274290001Sglebiusisc_result_t
275290001Sglebiusisc_interfaceiter_first(isc_interfaceiter_t *iter) {
276290001Sglebius	isc_result_t result;
277290001Sglebius
278290001Sglebius	REQUIRE(VALID_IFITER(iter));
279290001Sglebius
280290001Sglebius	internal_first(iter);
281290001Sglebius	for (;;) {
282290001Sglebius		result = internal_current(iter);
283290001Sglebius		if (result != ISC_R_IGNORE)
284290001Sglebius			break;
285290001Sglebius		result = internal_next(iter);
286290001Sglebius		if (result != ISC_R_SUCCESS)
287290001Sglebius			break;
288290001Sglebius	}
289290001Sglebius	iter->result = result;
290290001Sglebius	return (result);
291290001Sglebius}
292290001Sglebius
293290001Sglebiusisc_result_t
294290001Sglebiusisc_interfaceiter_next(isc_interfaceiter_t *iter) {
295290001Sglebius	isc_result_t result;
296290001Sglebius
297290001Sglebius	REQUIRE(VALID_IFITER(iter));
298290001Sglebius	REQUIRE(iter->result == ISC_R_SUCCESS);
299290001Sglebius
300290001Sglebius	for (;;) {
301290001Sglebius		result = internal_next(iter);
302290001Sglebius		if (result != ISC_R_SUCCESS)
303290001Sglebius			break;
304290001Sglebius		result = internal_current(iter);
305290001Sglebius		if (result != ISC_R_IGNORE)
306290001Sglebius			break;
307290001Sglebius	}
308290001Sglebius	iter->result = result;
309290001Sglebius	return (result);
310290001Sglebius}
311290001Sglebius
312290001Sglebiusvoid
313290001Sglebiusisc_interfaceiter_destroy(isc_interfaceiter_t **iterp)
314290001Sglebius{
315290001Sglebius	isc_interfaceiter_t *iter;
316290001Sglebius	REQUIRE(iterp != NULL);
317290001Sglebius	iter = *iterp;
318290001Sglebius	REQUIRE(VALID_IFITER(iter));
319290001Sglebius
320290001Sglebius	internal_destroy(iter);
321290001Sglebius	if (iter->buf != NULL)
322290001Sglebius		isc_mem_put(iter->mctx, iter->buf, iter->bufsize);
323290001Sglebius
324290001Sglebius	iter->magic = 0;
325290001Sglebius	isc_mem_put(iter->mctx, iter, sizeof(*iter));
326290001Sglebius	*iterp = NULL;
327290001Sglebius}
328