1/*- 2 * Copyright (c) 2013-2020 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This material is based upon work partially supported by The 6 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30/* 31 * NPF configuration loading mechanism. 32 * 33 * The main operations on the configuration are the following: 34 * 1) Read access, primarily from the npf_packet_handler() function. 35 * 2) Write access on a particular set, mainly rule or table updates. 36 * 3) Deletion of the configuration after the reload operation. 37 * 38 * Synchronization 39 * 40 * For the (1) case, EBR is used to allow concurrent access to 41 * the configuration set (ruleset, etc). It guarantees that the 42 * configuration will not be destroyed while accessing it. 43 * 44 * For the cases (2) and (3), mutual exclusion (npf_t::config_lock) 45 * is used with, when necessary, the writer-side barrier of EBR. 46 */ 47 48#ifdef _KERNEL 49#include <sys/cdefs.h> 50__KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.18 2022/02/13 19:20:11 riastradh Exp $"); 51 52#include <sys/param.h> 53#include <sys/types.h> 54 55#include <sys/atomic.h> 56#include <sys/kmem.h> 57#include <sys/mutex.h> 58#endif 59 60#include "npf_impl.h" 61#include "npf_conn.h" 62 63void 64npf_config_init(npf_t *npf) 65{ 66 npf_config_t *nc; 67 68 mutex_init(&npf->config_lock, MUTEX_DEFAULT, IPL_SOFTNET); 69 nc = npf_config_create(); 70 71 /* 72 * Load an empty configuration. 73 */ 74 nc->ruleset = npf_ruleset_create(0); 75 nc->nat_ruleset = npf_ruleset_create(0); 76 nc->rule_procs = npf_rprocset_create(); 77 nc->tableset = npf_tableset_create(0); 78 nc->default_pass = true; 79 80 npf_config_load(npf, nc, NULL, true); 81 KASSERT(npf->config != NULL); 82} 83 84npf_config_t * 85npf_config_create(void) 86{ 87 return kmem_zalloc(sizeof(npf_config_t), KM_SLEEP); 88} 89 90void 91npf_config_destroy(npf_config_t *nc) 92{ 93 /* 94 * Note: the rulesets must be destroyed first, in order to drop 95 * any references to the tableset. 96 */ 97 if (nc->ruleset) { 98 npf_ruleset_destroy(nc->ruleset); 99 } 100 if (nc->nat_ruleset) { 101 npf_ruleset_destroy(nc->nat_ruleset); 102 } 103 if (nc->rule_procs) { 104 npf_rprocset_destroy(nc->rule_procs); 105 } 106 if (nc->tableset) { 107 npf_tableset_destroy(nc->tableset); 108 } 109 kmem_free(nc, sizeof(npf_config_t)); 110} 111 112void 113npf_config_fini(npf_t *npf) 114{ 115 npf_conndb_t *cd = npf_conndb_create(); 116 117 /* Flush the connections. */ 118 mutex_enter(&npf->config_lock); 119 npf_conn_tracking(npf, false); 120 npf_ebr_full_sync(npf->ebr); 121 npf_conn_load(npf, cd, false); 122 npf_ifmap_flush(npf); 123 mutex_exit(&npf->config_lock); 124 125 npf_config_destroy(npf->config); 126 mutex_destroy(&npf->config_lock); 127} 128 129/* 130 * npf_config_load: the main routine performing configuration load. 131 * Performs the necessary synchronization and destroys the old config. 132 */ 133void 134npf_config_load(npf_t *npf, npf_config_t *nc, npf_conndb_t *conns, bool flush) 135{ 136 const bool load = conns != NULL; 137 npf_config_t *onc; 138 139 nc->default_pass = flush; 140 141 /* 142 * Acquire the lock and perform the first phase: 143 * - Scan and use existing dynamic tables, reload only static. 144 * - Scan and use matching NAT policies to preserve the connections. 145 */ 146 mutex_enter(&npf->config_lock); 147 if ((onc = atomic_load_relaxed(&npf->config)) != NULL) { 148 npf_ruleset_reload(npf, nc->ruleset, onc->ruleset, load); 149 npf_tableset_reload(npf, nc->tableset, onc->tableset); 150 npf_ruleset_reload(npf, nc->nat_ruleset, onc->nat_ruleset, load); 151 } 152 153 /* 154 * Set the new config and release the lock. 155 */ 156 atomic_store_release(&npf->config, nc); 157 if (onc == NULL) { 158 /* Initial load, done. */ 159 npf_ifmap_flush(npf); 160 npf_conn_load(npf, conns, !flush); 161 mutex_exit(&npf->config_lock); 162 goto done; 163 } 164 165 /* 166 * If we are going to flush the connections or load the new ones, 167 * then disable the connection tracking for the grace period. 168 */ 169 if (flush || conns) { 170 npf_conn_tracking(npf, false); 171 } 172 173 /* Synchronise: drain all references. */ 174 npf_ebr_full_sync(npf->ebr); 175 if (flush) { 176 npf_portmap_flush(npf->portmap); 177 npf_ifmap_flush(npf); 178 } 179 180 /* 181 * G/C the existing connections and, if passed, load the new ones. 182 * If not flushing - enable the connection tracking. 183 */ 184 npf_conn_load(npf, conns, !flush); 185 mutex_exit(&npf->config_lock); 186 187 /* Finally, it is safe to destroy the old config. */ 188 npf_config_destroy(onc); 189done: 190 /* Sync all interface address tables (can be done asynchronously). */ 191 npf_ifaddr_syncall(npf); 192} 193 194/* 195 * Writer-side exclusive locking. 196 */ 197 198npf_config_t * 199npf_config_enter(npf_t *npf) 200{ 201 mutex_enter(&npf->config_lock); 202 return npf->config; 203} 204 205void 206npf_config_exit(npf_t *npf) 207{ 208 mutex_exit(&npf->config_lock); 209} 210 211bool 212npf_config_locked_p(npf_t *npf) 213{ 214 return mutex_owned(&npf->config_lock); 215} 216 217void 218npf_config_sync(npf_t *npf) 219{ 220 KASSERT(npf_config_locked_p(npf)); 221 npf_ebr_full_sync(npf->ebr); 222} 223 224/* 225 * Reader-side synchronization routines. 226 */ 227 228int 229npf_config_read_enter(npf_t *npf) 230{ 231 /* Note: issues an acquire fence. */ 232 return npf_ebr_enter(npf->ebr); 233} 234 235void 236npf_config_read_exit(npf_t *npf, int s) 237{ 238 /* Note: issues a release fence. */ 239 npf_ebr_exit(npf->ebr, s); 240} 241 242/* 243 * Accessors. 244 */ 245 246npf_ruleset_t * 247npf_config_ruleset(npf_t *npf) 248{ 249 npf_config_t *config = atomic_load_consume(&npf->config); 250 KASSERT(npf_config_locked_p(npf) || npf_ebr_incrit_p(npf->ebr)); 251 return config->ruleset; 252} 253 254npf_ruleset_t * 255npf_config_natset(npf_t *npf) 256{ 257 npf_config_t *config = atomic_load_consume(&npf->config); 258 KASSERT(npf_config_locked_p(npf) || npf_ebr_incrit_p(npf->ebr)); 259 return config->nat_ruleset; 260} 261 262npf_tableset_t * 263npf_config_tableset(npf_t *npf) 264{ 265 npf_config_t *config = atomic_load_consume(&npf->config); 266 KASSERT(npf_config_locked_p(npf) || npf_ebr_incrit_p(npf->ebr)); 267 return config->tableset; 268} 269 270bool 271npf_default_pass(npf_t *npf) 272{ 273 npf_config_t *config = atomic_load_consume(&npf->config); 274 KASSERT(npf_config_locked_p(npf) || npf_ebr_incrit_p(npf->ebr)); 275 return config->default_pass; 276} 277