1 1.1 rmind /*- 2 1.6 rmind * Copyright (c) 2009-2013 The NetBSD Foundation, Inc. 3 1.1 rmind * All rights reserved. 4 1.1 rmind * 5 1.1 rmind * This material is based upon work partially supported by The 6 1.1 rmind * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 7 1.1 rmind * 8 1.1 rmind * Redistribution and use in source and binary forms, with or without 9 1.1 rmind * modification, are permitted provided that the following conditions 10 1.1 rmind * are met: 11 1.1 rmind * 1. Redistributions of source code must retain the above copyright 12 1.1 rmind * notice, this list of conditions and the following disclaimer. 13 1.1 rmind * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 rmind * notice, this list of conditions and the following disclaimer in the 15 1.1 rmind * documentation and/or other materials provided with the distribution. 16 1.1 rmind * 17 1.1 rmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 1.1 rmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 1.1 rmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 1.1 rmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 1.1 rmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 1.1 rmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 1.1 rmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 1.1 rmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 1.1 rmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 1.1 rmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 1.1 rmind * POSSIBILITY OF SUCH DAMAGE. 28 1.1 rmind */ 29 1.1 rmind 30 1.1 rmind /* 31 1.3 rmind * NPF extension and rule procedure interface. 32 1.1 rmind */ 33 1.1 rmind 34 1.14 christos #ifdef _KERNEL 35 1.1 rmind #include <sys/cdefs.h> 36 1.23 riastrad __KERNEL_RCSID(0, "$NetBSD: npf_rproc.c,v 1.23 2023/02/24 11:03:01 riastradh Exp $"); 37 1.1 rmind 38 1.1 rmind #include <sys/param.h> 39 1.2 rmind #include <sys/types.h> 40 1.1 rmind 41 1.1 rmind #include <sys/atomic.h> 42 1.1 rmind #include <sys/kmem.h> 43 1.3 rmind #include <sys/mutex.h> 44 1.7 christos #include <sys/module.h> 45 1.14 christos #endif 46 1.1 rmind 47 1.1 rmind #include "npf_impl.h" 48 1.1 rmind 49 1.3 rmind #define EXT_NAME_LEN 32 50 1.3 rmind 51 1.3 rmind typedef struct npf_ext { 52 1.3 rmind char ext_callname[EXT_NAME_LEN]; 53 1.3 rmind LIST_ENTRY(npf_ext) ext_entry; 54 1.3 rmind const npf_ext_ops_t * ext_ops; 55 1.3 rmind unsigned ext_refcnt; 56 1.3 rmind } npf_ext_t; 57 1.3 rmind 58 1.6 rmind struct npf_rprocset { 59 1.6 rmind LIST_HEAD(, npf_rproc) rps_list; 60 1.6 rmind }; 61 1.6 rmind 62 1.3 rmind #define RPROC_NAME_LEN 32 63 1.3 rmind #define RPROC_EXT_COUNT 16 64 1.1 rmind 65 1.1 rmind struct npf_rproc { 66 1.6 rmind /* Flags and reference count. */ 67 1.6 rmind uint32_t rp_flags; 68 1.20 rmind unsigned rp_refcnt; 69 1.6 rmind 70 1.3 rmind /* Associated extensions and their metadata . */ 71 1.3 rmind unsigned rp_ext_count; 72 1.3 rmind npf_ext_t * rp_ext[RPROC_EXT_COUNT]; 73 1.3 rmind void * rp_ext_meta[RPROC_EXT_COUNT]; 74 1.6 rmind 75 1.6 rmind /* Name of the procedure and list entry. */ 76 1.6 rmind char rp_name[RPROC_NAME_LEN]; 77 1.6 rmind LIST_ENTRY(npf_rproc) rp_entry; 78 1.1 rmind }; 79 1.1 rmind 80 1.3 rmind void 81 1.14 christos npf_ext_init(npf_t *npf) 82 1.3 rmind { 83 1.14 christos mutex_init(&npf->ext_lock, MUTEX_DEFAULT, IPL_NONE); 84 1.14 christos LIST_INIT(&npf->ext_list); 85 1.3 rmind } 86 1.3 rmind 87 1.3 rmind void 88 1.14 christos npf_ext_fini(npf_t *npf) 89 1.3 rmind { 90 1.14 christos KASSERT(LIST_EMPTY(&npf->ext_list)); 91 1.14 christos mutex_destroy(&npf->ext_lock); 92 1.3 rmind } 93 1.3 rmind 94 1.3 rmind /* 95 1.3 rmind * NPF extension management for the rule procedures. 96 1.3 rmind */ 97 1.3 rmind 98 1.8 christos static const char npf_ext_prefix[] = "npf_ext_"; 99 1.8 christos #define NPF_EXT_PREFLEN (sizeof(npf_ext_prefix) - 1) 100 1.8 christos 101 1.3 rmind static npf_ext_t * 102 1.14 christos npf_ext_lookup(npf_t *npf, const char *name, bool autoload) 103 1.3 rmind { 104 1.8 christos npf_ext_t *ext; 105 1.8 christos char modname[RPROC_NAME_LEN + NPF_EXT_PREFLEN]; 106 1.9 christos int error; 107 1.3 rmind 108 1.14 christos KASSERT(mutex_owned(&npf->ext_lock)); 109 1.3 rmind 110 1.8 christos again: 111 1.14 christos LIST_FOREACH(ext, &npf->ext_list, ext_entry) 112 1.3 rmind if (strcmp(ext->ext_callname, name) == 0) 113 1.3 rmind break; 114 1.8 christos 115 1.9 christos if (ext != NULL || !autoload) 116 1.8 christos return ext; 117 1.8 christos 118 1.14 christos mutex_exit(&npf->ext_lock); 119 1.9 christos autoload = false; 120 1.8 christos snprintf(modname, sizeof(modname), "%s%s", npf_ext_prefix, name); 121 1.8 christos error = module_autoload(modname, MODULE_CLASS_MISC); 122 1.14 christos mutex_enter(&npf->ext_lock); 123 1.8 christos 124 1.8 christos if (error) 125 1.8 christos return NULL; 126 1.8 christos goto again; 127 1.3 rmind } 128 1.3 rmind 129 1.3 rmind void * 130 1.14 christos npf_ext_register(npf_t *npf, const char *name, const npf_ext_ops_t *ops) 131 1.3 rmind { 132 1.3 rmind npf_ext_t *ext; 133 1.3 rmind 134 1.3 rmind ext = kmem_zalloc(sizeof(npf_ext_t), KM_SLEEP); 135 1.3 rmind strlcpy(ext->ext_callname, name, EXT_NAME_LEN); 136 1.3 rmind ext->ext_ops = ops; 137 1.3 rmind 138 1.14 christos mutex_enter(&npf->ext_lock); 139 1.14 christos if (npf_ext_lookup(npf, name, false)) { 140 1.14 christos mutex_exit(&npf->ext_lock); 141 1.3 rmind kmem_free(ext, sizeof(npf_ext_t)); 142 1.3 rmind return NULL; 143 1.3 rmind } 144 1.14 christos LIST_INSERT_HEAD(&npf->ext_list, ext, ext_entry); 145 1.14 christos mutex_exit(&npf->ext_lock); 146 1.3 rmind 147 1.3 rmind return (void *)ext; 148 1.3 rmind } 149 1.3 rmind 150 1.3 rmind int 151 1.14 christos npf_ext_unregister(npf_t *npf, void *extid) 152 1.3 rmind { 153 1.3 rmind npf_ext_t *ext = extid; 154 1.3 rmind 155 1.3 rmind /* 156 1.3 rmind * Check if in-use first (re-check with the lock held). 157 1.3 rmind */ 158 1.20 rmind if (atomic_load_relaxed(&ext->ext_refcnt)) { 159 1.3 rmind return EBUSY; 160 1.3 rmind } 161 1.3 rmind 162 1.14 christos mutex_enter(&npf->ext_lock); 163 1.20 rmind if (atomic_load_relaxed(&ext->ext_refcnt)) { 164 1.14 christos mutex_exit(&npf->ext_lock); 165 1.3 rmind return EBUSY; 166 1.3 rmind } 167 1.14 christos KASSERT(npf_ext_lookup(npf, ext->ext_callname, false)); 168 1.3 rmind LIST_REMOVE(ext, ext_entry); 169 1.14 christos mutex_exit(&npf->ext_lock); 170 1.3 rmind 171 1.3 rmind kmem_free(ext, sizeof(npf_ext_t)); 172 1.3 rmind return 0; 173 1.3 rmind } 174 1.3 rmind 175 1.3 rmind int 176 1.14 christos npf_ext_construct(npf_t *npf, const char *name, 177 1.17 rmind npf_rproc_t *rp, const nvlist_t *params) 178 1.3 rmind { 179 1.3 rmind const npf_ext_ops_t *extops; 180 1.3 rmind npf_ext_t *ext; 181 1.3 rmind unsigned i; 182 1.3 rmind int error; 183 1.3 rmind 184 1.3 rmind if (rp->rp_ext_count >= RPROC_EXT_COUNT) { 185 1.3 rmind return ENOSPC; 186 1.3 rmind } 187 1.3 rmind 188 1.14 christos mutex_enter(&npf->ext_lock); 189 1.14 christos ext = npf_ext_lookup(npf, name, true); 190 1.3 rmind if (ext) { 191 1.3 rmind atomic_inc_uint(&ext->ext_refcnt); 192 1.3 rmind } 193 1.14 christos mutex_exit(&npf->ext_lock); 194 1.4 mlelstv 195 1.3 rmind if (!ext) { 196 1.3 rmind return ENOENT; 197 1.3 rmind } 198 1.3 rmind 199 1.4 mlelstv extops = ext->ext_ops; 200 1.4 mlelstv KASSERT(extops != NULL); 201 1.4 mlelstv 202 1.3 rmind error = extops->ctor(rp, params); 203 1.3 rmind if (error) { 204 1.3 rmind atomic_dec_uint(&ext->ext_refcnt); 205 1.3 rmind return error; 206 1.3 rmind } 207 1.3 rmind i = rp->rp_ext_count++; 208 1.3 rmind rp->rp_ext[i] = ext; 209 1.3 rmind return 0; 210 1.3 rmind } 211 1.3 rmind 212 1.3 rmind /* 213 1.3 rmind * Rule procedure management. 214 1.3 rmind */ 215 1.3 rmind 216 1.6 rmind npf_rprocset_t * 217 1.6 rmind npf_rprocset_create(void) 218 1.6 rmind { 219 1.6 rmind npf_rprocset_t *rpset; 220 1.6 rmind 221 1.6 rmind rpset = kmem_zalloc(sizeof(npf_rprocset_t), KM_SLEEP); 222 1.6 rmind LIST_INIT(&rpset->rps_list); 223 1.6 rmind return rpset; 224 1.6 rmind } 225 1.6 rmind 226 1.6 rmind void 227 1.6 rmind npf_rprocset_destroy(npf_rprocset_t *rpset) 228 1.6 rmind { 229 1.6 rmind npf_rproc_t *rp; 230 1.6 rmind 231 1.6 rmind while ((rp = LIST_FIRST(&rpset->rps_list)) != NULL) { 232 1.6 rmind LIST_REMOVE(rp, rp_entry); 233 1.6 rmind npf_rproc_release(rp); 234 1.6 rmind } 235 1.6 rmind kmem_free(rpset, sizeof(npf_rprocset_t)); 236 1.6 rmind } 237 1.6 rmind 238 1.6 rmind /* 239 1.6 rmind * npf_rproc_lookup: find a rule procedure by the name. 240 1.6 rmind */ 241 1.6 rmind npf_rproc_t * 242 1.6 rmind npf_rprocset_lookup(npf_rprocset_t *rpset, const char *name) 243 1.6 rmind { 244 1.6 rmind npf_rproc_t *rp; 245 1.6 rmind 246 1.6 rmind LIST_FOREACH(rp, &rpset->rps_list, rp_entry) { 247 1.6 rmind if (strncmp(rp->rp_name, name, RPROC_NAME_LEN) == 0) 248 1.6 rmind break; 249 1.6 rmind } 250 1.8 christos return rp; 251 1.6 rmind } 252 1.6 rmind 253 1.6 rmind /* 254 1.6 rmind * npf_rproc_insert: insert a new rule procedure into the set. 255 1.6 rmind */ 256 1.6 rmind void 257 1.6 rmind npf_rprocset_insert(npf_rprocset_t *rpset, npf_rproc_t *rp) 258 1.6 rmind { 259 1.6 rmind LIST_INSERT_HEAD(&rpset->rps_list, rp, rp_entry); 260 1.6 rmind } 261 1.6 rmind 262 1.12 rmind int 263 1.20 rmind npf_rprocset_export(const npf_rprocset_t *rpset, nvlist_t *nvl) 264 1.12 rmind { 265 1.12 rmind const npf_rproc_t *rp; 266 1.12 rmind 267 1.12 rmind LIST_FOREACH(rp, &rpset->rps_list, rp_entry) { 268 1.17 rmind nvlist_t *rproc = nvlist_create(0); 269 1.17 rmind #if 0 // FIXME/TODO 270 1.17 rmind for (unsigned i = 0; i < rp->rp_ext_count; i++) { 271 1.17 rmind nvlist_t *meta = rp->rp_ext_meta[i]; 272 1.17 rmind ... 273 1.17 rmind nvlist_append_nvlist_array(rproc, "extcalls", meta); 274 1.17 rmind } 275 1.17 rmind #endif 276 1.17 rmind nvlist_add_string(rproc, "name", rp->rp_name); 277 1.17 rmind nvlist_add_number(rproc, "flags", rp->rp_flags); 278 1.20 rmind nvlist_append_nvlist_array(nvl, "rprocs", rproc); 279 1.17 rmind nvlist_destroy(rproc); 280 1.12 rmind } 281 1.12 rmind return 0; 282 1.12 rmind } 283 1.12 rmind 284 1.3 rmind /* 285 1.3 rmind * npf_rproc_create: construct a new rule procedure, lookup and associate 286 1.3 rmind * the extension calls with it. 287 1.3 rmind */ 288 1.1 rmind npf_rproc_t * 289 1.17 rmind npf_rproc_create(const nvlist_t *rproc) 290 1.1 rmind { 291 1.3 rmind const char *name; 292 1.1 rmind npf_rproc_t *rp; 293 1.3 rmind 294 1.17 rmind if ((name = dnvlist_get_string(rproc, "name", NULL)) == NULL) { 295 1.3 rmind return NULL; 296 1.3 rmind } 297 1.1 rmind 298 1.2 rmind rp = kmem_intr_zalloc(sizeof(npf_rproc_t), KM_SLEEP); 299 1.1 rmind rp->rp_refcnt = 1; 300 1.1 rmind 301 1.3 rmind strlcpy(rp->rp_name, name, RPROC_NAME_LEN); 302 1.17 rmind rp->rp_flags = dnvlist_get_number(rproc, "flags", 0); 303 1.1 rmind return rp; 304 1.1 rmind } 305 1.1 rmind 306 1.3 rmind /* 307 1.3 rmind * npf_rproc_acquire: acquire the reference on the rule procedure. 308 1.3 rmind */ 309 1.1 rmind void 310 1.1 rmind npf_rproc_acquire(npf_rproc_t *rp) 311 1.1 rmind { 312 1.1 rmind atomic_inc_uint(&rp->rp_refcnt); 313 1.1 rmind } 314 1.1 rmind 315 1.3 rmind /* 316 1.15 christos * npf_rproc_getname: return the name of the given rproc 317 1.15 christos */ 318 1.15 christos const char * 319 1.15 christos npf_rproc_getname(const npf_rproc_t *rp) 320 1.15 christos { 321 1.15 christos return rp->rp_name; 322 1.15 christos } 323 1.15 christos 324 1.15 christos /* 325 1.3 rmind * npf_rproc_release: drop the reference count and destroy the rule 326 1.3 rmind * procedure on the last reference. 327 1.3 rmind */ 328 1.1 rmind void 329 1.1 rmind npf_rproc_release(npf_rproc_t *rp) 330 1.1 rmind { 331 1.20 rmind KASSERT(atomic_load_relaxed(&rp->rp_refcnt) > 0); 332 1.1 rmind 333 1.22 riastrad membar_release(); 334 1.1 rmind if (atomic_dec_uint_nv(&rp->rp_refcnt) != 0) { 335 1.1 rmind return; 336 1.1 rmind } 337 1.22 riastrad membar_acquire(); 338 1.23 riastrad 339 1.3 rmind /* XXXintr */ 340 1.3 rmind for (unsigned i = 0; i < rp->rp_ext_count; i++) { 341 1.3 rmind npf_ext_t *ext = rp->rp_ext[i]; 342 1.3 rmind const npf_ext_ops_t *extops = ext->ext_ops; 343 1.3 rmind 344 1.3 rmind extops->dtor(rp, rp->rp_ext_meta[i]); 345 1.3 rmind atomic_dec_uint(&ext->ext_refcnt); 346 1.3 rmind } 347 1.2 rmind kmem_intr_free(rp, sizeof(npf_rproc_t)); 348 1.1 rmind } 349 1.1 rmind 350 1.1 rmind void 351 1.3 rmind npf_rproc_assign(npf_rproc_t *rp, void *params) 352 1.1 rmind { 353 1.3 rmind unsigned i = rp->rp_ext_count; 354 1.3 rmind 355 1.3 rmind /* Note: params may be NULL. */ 356 1.3 rmind KASSERT(i < RPROC_EXT_COUNT); 357 1.3 rmind rp->rp_ext_meta[i] = params; 358 1.3 rmind } 359 1.3 rmind 360 1.3 rmind /* 361 1.3 rmind * npf_rproc_run: run the rule procedure by executing each extension call. 362 1.3 rmind * 363 1.3 rmind * => Reference on the rule procedure must be held. 364 1.3 rmind */ 365 1.10 jakllsch bool 366 1.16 christos npf_rproc_run(npf_cache_t *npc, npf_rproc_t *rp, const npf_match_info_t *mi, 367 1.16 christos int *decision) 368 1.3 rmind { 369 1.3 rmind const unsigned extcount = rp->rp_ext_count; 370 1.1 rmind 371 1.11 rmind KASSERT(!nbuf_flag_p(npc->npc_nbuf, NBUF_DATAREF_RESET)); 372 1.20 rmind KASSERT(atomic_load_relaxed(&rp->rp_refcnt) > 0); 373 1.1 rmind 374 1.3 rmind for (unsigned i = 0; i < extcount; i++) { 375 1.3 rmind const npf_ext_t *ext = rp->rp_ext[i]; 376 1.3 rmind const npf_ext_ops_t *extops = ext->ext_ops; 377 1.1 rmind 378 1.20 rmind KASSERT(atomic_load_relaxed(&ext->ext_refcnt) > 0); 379 1.20 rmind 380 1.16 christos if (!extops->proc(npc, rp->rp_ext_meta[i], mi, decision)) { 381 1.10 jakllsch return false; 382 1.10 jakllsch } 383 1.5 rmind 384 1.11 rmind if (nbuf_flag_p(npc->npc_nbuf, NBUF_DATAREF_RESET)) { 385 1.11 rmind npf_recache(npc); 386 1.5 rmind } 387 1.1 rmind } 388 1.10 jakllsch 389 1.10 jakllsch return true; 390 1.1 rmind } 391