1 /*- 2 * Copyright (c) 2009-2025 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 device control. 32 * 33 * Implementation of (re)loading, construction of tables and rules. 34 * NPF nvlist(3) consumer. 35 */ 36 37 #ifdef _KERNEL 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.62 2025/06/01 00:54:36 joe Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/conf.h> 43 #include <sys/kmem.h> 44 #include <net/bpf.h> 45 #endif 46 47 #include "npf_impl.h" 48 #include "npf_conn.h" 49 50 #define NPF_ERR_DEBUG(e) \ 51 nvlist_add_string((e), "source-file", __FILE__); \ 52 nvlist_add_number((e), "source-line", __LINE__); 53 54 static int __noinline 55 npf_mk_params(npf_t *npf, const nvlist_t *req, nvlist_t *resp, bool set) 56 { 57 const nvlist_t *params; 58 int type, error, val; 59 const char *name; 60 void *cookie; 61 62 params = dnvlist_get_nvlist(req, "params", NULL); 63 if (params == NULL) { 64 return 0; 65 } 66 cookie = NULL; 67 while ((name = nvlist_next(params, &type, &cookie)) != NULL) { 68 if (type != NV_TYPE_NUMBER) { 69 NPF_ERR_DEBUG(resp); 70 return EINVAL; 71 } 72 val = (int)nvlist_get_number(params, name); 73 if (set) { 74 /* Actually set the parameter. */ 75 error = npfk_param_set(npf, name, val); 76 KASSERT(error == 0); 77 continue; 78 } 79 80 /* Validate the parameter and its value. */ 81 error = npf_param_check(npf, name, val); 82 if (__predict_true(error == 0)) { 83 continue; 84 } 85 if (error == ENOENT) { 86 nvlist_add_stringf(resp, "error-msg", 87 "invalid parameter `%s`", name); 88 } 89 if (error == EINVAL) { 90 nvlist_add_stringf(resp, "error-msg", 91 "invalid parameter `%s` value %d", name, val); 92 } 93 return error; 94 } 95 return 0; 96 } 97 98 static int __noinline 99 npf_mk_table_entries(npf_table_t *t, const nvlist_t *req, nvlist_t *resp) 100 { 101 const nvlist_t * const *entries; 102 size_t nitems; 103 int error = 0; 104 105 if (!nvlist_exists_nvlist_array(req, "entries")) { 106 return 0; 107 } 108 entries = nvlist_get_nvlist_array(req, "entries", &nitems); 109 for (unsigned i = 0; i < nitems; i++) { 110 const nvlist_t *entry = entries[i]; 111 const npf_addr_t *addr; 112 npf_netmask_t mask; 113 size_t alen; 114 115 /* Get address and mask; add a table entry. */ 116 addr = dnvlist_get_binary(entry, "addr", &alen, NULL, 0); 117 mask = dnvlist_get_number(entry, "mask", NPF_NO_NETMASK); 118 if (addr == NULL || alen == 0) { 119 NPF_ERR_DEBUG(resp); 120 error = EINVAL; 121 break; 122 } 123 error = npf_table_insert(t, alen, addr, mask); 124 if (__predict_false(error)) { 125 if (error == EEXIST) { 126 nvlist_add_stringf(resp, "error-msg", 127 "table `%s' has a duplicate entry", 128 nvlist_get_string(req, "name")); 129 } else { 130 NPF_ERR_DEBUG(resp); 131 } 132 break; 133 } 134 } 135 return error; 136 } 137 138 /* 139 * npf_mk_table: create a table from provided nvlist. 140 */ 141 static int __noinline 142 npf_mk_table(npf_t *npf, const nvlist_t *req, nvlist_t *resp, 143 npf_tableset_t *tblset, npf_table_t **tblp, bool replacing) 144 { 145 npf_table_t *t; 146 const char *name; 147 const void *blob; 148 uint64_t tid; 149 size_t size; 150 int type; 151 int error = 0; 152 153 KASSERT(tblp != NULL); 154 155 /* Table name, ID and type. Validate them. */ 156 name = dnvlist_get_string(req, "name", NULL); 157 if (!name) { 158 NPF_ERR_DEBUG(resp); 159 error = EINVAL; 160 goto out; 161 } 162 tid = dnvlist_get_number(req, "id", UINT64_MAX); 163 type = dnvlist_get_number(req, "type", UINT64_MAX); 164 error = npf_table_check(tblset, name, tid, type, replacing); 165 if (error) { 166 NPF_ERR_DEBUG(resp); 167 goto out; 168 } 169 170 /* Get the entries or binary data. */ 171 blob = dnvlist_get_binary(req, "data", &size, NULL, 0); 172 if (type == NPF_TABLE_CONST && (blob == NULL || size == 0)) { 173 NPF_ERR_DEBUG(resp); 174 error = EINVAL; 175 goto out; 176 } 177 178 t = npf_table_create(name, (unsigned)tid, type, blob, size); 179 if (t == NULL) { 180 NPF_ERR_DEBUG(resp); 181 error = ENOMEM; 182 goto out; 183 } 184 185 if ((error = npf_mk_table_entries(t, req, resp)) != 0) { 186 npf_table_destroy(t); 187 goto out; 188 } 189 190 *tblp = t; 191 out: 192 return error; 193 } 194 195 static int __noinline 196 npf_mk_tables(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc) 197 { 198 const nvlist_t * const *tables; 199 npf_tableset_t *tblset; 200 size_t nitems; 201 int error = 0; 202 203 if (nvlist_exists_nvlist_array(req, "tables")) { 204 tables = nvlist_get_nvlist_array(req, "tables", &nitems); 205 if (nitems > NPF_MAX_TABLES) { 206 NPF_ERR_DEBUG(resp); 207 return E2BIG; 208 } 209 } else { 210 tables = NULL; 211 nitems = 0; 212 } 213 tblset = npf_tableset_create(nitems); 214 for (unsigned i = 0; i < nitems; i++) { 215 const nvlist_t *table = tables[i]; 216 npf_table_t *t; 217 218 error = npf_mk_table(npf, table, resp, tblset, &t, 0); 219 if (error) { 220 break; 221 } 222 223 error = npf_tableset_insert(tblset, t); 224 KASSERT(error == 0); 225 } 226 nc->tableset = tblset; 227 return error; 228 } 229 230 static npf_rproc_t * 231 npf_mk_singlerproc(npf_t *npf, const nvlist_t *rproc, nvlist_t *resp) 232 { 233 const nvlist_t * const *extcalls; 234 size_t nitems; 235 npf_rproc_t *rp; 236 237 if ((rp = npf_rproc_create(rproc)) == NULL) { 238 NPF_ERR_DEBUG(resp); 239 return NULL; 240 } 241 if (!nvlist_exists_nvlist_array(rproc, "extcalls")) { 242 return rp; 243 } 244 extcalls = nvlist_get_nvlist_array(rproc, "extcalls", &nitems); 245 for (unsigned i = 0; i < nitems; i++) { 246 const nvlist_t *extcall = extcalls[i]; 247 const char *name; 248 249 name = dnvlist_get_string(extcall, "name", NULL); 250 if (!name || npf_ext_construct(npf, name, rp, extcall)) { 251 NPF_ERR_DEBUG(resp); 252 npf_rproc_release(rp); 253 rp = NULL; 254 break; 255 } 256 } 257 return rp; 258 } 259 260 static int __noinline 261 npf_mk_rprocs(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc) 262 { 263 const nvlist_t * const *rprocs; 264 npf_rprocset_t *rpset; 265 size_t nitems; 266 int error = 0; 267 268 if (nvlist_exists_nvlist_array(req, "rprocs")) { 269 rprocs = nvlist_get_nvlist_array(req, "rprocs", &nitems); 270 if (nitems > NPF_MAX_RPROCS) { 271 NPF_ERR_DEBUG(resp); 272 return E2BIG; 273 } 274 } else { 275 rprocs = NULL; 276 nitems = 0; 277 } 278 rpset = npf_rprocset_create(); 279 for (unsigned i = 0; i < nitems; i++) { 280 const nvlist_t *rproc = rprocs[i]; 281 npf_rproc_t *rp; 282 283 if ((rp = npf_mk_singlerproc(npf, rproc, resp)) == NULL) { 284 error = EINVAL; 285 break; 286 } 287 npf_rprocset_insert(rpset, rp); 288 } 289 nc->rule_procs = rpset; 290 return error; 291 } 292 293 static int __noinline 294 npf_mk_algs(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 295 { 296 const nvlist_t * const *algs; 297 size_t nitems; 298 299 if (nvlist_exists_nvlist_array(req, "algs")) { 300 algs = nvlist_get_nvlist_array(req, "algs", &nitems); 301 } else { 302 algs = NULL; 303 nitems = 0; 304 } 305 for (unsigned i = 0; i < nitems; i++) { 306 const nvlist_t *alg = algs[i]; 307 const char *name; 308 309 name = dnvlist_get_string(alg, "name", NULL); 310 if (!name) { 311 NPF_ERR_DEBUG(resp); 312 return EINVAL; 313 } 314 if (!npf_alg_construct(npf, name)) { 315 NPF_ERR_DEBUG(resp); 316 return EINVAL; 317 } 318 } 319 return 0; 320 } 321 322 static int __noinline 323 npf_mk_singlerule(npf_t *npf, const nvlist_t *req, nvlist_t *resp, 324 npf_rprocset_t *rpset, npf_rule_t **rlret) 325 { 326 npf_rule_t *rl; 327 const char *rname; 328 const void *code; 329 size_t clen; 330 int error = 0; 331 332 if ((rl = npf_rule_alloc(npf, req)) == NULL) { 333 NPF_ERR_DEBUG(resp); 334 return EINVAL; 335 } 336 337 /* Assign the rule procedure, if any. */ 338 if ((rname = dnvlist_get_string(req, "rproc", NULL)) != NULL) { 339 npf_rproc_t *rp; 340 341 if (rpset == NULL) { 342 NPF_ERR_DEBUG(resp); 343 error = EINVAL; 344 goto err; 345 } 346 if ((rp = npf_rprocset_lookup(rpset, rname)) == NULL) { 347 NPF_ERR_DEBUG(resp); 348 error = EINVAL; 349 goto err; 350 } 351 npf_rule_setrproc(rl, rp); 352 } 353 354 /* Filter byte-code (binary data). */ 355 code = dnvlist_get_binary(req, "code", &clen, NULL, 0); 356 if (code) { 357 void *bc; 358 int type; 359 360 type = dnvlist_get_number(req, "code-type", UINT64_MAX); 361 if (type != NPF_CODE_BPF) { 362 NPF_ERR_DEBUG(resp); 363 error = ENOTSUP; 364 goto err; 365 } 366 if (clen == 0) { 367 NPF_ERR_DEBUG(resp); 368 error = EINVAL; 369 goto err; 370 } 371 if (!npf_bpf_validate(code, clen)) { 372 NPF_ERR_DEBUG(resp); 373 error = EINVAL; 374 goto err; 375 } 376 bc = kmem_alloc(clen, KM_SLEEP); 377 memcpy(bc, code, clen); // XXX: use nvlist_take 378 npf_rule_setcode(rl, type, bc, clen); 379 } 380 381 /* user and group ids filt option if set */ 382 if (nvlist_exists_number_array(req, "r_user")) { 383 npf_rule_setrid(req, rl, "r_user"); 384 } 385 386 if (nvlist_exists_number_array(req, "r_group")) { 387 npf_rule_setrid(req, rl, "r_group"); 388 } 389 390 *rlret = rl; 391 return 0; 392 err: 393 nvlist_add_number(resp, "id", dnvlist_get_number(req, "prio", 0)); 394 npf_rule_free(rl); 395 return error; 396 } 397 398 static int __noinline 399 npf_mk_rules(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc) 400 { 401 const nvlist_t * const *rules; 402 npf_ruleset_t *rlset; 403 size_t nitems; 404 int error = 0; 405 406 if (nvlist_exists_nvlist_array(req, "rules")) { 407 rules = nvlist_get_nvlist_array(req, "rules", &nitems); 408 if (nitems > NPF_MAX_RULES) { 409 NPF_ERR_DEBUG(resp); 410 return E2BIG; 411 } 412 } else { 413 rules = NULL; 414 nitems = 0; 415 } 416 rlset = npf_ruleset_create(nitems); 417 for (unsigned i = 0; i < nitems; i++) { 418 const nvlist_t *rule = rules[i]; 419 npf_rule_t *rl = NULL; 420 const char *name; 421 422 error = npf_mk_singlerule(npf, rule, resp, nc->rule_procs, &rl); 423 if (error) { 424 break; 425 } 426 name = dnvlist_get_string(rule, "name", NULL); 427 if (name && npf_ruleset_lookup(rlset, name)) { 428 NPF_ERR_DEBUG(resp); 429 npf_rule_free(rl); 430 error = EEXIST; 431 break; 432 } 433 npf_ruleset_insert(rlset, rl); 434 } 435 nc->ruleset = rlset; 436 return error; 437 } 438 439 static int __noinline 440 npf_mk_singlenat(npf_t *npf, const nvlist_t *nat, nvlist_t *resp, 441 npf_ruleset_t *ntset, npf_tableset_t *tblset, npf_rule_t **rlp) 442 { 443 npf_rule_t *rl = NULL; 444 npf_natpolicy_t *np; 445 int error; 446 447 /* 448 * NAT rules are standard rules, plus the translation policy. 449 * We first construct the rule structure. 450 */ 451 error = npf_mk_singlerule(npf, nat, resp, NULL, &rl); 452 if (error) { 453 return error; 454 } 455 KASSERT(rl != NULL); 456 *rlp = rl; 457 458 /* If this rule is named, then it is a group with NAT policies. */ 459 if (dnvlist_get_string(nat, "name", NULL)) { 460 return 0; 461 } 462 463 /* Check the table ID. */ 464 if (nvlist_exists_number(nat, "nat-table-id")) { 465 unsigned tid = nvlist_get_number(nat, "nat-table-id"); 466 467 if (!npf_tableset_getbyid(tblset, tid)) { 468 NPF_ERR_DEBUG(resp); 469 error = EINVAL; 470 goto out; 471 } 472 } 473 474 /* Allocate a new NAT policy and assign it to the rule. */ 475 np = npf_natpolicy_create(npf, nat, ntset); 476 if (np == NULL) { 477 NPF_ERR_DEBUG(resp); 478 error = ENOMEM; 479 goto out; 480 } 481 npf_rule_setnat(rl, np); 482 out: 483 if (error) { 484 npf_rule_free(rl); 485 } 486 return error; 487 } 488 489 static int __noinline 490 npf_mk_natlist(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc) 491 { 492 const nvlist_t * const *nat_rules; 493 npf_ruleset_t *ntset; 494 size_t nitems; 495 int error = 0; 496 497 /* 498 * NAT policies must be an array, but enforce a limit. 499 */ 500 if (nvlist_exists_nvlist_array(req, "nat")) { 501 nat_rules = nvlist_get_nvlist_array(req, "nat", &nitems); 502 if (nitems > NPF_MAX_RULES) { 503 NPF_ERR_DEBUG(resp); 504 return E2BIG; 505 } 506 } else { 507 nat_rules = NULL; 508 nitems = 0; 509 } 510 ntset = npf_ruleset_create(nitems); 511 for (unsigned i = 0; i < nitems; i++) { 512 const nvlist_t *nat = nat_rules[i]; 513 npf_rule_t *rl = NULL; 514 515 error = npf_mk_singlenat(npf, nat, resp, ntset, 516 nc->tableset, &rl); 517 if (error) { 518 break; 519 } 520 npf_ruleset_insert(ntset, rl); 521 } 522 nc->nat_ruleset = ntset; 523 return error; 524 } 525 526 /* 527 * npf_mk_connlist: import a list of connections and load them. 528 */ 529 static int __noinline 530 npf_mk_connlist(npf_t *npf, const nvlist_t *req, nvlist_t *resp, 531 npf_config_t *nc, npf_conndb_t **conndb) 532 { 533 const nvlist_t * const *conns; 534 npf_conndb_t *cd; 535 size_t nitems; 536 int error = 0; 537 538 if (!nvlist_exists_nvlist_array(req, "conn-list")) { 539 *conndb = NULL; 540 return 0; 541 } 542 cd = npf_conndb_create(); 543 conns = nvlist_get_nvlist_array(req, "conn-list", &nitems); 544 for (unsigned i = 0; i < nitems; i++) { 545 const nvlist_t *conn = conns[i]; 546 547 /* Construct and insert the connection. */ 548 error = npf_conn_import(npf, cd, conn, nc->nat_ruleset); 549 if (error) { 550 NPF_ERR_DEBUG(resp); 551 break; 552 } 553 } 554 if (error) { 555 npf_conndb_gc(npf, cd, true, false); 556 npf_conndb_destroy(cd); 557 } else { 558 *conndb = cd; 559 } 560 return error; 561 } 562 563 /* 564 * npfctl_load: store passed data i.e. the update settings, create the 565 * passed rules, tables, etc and atomically activate them all. 566 */ 567 static int 568 npfctl_load(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 569 { 570 npf_config_t *nc; 571 npf_conndb_t *conndb = NULL; 572 bool flush; 573 int error; 574 575 nc = npf_config_create(); 576 error = npf_mk_params(npf, req, resp, false /* validate */); 577 if (error) { 578 goto fail; 579 } 580 error = npf_mk_algs(npf, req, resp); 581 if (error) { 582 goto fail; 583 } 584 error = npf_mk_tables(npf, req, resp, nc); 585 if (error) { 586 goto fail; 587 } 588 error = npf_mk_rprocs(npf, req, resp, nc); 589 if (error) { 590 goto fail; 591 } 592 error = npf_mk_natlist(npf, req, resp, nc); 593 if (error) { 594 goto fail; 595 } 596 error = npf_mk_rules(npf, req, resp, nc); 597 if (error) { 598 goto fail; 599 } 600 error = npf_mk_connlist(npf, req, resp, nc, &conndb); 601 if (error) { 602 goto fail; 603 } 604 605 flush = dnvlist_get_bool(req, "flush", false); 606 nc->default_pass = flush; 607 608 /* 609 * Finally - perform the load. 610 */ 611 npf_config_load(npf, nc, conndb, flush); 612 npf_mk_params(npf, req, resp, true /* set the params */); 613 return 0; 614 615 fail: 616 npf_config_destroy(nc); 617 return error; 618 } 619 620 /* 621 * npfctl_save: export the active configuration, including the current 622 * snapshot of the connections. Additionally, set the version and indicate 623 * whether the ruleset is currently active. 624 */ 625 static int 626 npfctl_save(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 627 { 628 npf_config_t *nc; 629 int error; 630 631 /* 632 * Serialize the whole NPF configuration, including connections. 633 */ 634 nvlist_add_number(resp, "version", NPF_VERSION); 635 nc = npf_config_enter(npf); 636 error = npf_params_export(npf, resp); 637 if (error) { 638 goto out; 639 } 640 error = npf_conndb_export(npf, resp); 641 if (error) { 642 goto out; 643 } 644 error = npf_ruleset_export(npf, nc->ruleset, "rules", resp); 645 if (error) { 646 goto out; 647 } 648 error = npf_ruleset_export(npf, nc->nat_ruleset, "nat", resp); 649 if (error) { 650 goto out; 651 } 652 error = npf_tableset_export(npf, nc->tableset, resp); 653 if (error) { 654 goto out; 655 } 656 error = npf_rprocset_export(nc->rule_procs, resp); 657 if (error) { 658 goto out; 659 } 660 error = npf_alg_export(npf, resp); 661 if (error) { 662 goto out; 663 } 664 nvlist_add_bool(resp, "active", npf_active_p()); 665 out: 666 npf_config_exit(npf); 667 return error; 668 } 669 670 /* 671 * npfctl_table_replace: atomically replace a table's contents with 672 * the passed table data. 673 */ 674 static int __noinline 675 npfctl_table_replace(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 676 { 677 npf_table_t *tbl, *gc_tbl = NULL; 678 npf_config_t *nc; 679 int error = 0; 680 681 nc = npf_config_enter(npf); 682 error = npf_mk_table(npf, req, resp, nc->tableset, &tbl, true); 683 if (error) { 684 goto err; 685 } 686 gc_tbl = npf_tableset_swap(nc->tableset, tbl); 687 if (gc_tbl == NULL) { 688 error = EINVAL; 689 gc_tbl = tbl; 690 goto err; 691 } 692 npf_config_sync(npf); 693 err: 694 npf_config_exit(npf); 695 if (gc_tbl) { 696 npf_table_destroy(gc_tbl); 697 } 698 return error; 699 } 700 701 /* 702 * npfctl_rule: add or remove dynamic rules in the specified ruleset. 703 */ 704 static int 705 npfctl_rule(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 706 { 707 npf_ruleset_t *rlset; 708 npf_rule_t *rl = NULL; 709 const char *ruleset_name; 710 npf_config_t *nc; 711 uint32_t rcmd; 712 int error = 0; 713 bool natset; 714 715 rcmd = dnvlist_get_number(req, "command", 0); 716 natset = dnvlist_get_bool(req, "nat-ruleset", false); 717 ruleset_name = dnvlist_get_string(req, "ruleset-name", NULL); 718 if (!ruleset_name) { 719 error = EINVAL; 720 goto out; 721 } 722 723 nc = npf_config_enter(npf); 724 rlset = natset ? nc->nat_ruleset : nc->ruleset; 725 switch (rcmd) { 726 case NPF_CMD_RULE_ADD: { 727 if (natset) { 728 /* 729 * Translation rule. 730 */ 731 error = npf_mk_singlenat(npf, req, resp, rlset, 732 nc->tableset, &rl); 733 } else { 734 /* 735 * Standard rule. 736 */ 737 error = npf_mk_singlerule(npf, req, resp, NULL, &rl); 738 } 739 if (error) { 740 goto out; 741 } 742 if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) { 743 /* Success. */ 744 uint64_t id = npf_rule_getid(rl); 745 nvlist_add_number(resp, "id", id); 746 rl = NULL; 747 } 748 break; 749 } 750 case NPF_CMD_RULE_REMOVE: { 751 uint64_t id = dnvlist_get_number(req, "id", UINT64_MAX); 752 error = npf_ruleset_remove(rlset, ruleset_name, id); 753 break; 754 } 755 case NPF_CMD_RULE_REMKEY: { 756 const void *key; 757 size_t len; 758 759 key = dnvlist_get_binary(req, "key", &len, NULL, 0); 760 if (len == 0 || len > NPF_RULE_MAXKEYLEN) { 761 error = EINVAL; 762 break; 763 } 764 error = npf_ruleset_remkey(rlset, ruleset_name, key, len); 765 break; 766 } 767 case NPF_CMD_RULE_LIST: { 768 error = npf_ruleset_list(npf, rlset, ruleset_name, resp); 769 break; 770 } 771 case NPF_CMD_RULE_FLUSH: { 772 error = npf_ruleset_flush(rlset, ruleset_name); 773 break; 774 } 775 default: 776 error = EINVAL; 777 break; 778 } 779 780 /* Destroy any removed rules. */ 781 if (!error && rcmd != NPF_CMD_RULE_ADD && rcmd != NPF_CMD_RULE_LIST) { 782 npf_config_sync(npf); 783 npf_ruleset_gc(rlset); 784 } 785 out: 786 npf_config_exit(npf); 787 788 if (rl) { 789 KASSERT(error); 790 npf_rule_free(rl); 791 } 792 return error; 793 } 794 795 /* 796 * npfctl_table: add, remove or query entries in the specified table. 797 * 798 * For maximum performance, the interface is using plain structures. 799 */ 800 int 801 npfctl_table(npf_t *npf, void *data) 802 { 803 const npf_ioctl_table_t *nct = data; 804 char tname[NPF_TABLE_MAXNAMELEN]; 805 npf_config_t *nc; 806 npf_table_t *t; 807 int error; 808 809 error = copyinstr(nct->nct_name, tname, sizeof(tname), NULL); 810 if (error) { 811 return error; 812 } 813 814 nc = npf_config_enter(npf); 815 if ((t = npf_tableset_getbyname(nc->tableset, tname)) == NULL) { 816 npf_config_exit(npf); 817 return EINVAL; 818 } 819 820 switch (nct->nct_cmd) { 821 case NPF_CMD_TABLE_LOOKUP: 822 error = npf_table_lookup(t, nct->nct_data.ent.alen, 823 &nct->nct_data.ent.addr); 824 break; 825 case NPF_CMD_TABLE_ADD: 826 error = npf_table_insert(t, nct->nct_data.ent.alen, 827 &nct->nct_data.ent.addr, nct->nct_data.ent.mask); 828 break; 829 case NPF_CMD_TABLE_REMOVE: 830 error = npf_table_remove(t, nct->nct_data.ent.alen, 831 &nct->nct_data.ent.addr, nct->nct_data.ent.mask); 832 break; 833 case NPF_CMD_TABLE_LIST: 834 error = npf_table_list(t, nct->nct_data.buf.buf, 835 nct->nct_data.buf.len); 836 break; 837 case NPF_CMD_TABLE_FLUSH: 838 error = npf_table_flush(t); 839 break; 840 default: 841 error = EINVAL; 842 break; 843 } 844 npf_table_gc(npf, t); 845 npf_config_exit(npf); 846 847 return error; 848 } 849 850 /* 851 * npfctl_run_op: run a particular NPF operation with a given the request. 852 * 853 * => Checks the ABI version. 854 * => Sets the error number for the response. 855 */ 856 int 857 npfctl_run_op(npf_t *npf, unsigned op, const nvlist_t *req, nvlist_t *resp) 858 { 859 uint64_t ver; 860 int error; 861 862 ver = dnvlist_get_number(req, "version", UINT64_MAX); 863 if (__predict_false(ver != UINT64_MAX && ver != NPF_VERSION)) { 864 return EPROGMISMATCH; 865 } 866 switch (op) { 867 case IOC_NPF_LOAD: 868 error = npfctl_load(npf, req, resp); 869 break; 870 case IOC_NPF_SAVE: 871 error = npfctl_save(npf, req, resp); 872 break; 873 case IOC_NPF_RULE: 874 error = npfctl_rule(npf, req, resp); 875 break; 876 case IOC_NPF_CONN_LOOKUP: 877 error = npf_conn_find(npf, req, resp); 878 break; 879 case IOC_NPF_TABLE_REPLACE: 880 error = npfctl_table_replace(npf, req, resp); 881 break; 882 default: 883 error = ENOTTY; 884 break; 885 } 886 nvlist_add_number(resp, "errno", error); 887 return error; 888 } 889