1 /*- 2 * Copyright (c) 2009-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 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.60 2020/05/30 14:16:56 rmind 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 *rlret = rl; 382 return 0; 383 err: 384 nvlist_add_number(resp, "id", dnvlist_get_number(req, "prio", 0)); 385 npf_rule_free(rl); 386 return error; 387 } 388 389 static int __noinline 390 npf_mk_rules(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc) 391 { 392 const nvlist_t * const *rules; 393 npf_ruleset_t *rlset; 394 size_t nitems; 395 int error = 0; 396 397 if (nvlist_exists_nvlist_array(req, "rules")) { 398 rules = nvlist_get_nvlist_array(req, "rules", &nitems); 399 if (nitems > NPF_MAX_RULES) { 400 NPF_ERR_DEBUG(resp); 401 return E2BIG; 402 } 403 } else { 404 rules = NULL; 405 nitems = 0; 406 } 407 rlset = npf_ruleset_create(nitems); 408 for (unsigned i = 0; i < nitems; i++) { 409 const nvlist_t *rule = rules[i]; 410 npf_rule_t *rl = NULL; 411 const char *name; 412 413 error = npf_mk_singlerule(npf, rule, resp, nc->rule_procs, &rl); 414 if (error) { 415 break; 416 } 417 name = dnvlist_get_string(rule, "name", NULL); 418 if (name && npf_ruleset_lookup(rlset, name)) { 419 NPF_ERR_DEBUG(resp); 420 npf_rule_free(rl); 421 error = EEXIST; 422 break; 423 } 424 npf_ruleset_insert(rlset, rl); 425 } 426 nc->ruleset = rlset; 427 return error; 428 } 429 430 static int __noinline 431 npf_mk_singlenat(npf_t *npf, const nvlist_t *nat, nvlist_t *resp, 432 npf_ruleset_t *ntset, npf_tableset_t *tblset, npf_rule_t **rlp) 433 { 434 npf_rule_t *rl = NULL; 435 npf_natpolicy_t *np; 436 int error; 437 438 /* 439 * NAT rules are standard rules, plus the translation policy. 440 * We first construct the rule structure. 441 */ 442 error = npf_mk_singlerule(npf, nat, resp, NULL, &rl); 443 if (error) { 444 return error; 445 } 446 KASSERT(rl != NULL); 447 *rlp = rl; 448 449 /* If this rule is named, then it is a group with NAT policies. */ 450 if (dnvlist_get_string(nat, "name", NULL)) { 451 return 0; 452 } 453 454 /* Check the table ID. */ 455 if (nvlist_exists_number(nat, "nat-table-id")) { 456 unsigned tid = nvlist_get_number(nat, "nat-table-id"); 457 458 if (!npf_tableset_getbyid(tblset, tid)) { 459 NPF_ERR_DEBUG(resp); 460 error = EINVAL; 461 goto out; 462 } 463 } 464 465 /* Allocate a new NAT policy and assign it to the rule. */ 466 np = npf_natpolicy_create(npf, nat, ntset); 467 if (np == NULL) { 468 NPF_ERR_DEBUG(resp); 469 error = ENOMEM; 470 goto out; 471 } 472 npf_rule_setnat(rl, np); 473 out: 474 if (error) { 475 npf_rule_free(rl); 476 } 477 return error; 478 } 479 480 static int __noinline 481 npf_mk_natlist(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc) 482 { 483 const nvlist_t * const *nat_rules; 484 npf_ruleset_t *ntset; 485 size_t nitems; 486 int error = 0; 487 488 /* 489 * NAT policies must be an array, but enforce a limit. 490 */ 491 if (nvlist_exists_nvlist_array(req, "nat")) { 492 nat_rules = nvlist_get_nvlist_array(req, "nat", &nitems); 493 if (nitems > NPF_MAX_RULES) { 494 NPF_ERR_DEBUG(resp); 495 return E2BIG; 496 } 497 } else { 498 nat_rules = NULL; 499 nitems = 0; 500 } 501 ntset = npf_ruleset_create(nitems); 502 for (unsigned i = 0; i < nitems; i++) { 503 const nvlist_t *nat = nat_rules[i]; 504 npf_rule_t *rl = NULL; 505 506 error = npf_mk_singlenat(npf, nat, resp, ntset, 507 nc->tableset, &rl); 508 if (error) { 509 break; 510 } 511 npf_ruleset_insert(ntset, rl); 512 } 513 nc->nat_ruleset = ntset; 514 return error; 515 } 516 517 /* 518 * npf_mk_connlist: import a list of connections and load them. 519 */ 520 static int __noinline 521 npf_mk_connlist(npf_t *npf, const nvlist_t *req, nvlist_t *resp, 522 npf_config_t *nc, npf_conndb_t **conndb) 523 { 524 const nvlist_t * const *conns; 525 npf_conndb_t *cd; 526 size_t nitems; 527 int error = 0; 528 529 if (!nvlist_exists_nvlist_array(req, "conn-list")) { 530 *conndb = NULL; 531 return 0; 532 } 533 cd = npf_conndb_create(); 534 conns = nvlist_get_nvlist_array(req, "conn-list", &nitems); 535 for (unsigned i = 0; i < nitems; i++) { 536 const nvlist_t *conn = conns[i]; 537 538 /* Construct and insert the connection. */ 539 error = npf_conn_import(npf, cd, conn, nc->nat_ruleset); 540 if (error) { 541 NPF_ERR_DEBUG(resp); 542 break; 543 } 544 } 545 if (error) { 546 npf_conndb_gc(npf, cd, true, false); 547 npf_conndb_destroy(cd); 548 } else { 549 *conndb = cd; 550 } 551 return error; 552 } 553 554 /* 555 * npfctl_load: store passed data i.e. the update settings, create the 556 * passed rules, tables, etc and atomically activate them all. 557 */ 558 static int 559 npfctl_load(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 560 { 561 npf_config_t *nc; 562 npf_conndb_t *conndb = NULL; 563 bool flush; 564 int error; 565 566 nc = npf_config_create(); 567 error = npf_mk_params(npf, req, resp, false /* validate */); 568 if (error) { 569 goto fail; 570 } 571 error = npf_mk_algs(npf, req, resp); 572 if (error) { 573 goto fail; 574 } 575 error = npf_mk_tables(npf, req, resp, nc); 576 if (error) { 577 goto fail; 578 } 579 error = npf_mk_rprocs(npf, req, resp, nc); 580 if (error) { 581 goto fail; 582 } 583 error = npf_mk_natlist(npf, req, resp, nc); 584 if (error) { 585 goto fail; 586 } 587 error = npf_mk_rules(npf, req, resp, nc); 588 if (error) { 589 goto fail; 590 } 591 error = npf_mk_connlist(npf, req, resp, nc, &conndb); 592 if (error) { 593 goto fail; 594 } 595 596 flush = dnvlist_get_bool(req, "flush", false); 597 nc->default_pass = flush; 598 599 /* 600 * Finally - perform the load. 601 */ 602 npf_config_load(npf, nc, conndb, flush); 603 npf_mk_params(npf, req, resp, true /* set the params */); 604 return 0; 605 606 fail: 607 npf_config_destroy(nc); 608 return error; 609 } 610 611 /* 612 * npfctl_save: export the active configuration, including the current 613 * snapshot of the connections. Additionally, set the version and indicate 614 * whether the ruleset is currently active. 615 */ 616 static int 617 npfctl_save(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 618 { 619 npf_config_t *nc; 620 int error; 621 622 /* 623 * Serialize the whole NPF configuration, including connections. 624 */ 625 nvlist_add_number(resp, "version", NPF_VERSION); 626 nc = npf_config_enter(npf); 627 error = npf_params_export(npf, resp); 628 if (error) { 629 goto out; 630 } 631 error = npf_conndb_export(npf, resp); 632 if (error) { 633 goto out; 634 } 635 error = npf_ruleset_export(npf, nc->ruleset, "rules", resp); 636 if (error) { 637 goto out; 638 } 639 error = npf_ruleset_export(npf, nc->nat_ruleset, "nat", resp); 640 if (error) { 641 goto out; 642 } 643 error = npf_tableset_export(npf, nc->tableset, resp); 644 if (error) { 645 goto out; 646 } 647 error = npf_rprocset_export(nc->rule_procs, resp); 648 if (error) { 649 goto out; 650 } 651 error = npf_alg_export(npf, resp); 652 if (error) { 653 goto out; 654 } 655 nvlist_add_bool(resp, "active", npf_active_p()); 656 out: 657 npf_config_exit(npf); 658 return error; 659 } 660 661 /* 662 * npfctl_table_replace: atomically replace a table's contents with 663 * the passed table data. 664 */ 665 static int __noinline 666 npfctl_table_replace(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 667 { 668 npf_table_t *tbl, *gc_tbl = NULL; 669 npf_config_t *nc; 670 int error = 0; 671 672 nc = npf_config_enter(npf); 673 error = npf_mk_table(npf, req, resp, nc->tableset, &tbl, true); 674 if (error) { 675 goto err; 676 } 677 gc_tbl = npf_tableset_swap(nc->tableset, tbl); 678 if (gc_tbl == NULL) { 679 error = EINVAL; 680 gc_tbl = tbl; 681 goto err; 682 } 683 npf_config_sync(npf); 684 err: 685 npf_config_exit(npf); 686 if (gc_tbl) { 687 npf_table_destroy(gc_tbl); 688 } 689 return error; 690 } 691 692 /* 693 * npfctl_rule: add or remove dynamic rules in the specified ruleset. 694 */ 695 static int 696 npfctl_rule(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 697 { 698 npf_ruleset_t *rlset; 699 npf_rule_t *rl = NULL; 700 const char *ruleset_name; 701 npf_config_t *nc; 702 uint32_t rcmd; 703 int error = 0; 704 bool natset; 705 706 rcmd = dnvlist_get_number(req, "command", 0); 707 natset = dnvlist_get_bool(req, "nat-ruleset", false); 708 ruleset_name = dnvlist_get_string(req, "ruleset-name", NULL); 709 if (!ruleset_name) { 710 error = EINVAL; 711 goto out; 712 } 713 714 nc = npf_config_enter(npf); 715 rlset = natset ? nc->nat_ruleset : nc->ruleset; 716 switch (rcmd) { 717 case NPF_CMD_RULE_ADD: { 718 if (natset) { 719 /* 720 * Translation rule. 721 */ 722 error = npf_mk_singlenat(npf, req, resp, rlset, 723 nc->tableset, &rl); 724 } else { 725 /* 726 * Standard rule. 727 */ 728 error = npf_mk_singlerule(npf, req, resp, NULL, &rl); 729 } 730 if (error) { 731 goto out; 732 } 733 if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) { 734 /* Success. */ 735 uint64_t id = npf_rule_getid(rl); 736 nvlist_add_number(resp, "id", id); 737 rl = NULL; 738 } 739 break; 740 } 741 case NPF_CMD_RULE_REMOVE: { 742 uint64_t id = dnvlist_get_number(req, "id", UINT64_MAX); 743 error = npf_ruleset_remove(rlset, ruleset_name, id); 744 break; 745 } 746 case NPF_CMD_RULE_REMKEY: { 747 const void *key; 748 size_t len; 749 750 key = dnvlist_get_binary(req, "key", &len, NULL, 0); 751 if (len == 0 || len > NPF_RULE_MAXKEYLEN) { 752 error = EINVAL; 753 break; 754 } 755 error = npf_ruleset_remkey(rlset, ruleset_name, key, len); 756 break; 757 } 758 case NPF_CMD_RULE_LIST: { 759 error = npf_ruleset_list(npf, rlset, ruleset_name, resp); 760 break; 761 } 762 case NPF_CMD_RULE_FLUSH: { 763 error = npf_ruleset_flush(rlset, ruleset_name); 764 break; 765 } 766 default: 767 error = EINVAL; 768 break; 769 } 770 771 /* Destroy any removed rules. */ 772 if (!error && rcmd != NPF_CMD_RULE_ADD && rcmd != NPF_CMD_RULE_LIST) { 773 npf_config_sync(npf); 774 npf_ruleset_gc(rlset); 775 } 776 out: 777 npf_config_exit(npf); 778 779 if (rl) { 780 KASSERT(error); 781 npf_rule_free(rl); 782 } 783 return error; 784 } 785 786 /* 787 * npfctl_table: add, remove or query entries in the specified table. 788 * 789 * For maximum performance, the interface is using plain structures. 790 */ 791 int 792 npfctl_table(npf_t *npf, void *data) 793 { 794 const npf_ioctl_table_t *nct = data; 795 char tname[NPF_TABLE_MAXNAMELEN]; 796 npf_config_t *nc; 797 npf_table_t *t; 798 int error; 799 800 error = copyinstr(nct->nct_name, tname, sizeof(tname), NULL); 801 if (error) { 802 return error; 803 } 804 805 nc = npf_config_enter(npf); 806 if ((t = npf_tableset_getbyname(nc->tableset, tname)) == NULL) { 807 npf_config_exit(npf); 808 return EINVAL; 809 } 810 811 switch (nct->nct_cmd) { 812 case NPF_CMD_TABLE_LOOKUP: 813 error = npf_table_lookup(t, nct->nct_data.ent.alen, 814 &nct->nct_data.ent.addr); 815 break; 816 case NPF_CMD_TABLE_ADD: 817 error = npf_table_insert(t, nct->nct_data.ent.alen, 818 &nct->nct_data.ent.addr, nct->nct_data.ent.mask); 819 break; 820 case NPF_CMD_TABLE_REMOVE: 821 error = npf_table_remove(t, nct->nct_data.ent.alen, 822 &nct->nct_data.ent.addr, nct->nct_data.ent.mask); 823 break; 824 case NPF_CMD_TABLE_LIST: 825 error = npf_table_list(t, nct->nct_data.buf.buf, 826 nct->nct_data.buf.len); 827 break; 828 case NPF_CMD_TABLE_FLUSH: 829 error = npf_table_flush(t); 830 break; 831 default: 832 error = EINVAL; 833 break; 834 } 835 npf_table_gc(npf, t); 836 npf_config_exit(npf); 837 838 return error; 839 } 840 841 /* 842 * npfctl_run_op: run a particular NPF operation with a given the request. 843 * 844 * => Checks the ABI version. 845 * => Sets the error number for the response. 846 */ 847 int 848 npfctl_run_op(npf_t *npf, unsigned op, const nvlist_t *req, nvlist_t *resp) 849 { 850 uint64_t ver; 851 int error; 852 853 ver = dnvlist_get_number(req, "version", UINT64_MAX); 854 if (__predict_false(ver != UINT64_MAX && ver != NPF_VERSION)) { 855 return EPROGMISMATCH; 856 } 857 switch (op) { 858 case IOC_NPF_LOAD: 859 error = npfctl_load(npf, req, resp); 860 break; 861 case IOC_NPF_SAVE: 862 error = npfctl_save(npf, req, resp); 863 break; 864 case IOC_NPF_RULE: 865 error = npfctl_rule(npf, req, resp); 866 break; 867 case IOC_NPF_CONN_LOOKUP: 868 error = npf_conn_find(npf, req, resp); 869 break; 870 case IOC_NPF_TABLE_REPLACE: 871 error = npfctl_table_replace(npf, req, resp); 872 break; 873 default: 874 error = ENOTTY; 875 break; 876 } 877 nvlist_add_number(resp, "errno", error); 878 return error; 879 } 880