From 6df5cf1ee6f39dd66fe77b1f7c9a9e7cdf031d57 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Tue, 14 Dec 2021 16:51:02 -0800 Subject: [PATCH] keep track of non-listening interfaces previously, if "listen-on-v6" was set to "none", then every time a scan saw an IPv6 address it would appear to be a new one. this commit retains all known interfaces in a list and sets a flag in the ones that are listening, so that configured interfaces that have been seen before will be recognized as such. as an incidental fix, the ns__interfacemgr_getif() and _nextif() functions have been removed since they were never used. --- lib/ns/include/ns/interfacemgr.h | 12 +-- lib/ns/interfacemgr.c | 169 +++++++++++++++---------------- 2 files changed, 82 insertions(+), 99 deletions(-) diff --git a/lib/ns/include/ns/interfacemgr.h b/lib/ns/include/ns/interfacemgr.h index 2331fc63e3..8783707256 100644 --- a/lib/ns/include/ns/interfacemgr.h +++ b/lib/ns/include/ns/interfacemgr.h @@ -59,7 +59,8 @@ #define IFACE_MAGIC ISC_MAGIC('I', ':', '-', ')') #define NS_INTERFACE_VALID(t) ISC_MAGIC_VALID(t, IFACE_MAGIC) -#define NS_INTERFACEFLAG_ANYADDR 0x01U /*%< bound to "any" address */ +#define NS_INTERFACEFLAG_ANYADDR 0x01U /*%< bound to "any" address */ +#define NS_INTERFACEFLAG_LISTENING 0x02U /*%< listening */ #define MAX_UDP_DISPATCH \ 128 /*%< Maximum number of UDP dispatchers \ * to start per interface */ @@ -194,12 +195,3 @@ ns_interfacemgr_getclientmgr(ns_interfacemgr_t *mgr); * Returns the client manager for the current worker thread. * (This cannot be run from outside a network manager thread.) */ - -ns_interface_t * -ns__interfacemgr_getif(ns_interfacemgr_t *mgr); -ns_interface_t * -ns__interfacemgr_nextif(ns_interface_t *ifp); -/*%< - * Functions to allow external callers to walk the interfaces list. - * (Not intended for use outside this module and associated tests.) - */ diff --git a/lib/ns/interfacemgr.c b/lib/ns/interfacemgr.c index 6900237517..d2c41d022a 100644 --- a/lib/ns/interfacemgr.c +++ b/lib/ns/interfacemgr.c @@ -50,6 +50,8 @@ #endif /* if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H) \ */ +#define LISTENING(ifp) (((ifp)->flags & NS_INTERFACEFLAG_LISTENING) != 0) + #ifdef TUNE_LARGE #define UDPBUFFERS 32768 #else /* ifdef TUNE_LARGE */ @@ -131,11 +133,13 @@ need_rescan(ns_interfacemgr_t *mgr, struct MSGHDR *rtm, size_t len) { */ if (rth->rta_type == IFA_ADDRESS && ifa->ifa_family == AF_INET6) { + bool existed = false; bool was_listening = false; isc_netaddr_t addr = { 0 }; ns_interface_t *ifp = NULL; isc_netaddr_fromin6(&addr, RTA_DATA(rth)); + INSIST(isc_netaddr_getzone(&addr) == 0); /* * Check whether we were listening on the @@ -153,8 +157,21 @@ need_rescan(ns_interfacemgr_t *mgr, struct MSGHDR *rtm, size_t len) { isc_netaddr_t tmp = { 0 }; isc_netaddr_fromsockaddr(&tmp, &ifp->addr); + if (tmp.family != AF_INET6) { + continue; + } + + /* + * We have to nullify the zone (IPv6 + * scope ID) because we haven't got one + * from the kernel. Otherwise match + * could fail even for an existing + * address. + */ + isc_netaddr_setzone(&tmp, 0); if (isc_netaddr_equal(&tmp, &addr)) { - was_listening = true; + was_listening = LISTENING(ifp); + existed = true; break; } } @@ -164,9 +181,8 @@ need_rescan(ns_interfacemgr_t *mgr, struct MSGHDR *rtm, size_t len) { * Do rescan if the state of the interface * has changed. */ - if ((!was_listening && - rtm->MSGTYPE == RTM_NEWADDR) || - (was_listening && + if ((!existed && rtm->MSGTYPE == RTM_NEWADDR) || + (existed && was_listening && rtm->MSGTYPE == RTM_DELADDR)) { return (true); @@ -457,9 +473,9 @@ ns_interfacemgr_shutdown(ns_interfacemgr_t *mgr) { } } -static isc_result_t -ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, - const char *name, ns_interface_t **ifpret) { +static void +interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, const char *name, + ns_interface_t **ifpret) { ns_interface_t *ifp = NULL; REQUIRE(NS_INTERFACEMGR_VALID(mgr)); @@ -473,12 +489,6 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, isc_mutex_init(&ifp->lock); - /* - * 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. - */ isc_refcount_init(&ifp->ntcpaccepting, 0); isc_refcount_init(&ifp->ntcpactive, 0); @@ -492,13 +502,7 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, isc_refcount_init(&ifp->references, 1); ifp->magic = IFACE_MAGIC; - ifp->tcplistensocket = NULL; - ifp->http_listensocket = NULL; - ifp->http_secure_listensocket = NULL; - *ifpret = ifp; - - return (ISC_R_SUCCESS); } static isc_result_t @@ -654,20 +658,25 @@ ns_interface_listenhttp(ns_interface_t *ifp, isc_tlsctx_t *sslctx, char **eps, } static isc_result_t -ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, - const char *name, ns_interface_t **ifpret, - ns_listenelt_t *elt, bool *addr_in_use) { +interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, const char *name, + ns_interface_t **ifpret, ns_listenelt_t *elt, + bool *addr_in_use) { isc_result_t result; ns_interface_t *ifp = NULL; - REQUIRE(ifpret != NULL && *ifpret == NULL); + + REQUIRE(ifpret != NULL); REQUIRE(addr_in_use == NULL || !*addr_in_use); - result = ns_interface_create(mgr, addr, name, &ifp); - if (result != ISC_R_SUCCESS) { - return (result); + ifp = *ifpret; + + if (ifp == NULL) { + interface_create(mgr, addr, name, &ifp); + } else { + REQUIRE(!LISTENING(ifp)); } ifp->dscp = elt->dscp; + ifp->flags |= NS_INTERFACEFLAG_LISTENING; if (elt->is_http) { result = ns_interface_listenhttp( @@ -719,16 +728,14 @@ ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, return (result); cleanup_interface: - LOCK(&ifp->mgr->lock); - ISC_LIST_UNLINK(ifp->mgr->interfaces, ifp, link); - UNLOCK(&ifp->mgr->lock); ns_interface_shutdown(ifp); - ns_interface_detach(&ifp); return (result); } void ns_interface_shutdown(ns_interface_t *ifp) { + ifp->flags &= ~NS_INTERFACEFLAG_LISTENING; + if (ifp->udplistensocket != NULL) { isc_nm_stoplistening(ifp->udplistensocket); isc_nmsocket_close(&ifp->udplistensocket); @@ -748,23 +755,26 @@ ns_interface_shutdown(ns_interface_t *ifp) { } static void -ns_interface_destroy(ns_interface_t *ifp) { +interface_destroy(ns_interface_t *ifp) { + ns_interfacemgr_t *mgr; + REQUIRE(NS_INTERFACE_VALID(ifp)); - isc_mem_t *mctx = ifp->mgr->mctx; + mgr = ifp->mgr; ns_interface_shutdown(ifp); + if (ISC_LINK_LINKED(ifp, link)) { + ISC_LIST_UNLINK(mgr->interfaces, ifp, link); + } + + ifp->magic = 0; isc_mutex_destroy(&ifp->lock); - ns_interfacemgr_detach(&ifp->mgr); - isc_refcount_destroy(&ifp->ntcpactive); isc_refcount_destroy(&ifp->ntcpaccepting); - ifp->magic = 0; - - isc_mem_put(mctx, ifp, sizeof(*ifp)); + isc_mem_put(mgr->mctx, ifp, sizeof(*ifp)); } void @@ -781,7 +791,7 @@ ns_interface_detach(ns_interface_t **targetp) { REQUIRE(target != NULL); REQUIRE(NS_INTERFACE_VALID(target)); if (isc_refcount_decrement(&target->references) == 1) { - ns_interface_destroy(target); + interface_destroy(target); } } @@ -817,10 +827,14 @@ purge_old_interfaces(ns_interfacemgr_t *mgr) { if (ifp->generation != mgr->generation) { char sabuf[256]; ISC_LIST_UNLINK(ifp->mgr->interfaces, ifp, link); - isc_sockaddr_format(&ifp->addr, sabuf, sizeof(sabuf)); - isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_INFO, - "no longer listening on %s", sabuf); - ns_interface_shutdown(ifp); + if (LISTENING(ifp)) { + isc_sockaddr_format(&ifp->addr, sabuf, + sizeof(sabuf)); + isc_log_write( + IFMGR_COMMON_LOGARGS, ISC_LOG_INFO, + "no longer listening on %s", sabuf); + ns_interface_shutdown(ifp); + } ns_interface_detach(&ifp); } } @@ -949,7 +963,7 @@ do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { isc_netaddr_t zero_address, zero_address6; ns_listenelt_t *le; isc_sockaddr_t listen_addr; - ns_interface_t *ifp; + ns_interface_t *ifp = NULL; bool log_explicit = false; bool dolistenon; char sabuf[ISC_SOCKADDR_FORMATSIZE]; @@ -1010,7 +1024,8 @@ do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { * during reconfiguration because the * certificates could have been changed. */ - if (config && le->sslctx != NULL) { + if (config && LISTENING(ifp) && + le->sslctx != NULL) { INSIST(NS_INTERFACE_VALID(ifp)); LOCK(&mgr->lock); ISC_LIST_UNLINK(ifp->mgr->interfaces, @@ -1040,7 +1055,9 @@ do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { "values, using %d", sabuf, ifp->dscp); } - continue; + if (LISTENING(ifp)) { + continue; + } } } @@ -1048,8 +1065,8 @@ do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { "listening on IPv6 " "interfaces, port %u", le->port); - result = ns_interface_setup(mgr, &listen_addr, "", - &ifp, le, NULL); + result = interface_setup(mgr, &listen_addr, "", + &ifp, le, NULL); if (result == ISC_R_SUCCESS) { ifp->flags |= NS_INTERFACEFLAG_ANYADDR; } else { @@ -1144,33 +1161,23 @@ do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { int match; bool addr_in_use = false; bool ipv6_wildcard = false; - isc_netaddr_t listen_netaddr; isc_sockaddr_t listen_sockaddr; - /* - * Construct a socket address for this IP/port - * combination. - */ - if (family == AF_INET) { - isc_netaddr_fromin(&listen_netaddr, - &interface.address.type.in); - } else { - isc_netaddr_fromin6( - &listen_netaddr, - &interface.address.type.in6); - isc_netaddr_setzone(&listen_netaddr, - interface.address.zone); - } isc_sockaddr_fromnetaddr(&listen_sockaddr, - &listen_netaddr, le->port); + &interface.address, le->port); /* * See if the address matches the listen-on statement; - * if not, ignore the interface. + * if not, ignore the interface, but store it in + * the interface table so we know we've seen it + * before. */ - (void)dns_acl_match(&listen_netaddr, NULL, le->acl, + (void)dns_acl_match(&interface.address, NULL, le->acl, mgr->aclenv, &match, NULL); if (match <= 0) { + ns_interface_t *new = NULL; + interface_create(mgr, &listen_sockaddr, + interface.name, &new); continue; } @@ -1195,7 +1202,8 @@ do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { * during a reconfiguration because the * certificates could have been changed. */ - if (config && le->sslctx != NULL) { + if (config && LISTENING(ifp) && + le->sslctx != NULL) { INSIST(NS_INTERFACE_VALID(ifp)); LOCK(&mgr->lock); ISC_LIST_UNLINK(ifp->mgr->interfaces, @@ -1225,7 +1233,9 @@ do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { "values, using %d", sabuf, ifp->dscp); } - continue; + if (LISTENING(ifp)) { + continue; + } } } @@ -1252,9 +1262,9 @@ do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { (family == AF_INET) ? "IPv4" : "IPv6", interface.name, sabuf); - result = ns_interface_setup(mgr, &listen_sockaddr, - interface.name, &ifp, le, - &addr_in_use); + result = interface_setup(mgr, &listen_sockaddr, + interface.name, &ifp, le, + &addr_in_use); tried_listening = true; if (!addr_in_use) { @@ -1426,25 +1436,6 @@ ns_interfacemgr_getserver(ns_interfacemgr_t *mgr) { return (mgr->sctx); } -ns_interface_t * -ns__interfacemgr_getif(ns_interfacemgr_t *mgr) { - ns_interface_t *head; - REQUIRE(NS_INTERFACEMGR_VALID(mgr)); - LOCK(&mgr->lock); - head = ISC_LIST_HEAD(mgr->interfaces); - UNLOCK(&mgr->lock); - return (head); -} - -ns_interface_t * -ns__interfacemgr_nextif(ns_interface_t *ifp) { - ns_interface_t *next; - LOCK(&ifp->lock); - next = ISC_LIST_NEXT(ifp, link); - UNLOCK(&ifp->lock); - return (next); -} - ns_clientmgr_t * ns_interfacemgr_getclientmgr(ns_interfacemgr_t *mgr) { int tid = isc_nm_tid();