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