1135446Strhodes/*
2254897Serwin * Copyright (C) 2004-2009, 2011-2013  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2002  Internet Software Consortium.
4135446Strhodes *
5182645Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18254897Serwin/* $Id: interfacemgr.c,v 1.101 2011/11/09 18:44:03 each Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24135446Strhodes#include <isc/interfaceiter.h>
25254897Serwin#include <isc/os.h>
26135446Strhodes#include <isc/string.h>
27135446Strhodes#include <isc/task.h>
28135446Strhodes#include <isc/util.h>
29135446Strhodes
30135446Strhodes#include <dns/acl.h>
31135446Strhodes#include <dns/dispatch.h>
32135446Strhodes
33135446Strhodes#include <named/client.h>
34135446Strhodes#include <named/log.h>
35135446Strhodes#include <named/interfacemgr.h>
36135446Strhodes
37135446Strhodes#define IFMGR_MAGIC			ISC_MAGIC('I', 'F', 'M', 'G')
38135446Strhodes#define NS_INTERFACEMGR_VALID(t)	ISC_MAGIC_VALID(t, IFMGR_MAGIC)
39135446Strhodes
40135446Strhodes#define IFMGR_COMMON_LOGARGS \
41135446Strhodes	ns_g_lctx, NS_LOGCATEGORY_NETWORK, NS_LOGMODULE_INTERFACEMGR
42135446Strhodes
43170222Sdougb/*% nameserver interface manager structure */
44135446Strhodesstruct ns_interfacemgr {
45170222Sdougb	unsigned int		magic;		/*%< Magic number. */
46135446Strhodes	int			references;
47135446Strhodes	isc_mutex_t		lock;
48170222Sdougb	isc_mem_t *		mctx;		/*%< Memory context. */
49170222Sdougb	isc_taskmgr_t *		taskmgr;	/*%< Task manager. */
50170222Sdougb	isc_socketmgr_t *	socketmgr;	/*%< Socket manager. */
51135446Strhodes	dns_dispatchmgr_t *	dispatchmgr;
52170222Sdougb	unsigned int		generation;	/*%< Current generation no. */
53135446Strhodes	ns_listenlist_t *	listenon4;
54135446Strhodes	ns_listenlist_t *	listenon6;
55170222Sdougb	dns_aclenv_t		aclenv;		/*%< Localhost/localnets ACLs */
56170222Sdougb	ISC_LIST(ns_interface_t) interfaces;	/*%< List of interfaces. */
57170222Sdougb	ISC_LIST(isc_sockaddr_t) listenon;
58135446Strhodes};
59135446Strhodes
60135446Strhodesstatic void
61135446Strhodespurge_old_interfaces(ns_interfacemgr_t *mgr);
62135446Strhodes
63170222Sdougbstatic void
64170222Sdougbclearlistenon(ns_interfacemgr_t *mgr);
65170222Sdougb
66135446Strhodesisc_result_t
67135446Strhodesns_interfacemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
68135446Strhodes		       isc_socketmgr_t *socketmgr,
69135446Strhodes		       dns_dispatchmgr_t *dispatchmgr,
70135446Strhodes		       ns_interfacemgr_t **mgrp)
71135446Strhodes{
72135446Strhodes	isc_result_t result;
73135446Strhodes	ns_interfacemgr_t *mgr;
74135446Strhodes
75135446Strhodes	REQUIRE(mctx != NULL);
76135446Strhodes	REQUIRE(mgrp != NULL);
77135446Strhodes	REQUIRE(*mgrp == NULL);
78135446Strhodes
79135446Strhodes	mgr = isc_mem_get(mctx, sizeof(*mgr));
80135446Strhodes	if (mgr == NULL)
81135446Strhodes		return (ISC_R_NOMEMORY);
82135446Strhodes
83254402Serwin	mgr->mctx = NULL;
84254402Serwin	isc_mem_attach(mctx, &mgr->mctx);
85254402Serwin
86135446Strhodes	result = isc_mutex_init(&mgr->lock);
87135446Strhodes	if (result != ISC_R_SUCCESS)
88135446Strhodes		goto cleanup_mem;
89135446Strhodes
90135446Strhodes	mgr->taskmgr = taskmgr;
91135446Strhodes	mgr->socketmgr = socketmgr;
92135446Strhodes	mgr->dispatchmgr = dispatchmgr;
93135446Strhodes	mgr->generation = 1;
94135446Strhodes	mgr->listenon4 = NULL;
95135446Strhodes	mgr->listenon6 = NULL;
96186462Sdougb
97135446Strhodes	ISC_LIST_INIT(mgr->interfaces);
98170222Sdougb	ISC_LIST_INIT(mgr->listenon);
99135446Strhodes
100135446Strhodes	/*
101135446Strhodes	 * The listen-on lists are initially empty.
102135446Strhodes	 */
103135446Strhodes	result = ns_listenlist_create(mctx, &mgr->listenon4);
104135446Strhodes	if (result != ISC_R_SUCCESS)
105135446Strhodes		goto cleanup_mem;
106135446Strhodes	ns_listenlist_attach(mgr->listenon4, &mgr->listenon6);
107135446Strhodes
108135446Strhodes	result = dns_aclenv_init(mctx, &mgr->aclenv);
109135446Strhodes	if (result != ISC_R_SUCCESS)
110135446Strhodes		goto cleanup_listenon;
111135446Strhodes
112135446Strhodes	mgr->references = 1;
113135446Strhodes	mgr->magic = IFMGR_MAGIC;
114135446Strhodes	*mgrp = mgr;
115135446Strhodes	return (ISC_R_SUCCESS);
116135446Strhodes
117135446Strhodes cleanup_listenon:
118135446Strhodes	ns_listenlist_detach(&mgr->listenon4);
119135446Strhodes	ns_listenlist_detach(&mgr->listenon6);
120135446Strhodes cleanup_mem:
121254402Serwin	isc_mem_putanddetach(&mgr->mctx, mgr, sizeof(*mgr));
122135446Strhodes	return (result);
123135446Strhodes}
124135446Strhodes
125135446Strhodesstatic void
126135446Strhodesns_interfacemgr_destroy(ns_interfacemgr_t *mgr) {
127135446Strhodes	REQUIRE(NS_INTERFACEMGR_VALID(mgr));
128135446Strhodes	dns_aclenv_destroy(&mgr->aclenv);
129135446Strhodes	ns_listenlist_detach(&mgr->listenon4);
130135446Strhodes	ns_listenlist_detach(&mgr->listenon6);
131170222Sdougb	clearlistenon(mgr);
132135446Strhodes	DESTROYLOCK(&mgr->lock);
133135446Strhodes	mgr->magic = 0;
134254402Serwin	isc_mem_putanddetach(&mgr->mctx, mgr, sizeof(*mgr));
135135446Strhodes}
136135446Strhodes
137135446Strhodesdns_aclenv_t *
138135446Strhodesns_interfacemgr_getaclenv(ns_interfacemgr_t *mgr) {
139135446Strhodes	return (&mgr->aclenv);
140135446Strhodes}
141135446Strhodes
142135446Strhodesvoid
143135446Strhodesns_interfacemgr_attach(ns_interfacemgr_t *source, ns_interfacemgr_t **target) {
144135446Strhodes	REQUIRE(NS_INTERFACEMGR_VALID(source));
145135446Strhodes	LOCK(&source->lock);
146135446Strhodes	INSIST(source->references > 0);
147135446Strhodes	source->references++;
148135446Strhodes	UNLOCK(&source->lock);
149135446Strhodes	*target = source;
150135446Strhodes}
151135446Strhodes
152135446Strhodesvoid
153135446Strhodesns_interfacemgr_detach(ns_interfacemgr_t **targetp) {
154135446Strhodes	isc_result_t need_destroy = ISC_FALSE;
155135446Strhodes	ns_interfacemgr_t *target = *targetp;
156135446Strhodes	REQUIRE(target != NULL);
157135446Strhodes	REQUIRE(NS_INTERFACEMGR_VALID(target));
158135446Strhodes	LOCK(&target->lock);
159135446Strhodes	REQUIRE(target->references > 0);
160135446Strhodes	target->references--;
161135446Strhodes	if (target->references == 0)
162135446Strhodes		need_destroy = ISC_TRUE;
163135446Strhodes	UNLOCK(&target->lock);
164135446Strhodes	if (need_destroy)
165135446Strhodes		ns_interfacemgr_destroy(target);
166135446Strhodes	*targetp = NULL;
167135446Strhodes}
168135446Strhodes
169135446Strhodesvoid
170135446Strhodesns_interfacemgr_shutdown(ns_interfacemgr_t *mgr) {
171135446Strhodes	REQUIRE(NS_INTERFACEMGR_VALID(mgr));
172135446Strhodes
173170222Sdougb	/*%
174135446Strhodes	 * Shut down and detach all interfaces.
175135446Strhodes	 * By incrementing the generation count, we make purge_old_interfaces()
176135446Strhodes	 * consider all interfaces "old".
177135446Strhodes	 */
178135446Strhodes	mgr->generation++;
179135446Strhodes	purge_old_interfaces(mgr);
180135446Strhodes}
181135446Strhodes
182135446Strhodes
183135446Strhodesstatic isc_result_t
184135446Strhodesns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
185135446Strhodes		    const char *name, ns_interface_t **ifpret)
186135446Strhodes{
187135446Strhodes	ns_interface_t *ifp;
188135446Strhodes	isc_result_t result;
189254897Serwin	int disp;
190135446Strhodes
191135446Strhodes	REQUIRE(NS_INTERFACEMGR_VALID(mgr));
192254897Serwin
193135446Strhodes	ifp = isc_mem_get(mgr->mctx, sizeof(*ifp));
194135446Strhodes	if (ifp == NULL)
195135446Strhodes		return (ISC_R_NOMEMORY);
196254897Serwin
197135446Strhodes	ifp->mgr = NULL;
198135446Strhodes	ifp->generation = mgr->generation;
199135446Strhodes	ifp->addr = *addr;
200165071Sdougb	ifp->flags = 0;
201135446Strhodes	strncpy(ifp->name, name, sizeof(ifp->name));
202135446Strhodes	ifp->name[sizeof(ifp->name)-1] = '\0';
203135446Strhodes	ifp->clientmgr = NULL;
204135446Strhodes
205135446Strhodes	result = isc_mutex_init(&ifp->lock);
206135446Strhodes	if (result != ISC_R_SUCCESS)
207135446Strhodes		goto lock_create_failure;
208135446Strhodes
209135446Strhodes	result = ns_clientmgr_create(mgr->mctx, mgr->taskmgr,
210135446Strhodes				     ns_g_timermgr,
211135446Strhodes				     &ifp->clientmgr);
212135446Strhodes	if (result != ISC_R_SUCCESS) {
213135446Strhodes		isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
214135446Strhodes			      "ns_clientmgr_create() failed: %s",
215135446Strhodes			      isc_result_totext(result));
216135446Strhodes		goto clientmgr_create_failure;
217135446Strhodes	}
218135446Strhodes
219254897Serwin	for (disp = 0; disp < MAX_UDP_DISPATCH; disp++)
220254897Serwin		ifp->udpdispatch[disp] = NULL;
221135446Strhodes
222135446Strhodes	ifp->tcpsocket = NULL;
223254897Serwin
224135446Strhodes	/*
225135446Strhodes	 * Create a single TCP client object.  It will replace itself
226135446Strhodes	 * with a new one as soon as it gets a connection, so the actual
227135446Strhodes	 * connections will be handled in parallel even though there is
228135446Strhodes	 * only one client initially.
229135446Strhodes	 */
230135446Strhodes	ifp->ntcptarget = 1;
231135446Strhodes	ifp->ntcpcurrent = 0;
232254897Serwin	ifp->nudpdispatch = 0;
233135446Strhodes
234135446Strhodes	ISC_LINK_INIT(ifp, link);
235135446Strhodes
236135446Strhodes	ns_interfacemgr_attach(mgr, &ifp->mgr);
237135446Strhodes	ISC_LIST_APPEND(mgr->interfaces, ifp, link);
238135446Strhodes
239135446Strhodes	ifp->references = 1;
240135446Strhodes	ifp->magic = IFACE_MAGIC;
241135446Strhodes	*ifpret = ifp;
242135446Strhodes
243135446Strhodes	return (ISC_R_SUCCESS);
244135446Strhodes
245135446Strhodes clientmgr_create_failure:
246135446Strhodes	DESTROYLOCK(&ifp->lock);
247254897Serwin
248135446Strhodes lock_create_failure:
249135446Strhodes	ifp->magic = 0;
250135446Strhodes	isc_mem_put(mgr->mctx, ifp, sizeof(*ifp));
251135446Strhodes
252135446Strhodes	return (ISC_R_UNEXPECTED);
253135446Strhodes}
254135446Strhodes
255135446Strhodesstatic isc_result_t
256135446Strhodesns_interface_listenudp(ns_interface_t *ifp) {
257135446Strhodes	isc_result_t result;
258135446Strhodes	unsigned int attrs;
259135446Strhodes	unsigned int attrmask;
260254897Serwin	int disp, i;
261135446Strhodes
262135446Strhodes	attrs = 0;
263135446Strhodes	attrs |= DNS_DISPATCHATTR_UDP;
264135446Strhodes	if (isc_sockaddr_pf(&ifp->addr) == AF_INET)
265135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
266135446Strhodes	else
267135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
268135446Strhodes	attrs |= DNS_DISPATCHATTR_NOLISTEN;
269135446Strhodes	attrmask = 0;
270135446Strhodes	attrmask |= DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP;
271135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
272254897Serwin
273254897Serwin	ifp->nudpdispatch = ISC_MIN(ns_g_udpdisp, MAX_UDP_DISPATCH);
274254897Serwin	for (disp = 0; disp < ifp->nudpdispatch; disp++) {
275254897Serwin		result = dns_dispatch_getudp_dup(ifp->mgr->dispatchmgr,
276254897Serwin						 ns_g_socketmgr,
277254897Serwin						 ns_g_taskmgr, &ifp->addr,
278254897Serwin						 4096, 1000, 32768, 8219, 8237,
279254897Serwin						 attrs, attrmask,
280254897Serwin						 &ifp->udpdispatch[disp],
281254897Serwin						 disp == 0
282254897Serwin						    ? NULL
283254897Serwin						    : ifp->udpdispatch[0]);
284254897Serwin		if (result != ISC_R_SUCCESS) {
285254897Serwin			isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
286254897Serwin				      "could not listen on UDP socket: %s",
287254897Serwin				      isc_result_totext(result));
288254897Serwin			goto udp_dispatch_failure;
289254897Serwin		}
290254897Serwin
291135446Strhodes	}
292135446Strhodes
293254897Serwin	result = ns_clientmgr_createclients(ifp->clientmgr, ifp->nudpdispatch,
294135446Strhodes					    ifp, ISC_FALSE);
295135446Strhodes	if (result != ISC_R_SUCCESS) {
296135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
297135446Strhodes				 "UDP ns_clientmgr_createclients(): %s",
298135446Strhodes				 isc_result_totext(result));
299135446Strhodes		goto addtodispatch_failure;
300135446Strhodes	}
301254897Serwin
302135446Strhodes	return (ISC_R_SUCCESS);
303135446Strhodes
304135446Strhodes addtodispatch_failure:
305254897Serwin	for (i = disp - 1; i <= 0; i--) {
306254897Serwin		dns_dispatch_changeattributes(ifp->udpdispatch[i], 0,
307254897Serwin					      DNS_DISPATCHATTR_NOLISTEN);
308254897Serwin		dns_dispatch_detach(&(ifp->udpdispatch[i]));
309254897Serwin	}
310254897Serwin	ifp->nudpdispatch = 0;
311254897Serwin
312135446Strhodes udp_dispatch_failure:
313135446Strhodes	return (result);
314135446Strhodes}
315135446Strhodes
316135446Strhodesstatic isc_result_t
317135446Strhodesns_interface_accepttcp(ns_interface_t *ifp) {
318135446Strhodes	isc_result_t result;
319135446Strhodes
320135446Strhodes	/*
321135446Strhodes	 * Open a TCP socket.
322135446Strhodes	 */
323135446Strhodes	result = isc_socket_create(ifp->mgr->socketmgr,
324135446Strhodes				   isc_sockaddr_pf(&ifp->addr),
325135446Strhodes				   isc_sockettype_tcp,
326135446Strhodes				   &ifp->tcpsocket);
327135446Strhodes	if (result != ISC_R_SUCCESS) {
328135446Strhodes		isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
329135446Strhodes				 "creating TCP socket: %s",
330135446Strhodes				 isc_result_totext(result));
331135446Strhodes		goto tcp_socket_failure;
332135446Strhodes	}
333193149Sdougb	isc_socket_setname(ifp->tcpsocket, "dispatcher", NULL);
334135446Strhodes#ifndef ISC_ALLOW_MAPPED
335135446Strhodes	isc_socket_ipv6only(ifp->tcpsocket, ISC_TRUE);
336135446Strhodes#endif
337182645Sdougb	result = isc_socket_bind(ifp->tcpsocket, &ifp->addr,
338182645Sdougb				 ISC_SOCKET_REUSEADDRESS);
339135446Strhodes	if (result != ISC_R_SUCCESS) {
340135446Strhodes		isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
341135446Strhodes				 "binding TCP socket: %s",
342135446Strhodes				 isc_result_totext(result));
343135446Strhodes		goto tcp_bind_failure;
344135446Strhodes	}
345135446Strhodes	result = isc_socket_listen(ifp->tcpsocket, ns_g_listen);
346135446Strhodes	if (result != ISC_R_SUCCESS) {
347135446Strhodes		isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
348135446Strhodes				 "listening on TCP socket: %s",
349135446Strhodes				 isc_result_totext(result));
350135446Strhodes		goto tcp_listen_failure;
351135446Strhodes	}
352135446Strhodes
353186462Sdougb	/*
354135446Strhodes	 * If/when there a multiple filters listen to the
355135446Strhodes	 * result.
356135446Strhodes	 */
357135446Strhodes	(void)isc_socket_filter(ifp->tcpsocket, "dataready");
358135446Strhodes
359135446Strhodes	result = ns_clientmgr_createclients(ifp->clientmgr,
360135446Strhodes					    ifp->ntcptarget, ifp,
361135446Strhodes					    ISC_TRUE);
362135446Strhodes	if (result != ISC_R_SUCCESS) {
363135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
364135446Strhodes				 "TCP ns_clientmgr_createclients(): %s",
365135446Strhodes				 isc_result_totext(result));
366135446Strhodes		goto accepttcp_failure;
367135446Strhodes	}
368135446Strhodes	return (ISC_R_SUCCESS);
369135446Strhodes
370135446Strhodes accepttcp_failure:
371135446Strhodes tcp_listen_failure:
372135446Strhodes tcp_bind_failure:
373135446Strhodes	isc_socket_detach(&ifp->tcpsocket);
374135446Strhodes tcp_socket_failure:
375135446Strhodes	return (ISC_R_SUCCESS);
376135446Strhodes}
377135446Strhodes
378135446Strhodesstatic isc_result_t
379135446Strhodesns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
380135446Strhodes		   const char *name, ns_interface_t **ifpret,
381135446Strhodes		   isc_boolean_t accept_tcp)
382135446Strhodes{
383135446Strhodes	isc_result_t result;
384135446Strhodes	ns_interface_t *ifp = NULL;
385135446Strhodes	REQUIRE(ifpret != NULL && *ifpret == NULL);
386135446Strhodes
387135446Strhodes	result = ns_interface_create(mgr, addr, name, &ifp);
388135446Strhodes	if (result != ISC_R_SUCCESS)
389135446Strhodes		return (result);
390135446Strhodes
391135446Strhodes	result = ns_interface_listenudp(ifp);
392135446Strhodes	if (result != ISC_R_SUCCESS)
393135446Strhodes		goto cleanup_interface;
394135446Strhodes
395135446Strhodes	if (accept_tcp == ISC_TRUE) {
396135446Strhodes		result = ns_interface_accepttcp(ifp);
397135446Strhodes		if (result != ISC_R_SUCCESS) {
398135446Strhodes			/*
399135446Strhodes			 * XXXRTH We don't currently have a way to easily stop
400135446Strhodes			 * dispatch service, so we currently return
401135446Strhodes			 * ISC_R_SUCCESS (the UDP stuff will work even if TCP
402135446Strhodes			 * creation failed).  This will be fixed later.
403135446Strhodes			 */
404135446Strhodes			result = ISC_R_SUCCESS;
405135446Strhodes		}
406135446Strhodes	}
407135446Strhodes	*ifpret = ifp;
408225361Sdougb	return (result);
409135446Strhodes
410135446Strhodes cleanup_interface:
411135446Strhodes	ISC_LIST_UNLINK(ifp->mgr->interfaces, ifp, link);
412135446Strhodes	ns_interface_detach(&ifp);
413135446Strhodes	return (result);
414135446Strhodes}
415135446Strhodes
416135446Strhodesvoid
417135446Strhodesns_interface_shutdown(ns_interface_t *ifp) {
418135446Strhodes	if (ifp->clientmgr != NULL)
419135446Strhodes		ns_clientmgr_destroy(&ifp->clientmgr);
420135446Strhodes}
421135446Strhodes
422135446Strhodesstatic void
423135446Strhodesns_interface_destroy(ns_interface_t *ifp) {
424135446Strhodes	isc_mem_t *mctx = ifp->mgr->mctx;
425254897Serwin	int disp;
426254897Serwin
427135446Strhodes	REQUIRE(NS_INTERFACE_VALID(ifp));
428135446Strhodes
429135446Strhodes	ns_interface_shutdown(ifp);
430135446Strhodes
431254897Serwin	for (disp = 0; disp < ifp->nudpdispatch; disp++)
432254897Serwin		if (ifp->udpdispatch[disp] != NULL) {
433254897Serwin			dns_dispatch_changeattributes(ifp->udpdispatch[disp], 0,
434254897Serwin						    DNS_DISPATCHATTR_NOLISTEN);
435254897Serwin			dns_dispatch_detach(&(ifp->udpdispatch[disp]));
436254897Serwin		}
437254897Serwin
438135446Strhodes	if (ifp->tcpsocket != NULL)
439135446Strhodes		isc_socket_detach(&ifp->tcpsocket);
440135446Strhodes
441135446Strhodes	DESTROYLOCK(&ifp->lock);
442135446Strhodes
443135446Strhodes	ns_interfacemgr_detach(&ifp->mgr);
444135446Strhodes
445135446Strhodes	ifp->magic = 0;
446135446Strhodes	isc_mem_put(mctx, ifp, sizeof(*ifp));
447135446Strhodes}
448135446Strhodes
449135446Strhodesvoid
450135446Strhodesns_interface_attach(ns_interface_t *source, ns_interface_t **target) {
451135446Strhodes	REQUIRE(NS_INTERFACE_VALID(source));
452135446Strhodes	LOCK(&source->lock);
453135446Strhodes	INSIST(source->references > 0);
454135446Strhodes	source->references++;
455135446Strhodes	UNLOCK(&source->lock);
456135446Strhodes	*target = source;
457135446Strhodes}
458135446Strhodes
459135446Strhodesvoid
460135446Strhodesns_interface_detach(ns_interface_t **targetp) {
461135446Strhodes	isc_result_t need_destroy = ISC_FALSE;
462135446Strhodes	ns_interface_t *target = *targetp;
463135446Strhodes	REQUIRE(target != NULL);
464135446Strhodes	REQUIRE(NS_INTERFACE_VALID(target));
465135446Strhodes	LOCK(&target->lock);
466135446Strhodes	REQUIRE(target->references > 0);
467135446Strhodes	target->references--;
468135446Strhodes	if (target->references == 0)
469135446Strhodes		need_destroy = ISC_TRUE;
470135446Strhodes	UNLOCK(&target->lock);
471135446Strhodes	if (need_destroy)
472135446Strhodes		ns_interface_destroy(target);
473135446Strhodes	*targetp = NULL;
474135446Strhodes}
475135446Strhodes
476170222Sdougb/*%
477135446Strhodes * Search the interface list for an interface whose address and port
478135446Strhodes * both match those of 'addr'.  Return a pointer to it, or NULL if not found.
479135446Strhodes */
480135446Strhodesstatic ns_interface_t *
481135446Strhodesfind_matching_interface(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr) {
482135446Strhodes	ns_interface_t *ifp;
483135446Strhodes	for (ifp = ISC_LIST_HEAD(mgr->interfaces); ifp != NULL;
484135446Strhodes	     ifp = ISC_LIST_NEXT(ifp, link)) {
485135446Strhodes		if (isc_sockaddr_equal(&ifp->addr, addr))
486135446Strhodes			break;
487135446Strhodes	}
488135446Strhodes	return (ifp);
489135446Strhodes}
490135446Strhodes
491170222Sdougb/*%
492135446Strhodes * Remove any interfaces whose generation number is not the current one.
493135446Strhodes */
494135446Strhodesstatic void
495135446Strhodespurge_old_interfaces(ns_interfacemgr_t *mgr) {
496135446Strhodes	ns_interface_t *ifp, *next;
497135446Strhodes	for (ifp = ISC_LIST_HEAD(mgr->interfaces); ifp != NULL; ifp = next) {
498135446Strhodes		INSIST(NS_INTERFACE_VALID(ifp));
499135446Strhodes		next = ISC_LIST_NEXT(ifp, link);
500135446Strhodes		if (ifp->generation != mgr->generation) {
501135446Strhodes			char sabuf[256];
502135446Strhodes			ISC_LIST_UNLINK(ifp->mgr->interfaces, ifp, link);
503135446Strhodes			isc_sockaddr_format(&ifp->addr, sabuf, sizeof(sabuf));
504135446Strhodes			isc_log_write(IFMGR_COMMON_LOGARGS,
505135446Strhodes				      ISC_LOG_INFO,
506135446Strhodes				      "no longer listening on %s", sabuf);
507135446Strhodes			ns_interface_shutdown(ifp);
508135446Strhodes			ns_interface_detach(&ifp);
509135446Strhodes		}
510135446Strhodes	}
511135446Strhodes}
512135446Strhodes
513135446Strhodesstatic isc_result_t
514135446Strhodesclearacl(isc_mem_t *mctx, dns_acl_t **aclp) {
515135446Strhodes	dns_acl_t *newacl = NULL;
516135446Strhodes	isc_result_t result;
517193149Sdougb	result = dns_acl_create(mctx, 0, &newacl);
518135446Strhodes	if (result != ISC_R_SUCCESS)
519135446Strhodes		return (result);
520135446Strhodes	dns_acl_detach(aclp);
521135446Strhodes	dns_acl_attach(newacl, aclp);
522135446Strhodes	dns_acl_detach(&newacl);
523135446Strhodes	return (ISC_R_SUCCESS);
524135446Strhodes}
525135446Strhodes
526135446Strhodesstatic isc_boolean_t
527135446Strhodeslistenon_is_ip6_any(ns_listenelt_t *elt) {
528193149Sdougb	REQUIRE(elt && elt->acl);
529193149Sdougb	return dns_acl_isany(elt->acl);
530135446Strhodes}
531135446Strhodes
532135446Strhodesstatic isc_result_t
533135446Strhodessetup_locals(ns_interfacemgr_t *mgr, isc_interface_t *interface) {
534135446Strhodes	isc_result_t result;
535135446Strhodes	unsigned int prefixlen;
536193149Sdougb	isc_netaddr_t *netaddr;
537135446Strhodes
538193149Sdougb	netaddr = &interface->address;
539186462Sdougb
540193149Sdougb	/* First add localhost address */
541193149Sdougb	prefixlen = (netaddr->family == AF_INET) ? 32 : 128;
542193149Sdougb	result = dns_iptable_addprefix(mgr->aclenv.localhost->iptable,
543193149Sdougb				       netaddr, prefixlen, ISC_TRUE);
544135446Strhodes	if (result != ISC_R_SUCCESS)
545135446Strhodes		return (result);
546135446Strhodes
547193149Sdougb	/* Then add localnets prefix */
548135446Strhodes	result = isc_netaddr_masktoprefixlen(&interface->netmask,
549135446Strhodes					     &prefixlen);
550135446Strhodes
551193149Sdougb	/* Non contiguous netmasks not allowed by IPv6 arch. */
552193149Sdougb	if (result != ISC_R_SUCCESS && netaddr->family == AF_INET6)
553135446Strhodes		return (result);
554135446Strhodes
555135446Strhodes	if (result != ISC_R_SUCCESS) {
556262706Serwin		isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_WARNING,
557135446Strhodes			      "omitting IPv4 interface %s from "
558262706Serwin			      "localnets ACL: %s", interface->name,
559135446Strhodes			      isc_result_totext(result));
560193149Sdougb		return (ISC_R_SUCCESS);
561135446Strhodes	}
562135446Strhodes
563262706Serwin	if (prefixlen == 0U) {
564262706Serwin		isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_WARNING,
565262706Serwin			      "omitting %s interface %s from localnets ACL: "
566262706Serwin			      "zero prefix length detected",
567262706Serwin			      (netaddr->family == AF_INET) ? "IPv4" : "IPv6",
568262706Serwin			      interface->name);
569262706Serwin		return (ISC_R_SUCCESS);
570262706Serwin	}
571262706Serwin
572193149Sdougb	result = dns_iptable_addprefix(mgr->aclenv.localnets->iptable,
573193149Sdougb				       netaddr, prefixlen, ISC_TRUE);
574193149Sdougb	if (result != ISC_R_SUCCESS)
575193149Sdougb		return (result);
576193149Sdougb
577135446Strhodes	return (ISC_R_SUCCESS);
578135446Strhodes}
579135446Strhodes
580170222Sdougbstatic void
581170222Sdougbsetup_listenon(ns_interfacemgr_t *mgr, isc_interface_t *interface,
582170222Sdougb	       in_port_t port)
583186462Sdougb{
584170222Sdougb	isc_sockaddr_t *addr;
585170222Sdougb	isc_sockaddr_t *old;
586170222Sdougb
587170222Sdougb	addr = isc_mem_get(mgr->mctx, sizeof(*addr));
588170222Sdougb	if (addr == NULL)
589170222Sdougb		return;
590170222Sdougb
591170222Sdougb	isc_sockaddr_fromnetaddr(addr, &interface->address, port);
592170222Sdougb
593170222Sdougb	for (old = ISC_LIST_HEAD(mgr->listenon);
594170222Sdougb	     old != NULL;
595170222Sdougb	     old = ISC_LIST_NEXT(old, link))
596170222Sdougb		if (isc_sockaddr_equal(addr, old))
597186462Sdougb			break;
598170222Sdougb
599170222Sdougb	if (old != NULL)
600170222Sdougb		isc_mem_put(mgr->mctx, addr, sizeof(*addr));
601170222Sdougb	else
602170222Sdougb		ISC_LIST_APPEND(mgr->listenon, addr, link);
603170222Sdougb}
604170222Sdougb
605170222Sdougbstatic void
606170222Sdougbclearlistenon(ns_interfacemgr_t *mgr) {
607170222Sdougb	isc_sockaddr_t *old;
608170222Sdougb
609170222Sdougb	old = ISC_LIST_HEAD(mgr->listenon);
610170222Sdougb	while (old != NULL) {
611170222Sdougb		ISC_LIST_UNLINK(mgr->listenon, old, link);
612170222Sdougb		isc_mem_put(mgr->mctx, old, sizeof(*old));
613170222Sdougb		old = ISC_LIST_HEAD(mgr->listenon);
614170222Sdougb	}
615170222Sdougb}
616170222Sdougb
617135446Strhodesstatic isc_result_t
618135446Strhodesdo_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
619135446Strhodes	isc_boolean_t verbose)
620135446Strhodes{
621135446Strhodes	isc_interfaceiter_t *iter = NULL;
622135446Strhodes	isc_boolean_t scan_ipv4 = ISC_FALSE;
623135446Strhodes	isc_boolean_t scan_ipv6 = ISC_FALSE;
624135446Strhodes	isc_boolean_t adjusting = ISC_FALSE;
625135446Strhodes	isc_boolean_t ipv6only = ISC_TRUE;
626135446Strhodes	isc_boolean_t ipv6pktinfo = ISC_TRUE;
627135446Strhodes	isc_result_t result;
628135446Strhodes	isc_netaddr_t zero_address, zero_address6;
629135446Strhodes	ns_listenelt_t *le;
630135446Strhodes	isc_sockaddr_t listen_addr;
631135446Strhodes	ns_interface_t *ifp;
632135446Strhodes	isc_boolean_t log_explicit = ISC_FALSE;
633170222Sdougb	isc_boolean_t dolistenon;
634135446Strhodes
635135446Strhodes	if (ext_listen != NULL)
636135446Strhodes		adjusting = ISC_TRUE;
637135446Strhodes
638135446Strhodes	if (isc_net_probeipv6() == ISC_R_SUCCESS)
639135446Strhodes		scan_ipv6 = ISC_TRUE;
640135446Strhodes#ifdef WANT_IPV6
641135446Strhodes	else
642135446Strhodes		isc_log_write(IFMGR_COMMON_LOGARGS,
643135446Strhodes			      verbose ? ISC_LOG_INFO : ISC_LOG_DEBUG(1),
644135446Strhodes			      "no IPv6 interfaces found");
645135446Strhodes#endif
646135446Strhodes
647135446Strhodes	if (isc_net_probeipv4() == ISC_R_SUCCESS)
648135446Strhodes		scan_ipv4 = ISC_TRUE;
649135446Strhodes	else
650135446Strhodes		isc_log_write(IFMGR_COMMON_LOGARGS,
651135446Strhodes			      verbose ? ISC_LOG_INFO : ISC_LOG_DEBUG(1),
652135446Strhodes			      "no IPv4 interfaces found");
653135446Strhodes
654135446Strhodes	/*
655135446Strhodes	 * A special, but typical case; listen-on-v6 { any; }.
656135446Strhodes	 * When we can make the socket IPv6-only, open a single wildcard
657135446Strhodes	 * socket for IPv6 communication.  Otherwise, make separate socket
658135446Strhodes	 * for each IPv6 address in order to avoid accepting IPv4 packets
659135446Strhodes	 * as the form of mapped addresses unintentionally unless explicitly
660135446Strhodes	 * allowed.
661135446Strhodes	 */
662135446Strhodes#ifndef ISC_ALLOW_MAPPED
663135446Strhodes	if (scan_ipv6 == ISC_TRUE &&
664135446Strhodes	    isc_net_probe_ipv6only() != ISC_R_SUCCESS) {
665135446Strhodes		ipv6only = ISC_FALSE;
666135446Strhodes		log_explicit = ISC_TRUE;
667135446Strhodes	}
668135446Strhodes#endif
669135446Strhodes	if (scan_ipv6 == ISC_TRUE &&
670135446Strhodes	    isc_net_probe_ipv6pktinfo() != ISC_R_SUCCESS) {
671135446Strhodes		ipv6pktinfo = ISC_FALSE;
672135446Strhodes		log_explicit = ISC_TRUE;
673135446Strhodes	}
674135446Strhodes	if (scan_ipv6 == ISC_TRUE && ipv6only && ipv6pktinfo) {
675135446Strhodes		for (le = ISC_LIST_HEAD(mgr->listenon6->elts);
676135446Strhodes		     le != NULL;
677135446Strhodes		     le = ISC_LIST_NEXT(le, link)) {
678135446Strhodes			struct in6_addr in6a;
679135446Strhodes
680135446Strhodes			if (!listenon_is_ip6_any(le))
681135446Strhodes				continue;
682135446Strhodes
683135446Strhodes			in6a = in6addr_any;
684135446Strhodes			isc_sockaddr_fromin6(&listen_addr, &in6a, le->port);
685135446Strhodes
686135446Strhodes			ifp = find_matching_interface(mgr, &listen_addr);
687135446Strhodes			if (ifp != NULL) {
688135446Strhodes				ifp->generation = mgr->generation;
689135446Strhodes			} else {
690135446Strhodes				isc_log_write(IFMGR_COMMON_LOGARGS,
691135446Strhodes					      ISC_LOG_INFO,
692135446Strhodes					      "listening on IPv6 "
693135446Strhodes					      "interfaces, port %u",
694135446Strhodes					      le->port);
695135446Strhodes				result = ns_interface_setup(mgr, &listen_addr,
696135446Strhodes							    "<any>", &ifp,
697135446Strhodes							    ISC_TRUE);
698135446Strhodes				if (result == ISC_R_SUCCESS)
699135446Strhodes					ifp->flags |= NS_INTERFACEFLAG_ANYADDR;
700135446Strhodes				else
701135446Strhodes					isc_log_write(IFMGR_COMMON_LOGARGS,
702135446Strhodes						      ISC_LOG_ERROR,
703135446Strhodes						      "listening on all IPv6 "
704135446Strhodes						      "interfaces failed");
705135446Strhodes				/* Continue. */
706135446Strhodes			}
707135446Strhodes		}
708135446Strhodes	}
709135446Strhodes
710135446Strhodes	isc_netaddr_any(&zero_address);
711135446Strhodes	isc_netaddr_any6(&zero_address6);
712135446Strhodes
713135446Strhodes	result = isc_interfaceiter_create(mgr->mctx, &iter);
714135446Strhodes	if (result != ISC_R_SUCCESS)
715135446Strhodes		return (result);
716135446Strhodes
717135446Strhodes	if (adjusting == ISC_FALSE) {
718135446Strhodes		result = clearacl(mgr->mctx, &mgr->aclenv.localhost);
719135446Strhodes		if (result != ISC_R_SUCCESS)
720135446Strhodes			goto cleanup_iter;
721135446Strhodes		result = clearacl(mgr->mctx, &mgr->aclenv.localnets);
722135446Strhodes		if (result != ISC_R_SUCCESS)
723135446Strhodes			goto cleanup_iter;
724170222Sdougb		clearlistenon(mgr);
725135446Strhodes	}
726135446Strhodes
727135446Strhodes	for (result = isc_interfaceiter_first(iter);
728135446Strhodes	     result == ISC_R_SUCCESS;
729135446Strhodes	     result = isc_interfaceiter_next(iter))
730135446Strhodes	{
731135446Strhodes		isc_interface_t interface;
732135446Strhodes		ns_listenlist_t *ll;
733186462Sdougb		unsigned int family;
734135446Strhodes
735135446Strhodes		result = isc_interfaceiter_current(iter, &interface);
736135446Strhodes		if (result != ISC_R_SUCCESS)
737135446Strhodes			break;
738135446Strhodes
739135446Strhodes		family = interface.address.family;
740135446Strhodes		if (family != AF_INET && family != AF_INET6)
741135446Strhodes			continue;
742135446Strhodes		if (scan_ipv4 == ISC_FALSE && family == AF_INET)
743135446Strhodes			continue;
744135446Strhodes		if (scan_ipv6 == ISC_FALSE && family == AF_INET6)
745135446Strhodes			continue;
746135446Strhodes
747135446Strhodes		/*
748135446Strhodes		 * Test for the address being nonzero rather than testing
749135446Strhodes		 * INTERFACE_F_UP, because on some systems the latter
750135446Strhodes		 * follows the media state and we could end up ignoring
751135446Strhodes		 * the interface for an entire rescan interval due to
752135446Strhodes		 * a temporary media glitch at rescan time.
753135446Strhodes		 */
754135446Strhodes		if (family == AF_INET &&
755135446Strhodes		    isc_netaddr_equal(&interface.address, &zero_address)) {
756135446Strhodes			continue;
757135446Strhodes		}
758135446Strhodes		if (family == AF_INET6 &&
759135446Strhodes		    isc_netaddr_equal(&interface.address, &zero_address6)) {
760135446Strhodes			continue;
761135446Strhodes		}
762135446Strhodes
763135446Strhodes		if (adjusting == ISC_FALSE) {
764135446Strhodes			result = setup_locals(mgr, &interface);
765135446Strhodes			if (result != ISC_R_SUCCESS)
766135446Strhodes				goto ignore_interface;
767135446Strhodes		}
768135446Strhodes
769135446Strhodes		ll = (family == AF_INET) ? mgr->listenon4 : mgr->listenon6;
770170222Sdougb		dolistenon = ISC_TRUE;
771135446Strhodes		for (le = ISC_LIST_HEAD(ll->elts);
772135446Strhodes		     le != NULL;
773135446Strhodes		     le = ISC_LIST_NEXT(le, link))
774135446Strhodes		{
775135446Strhodes			int match;
776135446Strhodes			isc_boolean_t ipv6_wildcard = ISC_FALSE;
777135446Strhodes			isc_netaddr_t listen_netaddr;
778135446Strhodes			isc_sockaddr_t listen_sockaddr;
779135446Strhodes
780135446Strhodes			/*
781135446Strhodes			 * Construct a socket address for this IP/port
782135446Strhodes			 * combination.
783135446Strhodes			 */
784135446Strhodes			if (family == AF_INET) {
785135446Strhodes				isc_netaddr_fromin(&listen_netaddr,
786135446Strhodes						   &interface.address.type.in);
787135446Strhodes			} else {
788135446Strhodes				isc_netaddr_fromin6(&listen_netaddr,
789135446Strhodes						    &interface.address.type.in6);
790135446Strhodes				isc_netaddr_setzone(&listen_netaddr,
791135446Strhodes						    interface.address.zone);
792135446Strhodes			}
793135446Strhodes			isc_sockaddr_fromnetaddr(&listen_sockaddr,
794135446Strhodes						 &listen_netaddr,
795135446Strhodes						 le->port);
796135446Strhodes
797135446Strhodes			/*
798135446Strhodes			 * See if the address matches the listen-on statement;
799135446Strhodes			 * if not, ignore the interface.
800135446Strhodes			 */
801165071Sdougb			(void)dns_acl_match(&listen_netaddr, NULL, le->acl,
802165071Sdougb					    &mgr->aclenv, &match, NULL);
803135446Strhodes			if (match <= 0)
804135446Strhodes				continue;
805135446Strhodes
806170222Sdougb			if (adjusting == ISC_FALSE && dolistenon == ISC_TRUE) {
807170222Sdougb				setup_listenon(mgr, &interface, le->port);
808170222Sdougb				dolistenon = ISC_FALSE;
809170222Sdougb			}
810170222Sdougb
811135446Strhodes			/*
812135446Strhodes			 * The case of "any" IPv6 address will require
813135446Strhodes			 * special considerations later, so remember it.
814135446Strhodes			 */
815135446Strhodes			if (family == AF_INET6 && ipv6only && ipv6pktinfo &&
816135446Strhodes			    listenon_is_ip6_any(le))
817135446Strhodes				ipv6_wildcard = ISC_TRUE;
818135446Strhodes
819135446Strhodes			/*
820135446Strhodes			 * When adjusting interfaces with extra a listening
821135446Strhodes			 * list, see if the address matches the extra list.
822135446Strhodes			 * If it does, and is also covered by a wildcard
823135446Strhodes			 * interface, we need to listen on the address
824135446Strhodes			 * explicitly.
825135446Strhodes			 */
826135446Strhodes			if (adjusting == ISC_TRUE) {
827135446Strhodes				ns_listenelt_t *ele;
828135446Strhodes
829135446Strhodes				match = 0;
830135446Strhodes				for (ele = ISC_LIST_HEAD(ext_listen->elts);
831135446Strhodes				     ele != NULL;
832135446Strhodes				     ele = ISC_LIST_NEXT(ele, link)) {
833165071Sdougb					(void)dns_acl_match(&listen_netaddr,
834165071Sdougb							    NULL, ele->acl,
835165071Sdougb							    NULL, &match, NULL);
836193149Sdougb					if (match > 0 &&
837193149Sdougb					    (ele->port == le->port ||
838193149Sdougb					    ele->port == 0))
839135446Strhodes						break;
840135446Strhodes					else
841135446Strhodes						match = 0;
842135446Strhodes				}
843135446Strhodes				if (ipv6_wildcard == ISC_TRUE && match == 0)
844135446Strhodes					continue;
845135446Strhodes			}
846135446Strhodes
847135446Strhodes			ifp = find_matching_interface(mgr, &listen_sockaddr);
848135446Strhodes			if (ifp != NULL) {
849135446Strhodes				ifp->generation = mgr->generation;
850135446Strhodes			} else {
851135446Strhodes				char sabuf[ISC_SOCKADDR_FORMATSIZE];
852135446Strhodes
853135446Strhodes				if (adjusting == ISC_FALSE &&
854135446Strhodes				    ipv6_wildcard == ISC_TRUE)
855135446Strhodes					continue;
856135446Strhodes
857135446Strhodes				if (log_explicit && family == AF_INET6 &&
858135446Strhodes				    !adjusting && listenon_is_ip6_any(le)) {
859135446Strhodes					isc_log_write(IFMGR_COMMON_LOGARGS,
860135446Strhodes						      verbose ? ISC_LOG_INFO :
861135446Strhodes							      ISC_LOG_DEBUG(1),
862135446Strhodes						      "IPv6 socket API is "
863135446Strhodes						      "incomplete; explicitly "
864135446Strhodes						      "binding to each IPv6 "
865135446Strhodes						      "address separately");
866135446Strhodes					log_explicit = ISC_FALSE;
867135446Strhodes				}
868135446Strhodes				isc_sockaddr_format(&listen_sockaddr,
869135446Strhodes						    sabuf, sizeof(sabuf));
870135446Strhodes				isc_log_write(IFMGR_COMMON_LOGARGS,
871135446Strhodes					      ISC_LOG_INFO,
872135446Strhodes					      "%s"
873135446Strhodes					      "listening on %s interface "
874135446Strhodes					      "%s, %s",
875135446Strhodes					      (adjusting == ISC_TRUE) ?
876135446Strhodes					      "additionally " : "",
877135446Strhodes					      (family == AF_INET) ?
878135446Strhodes					      "IPv4" : "IPv6",
879135446Strhodes					      interface.name, sabuf);
880135446Strhodes
881135446Strhodes				result = ns_interface_setup(mgr,
882135446Strhodes							    &listen_sockaddr,
883135446Strhodes							    interface.name,
884135446Strhodes							    &ifp,
885135446Strhodes							    (adjusting == ISC_TRUE) ?
886135446Strhodes							    ISC_FALSE :
887135446Strhodes							    ISC_TRUE);
888135446Strhodes
889135446Strhodes				if (result != ISC_R_SUCCESS) {
890135446Strhodes					isc_log_write(IFMGR_COMMON_LOGARGS,
891135446Strhodes						      ISC_LOG_ERROR,
892135446Strhodes						      "creating %s interface "
893135446Strhodes						      "%s failed; interface "
894135446Strhodes						      "ignored",
895135446Strhodes						      (family == AF_INET) ?
896135446Strhodes						      "IPv4" : "IPv6",
897135446Strhodes						      interface.name);
898135446Strhodes				}
899135446Strhodes				/* Continue. */
900135446Strhodes			}
901135446Strhodes
902135446Strhodes		}
903135446Strhodes		continue;
904135446Strhodes
905135446Strhodes	ignore_interface:
906135446Strhodes		isc_log_write(IFMGR_COMMON_LOGARGS,
907135446Strhodes			      ISC_LOG_ERROR,
908135446Strhodes			      "ignoring %s interface %s: %s",
909135446Strhodes			      (family == AF_INET) ? "IPv4" : "IPv6",
910135446Strhodes			      interface.name, isc_result_totext(result));
911135446Strhodes		continue;
912135446Strhodes	}
913135446Strhodes	if (result != ISC_R_NOMORE)
914135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
915135446Strhodes				 "interface iteration failed: %s",
916135446Strhodes				 isc_result_totext(result));
917186462Sdougb	else
918135446Strhodes		result = ISC_R_SUCCESS;
919135446Strhodes cleanup_iter:
920135446Strhodes	isc_interfaceiter_destroy(&iter);
921135446Strhodes	return (result);
922135446Strhodes}
923135446Strhodes
924135446Strhodesstatic void
925135446Strhodesns_interfacemgr_scan0(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
926135446Strhodes		      isc_boolean_t verbose)
927135446Strhodes{
928135446Strhodes	isc_boolean_t purge = ISC_TRUE;
929135446Strhodes
930135446Strhodes	REQUIRE(NS_INTERFACEMGR_VALID(mgr));
931135446Strhodes
932135446Strhodes	mgr->generation++;	/* Increment the generation count. */
933135446Strhodes
934135446Strhodes	if (do_scan(mgr, ext_listen, verbose) != ISC_R_SUCCESS)
935135446Strhodes		purge = ISC_FALSE;
936135446Strhodes
937135446Strhodes	/*
938135446Strhodes	 * Now go through the interface list and delete anything that
939135446Strhodes	 * does not have the current generation number.  This is
940135446Strhodes	 * how we catch interfaces that go away or change their
941135446Strhodes	 * addresses.
942135446Strhodes	 */
943135446Strhodes	if (purge)
944135446Strhodes		purge_old_interfaces(mgr);
945135446Strhodes
946135446Strhodes	/*
947135446Strhodes	 * Warn if we are not listening on any interface, unless
948186462Sdougb	 * we're in lwresd-only mode, in which case that is to
949135446Strhodes	 * be expected.
950135446Strhodes	 */
951135446Strhodes	if (ext_listen == NULL &&
952135446Strhodes	    ISC_LIST_EMPTY(mgr->interfaces) && ! ns_g_lwresdonly) {
953135446Strhodes		isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_WARNING,
954135446Strhodes			      "not listening on any interfaces");
955135446Strhodes	}
956135446Strhodes}
957135446Strhodes
958135446Strhodesvoid
959135446Strhodesns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) {
960135446Strhodes	ns_interfacemgr_scan0(mgr, NULL, verbose);
961135446Strhodes}
962135446Strhodes
963135446Strhodesvoid
964135446Strhodesns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list,
965135446Strhodes		       isc_boolean_t verbose)
966135446Strhodes{
967135446Strhodes	ns_interfacemgr_scan0(mgr, list, verbose);
968135446Strhodes}
969135446Strhodes
970135446Strhodesvoid
971135446Strhodesns_interfacemgr_setlistenon4(ns_interfacemgr_t *mgr, ns_listenlist_t *value) {
972135446Strhodes	LOCK(&mgr->lock);
973135446Strhodes	ns_listenlist_detach(&mgr->listenon4);
974135446Strhodes	ns_listenlist_attach(value, &mgr->listenon4);
975135446Strhodes	UNLOCK(&mgr->lock);
976135446Strhodes}
977135446Strhodes
978135446Strhodesvoid
979135446Strhodesns_interfacemgr_setlistenon6(ns_interfacemgr_t *mgr, ns_listenlist_t *value) {
980135446Strhodes	LOCK(&mgr->lock);
981135446Strhodes	ns_listenlist_detach(&mgr->listenon6);
982135446Strhodes	ns_listenlist_attach(value, &mgr->listenon6);
983135446Strhodes	UNLOCK(&mgr->lock);
984135446Strhodes}
985135446Strhodes
986135446Strhodesvoid
987135446Strhodesns_interfacemgr_dumprecursing(FILE *f, ns_interfacemgr_t *mgr) {
988135446Strhodes	ns_interface_t *interface;
989135446Strhodes
990135446Strhodes	LOCK(&mgr->lock);
991135446Strhodes	interface = ISC_LIST_HEAD(mgr->interfaces);
992135446Strhodes	while (interface != NULL) {
993135446Strhodes		if (interface->clientmgr != NULL)
994135446Strhodes			ns_client_dumprecursing(f, interface->clientmgr);
995135446Strhodes		interface = ISC_LIST_NEXT(interface, link);
996135446Strhodes	}
997135446Strhodes	UNLOCK(&mgr->lock);
998135446Strhodes}
999170222Sdougb
1000170222Sdougbisc_boolean_t
1001170222Sdougbns_interfacemgr_listeningon(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr) {
1002170222Sdougb	isc_sockaddr_t *old;
1003170222Sdougb
1004170222Sdougb	for (old = ISC_LIST_HEAD(mgr->listenon);
1005170222Sdougb	     old != NULL;
1006170222Sdougb	     old = ISC_LIST_NEXT(old, link))
1007170222Sdougb		if (isc_sockaddr_equal(old, addr))
1008170222Sdougb			return (ISC_TRUE);
1009170222Sdougb	return (ISC_FALSE);
1010170222Sdougb}
1011