1/*	$NetBSD: iptable.c,v 1.2.6.1 2012/06/05 21:15:00 bouyer Exp $	*/
2
3/*
4 * Copyright (C) 2007-2009  Internet Systems Consortium, Inc. ("ISC")
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/* Id: iptable.c,v 1.15 2009/02/18 23:47:48 tbox Exp  */
20
21#include <config.h>
22
23#include <isc/mem.h>
24#include <isc/radix.h>
25
26#include <dns/acl.h>
27
28static void destroy_iptable(dns_iptable_t *dtab);
29
30/*
31 * Create a new IP table and the underlying radix structure
32 */
33isc_result_t
34dns_iptable_create(isc_mem_t *mctx, dns_iptable_t **target) {
35	isc_result_t result;
36	dns_iptable_t *tab;
37
38	tab = isc_mem_get(mctx, sizeof(*tab));
39	if (tab == NULL)
40		return (ISC_R_NOMEMORY);
41	tab->mctx = mctx;
42	isc_refcount_init(&tab->refcount, 1);
43	tab->radix = NULL;
44	tab->magic = DNS_IPTABLE_MAGIC;
45
46	result = isc_radix_create(mctx, &tab->radix, RADIX_MAXBITS);
47	if (result != ISC_R_SUCCESS)
48		goto cleanup;
49
50	*target = tab;
51	return (ISC_R_SUCCESS);
52
53 cleanup:
54	dns_iptable_detach(&tab);
55	return (result);
56}
57
58isc_boolean_t dns_iptable_neg = ISC_FALSE;
59isc_boolean_t dns_iptable_pos = ISC_TRUE;
60
61/*
62 * Add an IP prefix to an existing IP table
63 */
64isc_result_t
65dns_iptable_addprefix(dns_iptable_t *tab, isc_netaddr_t *addr,
66		      isc_uint16_t bitlen, isc_boolean_t pos)
67{
68	isc_result_t result;
69	isc_prefix_t pfx;
70	isc_radix_node_t *node = NULL;
71	int family;
72
73	INSIST(DNS_IPTABLE_VALID(tab));
74	INSIST(tab->radix);
75
76	NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
77
78	result = isc_radix_insert(tab->radix, &node, NULL, &pfx);
79	if (result != ISC_R_SUCCESS) {
80		isc_refcount_destroy(&pfx.refcount);
81		return(result);
82	}
83
84	/* If a node already contains data, don't overwrite it */
85	family = pfx.family;
86	if (family == AF_UNSPEC) {
87		/* "any" or "none" */
88		INSIST(pfx.bitlen == 0);
89		if (pos) {
90			if (node->data[0] == NULL)
91				node->data[0] = &dns_iptable_pos;
92			if (node->data[1] == NULL)
93				node->data[1] = &dns_iptable_pos;
94		} else {
95			if (node->data[0] == NULL)
96				node->data[0] = &dns_iptable_neg;
97			if (node->data[1] == NULL)
98				node->data[1] = &dns_iptable_neg;
99		}
100	} else {
101		/* any other prefix */
102		if (node->data[ISC_IS6(family)] == NULL) {
103			if (pos)
104				node->data[ISC_IS6(family)] = &dns_iptable_pos;
105			else
106				node->data[ISC_IS6(family)] = &dns_iptable_neg;
107		}
108	}
109
110	isc_refcount_destroy(&pfx.refcount);
111	return (ISC_R_SUCCESS);
112}
113
114/*
115 * Merge one IP table into another one.
116 */
117isc_result_t
118dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, isc_boolean_t pos)
119{
120	isc_result_t result;
121	isc_radix_node_t *node, *new_node;
122	int max_node = 0;
123
124	RADIX_WALK (source->radix->head, node) {
125		new_node = NULL;
126		result = isc_radix_insert (tab->radix, &new_node, node, NULL);
127
128		if (result != ISC_R_SUCCESS)
129			return(result);
130
131		/*
132		 * If we're negating a nested ACL, then we should
133		 * reverse the sense of every node.  However, this
134		 * could lead to a negative node in a nested ACL
135		 * becoming a positive match in the parent, which
136		 * could be a security risk.  To prevent this, we
137		 * just leave the negative nodes negative.
138		 */
139		if (!pos) {
140			if (node->data[0] &&
141			    *(isc_boolean_t *) node->data[0] == ISC_TRUE)
142				new_node->data[0] = &dns_iptable_neg;
143
144			if (node->data[1] &&
145			    *(isc_boolean_t *) node->data[1] == ISC_TRUE)
146				new_node->data[1] = &dns_iptable_neg;
147		}
148
149		if (node->node_num[0] > max_node)
150			max_node = node->node_num[0];
151		if (node->node_num[1] > max_node)
152			max_node = node->node_num[1];
153	} RADIX_WALK_END;
154
155	tab->radix->num_added_node += max_node;
156	return (ISC_R_SUCCESS);
157}
158
159void
160dns_iptable_attach(dns_iptable_t *source, dns_iptable_t **target) {
161	REQUIRE(DNS_IPTABLE_VALID(source));
162	isc_refcount_increment(&source->refcount, NULL);
163	*target = source;
164}
165
166void
167dns_iptable_detach(dns_iptable_t **tabp) {
168	dns_iptable_t *tab = *tabp;
169	unsigned int refs;
170	REQUIRE(DNS_IPTABLE_VALID(tab));
171	isc_refcount_decrement(&tab->refcount, &refs);
172	if (refs == 0)
173		destroy_iptable(tab);
174	*tabp = NULL;
175}
176
177static void
178destroy_iptable(dns_iptable_t *dtab) {
179
180	REQUIRE(DNS_IPTABLE_VALID(dtab));
181
182	if (dtab->radix != NULL) {
183		isc_radix_destroy(dtab->radix, NULL);
184		dtab->radix = NULL;
185	}
186
187	isc_refcount_destroy(&dtab->refcount);
188	dtab->magic = 0;
189	isc_mem_put(dtab->mctx, dtab, sizeof(*dtab));
190}
191