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 23 static void 24 destroy_iptable(dns_iptable_t *dtab); 25 26 /* 27 * Create a new IP table and the underlying radix structure 28 */ 29 isc_result_t 30 dns_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 49 cleanup: 50 dns_iptable_detach(&tab); 51 return (result); 52 } 53 54 static bool dns_iptable_neg = false; 55 static bool dns_iptable_pos = true; 56 57 /* 58 * Add an IP prefix to an existing IP table 59 */ 60 isc_result_t 61 dns_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 */ 105 isc_result_t 106 dns_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 144 void 145 dns_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 151 void 152 dns_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 163 static void 164 destroy_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