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