iptable.c revision 1.4
1/*	$NetBSD: iptable.c,v 1.4 2020/05/24 19:46:23 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14#include <inttypes.h>
15#include <stdbool.h>
16
17#include <isc/mem.h>
18#include <isc/radix.h>
19#include <isc/util.h>
20
21#include <dns/acl.h>
22
23static void
24destroy_iptable(dns_iptable_t *dtab);
25
26/*
27 * Create a new IP table and the underlying radix structure
28 */
29isc_result_t
30dns_iptable_create(isc_mem_t *mctx, dns_iptable_t **target) {
31	isc_result_t result;
32	dns_iptable_t *tab;
33
34	tab = isc_mem_get(mctx, sizeof(*tab));
35	tab->mctx = NULL;
36	isc_mem_attach(mctx, &tab->mctx);
37	isc_refcount_init(&tab->refcount, 1);
38	tab->radix = NULL;
39	tab->magic = DNS_IPTABLE_MAGIC;
40
41	result = isc_radix_create(mctx, &tab->radix, RADIX_MAXBITS);
42	if (result != ISC_R_SUCCESS) {
43		goto cleanup;
44	}
45
46	*target = tab;
47	return (ISC_R_SUCCESS);
48
49cleanup:
50	dns_iptable_detach(&tab);
51	return (result);
52}
53
54static bool dns_iptable_neg = false;
55static bool dns_iptable_pos = true;
56
57/*
58 * Add an IP prefix to an existing IP table
59 */
60isc_result_t
61dns_iptable_addprefix(dns_iptable_t *tab, const isc_netaddr_t *addr,
62		      uint16_t bitlen, bool pos) {
63	isc_result_t result;
64	isc_prefix_t pfx;
65	isc_radix_node_t *node = NULL;
66	int i;
67
68	INSIST(DNS_IPTABLE_VALID(tab));
69	INSIST(tab->radix != NULL);
70
71	NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
72
73	result = isc_radix_insert(tab->radix, &node, NULL, &pfx);
74	if (result != ISC_R_SUCCESS) {
75		isc_refcount_destroy(&pfx.refcount);
76		return (result);
77	}
78
79	/* If a node already contains data, don't overwrite it */
80	if (pfx.family == AF_UNSPEC) {
81		/* "any" or "none" */
82		INSIST(pfx.bitlen == 0);
83		for (i = 0; i < RADIX_FAMILIES; i++) {
84			if (node->data[i] == NULL) {
85				node->data[i] = pos ? &dns_iptable_pos
86						    : &dns_iptable_neg;
87			}
88		}
89	} else {
90		/* any other prefix */
91		int fam = ISC_RADIX_FAMILY(&pfx);
92		if (node->data[fam] == NULL) {
93			node->data[fam] = pos ? &dns_iptable_pos
94					      : &dns_iptable_neg;
95		}
96	}
97
98	isc_refcount_destroy(&pfx.refcount);
99	return (ISC_R_SUCCESS);
100}
101
102/*
103 * Merge one IP table into another one.
104 */
105isc_result_t
106dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, bool pos) {
107	isc_result_t result;
108	isc_radix_node_t *node, *new_node;
109	int i, max_node = 0;
110
111	RADIX_WALK(source->radix->head, node) {
112		new_node = NULL;
113		result = isc_radix_insert(tab->radix, &new_node, node, NULL);
114
115		if (result != ISC_R_SUCCESS) {
116			return (result);
117		}
118
119		/*
120		 * If we're negating a nested ACL, then we should
121		 * reverse the sense of every node.  However, this
122		 * could lead to a negative node in a nested ACL
123		 * becoming a positive match in the parent, which
124		 * could be a security risk.  To prevent this, we
125		 * just leave the negative nodes negative.
126		 */
127		for (i = 0; i < RADIX_FAMILIES; i++) {
128			if (!pos) {
129				if (node->data[i] && *(bool *)node->data[i]) {
130					new_node->data[i] = &dns_iptable_neg;
131				}
132			}
133			if (node->node_num[i] > max_node) {
134				max_node = node->node_num[i];
135			}
136		}
137	}
138	RADIX_WALK_END;
139
140	tab->radix->num_added_node += max_node;
141	return (ISC_R_SUCCESS);
142}
143
144void
145dns_iptable_attach(dns_iptable_t *source, dns_iptable_t **target) {
146	REQUIRE(DNS_IPTABLE_VALID(source));
147	isc_refcount_increment(&source->refcount);
148	*target = source;
149}
150
151void
152dns_iptable_detach(dns_iptable_t **tabp) {
153	REQUIRE(tabp != NULL && DNS_IPTABLE_VALID(*tabp));
154	dns_iptable_t *tab = *tabp;
155	*tabp = NULL;
156
157	if (isc_refcount_decrement(&tab->refcount) == 1) {
158		isc_refcount_destroy(&tab->refcount);
159		destroy_iptable(tab);
160	}
161}
162
163static void
164destroy_iptable(dns_iptable_t *dtab) {
165	REQUIRE(DNS_IPTABLE_VALID(dtab));
166
167	if (dtab->radix != NULL) {
168		isc_radix_destroy(dtab->radix, NULL);
169		dtab->radix = NULL;
170	}
171
172	dtab->magic = 0;
173	isc_mem_putanddetach(&dtab->mctx, dtab, sizeof(*dtab));
174}
175