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