Commit c7e266b7 authored by Evan Hunt's avatar Evan Hunt
Browse files

Add support for O(1) ACL processing, based on radix tree code originally

written by kevin brintnall. [RT #16288]
parent c1ff5c2b
2233. [func] Add support for O(1) ACL processing, based on
radix tree code originally written by kevin
brintnall. [RT #16288]
2232. [bug] dns_adb_findaddrinfo() could fail and return
ISC_R_SUCCESS. [RT #17137]
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: controlconf.c,v 1.54 2007/06/18 23:47:18 tbox Exp $ */
/* $Id: controlconf.c,v 1.55 2007/09/12 01:09:07 each Exp $ */
/*! \file */
......@@ -1014,7 +1014,7 @@ update_listener(ns_controls_t *cp, controllistener_t **listenerp,
if (control != NULL && type == isc_sockettype_tcp) {
allow = cfg_tuple_get(control, "allow");
result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
aclconfctx, listener->mctx,
aclconfctx, listener->mctx, 0,
&new_acl);
} else {
result = dns_acl_any(listener->mctx, &new_acl);
......@@ -1101,7 +1101,8 @@ add_listener(ns_controls_t *cp, controllistener_t **listenerp,
if (control != NULL && type == isc_sockettype_tcp) {
allow = cfg_tuple_get(control, "allow");
result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
aclconfctx, mctx, &new_acl);
aclconfctx, mctx, 0,
&new_acl);
} else {
result = dns_acl_any(mctx, &new_acl);
}
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: interfacemgr.c,v 1.89 2007/06/18 23:47:18 tbox Exp $ */
/* $Id: interfacemgr.c,v 1.90 2007/09/12 01:09:07 each Exp $ */
/*! \file */
......@@ -483,7 +483,7 @@ static isc_result_t
clearacl(isc_mem_t *mctx, dns_acl_t **aclp) {
dns_acl_t *newacl = NULL;
isc_result_t result;
result = dns_acl_create(mctx, 10, &newacl);
result = dns_acl_create(mctx, 0, &newacl);
if (result != ISC_R_SUCCESS)
return (result);
dns_acl_detach(aclp);
......@@ -494,36 +494,31 @@ clearacl(isc_mem_t *mctx, dns_acl_t **aclp) {
static isc_boolean_t
listenon_is_ip6_any(ns_listenelt_t *elt) {
if (elt->acl->length != 1)
return (ISC_FALSE);
if (elt->acl->elements[0].negative == ISC_FALSE &&
elt->acl->elements[0].type == dns_aclelementtype_any)
return (ISC_TRUE); /* listen-on-v6 { any; } */
return (ISC_FALSE); /* All others */
REQUIRE(elt && elt->acl);
return dns_acl_isany(elt->acl);
}
static isc_result_t
setup_locals(ns_interfacemgr_t *mgr, isc_interface_t *interface) {
isc_result_t result;
dns_aclelement_t elt;
unsigned int family;
unsigned int prefixlen;
isc_netaddr_t *netaddr;
family = interface->address.family;
netaddr = &interface->address;
elt.type = dns_aclelementtype_ipprefix;
elt.negative = ISC_FALSE;
elt.u.ip_prefix.address = interface->address;
elt.u.ip_prefix.prefixlen = (family == AF_INET) ? 32 : 128;
result = dns_acl_appendelement(mgr->aclenv.localhost, &elt);
/* First add localhost address */
prefixlen = (netaddr->family == AF_INET) ? 32 : 128;
result = dns_iptable_addprefix(mgr->aclenv.localhost->iptable,
netaddr, prefixlen, ISC_TRUE);
if (result != ISC_R_SUCCESS)
return (result);
/* Then add localnets prefix */
result = isc_netaddr_masktoprefixlen(&interface->netmask,
&prefixlen);
/* Non contigious netmasks not allowed by IPv6 arch. */
if (result != ISC_R_SUCCESS && family == AF_INET6)
if (result != ISC_R_SUCCESS && netaddr->family == AF_INET6)
return (result);
if (result != ISC_R_SUCCESS) {
......@@ -533,17 +528,14 @@ setup_locals(ns_interfacemgr_t *mgr, isc_interface_t *interface) {
"localnets ACL: %s",
interface->name,
isc_result_totext(result));
} else {
elt.u.ip_prefix.prefixlen = prefixlen;
if (dns_acl_elementmatch(mgr->aclenv.localnets, &elt,
NULL) == ISC_R_NOTFOUND) {
result = dns_acl_appendelement(mgr->aclenv.localnets,
&elt);
if (result != ISC_R_SUCCESS)
return (result);
}
return (ISC_R_SUCCESS);
}
result = dns_iptable_addprefix(mgr->aclenv.localnets->iptable,
netaddr, prefixlen, ISC_TRUE);
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_SUCCESS);
}
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: server.c,v 1.489 2007/07/09 02:12:42 marka Exp $ */
/* $Id: server.c,v 1.490 2007/09/12 01:09:07 each Exp $ */
/*! \file */
......@@ -307,7 +307,51 @@ configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
return (ISC_R_SUCCESS);
result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
actx, mctx, aclp);
actx, mctx, 0, aclp);
return (result);
}
/*%
* Configure a sortlist at '*aclp'. Essentially the same as
* configure_view_acl() except it calls cfg_acl_fromconfig with a
* nest_level value of 2.
*/
static isc_result_t
configure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx,
dns_acl_t **aclp)
{
isc_result_t result;
const cfg_obj_t *maps[3];
const cfg_obj_t *aclobj = NULL;
int i = 0;
if (*aclp != NULL)
dns_acl_detach(aclp);
if (vconfig != NULL)
maps[i++] = cfg_tuple_get(vconfig, "options");
if (config != NULL) {
const cfg_obj_t *options = NULL;
(void)cfg_map_get(config, "options", &options);
if (options != NULL)
maps[i++] = options;
}
maps[i] = NULL;
(void)ns_config_get(maps, "sortlist", &aclobj);
if (aclobj == NULL)
return (ISC_R_SUCCESS);
/*
* Use a nest level of 2 for the "top level" of the sortlist;
* this means each entry in the top two levels will be stored as
* lists of separate, nested ACLs, rather than merged together
* into IP tables as is usually done with ACLs.
*/
result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
actx, mctx, 2, aclp);
return (result);
}
......@@ -1598,8 +1642,11 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
"allow-query-cache", actx,
ns_g_mctx, &view->queryacl));
CHECK(configure_view_acl(vconfig, config, "sortlist",
actx, ns_g_mctx, &view->sortlist));
/*
* Configure sortlist, if set
*/
CHECK(configure_view_sortlist(vconfig, config, actx, ns_g_mctx,
&view->sortlist));
obj = NULL;
result = ns_config_get(maps, "request-ixfr", &obj);
......@@ -2490,25 +2537,23 @@ add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr,
{
ns_listenelt_t *lelt = NULL;
dns_acl_t *src_acl = NULL;
dns_aclelement_t aelt;
isc_result_t result;
isc_sockaddr_t any_sa6;
isc_netaddr_t netaddr;
REQUIRE(isc_sockaddr_pf(addr) == AF_INET6);
isc_sockaddr_any6(&any_sa6);
if (!isc_sockaddr_equal(&any_sa6, addr) &&
(wcardport_ok || isc_sockaddr_getport(addr) != 0)) {
aelt.type = dns_aclelementtype_ipprefix;
aelt.negative = ISC_FALSE;
aelt.u.ip_prefix.prefixlen = 128;
isc_netaddr_fromin6(&aelt.u.ip_prefix.address,
&addr->type.sin6.sin6_addr);
result = dns_acl_create(mctx, 1, &src_acl);
isc_netaddr_fromin6(&netaddr, &addr->type.sin6.sin6_addr);
result = dns_acl_create(mctx, 0, &src_acl);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_acl_appendelement(src_acl, &aelt);
result = dns_iptable_addprefix(src_acl->iptable,
&netaddr, 128, ISC_TRUE);
if (result != ISC_R_SUCCESS)
goto clean;
......@@ -4391,7 +4436,8 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
return (result);
result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"),
config, ns_g_lctx, actx, mctx, &delt->acl);
config, ns_g_lctx, actx, mctx, 0,
&delt->acl);
if (result != ISC_R_SUCCESS) {
ns_listenelt_destroy(delt);
return (result);
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: sortlist.c,v 1.15 2007/06/19 23:46:59 tbox Exp $ */
/* $Id: sortlist.c,v 1.16 2007/09/12 01:09:07 each Exp $ */
/*! \file */
......@@ -51,7 +51,7 @@ ns_sortlist_setup(dns_acl_t *acl, isc_netaddr_t *clientaddr,
const dns_aclelement_t *matched_elt = NULL;
if (e->type == dns_aclelementtype_nestedacl) {
dns_acl_t *inner = e->u.nestedacl;
dns_acl_t *inner = e->nestedacl;
if (inner->length < 1 || inner->length > 2)
goto dont_sort;
......@@ -74,7 +74,7 @@ ns_sortlist_setup(dns_acl_t *acl, isc_netaddr_t *clientaddr,
if (order_elt != NULL) {
if (order_elt->type ==
dns_aclelementtype_nestedacl) {
*argp = order_elt->u.nestedacl;
*argp = order_elt->nestedacl;
return (NS_SORTLISTTYPE_2ELEMENT);
} else if (order_elt->type ==
dns_aclelementtype_localhost &&
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: zoneconf.c,v 1.137 2007/06/19 23:46:59 tbox Exp $ */
/* $Id: zoneconf.c,v 1.138 2007/09/12 01:09:07 each Exp $ */
/*% */
......@@ -90,7 +90,7 @@ configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
}
result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx, actx,
dns_zone_getmctx(zone), &dacl);
dns_zone_getmctx(zone), 0, &dacl);
if (result != ISC_R_SUCCESS)
return (result);
(*setzacl)(zone, dacl);
......
......@@ -18,7 +18,7 @@
- PERFORMANCE OF THIS SOFTWARE.
-->
<!-- File: $Id: Bv9ARM-book.xml,v 1.333 2007/09/07 07:29:22 marka Exp $ -->
<!-- File: $Id: Bv9ARM-book.xml,v 1.334 2007/09/12 01:09:08 each Exp $ -->
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<title>BIND 9 Administrator Reference Manual</title>
......@@ -3023,9 +3023,8 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
Address match lists are primarily used to determine access
control for various server operations. They are also used in
the <command>listen-on</command> and <command>sortlist</command>
statements. The elements
which constitute an address match list can be any of the
following:
statements. The elements which constitute an address match
list can be any of the following:
</para>
<itemizedlist>
<listitem>
......@@ -3053,28 +3052,30 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
<para>
Elements can be negated with a leading exclamation mark (`!'),
and the match list names "any", "none", "localhost", and
"localnets"
are predefined. More information on those names can be found in
the description of the acl statement.
"localnets" are predefined. More information on those names
can be found in the description of the acl statement.
</para>
<para>
The addition of the key clause made the name of this syntactic
element something of a misnomer, since security keys can be used
to validate access without regard to a host or network address.
Nonetheless,
the term "address match list" is still used throughout the
documentation.
Nonetheless, the term "address match list" is still used
throughout the documentation.
</para>
<para>
When a given IP address or prefix is compared to an address
match list, the list is traversed in order until an element
matches.
match list, the comparison takes place in approximately O(1)
time. However, key comparisons require that the list of keys
be traversed until a matching key is found, and therefore may
be somewhat slower.
</para>
<para>
The interpretation of a match depends on whether the list is being
used
for access control, defining listen-on ports, or in a sortlist,
and whether the element was negated.
used for access control, defining listen-on ports, or in a
sortlist, and whether the element was negated.
</para>
<para>
......@@ -3093,23 +3094,25 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
<command>allow-update-forwarding</command>, and
<command>blackhole</command> all use address match
lists. Similarly, the listen-on option will cause the
server to not accept queries on any of the machine's
server to refuse queries on any of the machine's
addresses which do not match the list.
</para>
<para>
Because of the first-match aspect of the algorithm, an element
that defines a subset of another element in the list should come
before the broader element, regardless of whether either is
negated. For
example, in
<command>1.2.3/24; ! 1.2.3.13;</command> the 1.2.3.13
element is
completely useless because the algorithm will match any lookup for
1.2.3.13 to the 1.2.3/24 element.
Using <command>! 1.2.3.13; 1.2.3/24</command> fixes
that problem by having 1.2.3.13 blocked by the negation but all
other 1.2.3.* hosts fall through.
Order of insertion is signficant. If more than one element
in an ACL is found to match a given IP address or prefix,
preference will be given to the one that came
<emphasis>first</emphasis> in the ACL definition.
Because of this first-match behavior, an element that
defines a subset of another element in the list should
come before the broader element, regardless of whether
either is negated. For example, in
<command>1.2.3/24; ! 1.2.3.13;</command>
the 1.2.3.13 element is completely useless because the
algorithm will match any lookup for 1.2.3.13 to the 1.2.3/24
element. Using <command>! 1.2.3.13; 1.2.3/24</command> fixes
that problem by having 1.2.3.13 blocked by the negation, but
all other 1.2.3.* hosts fall through.
</para>
</sect3>
</sect2>
......@@ -3390,8 +3393,7 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
<para>
Note that an address match list's name must be defined
with <command>acl</command> before it can be used
elsewhere; no
forward references are allowed.
elsewhere; no forward references are allowed.
</para>
<para>
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: check.c,v 1.81 2007/08/29 03:23:46 marka Exp $ */
/* $Id: check.c,v 1.82 2007/09/12 01:09:08 each Exp $ */
/*! \file */
......@@ -379,7 +379,8 @@ checkacl(const char *aclname, cfg_aclconfctx_t *actx, const cfg_obj_t *zconfig,
}
if (aclobj == NULL)
return (ISC_R_SUCCESS);
result = cfg_acl_fromconfig(aclobj, config, logctx, actx, mctx, &acl);
result = cfg_acl_fromconfig(aclobj, config, logctx,
actx, mctx, 0, &acl);
if (acl != NULL)
dns_acl_detach(&acl);
return (result);
......@@ -459,7 +460,7 @@ check_recursionacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
continue;
tresult = cfg_acl_fromconfig(aclobj, config, logctx,
actx, mctx, &acl);
actx, mctx, 0, &acl);
if (tresult != ISC_R_SUCCESS)
result = tresult;
......@@ -1932,7 +1933,7 @@ bind9_check_controls(const cfg_obj_t *config, isc_log_t *logctx,
control = cfg_listelt_value(element2);
allow = cfg_tuple_get(control, "allow");
tresult = cfg_acl_fromconfig(allow, config, logctx,
&actx, mctx, &acl);
&actx, mctx, 0, &acl);
if (acl != NULL)
dns_acl_detach(&acl);
if (tresult != ISC_R_SUCCESS)
......@@ -2114,8 +2115,9 @@ bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx,
}
}
tresult = cfg_map_get(config, "acl", &acls);
if (tresult == ISC_R_SUCCESS) {
cfg_map_get(config, "acl", &acls);
if (acls != NULL) {
const cfg_listelt_t *elt;
const cfg_listelt_t *elt2;
const char *aclname;
......@@ -2124,6 +2126,7 @@ bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx,
elt != NULL;
elt = cfg_list_next(elt)) {
const cfg_obj_t *acl = cfg_listelt_value(elt);
unsigned int line = cfg_obj_line(acl);
unsigned int i;
aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
......@@ -2148,7 +2151,6 @@ bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx,
"name"));
if (strcasecmp(aclname, name) == 0) {
const char *file = cfg_obj_file(acl);
unsigned int line = cfg_obj_line(acl);
if (file == NULL)
file = "<unknown file>";
......
......@@ -13,7 +13,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: Makefile.in,v 1.157 2007/06/19 23:47:16 tbox Exp $
# $Id: Makefile.in,v 1.158 2007/09/12 01:09:08 each Exp $
srcdir = @srcdir@
VPATH = @srcdir@
......@@ -53,8 +53,8 @@ DSTOBJS = @DST_EXTRA_OBJS@ \
DNSOBJS = acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \
cache.@O@ callbacks.@O@ compress.@O@ \
db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \
dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \
lib.@O@ log.@O@ lookup.@O@ \
dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ iptable.@O@ journal.@O@ \
keytable.@O@ lib.@O@ log.@O@ lookup.@O@ \
master.@O@ masterdump.@O@ message.@O@ \
name.@O@ ncache.@O@ nsec.@O@ order.@O@ peer.@O@ portlist.@O@ \
rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \
......@@ -79,8 +79,8 @@ DSTSRCS = @DST_EXTRA_SRCS@ \
DNSSRCS = acache.c acl.c adb.c byaddr.c \
cache.c callbacks.c compress.c \
db.c dbiterator.c dbtable.c diff.c dispatch.c \
dlz.c dnssec.c ds.c forward.c journal.c keytable.c \
lib.c log.c lookup.c \
dlz.c dnssec.c ds.c forward.c iptable.c journal.c \
keytable.c lib.c log.c lookup.c \
master.c masterdump.c message.c \
name.c ncache.c nsec.c order.c peer.c portlist.c \
rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c \
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: acl.c,v 1.32 2007/06/19 23:47:16 tbox Exp $ */
/* $Id: acl.c,v 1.33 2007/09/12 01:09:08 each Exp $ */
/*! \file */
......@@ -26,7 +26,13 @@
#include <isc/util.h>
#include <dns/acl.h>
#include <dns/iptable.h>
/*
* Create a new ACL, including an IP table and an array with room
* for 'n' ACL elements. The elements are uninitialized and the
* length is 0.
*/
isc_result_t
dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
isc_result_t result;
......@@ -43,11 +49,19 @@ dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
return (ISC_R_NOMEMORY);
acl->mctx = mctx;
acl->name = NULL;
result = isc_refcount_init(&acl->refcount, 1);
if (result != ISC_R_SUCCESS) {
isc_mem_put(mctx, acl, sizeof(*acl));
return (result);
}
result = dns_iptable_create(mctx, &acl->iptable);
if (result != ISC_R_SUCCESS) {
isc_mem_put(mctx, acl, sizeof(*acl));
return (result);
}
acl->elements = NULL;
acl->alloc = 0;
acl->length = 0;
......@@ -73,111 +87,234 @@ dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
return (result);
}
isc_result_t
dns_acl_appendelement(dns_acl_t *acl, const dns_aclelement_t *elt) {
if (acl->length + 1 > acl->alloc) {
/*
* Resize the ACL.
*/
unsigned int newalloc;
void *newmem;
newalloc = acl->alloc * 2;
if (newalloc < 4)
newalloc = 4;
newmem = isc_mem_get(acl->mctx,
newalloc * sizeof(dns_aclelement_t));
if (newmem == NULL)
return (ISC_R_NOMEMORY);
memcpy(newmem, acl->elements,
acl->length * sizeof(dns_aclelement_t));
isc_mem_put(acl->mctx, acl->elements,
acl->alloc * sizeof(dns_aclelement_t));
acl->elements = newmem;
acl->alloc = newalloc;
}
/*
* Append the new element.
*/
acl->elements[acl->length++] = *elt;
return (ISC_R_SUCCESS);
}
/*
* Create a new ACL and initialize it with the value "any" or "none",
* depending on the value of the "neg" parameter.
* "any" is a positive iptable entry with bit length 0.
* "none" is the same as "!any".
*/
static isc_result_t
dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
isc_result_t result;
dns_acl_t *acl = NULL;
result = dns_acl_create(mctx, 1, &acl);
result = dns_acl_create(mctx, 0, &acl);
if (result != ISC_R_SUCCESS)
return (result);
acl->elements[0].negative = neg;
acl->elements[0].type = dns_aclelementtype_any;
acl->length = 1;
dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
*target = acl;
return (result);
}
/*
* Create a new ACL that matches everything.
*/
isc_result_t
dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
return (dns_acl_anyornone(mctx, ISC_FALSE, target));
}
/*
* Create a new ACL that matches nothing.
*/
isc_result_t
dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
return (dns_acl_anyornone(mctx, ISC_TRUE, target));
}
/*
* If pos is ISC_TRUE, test whether acl is set to "{ any; }"
* If pos is ISC_FALSE, test whether acl is set to "{ none; }"
*/
static isc_boolean_t
dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
{
/* Should never happen but let's be safe */
if (acl == NULL ||
acl->iptable == NULL ||
acl->iptable->radix == NULL ||
acl->iptable->radix->head == NULL ||
acl->iptable->radix->head->prefix == NULL)
return (ISC_FALSE);
if (acl->length != 0 && acl->node_count != 1)
return (ISC_FALSE);
if (acl->iptable->radix->head->prefix->bitlen == 0 &&
*(isc_boolean_t *) (acl->iptable->radix->head->data) == pos)
return (ISC_TRUE);