1 /*- 2 * Copyright (c) 2009-2013 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 extension and rule procedure interface. 32 */ 33 34 #ifdef _KERNEL 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: npf_rproc.c,v 1.20 2020/05/30 14:16:56 rmind Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/types.h> 40 41 #include <sys/atomic.h> 42 #include <sys/kmem.h> 43 #include <sys/mutex.h> 44 #include <sys/module.h> 45 #endif 46 47 #include "npf_impl.h" 48 49 #define EXT_NAME_LEN 32 50 51 typedef struct npf_ext { 52 char ext_callname[EXT_NAME_LEN]; 53 LIST_ENTRY(npf_ext) ext_entry; 54 const npf_ext_ops_t * ext_ops; 55 unsigned ext_refcnt; 56 } npf_ext_t; 57 58 struct npf_rprocset { 59 LIST_HEAD(, npf_rproc) rps_list; 60 }; 61 62 #define RPROC_NAME_LEN 32 63 #define RPROC_EXT_COUNT 16 64 65 struct npf_rproc { 66 /* Flags and reference count. */ 67 uint32_t rp_flags; 68 unsigned rp_refcnt; 69 70 /* Associated extensions and their metadata . */ 71 unsigned rp_ext_count; 72 npf_ext_t * rp_ext[RPROC_EXT_COUNT]; 73 void * rp_ext_meta[RPROC_EXT_COUNT]; 74 75 /* Name of the procedure and list entry. */ 76 char rp_name[RPROC_NAME_LEN]; 77 LIST_ENTRY(npf_rproc) rp_entry; 78 }; 79 80 void 81 npf_ext_init(npf_t *npf) 82 { 83 mutex_init(&npf->ext_lock, MUTEX_DEFAULT, IPL_NONE); 84 LIST_INIT(&npf->ext_list); 85 } 86 87 void 88 npf_ext_fini(npf_t *npf) 89 { 90 KASSERT(LIST_EMPTY(&npf->ext_list)); 91 mutex_destroy(&npf->ext_lock); 92 } 93 94 /* 95 * NPF extension management for the rule procedures. 96 */ 97 98 static const char npf_ext_prefix[] = "npf_ext_"; 99 #define NPF_EXT_PREFLEN (sizeof(npf_ext_prefix) - 1) 100 101 static npf_ext_t * 102 npf_ext_lookup(npf_t *npf, const char *name, bool autoload) 103 { 104 npf_ext_t *ext; 105 char modname[RPROC_NAME_LEN + NPF_EXT_PREFLEN]; 106 int error; 107 108 KASSERT(mutex_owned(&npf->ext_lock)); 109 110 again: 111 LIST_FOREACH(ext, &npf->ext_list, ext_entry) 112 if (strcmp(ext->ext_callname, name) == 0) 113 break; 114 115 if (ext != NULL || !autoload) 116 return ext; 117 118 mutex_exit(&npf->ext_lock); 119 autoload = false; 120 snprintf(modname, sizeof(modname), "%s%s", npf_ext_prefix, name); 121 error = module_autoload(modname, MODULE_CLASS_MISC); 122 mutex_enter(&npf->ext_lock); 123 124 if (error) 125 return NULL; 126 goto again; 127 } 128 129 void * 130 npf_ext_register(npf_t *npf, const char *name, const npf_ext_ops_t *ops) 131 { 132 npf_ext_t *ext; 133 134 ext = kmem_zalloc(sizeof(npf_ext_t), KM_SLEEP); 135 strlcpy(ext->ext_callname, name, EXT_NAME_LEN); 136 ext->ext_ops = ops; 137 138 mutex_enter(&npf->ext_lock); 139 if (npf_ext_lookup(npf, name, false)) { 140 mutex_exit(&npf->ext_lock); 141 kmem_free(ext, sizeof(npf_ext_t)); 142 return NULL; 143 } 144 LIST_INSERT_HEAD(&npf->ext_list, ext, ext_entry); 145 mutex_exit(&npf->ext_lock); 146 147 return (void *)ext; 148 } 149 150 int 151 npf_ext_unregister(npf_t *npf, void *extid) 152 { 153 npf_ext_t *ext = extid; 154 155 /* 156 * Check if in-use first (re-check with the lock held). 157 */ 158 if (atomic_load_relaxed(&ext->ext_refcnt)) { 159 return EBUSY; 160 } 161 162 mutex_enter(&npf->ext_lock); 163 if (atomic_load_relaxed(&ext->ext_refcnt)) { 164 mutex_exit(&npf->ext_lock); 165 return EBUSY; 166 } 167 KASSERT(npf_ext_lookup(npf, ext->ext_callname, false)); 168 LIST_REMOVE(ext, ext_entry); 169 mutex_exit(&npf->ext_lock); 170 171 kmem_free(ext, sizeof(npf_ext_t)); 172 return 0; 173 } 174 175 int 176 npf_ext_construct(npf_t *npf, const char *name, 177 npf_rproc_t *rp, const nvlist_t *params) 178 { 179 const npf_ext_ops_t *extops; 180 npf_ext_t *ext; 181 unsigned i; 182 int error; 183 184 if (rp->rp_ext_count >= RPROC_EXT_COUNT) { 185 return ENOSPC; 186 } 187 188 mutex_enter(&npf->ext_lock); 189 ext = npf_ext_lookup(npf, name, true); 190 if (ext) { 191 atomic_inc_uint(&ext->ext_refcnt); 192 } 193 mutex_exit(&npf->ext_lock); 194 195 if (!ext) { 196 return ENOENT; 197 } 198 199 extops = ext->ext_ops; 200 KASSERT(extops != NULL); 201 202 error = extops->ctor(rp, params); 203 if (error) { 204 atomic_dec_uint(&ext->ext_refcnt); 205 return error; 206 } 207 i = rp->rp_ext_count++; 208 rp->rp_ext[i] = ext; 209 return 0; 210 } 211 212 /* 213 * Rule procedure management. 214 */ 215 216 npf_rprocset_t * 217 npf_rprocset_create(void) 218 { 219 npf_rprocset_t *rpset; 220 221 rpset = kmem_zalloc(sizeof(npf_rprocset_t), KM_SLEEP); 222 LIST_INIT(&rpset->rps_list); 223 return rpset; 224 } 225 226 void 227 npf_rprocset_destroy(npf_rprocset_t *rpset) 228 { 229 npf_rproc_t *rp; 230 231 while ((rp = LIST_FIRST(&rpset->rps_list)) != NULL) { 232 LIST_REMOVE(rp, rp_entry); 233 npf_rproc_release(rp); 234 } 235 kmem_free(rpset, sizeof(npf_rprocset_t)); 236 } 237 238 /* 239 * npf_rproc_lookup: find a rule procedure by the name. 240 */ 241 npf_rproc_t * 242 npf_rprocset_lookup(npf_rprocset_t *rpset, const char *name) 243 { 244 npf_rproc_t *rp; 245 246 LIST_FOREACH(rp, &rpset->rps_list, rp_entry) { 247 if (strncmp(rp->rp_name, name, RPROC_NAME_LEN) == 0) 248 break; 249 } 250 return rp; 251 } 252 253 /* 254 * npf_rproc_insert: insert a new rule procedure into the set. 255 */ 256 void 257 npf_rprocset_insert(npf_rprocset_t *rpset, npf_rproc_t *rp) 258 { 259 LIST_INSERT_HEAD(&rpset->rps_list, rp, rp_entry); 260 } 261 262 int 263 npf_rprocset_export(const npf_rprocset_t *rpset, nvlist_t *nvl) 264 { 265 const npf_rproc_t *rp; 266 267 LIST_FOREACH(rp, &rpset->rps_list, rp_entry) { 268 nvlist_t *rproc = nvlist_create(0); 269 #if 0 // FIXME/TODO 270 for (unsigned i = 0; i < rp->rp_ext_count; i++) { 271 nvlist_t *meta = rp->rp_ext_meta[i]; 272 ... 273 nvlist_append_nvlist_array(rproc, "extcalls", meta); 274 } 275 #endif 276 nvlist_add_string(rproc, "name", rp->rp_name); 277 nvlist_add_number(rproc, "flags", rp->rp_flags); 278 nvlist_append_nvlist_array(nvl, "rprocs", rproc); 279 nvlist_destroy(rproc); 280 } 281 return 0; 282 } 283 284 /* 285 * npf_rproc_create: construct a new rule procedure, lookup and associate 286 * the extension calls with it. 287 */ 288 npf_rproc_t * 289 npf_rproc_create(const nvlist_t *rproc) 290 { 291 const char *name; 292 npf_rproc_t *rp; 293 294 if ((name = dnvlist_get_string(rproc, "name", NULL)) == NULL) { 295 return NULL; 296 } 297 298 rp = kmem_intr_zalloc(sizeof(npf_rproc_t), KM_SLEEP); 299 rp->rp_refcnt = 1; 300 301 strlcpy(rp->rp_name, name, RPROC_NAME_LEN); 302 rp->rp_flags = dnvlist_get_number(rproc, "flags", 0); 303 return rp; 304 } 305 306 /* 307 * npf_rproc_acquire: acquire the reference on the rule procedure. 308 */ 309 void 310 npf_rproc_acquire(npf_rproc_t *rp) 311 { 312 atomic_inc_uint(&rp->rp_refcnt); 313 } 314 315 /* 316 * npf_rproc_getname: return the name of the given rproc 317 */ 318 const char * 319 npf_rproc_getname(const npf_rproc_t *rp) 320 { 321 return rp->rp_name; 322 } 323 324 /* 325 * npf_rproc_release: drop the reference count and destroy the rule 326 * procedure on the last reference. 327 */ 328 void 329 npf_rproc_release(npf_rproc_t *rp) 330 { 331 KASSERT(atomic_load_relaxed(&rp->rp_refcnt) > 0); 332 333 if (atomic_dec_uint_nv(&rp->rp_refcnt) != 0) { 334 return; 335 } 336 /* XXXintr */ 337 for (unsigned i = 0; i < rp->rp_ext_count; i++) { 338 npf_ext_t *ext = rp->rp_ext[i]; 339 const npf_ext_ops_t *extops = ext->ext_ops; 340 341 extops->dtor(rp, rp->rp_ext_meta[i]); 342 atomic_dec_uint(&ext->ext_refcnt); 343 } 344 kmem_intr_free(rp, sizeof(npf_rproc_t)); 345 } 346 347 void 348 npf_rproc_assign(npf_rproc_t *rp, void *params) 349 { 350 unsigned i = rp->rp_ext_count; 351 352 /* Note: params may be NULL. */ 353 KASSERT(i < RPROC_EXT_COUNT); 354 rp->rp_ext_meta[i] = params; 355 } 356 357 /* 358 * npf_rproc_run: run the rule procedure by executing each extension call. 359 * 360 * => Reference on the rule procedure must be held. 361 */ 362 bool 363 npf_rproc_run(npf_cache_t *npc, npf_rproc_t *rp, const npf_match_info_t *mi, 364 int *decision) 365 { 366 const unsigned extcount = rp->rp_ext_count; 367 368 KASSERT(!nbuf_flag_p(npc->npc_nbuf, NBUF_DATAREF_RESET)); 369 KASSERT(atomic_load_relaxed(&rp->rp_refcnt) > 0); 370 371 for (unsigned i = 0; i < extcount; i++) { 372 const npf_ext_t *ext = rp->rp_ext[i]; 373 const npf_ext_ops_t *extops = ext->ext_ops; 374 375 KASSERT(atomic_load_relaxed(&ext->ext_refcnt) > 0); 376 377 if (!extops->proc(npc, rp->rp_ext_meta[i], mi, decision)) { 378 return false; 379 } 380 381 if (nbuf_flag_p(npc->npc_nbuf, NBUF_DATAREF_RESET)) { 382 npf_recache(npc); 383 } 384 } 385 386 return true; 387 } 388