1 /* $NetBSD: npf_ctl.c,v 1.40 2014/08/24 20:36:30 rmind Exp $ */ 2 3 /*- 4 * Copyright (c) 2009-2014 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This material is based upon work partially supported by The 8 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * NPF device control. 34 * 35 * Implementation of (re)loading, construction of tables and rules. 36 * NPF proplib(9) dictionary consumer. 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.40 2014/08/24 20:36:30 rmind Exp $"); 41 42 #include <sys/param.h> 43 #include <sys/conf.h> 44 #include <sys/kmem.h> 45 #include <net/bpf.h> 46 47 #include <prop/proplib.h> 48 49 #include "npf_impl.h" 50 #include "npf_conn.h" 51 52 #if defined(DEBUG) || defined(DIAGNOSTIC) 53 #define NPF_ERR_DEBUG(e) \ 54 prop_dictionary_set_cstring_nocopy((e), "source-file", __FILE__); \ 55 prop_dictionary_set_uint32((e), "source-line", __LINE__); 56 #else 57 #define NPF_ERR_DEBUG(e) 58 #endif 59 60 /* 61 * npfctl_switch: enable or disable packet inspection. 62 */ 63 int 64 npfctl_switch(void *data) 65 { 66 const bool onoff = *(int *)data ? true : false; 67 int error; 68 69 if (onoff) { 70 /* Enable: add pfil hooks. */ 71 error = npf_pfil_register(false); 72 } else { 73 /* Disable: remove pfil hooks. */ 74 npf_pfil_unregister(false); 75 error = 0; 76 } 77 return error; 78 } 79 80 static int __noinline 81 npf_mk_table_entries(npf_table_t *t, prop_array_t entries) 82 { 83 prop_object_iterator_t eit; 84 prop_dictionary_t ent; 85 int error = 0; 86 87 if (prop_object_type(entries) != PROP_TYPE_ARRAY) { 88 return EINVAL; 89 } 90 eit = prop_array_iterator(entries); 91 while ((ent = prop_object_iterator_next(eit)) != NULL) { 92 const npf_addr_t *addr; 93 npf_netmask_t mask; 94 int alen; 95 96 /* Get address and mask. Add a table entry. */ 97 prop_object_t obj = prop_dictionary_get(ent, "addr"); 98 addr = (const npf_addr_t *)prop_data_data_nocopy(obj); 99 prop_dictionary_get_uint8(ent, "mask", &mask); 100 alen = prop_data_size(obj); 101 102 error = npf_table_insert(t, alen, addr, mask); 103 if (error) 104 break; 105 } 106 prop_object_iterator_release(eit); 107 return error; 108 } 109 110 static int __noinline 111 npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables, 112 prop_dictionary_t errdict) 113 { 114 prop_object_iterator_t it; 115 prop_dictionary_t tbldict; 116 int error = 0; 117 118 /* Tables - array. */ 119 if (prop_object_type(tables) != PROP_TYPE_ARRAY) { 120 NPF_ERR_DEBUG(errdict); 121 return EINVAL; 122 } 123 124 it = prop_array_iterator(tables); 125 while ((tbldict = prop_object_iterator_next(it)) != NULL) { 126 const char *name; 127 npf_table_t *t; 128 u_int tid; 129 int type; 130 131 /* Table - dictionary. */ 132 if (prop_object_type(tbldict) != PROP_TYPE_DICTIONARY) { 133 NPF_ERR_DEBUG(errdict); 134 error = EINVAL; 135 break; 136 } 137 138 /* Table name, ID and type. Validate them. */ 139 if (!prop_dictionary_get_cstring_nocopy(tbldict, "name", &name)) { 140 NPF_ERR_DEBUG(errdict); 141 error = EINVAL; 142 break; 143 } 144 prop_dictionary_get_uint32(tbldict, "id", &tid); 145 prop_dictionary_get_int32(tbldict, "type", &type); 146 error = npf_table_check(tblset, name, tid, type); 147 if (error) { 148 NPF_ERR_DEBUG(errdict); 149 break; 150 } 151 152 /* Get the entries or binary data. */ 153 prop_array_t ents = prop_dictionary_get(tbldict, "entries"); 154 prop_object_t obj = prop_dictionary_get(tbldict, "data"); 155 void *blob = prop_data_data(obj); 156 size_t size = prop_data_size(obj); 157 158 if (type == NPF_TABLE_CDB && (blob == NULL || size == 0)) { 159 NPF_ERR_DEBUG(errdict); 160 error = EINVAL; 161 break; 162 } 163 if (type == NPF_TABLE_HASH) { 164 size = 1024; /* XXX */ 165 } 166 167 /* Create and insert the table. */ 168 t = npf_table_create(name, tid, type, blob, size); 169 if (t == NULL) { 170 NPF_ERR_DEBUG(errdict); 171 error = ENOMEM; 172 break; 173 } 174 error = npf_tableset_insert(tblset, t); 175 KASSERT(error == 0); 176 177 if (ents && (error = npf_mk_table_entries(t, ents)) != 0) { 178 NPF_ERR_DEBUG(errdict); 179 break; 180 } 181 } 182 prop_object_iterator_release(it); 183 /* 184 * Note: in a case of error, caller will free the tableset. 185 */ 186 return error; 187 } 188 189 static npf_rproc_t * 190 npf_mk_singlerproc(prop_dictionary_t rpdict) 191 { 192 prop_object_iterator_t it; 193 prop_dictionary_t extdict; 194 prop_array_t extlist; 195 npf_rproc_t *rp; 196 197 extlist = prop_dictionary_get(rpdict, "extcalls"); 198 if (prop_object_type(extlist) != PROP_TYPE_ARRAY) { 199 return NULL; 200 } 201 202 rp = npf_rproc_create(rpdict); 203 if (rp == NULL) { 204 return NULL; 205 } 206 207 it = prop_array_iterator(extlist); 208 while ((extdict = prop_object_iterator_next(it)) != NULL) { 209 const char *name; 210 211 if (!prop_dictionary_get_cstring_nocopy(extdict, 212 "name", &name) || npf_ext_construct(name, rp, extdict)) { 213 npf_rproc_release(rp); 214 rp = NULL; 215 break; 216 } 217 } 218 prop_object_iterator_release(it); 219 return rp; 220 } 221 222 static int __noinline 223 npf_mk_rprocs(npf_rprocset_t *rpset, prop_array_t rprocs, 224 prop_dictionary_t errdict) 225 { 226 prop_object_iterator_t it; 227 prop_dictionary_t rpdict; 228 int error = 0; 229 230 it = prop_array_iterator(rprocs); 231 while ((rpdict = prop_object_iterator_next(it)) != NULL) { 232 npf_rproc_t *rp; 233 234 if ((rp = npf_mk_singlerproc(rpdict)) == NULL) { 235 NPF_ERR_DEBUG(errdict); 236 error = EINVAL; 237 break; 238 } 239 npf_rprocset_insert(rpset, rp); 240 } 241 prop_object_iterator_release(it); 242 return error; 243 } 244 245 static npf_alg_t * 246 npf_mk_singlealg(prop_dictionary_t aldict) 247 { 248 const char *name; 249 250 if (!prop_dictionary_get_cstring_nocopy(aldict, "name", &name)) 251 return NULL; 252 return npf_alg_construct(name); 253 } 254 255 static int __noinline 256 npf_mk_algs(prop_array_t alglist, prop_dictionary_t errdict) 257 { 258 prop_object_iterator_t it; 259 prop_dictionary_t nadict; 260 int error = 0; 261 262 it = prop_array_iterator(alglist); 263 while ((nadict = prop_object_iterator_next(it)) != NULL) { 264 if (npf_mk_singlealg(nadict) == NULL) { 265 NPF_ERR_DEBUG(errdict); 266 error = EINVAL; 267 break; 268 } 269 } 270 prop_object_iterator_release(it); 271 return error; 272 } 273 274 static int __noinline 275 npf_mk_code(prop_object_t obj, int type, void **code, size_t *csize, 276 prop_dictionary_t errdict) 277 { 278 const void *cptr; 279 size_t clen; 280 void *bc; 281 282 if (type != NPF_CODE_BPF) { 283 return ENOTSUP; 284 } 285 cptr = prop_data_data_nocopy(obj); 286 if (cptr == NULL || (clen = prop_data_size(obj)) == 0) { 287 NPF_ERR_DEBUG(errdict); 288 return EINVAL; 289 } 290 if (!npf_bpf_validate(cptr, clen)) { 291 NPF_ERR_DEBUG(errdict); 292 return EINVAL; 293 } 294 bc = kmem_alloc(clen, KM_SLEEP); 295 memcpy(bc, cptr, clen); 296 297 *code = bc; 298 *csize = clen; 299 return 0; 300 } 301 302 static int __noinline 303 npf_mk_singlerule(prop_dictionary_t rldict, npf_rprocset_t *rpset, 304 npf_rule_t **rlret, prop_dictionary_t errdict) 305 { 306 npf_rule_t *rl; 307 const char *rname; 308 prop_object_t obj; 309 int p, error = 0; 310 311 /* Rule - dictionary. */ 312 if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY) { 313 NPF_ERR_DEBUG(errdict); 314 return EINVAL; 315 } 316 if ((rl = npf_rule_alloc(rldict)) == NULL) { 317 NPF_ERR_DEBUG(errdict); 318 return EINVAL; 319 } 320 321 /* Assign rule procedure, if any. */ 322 if (prop_dictionary_get_cstring_nocopy(rldict, "rproc", &rname)) { 323 npf_rproc_t *rp; 324 325 if (rpset == NULL) { 326 error = EINVAL; 327 goto err; 328 } 329 if ((rp = npf_rprocset_lookup(rpset, rname)) == NULL) { 330 NPF_ERR_DEBUG(errdict); 331 error = EINVAL; 332 goto err; 333 } 334 npf_rule_setrproc(rl, rp); 335 } 336 337 /* Filter code (binary data). */ 338 if ((obj = prop_dictionary_get(rldict, "code")) != NULL) { 339 int type; 340 size_t len; 341 void *code; 342 343 prop_dictionary_get_int32(rldict, "code-type", &type); 344 error = npf_mk_code(obj, type, &code, &len, errdict); 345 if (error) { 346 goto err; 347 } 348 npf_rule_setcode(rl, type, code, len); 349 } 350 351 *rlret = rl; 352 return 0; 353 err: 354 npf_rule_free(rl); 355 prop_dictionary_get_int32(rldict, "prio", &p); /* XXX */ 356 prop_dictionary_set_int32(errdict, "id", p); 357 return error; 358 } 359 360 static int __noinline 361 npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules, npf_rprocset_t *rpset, 362 prop_dictionary_t errdict) 363 { 364 prop_object_iterator_t it; 365 prop_dictionary_t rldict; 366 int error; 367 368 if (prop_object_type(rules) != PROP_TYPE_ARRAY) { 369 NPF_ERR_DEBUG(errdict); 370 return EINVAL; 371 } 372 373 error = 0; 374 it = prop_array_iterator(rules); 375 while ((rldict = prop_object_iterator_next(it)) != NULL) { 376 npf_rule_t *rl = NULL; 377 378 /* Generate a single rule. */ 379 error = npf_mk_singlerule(rldict, rpset, &rl, errdict); 380 if (error) { 381 break; 382 } 383 npf_ruleset_insert(rlset, rl); 384 } 385 prop_object_iterator_release(it); 386 /* 387 * Note: in a case of error, caller will free the ruleset. 388 */ 389 return error; 390 } 391 392 static int __noinline 393 npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist, 394 prop_dictionary_t errdict) 395 { 396 prop_object_iterator_t it; 397 prop_dictionary_t natdict; 398 int error; 399 400 /* NAT policies - array. */ 401 if (prop_object_type(natlist) != PROP_TYPE_ARRAY) { 402 NPF_ERR_DEBUG(errdict); 403 return EINVAL; 404 } 405 406 error = 0; 407 it = prop_array_iterator(natlist); 408 while ((natdict = prop_object_iterator_next(it)) != NULL) { 409 npf_rule_t *rl = NULL; 410 npf_natpolicy_t *np; 411 412 /* NAT policy - dictionary. */ 413 if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) { 414 NPF_ERR_DEBUG(errdict); 415 error = EINVAL; 416 break; 417 } 418 419 /* 420 * NAT policies are standard rules, plus additional 421 * information for translation. Make a rule. 422 */ 423 error = npf_mk_singlerule(natdict, NULL, &rl, errdict); 424 if (error) { 425 break; 426 } 427 npf_ruleset_insert(nset, rl); 428 429 /* If rule is named, it is a group with NAT policies. */ 430 if (prop_dictionary_get(natdict, "name") && 431 prop_dictionary_get(natdict, "subrules")) { 432 continue; 433 } 434 435 /* Allocate a new NAT policy and assign to the rule. */ 436 np = npf_nat_newpolicy(natdict, nset); 437 if (np == NULL) { 438 NPF_ERR_DEBUG(errdict); 439 error = ENOMEM; 440 break; 441 } 442 npf_rule_setnat(rl, np); 443 } 444 prop_object_iterator_release(it); 445 /* 446 * Note: in a case of error, caller will free entire NAT ruleset 447 * with assigned NAT policies. 448 */ 449 return error; 450 } 451 452 /* 453 * npf_mk_connlist: import a list of connections and load them. 454 */ 455 static int __noinline 456 npf_mk_connlist(prop_array_t conlist, npf_ruleset_t *natlist, 457 npf_conndb_t **conndb, prop_dictionary_t errdict) 458 { 459 prop_dictionary_t condict; 460 prop_object_iterator_t it; 461 npf_conndb_t *cd; 462 int error = 0; 463 464 /* Connection list - array */ 465 if (prop_object_type(conlist) != PROP_TYPE_ARRAY) { 466 NPF_ERR_DEBUG(errdict); 467 return EINVAL; 468 } 469 470 /* Create a connection database. */ 471 cd = npf_conndb_create(); 472 it = prop_array_iterator(conlist); 473 while ((condict = prop_object_iterator_next(it)) != NULL) { 474 /* Connection - dictionary. */ 475 if (prop_object_type(condict) != PROP_TYPE_DICTIONARY) { 476 NPF_ERR_DEBUG(errdict); 477 error = EINVAL; 478 break; 479 } 480 /* Construct and insert the connection. */ 481 error = npf_conn_import(cd, condict, natlist); 482 if (error) { 483 NPF_ERR_DEBUG(errdict); 484 break; 485 } 486 } 487 prop_object_iterator_release(it); 488 if (error) { 489 npf_conn_gc(cd, true, false); 490 npf_conndb_destroy(cd); 491 } else { 492 *conndb = cd; 493 } 494 return error; 495 } 496 497 /* 498 * npfctl_load: store passed data i.e. update settings, create passed 499 * tables, rules and atomically activate all them. 500 */ 501 int 502 npfctl_load(u_long cmd, void *data) 503 { 504 struct plistref *pref = data; 505 prop_dictionary_t npf_dict, errdict; 506 prop_array_t alglist, natlist, tables, rprocs, rules, conlist; 507 npf_tableset_t *tblset = NULL; 508 npf_rprocset_t *rpset = NULL; 509 npf_ruleset_t *rlset = NULL; 510 npf_ruleset_t *nset = NULL; 511 npf_conndb_t *conndb = NULL; 512 uint32_t ver = 0; 513 size_t nitems; 514 bool flush; 515 int error; 516 517 /* Retrieve the dictionary. */ 518 #ifndef _NPF_TESTING 519 error = prop_dictionary_copyin_ioctl(pref, cmd, &npf_dict); 520 if (error) 521 return error; 522 #else 523 npf_dict = (prop_dictionary_t)pref; 524 #endif 525 526 /* Dictionary for error reporting and version check. */ 527 errdict = prop_dictionary_create(); 528 prop_dictionary_get_uint32(npf_dict, "version", &ver); 529 if (ver != NPF_VERSION) { 530 error = EPROGMISMATCH; 531 goto fail; 532 } 533 534 /* ALGs. */ 535 alglist = prop_dictionary_get(npf_dict, "algs"); 536 error = npf_mk_algs(alglist, errdict); 537 if (error) { 538 goto fail; 539 } 540 541 /* NAT policies. */ 542 natlist = prop_dictionary_get(npf_dict, "nat"); 543 if ((nitems = prop_array_count(natlist)) > NPF_MAX_RULES) { 544 error = E2BIG; 545 goto fail; 546 } 547 548 nset = npf_ruleset_create(nitems); 549 error = npf_mk_natlist(nset, natlist, errdict); 550 if (error) { 551 goto fail; 552 } 553 554 /* Tables. */ 555 tables = prop_dictionary_get(npf_dict, "tables"); 556 if ((nitems = prop_array_count(tables)) > NPF_MAX_TABLES) { 557 error = E2BIG; 558 goto fail; 559 } 560 tblset = npf_tableset_create(nitems); 561 error = npf_mk_tables(tblset, tables, errdict); 562 if (error) { 563 goto fail; 564 } 565 566 /* Rule procedures. */ 567 rprocs = prop_dictionary_get(npf_dict, "rprocs"); 568 if ((nitems = prop_array_count(rprocs)) > NPF_MAX_RPROCS) { 569 error = E2BIG; 570 goto fail; 571 } 572 rpset = npf_rprocset_create(); 573 error = npf_mk_rprocs(rpset, rprocs, errdict); 574 if (error) { 575 goto fail; 576 } 577 578 /* Rules. */ 579 rules = prop_dictionary_get(npf_dict, "rules"); 580 if ((nitems = prop_array_count(rules)) > NPF_MAX_RULES) { 581 error = E2BIG; 582 goto fail; 583 } 584 585 rlset = npf_ruleset_create(nitems); 586 error = npf_mk_rules(rlset, rules, rpset, errdict); 587 if (error) { 588 goto fail; 589 } 590 591 /* Connections (if loading any). */ 592 if ((conlist = prop_dictionary_get(npf_dict, "conn-list")) != NULL) { 593 error = npf_mk_connlist(conlist, nset, &conndb, errdict); 594 if (error) { 595 goto fail; 596 } 597 } 598 599 flush = false; 600 prop_dictionary_get_bool(npf_dict, "flush", &flush); 601 602 /* 603 * Finally - perform the load. 604 */ 605 npf_config_load(rlset, tblset, nset, rpset, conndb, flush); 606 607 /* Done. Since data is consumed now, we shall not destroy it. */ 608 tblset = NULL; 609 rpset = NULL; 610 rlset = NULL; 611 nset = NULL; 612 fail: 613 /* 614 * Note: destroy rulesets first, to drop references to the tableset. 615 */ 616 KASSERT(error == 0 || (nset || rpset || rlset || tblset)); 617 if (nset) { 618 npf_ruleset_destroy(nset); 619 } 620 if (rlset) { 621 npf_ruleset_destroy(rlset); 622 } 623 if (rpset) { 624 npf_rprocset_destroy(rpset); 625 } 626 if (tblset) { 627 npf_tableset_destroy(tblset); 628 } 629 prop_object_release(npf_dict); 630 631 /* Error report. */ 632 #ifndef _NPF_TESTING 633 prop_dictionary_set_int32(errdict, "errno", error); 634 prop_dictionary_copyout_ioctl(pref, cmd, errdict); 635 prop_object_release(errdict); 636 error = 0; 637 #endif 638 return error; 639 } 640 641 /* 642 * npfctl_save: export the config dictionary as it was submitted, 643 * including the current snapshot of the connections. Additionally, 644 * indicate whether the ruleset is currently active. 645 */ 646 int 647 npfctl_save(u_long cmd, void *data) 648 { 649 struct plistref *pref = data; 650 prop_array_t rulelist, natlist, tables, rprocs, conlist; 651 prop_dictionary_t npf_dict = NULL; 652 int error; 653 654 rulelist = prop_array_create(); 655 natlist = prop_array_create(); 656 tables = prop_array_create(); 657 rprocs = prop_array_create(); 658 conlist = prop_array_create(); 659 660 /* 661 * Serialise the connections and NAT policies. 662 */ 663 npf_config_enter(); 664 error = npf_conndb_export(conlist); 665 if (error) { 666 goto out; 667 } 668 error = npf_ruleset_export(npf_config_ruleset(), rulelist); 669 if (error) { 670 goto out; 671 } 672 error = npf_ruleset_export(npf_config_natset(), natlist); 673 if (error) { 674 goto out; 675 } 676 error = npf_tableset_export(npf_config_tableset(), tables); 677 if (error) { 678 goto out; 679 } 680 error = npf_rprocset_export(npf_config_rprocs(), rprocs); 681 if (error) { 682 goto out; 683 } 684 prop_array_t alglist = npf_alg_export(); 685 686 npf_dict = prop_dictionary_create(); 687 prop_dictionary_set_uint32(npf_dict, "version", NPF_VERSION); 688 prop_dictionary_set_and_rel(npf_dict, "algs", alglist); 689 prop_dictionary_set_and_rel(npf_dict, "rules", rulelist); 690 prop_dictionary_set_and_rel(npf_dict, "nat", natlist); 691 prop_dictionary_set_and_rel(npf_dict, "tables", tables); 692 prop_dictionary_set_and_rel(npf_dict, "rprocs", rprocs); 693 prop_dictionary_set_and_rel(npf_dict, "conn-list", conlist); 694 prop_dictionary_set_bool(npf_dict, "active", npf_pfil_registered_p()); 695 error = prop_dictionary_copyout_ioctl(pref, cmd, npf_dict); 696 out: 697 npf_config_exit(); 698 699 if (!npf_dict) { 700 prop_object_release(rulelist); 701 prop_object_release(natlist); 702 prop_object_release(tables); 703 prop_object_release(rprocs); 704 prop_object_release(conlist); 705 } else { 706 prop_object_release(npf_dict); 707 } 708 return error; 709 } 710 711 /* 712 * npfctl_rule: add or remove dynamic rules in the specified ruleset. 713 */ 714 int 715 npfctl_rule(u_long cmd, void *data) 716 { 717 struct plistref *pref = data; 718 prop_dictionary_t npf_rule, retdict = NULL; 719 npf_ruleset_t *rlset; 720 npf_rule_t *rl = NULL; 721 const char *ruleset_name; 722 uint32_t rcmd = 0; 723 int error; 724 725 error = prop_dictionary_copyin_ioctl(pref, cmd, &npf_rule); 726 if (error) { 727 return error; 728 } 729 prop_dictionary_get_uint32(npf_rule, "command", &rcmd); 730 if (!prop_dictionary_get_cstring_nocopy(npf_rule, 731 "ruleset-name", &ruleset_name)) { 732 error = EINVAL; 733 goto out; 734 } 735 736 if (rcmd == NPF_CMD_RULE_ADD) { 737 retdict = prop_dictionary_create(); 738 if (npf_mk_singlerule(npf_rule, NULL, &rl, retdict) != 0) { 739 error = EINVAL; 740 goto out; 741 } 742 } 743 744 npf_config_enter(); 745 rlset = npf_config_ruleset(); 746 747 switch (rcmd) { 748 case NPF_CMD_RULE_ADD: { 749 if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) { 750 /* Success. */ 751 uint64_t id = npf_rule_getid(rl); 752 prop_dictionary_set_uint64(retdict, "id", id); 753 rl = NULL; 754 } 755 break; 756 } 757 case NPF_CMD_RULE_REMOVE: { 758 uint64_t id; 759 760 if (!prop_dictionary_get_uint64(npf_rule, "id", &id)) { 761 error = EINVAL; 762 break; 763 } 764 error = npf_ruleset_remove(rlset, ruleset_name, id); 765 break; 766 } 767 case NPF_CMD_RULE_REMKEY: { 768 prop_object_t obj = prop_dictionary_get(npf_rule, "key"); 769 const void *key = prop_data_data_nocopy(obj); 770 size_t len = prop_data_size(obj); 771 772 if (len == 0 || len > NPF_RULE_MAXKEYLEN) { 773 error = EINVAL; 774 break; 775 } 776 error = npf_ruleset_remkey(rlset, ruleset_name, key, len); 777 break; 778 } 779 case NPF_CMD_RULE_LIST: { 780 retdict = npf_ruleset_list(rlset, ruleset_name); 781 break; 782 } 783 case NPF_CMD_RULE_FLUSH: { 784 error = npf_ruleset_flush(rlset, ruleset_name); 785 break; 786 } 787 default: 788 error = EINVAL; 789 break; 790 } 791 792 /* Destroy any removed rules. */ 793 if (!error && rcmd != NPF_CMD_RULE_ADD && rcmd != NPF_CMD_RULE_LIST) { 794 npf_config_sync(); 795 npf_ruleset_gc(rlset); 796 } 797 npf_config_exit(); 798 799 if (rl) { 800 npf_rule_free(rl); 801 } 802 out: 803 if (retdict) { 804 prop_object_release(npf_rule); 805 prop_dictionary_copyout_ioctl(pref, cmd, retdict); 806 prop_object_release(retdict); 807 } 808 return error; 809 } 810 811 /* 812 * npfctl_table: add, remove or query entries in the specified table. 813 * 814 * For maximum performance, interface is avoiding proplib(3)'s overhead. 815 */ 816 int 817 npfctl_table(void *data) 818 { 819 const npf_ioctl_table_t *nct = data; 820 char tname[NPF_TABLE_MAXNAMELEN]; 821 npf_tableset_t *ts; 822 npf_table_t *t; 823 int s, error; 824 825 error = copyinstr(nct->nct_name, tname, sizeof(tname), NULL); 826 if (error) { 827 return error; 828 } 829 830 s = npf_config_read_enter(); /* XXX */ 831 ts = npf_config_tableset(); 832 if ((t = npf_tableset_getbyname(ts, tname)) == NULL) { 833 npf_config_read_exit(s); 834 return EINVAL; 835 } 836 837 switch (nct->nct_cmd) { 838 case NPF_CMD_TABLE_LOOKUP: 839 error = npf_table_lookup(t, nct->nct_data.ent.alen, 840 &nct->nct_data.ent.addr); 841 break; 842 case NPF_CMD_TABLE_ADD: 843 error = npf_table_insert(t, nct->nct_data.ent.alen, 844 &nct->nct_data.ent.addr, nct->nct_data.ent.mask); 845 break; 846 case NPF_CMD_TABLE_REMOVE: 847 error = npf_table_remove(t, nct->nct_data.ent.alen, 848 &nct->nct_data.ent.addr, nct->nct_data.ent.mask); 849 break; 850 case NPF_CMD_TABLE_LIST: 851 error = npf_table_list(t, nct->nct_data.buf.buf, 852 nct->nct_data.buf.len); 853 break; 854 case NPF_CMD_TABLE_FLUSH: 855 error = npf_table_flush(t); 856 break; 857 default: 858 error = EINVAL; 859 break; 860 } 861 npf_config_read_exit(s); 862 863 return error; 864 } 865