interfacemgr.c revision 0c27b3fe77ac1d5094ba3521e8142d9e7973133f
/*
* Copyright (C) 1999-2002, 2004-2009, 2011-2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* $Id: interfacemgr.c,v 1.101 2011/11/09 18:44:03 each Exp $ */
/*! \file */
#include <config.h>
#include <isc/interfaceiter.h>
#include <dns/dispatch.h>
#include <named/interfacemgr.h>
#ifdef HAVE_NET_ROUTE_H
#define USE_ROUTE_SOCKET 1
#define ROUTE_SOCKET_PROTOCOL PF_ROUTE
#endif
#endif
#if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H)
#include <linux/rtnetlink.h>
#if defined(RTM_NEWADDR) && defined(RTM_DELADDR)
#define USE_ROUTE_SOCKET 1
#define ROUTE_SOCKET_PROTOCOL PF_NETLINK
#define MSGTYPE nlmsg_type
#endif
#endif
#ifdef TUNE_LARGE
#define UDPBUFFERS 32768
#else
#define UDPBUFFERS 1000
#endif /* TUNE_LARGE */
#define IFMGR_COMMON_LOGARGS \
/*% nameserver interface manager structure */
struct ns_interfacemgr {
unsigned int magic; /*%< Magic number. */
int references;
unsigned int generation; /*%< Current generation no. */
#ifdef USE_ROUTE_SOCKET
isc_task_t * task;
unsigned char buf[2048];
#endif
};
static void
static void
#ifdef USE_ROUTE_SOCKET
static void
isc_region_t r;
"automatic interface scanning "
"terminated: %s",
return;
}
#ifdef RTM_VERSION
"automatic interface rescanning disabled: "
"rtm->rtm_version mismatch (%u != %u) "
return;
}
#endif
case RTM_NEWADDR:
case RTM_DELADDR:
break;
default:
break;
}
/*
* Look for next route event.
*/
route_event, mgr);
if (result == ISC_R_SUCCESS)
}
if (done)
return;
}
#endif
{
#ifndef USE_ROUTE_SOCKET
#endif
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto cleanup_mem;
/*
* The listen-on lists are initially empty.
*/
if (result != ISC_R_SUCCESS)
goto cleanup_mem;
if (result != ISC_R_SUCCESS)
goto cleanup_listenon;
#ifdef HAVE_GEOIP
#endif
#ifdef USE_ROUTE_SOCKET
switch (result) {
case ISC_R_NOPERM:
case ISC_R_SUCCESS:
case ISC_R_NOTIMPLEMENTED:
case ISC_R_FAMILYNOSUPPORT:
break;
default:
goto cleanup_aclenv;
}
#else
#endif
#ifdef USE_ROUTE_SOCKET
route_event, mgr);
if (result != ISC_R_SUCCESS) {
}
}
#endif
return (ISC_R_SUCCESS);
#ifdef USE_ROUTE_SOCKET
#endif
return (result);
}
static void
#ifdef USE_ROUTE_SOCKET
#endif
}
}
void
source->references++;
}
void
target->references--;
if (target->references == 0)
if (need_destroy)
}
void
/*%
* Shut down and detach all interfaces.
* By incrementing the generation count, we make purge_old_interfaces()
* consider all interfaces "old".
*/
mgr->generation++;
#ifdef USE_ROUTE_SOCKET
}
#endif
}
static isc_result_t
{
int disp;
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto lock_create_failure;
if (result != ISC_R_SUCCESS) {
"ns_clientmgr_create() failed: %s",
goto clientmgr_create_failure;
}
/*
* Create a single TCP client object. It will replace itself
* with a new one as soon as it gets a connection, so the actual
* connections will be handled in parallel even though there is
* only one client initially.
*/
ifp->ntcpcurrent = 0;
ifp->nudpdispatch = 0;
return (ISC_R_SUCCESS);
return (ISC_R_UNEXPECTED);
}
static isc_result_t
unsigned int attrs;
unsigned int attrmask;
int disp, i;
attrs = 0;
else
attrmask = 0;
4096, UDPBUFFERS,
32768, 8219, 8237,
disp == 0
? NULL
: ifp->udpdispatch[0]);
if (result != ISC_R_SUCCESS) {
"could not listen on UDP socket: %s",
goto udp_dispatch_failure;
}
}
if (result != ISC_R_SUCCESS) {
"UDP ns_clientmgr_createclients(): %s",
goto addtodispatch_failure;
}
return (ISC_R_SUCCESS);
for (i = disp - 1; i >= 0; i--) {
}
ifp->nudpdispatch = 0;
return (result);
}
static isc_result_t
/*
* Open a TCP socket.
*/
if (result != ISC_R_SUCCESS) {
"creating TCP socket: %s",
goto tcp_socket_failure;
}
#ifndef ISC_ALLOW_MAPPED
#endif
if (result != ISC_R_SUCCESS) {
"binding TCP socket: %s",
goto tcp_bind_failure;
}
if (result != ISC_R_SUCCESS) {
"listening on TCP socket: %s",
goto tcp_listen_failure;
}
/*
* result.
*/
ISC_TRUE);
if (result != ISC_R_SUCCESS) {
"TCP ns_clientmgr_createclients(): %s",
goto accepttcp_failure;
}
return (ISC_R_SUCCESS);
return (result);
}
static isc_result_t
{
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS) {
*addr_in_use = ISC_TRUE;
goto cleanup_interface;
}
if (result != ISC_R_SUCCESS) {
if ((result == ISC_R_ADDRINUSE) &&
(addr_in_use != NULL))
*addr_in_use = ISC_TRUE;
/*
* XXXRTH We don't currently have a way to easily stop
* dispatch service, so we currently return
* ISC_R_SUCCESS (the UDP stuff will work even if TCP
* creation failed). This will be fixed later.
*/
}
}
return (result);
return (result);
}
void
}
static void
int disp;
}
}
void
source->references++;
}
void
target->references--;
if (target->references == 0)
if (need_destroy)
}
/*%
* Search the interface list for an interface whose address and port
* both match those of 'addr'. Return a pointer to it, or NULL if not found.
*/
static ns_interface_t *
break;
}
return (ifp);
}
/*%
* Remove any interfaces whose generation number is not the current one.
*/
static void
char sabuf[256];
"no longer listening on %s", sabuf);
}
}
}
static isc_result_t
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_SUCCESS);
}
static isc_boolean_t
}
static isc_result_t
unsigned int prefixlen;
/* First add localhost address */
if (result != ISC_R_SUCCESS)
return (result);
/* Then add localnets prefix */
&prefixlen);
/* Non contiguous netmasks not allowed by IPv6 arch. */
return (result);
if (result != ISC_R_SUCCESS) {
"omitting IPv4 interface %s from "
return (ISC_R_SUCCESS);
}
if (prefixlen == 0U) {
"omitting %s interface %s from localnets ACL: "
"zero prefix length detected",
return (ISC_R_SUCCESS);
}
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_SUCCESS);
}
static void
{
return;
break;
else
}
static void
}
}
static isc_result_t
{
char sabuf[ISC_SOCKADDR_FORMATSIZE];
if (ext_listen != NULL)
if (isc_net_probeipv6() == ISC_R_SUCCESS)
#ifdef WANT_IPV6
else if (!ns_g_disable6)
"no IPv6 interfaces found");
#endif
if (isc_net_probeipv4() == ISC_R_SUCCESS)
else if (!ns_g_disable4)
"no IPv4 interfaces found");
/*
* A special, but typical case; listen-on-v6 { any; }.
* When we can make the socket IPv6-only, open a single wildcard
* socket for IPv6 communication. Otherwise, make separate socket
* for each IPv6 address in order to avoid accepting IPv4 packets
* as the form of mapped addresses unintentionally unless explicitly
* allowed.
*/
#ifndef ISC_ALLOW_MAPPED
isc_net_probe_ipv6only() != ISC_R_SUCCESS) {
}
#endif
}
if (!listenon_is_ip6_any(le))
continue;
in6a = in6addr_any;
sizeof(sabuf));
"%s: conflicting DSCP "
"values, using %d",
}
} else {
"listening on IPv6 "
"interfaces, port %u",
"<any>", &ifp,
NULL);
if (result == ISC_R_SUCCESS)
else
"listening on all IPv6 "
"interfaces failed");
/* Continue. */
}
}
}
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
goto cleanup_iter;
if (result != ISC_R_SUCCESS)
goto cleanup_iter;
}
result == ISC_R_SUCCESS;
{
unsigned int family;
if (result != ISC_R_SUCCESS)
break;
continue;
continue;
continue;
/*
* Test for the address being nonzero rather than testing
* INTERFACE_F_UP, because on some systems the latter
* follows the media state and we could end up ignoring
* the interface for an entire rescan interval due to
* a temporary media glitch at rescan time.
*/
continue;
}
continue;
}
if (result != ISC_R_SUCCESS)
goto ignore_interface;
}
{
int match;
/*
* combination.
*/
} else {
}
/*
* See if the address matches the listen-on statement;
* if not, ignore the interface.
*/
if (match <= 0)
continue;
}
/*
* The case of "any" IPv6 address will require
* special considerations later, so remember it.
*/
/*
* When adjusting interfaces with extra a listening
* list, see if the address matches the extra list.
* If it does, and is also covered by a wildcard
* interface, we need to listen on the address
* explicitly.
*/
match = 0;
(void)dns_acl_match(&listen_netaddr,
if (match > 0 &&
break;
else
match = 0;
}
continue;
}
sizeof(sabuf));
"%s: conflicting DSCP "
"values, using %d",
}
} else {
continue;
verbose ? ISC_LOG_INFO :
ISC_LOG_DEBUG(1),
"IPv6 socket API is "
"incomplete; explicitly "
"binding to each IPv6 "
"address separately");
}
"%s"
"listening on %s interface "
"%s, %s",
"additionally " : "",
"IPv4" : "IPv6",
&ifp,
&addr_in_use);
if (!addr_in_use)
if (result != ISC_R_SUCCESS) {
"creating %s interface "
"%s failed; interface "
"ignored",
"IPv4" : "IPv6",
}
/* Continue. */
}
}
continue;
"ignoring %s interface %s: %s",
continue;
}
if (result != ISC_R_NOMORE)
"interface iteration failed: %s",
else
return (result);
}
static isc_result_t
{
/*
* Now go through the interface list and delete anything that
* does not have the current generation number. This is
* how we catch interfaces that go away or change their
* addresses.
*/
if (purge)
/*
* Warn if we are not listening on any interface, unless
* we're in lwresd-only mode, in which case that is to
* be expected.
*/
if (ext_listen == NULL &&
"not listening on any interfaces");
}
return (result);
}
}
}
{
}
void
}
void
}
void
}
}
return (ISC_TRUE);
return (ISC_FALSE);
}