1 /* $NetBSD: npf_ctl.c,v 1.50 2017/12/10 01:18:21 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 #ifdef _KERNEL 40 #include <sys/cdefs.h> 41 __KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.50 2017/12/10 01:18:21 rmind Exp $"); 42 43 #include <sys/param.h> 44 #include <sys/conf.h> 45 #include <sys/kmem.h> 46 #include <net/bpf.h> 47 48 #include <prop/proplib.h> 49 #endif 50 51 #include "npf_impl.h" 52 #include "npf_conn.h" 53 54 #define NPF_ERR_DEBUG(e) \ 55 prop_dictionary_set_cstring_nocopy((e), "source-file", __FILE__); \ 56 prop_dictionary_set_uint32((e), "source-line", __LINE__); 57 58 #ifdef _KERNEL 59 /* 60 * npfctl_switch: enable or disable packet inspection. 61 */ 62 int 63 npfctl_switch(void *data) 64 { 65 const bool onoff = *(int *)data ? true : false; 66 int error; 67 68 if (onoff) { 69 /* Enable: add pfil hooks. */ 70 error = npf_pfil_register(false); 71 } else { 72 /* Disable: remove pfil hooks. */ 73 npf_pfil_unregister(false); 74 error = 0; 75 } 76 return error; 77 } 78 #endif 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_t *npf, 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 uint64_t 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_uint64(tbldict, "id", &tid); 145 prop_dictionary_get_int32(tbldict, "type", &type); 146 error = npf_table_check(tblset, name, (u_int)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 164 /* Create and insert the table. */ 165 t = npf_table_create(name, (u_int)tid, type, blob, size); 166 if (t == NULL) { 167 NPF_ERR_DEBUG(errdict); 168 error = ENOMEM; 169 break; 170 } 171 error = npf_tableset_insert(tblset, t); 172 KASSERT(error == 0); 173 174 if (ents && (error = npf_mk_table_entries(t, ents)) != 0) { 175 NPF_ERR_DEBUG(errdict); 176 break; 177 } 178 } 179 prop_object_iterator_release(it); 180 /* 181 * Note: in a case of error, caller will free the tableset. 182 */ 183 return error; 184 } 185 186 static npf_rproc_t * 187 npf_mk_singlerproc(npf_t *npf, prop_dictionary_t rpdict) 188 { 189 prop_object_iterator_t it; 190 prop_dictionary_t extdict; 191 prop_array_t extlist; 192 npf_rproc_t *rp; 193 194 extlist = prop_dictionary_get(rpdict, "extcalls"); 195 if (prop_object_type(extlist) != PROP_TYPE_ARRAY) { 196 return NULL; 197 } 198 199 rp = npf_rproc_create(rpdict); 200 if (rp == NULL) { 201 return NULL; 202 } 203 204 it = prop_array_iterator(extlist); 205 while ((extdict = prop_object_iterator_next(it)) != NULL) { 206 const char *name; 207 208 if (!prop_dictionary_get_cstring_nocopy(extdict, "name", 209 &name) || npf_ext_construct(npf, name, rp, extdict)) { 210 npf_rproc_release(rp); 211 rp = NULL; 212 break; 213 } 214 } 215 prop_object_iterator_release(it); 216 return rp; 217 } 218 219 static int __noinline 220 npf_mk_rprocs(npf_t *npf, npf_rprocset_t *rpset, prop_array_t rprocs, 221 prop_dictionary_t errdict) 222 { 223 prop_object_iterator_t it; 224 prop_dictionary_t rpdict; 225 int error = 0; 226 227 it = prop_array_iterator(rprocs); 228 while ((rpdict = prop_object_iterator_next(it)) != NULL) { 229 npf_rproc_t *rp; 230 231 if ((rp = npf_mk_singlerproc(npf, rpdict)) == NULL) { 232 NPF_ERR_DEBUG(errdict); 233 error = EINVAL; 234 break; 235 } 236 npf_rprocset_insert(rpset, rp); 237 } 238 prop_object_iterator_release(it); 239 return error; 240 } 241 242 static npf_alg_t * 243 npf_mk_singlealg(npf_t *npf, prop_dictionary_t aldict) 244 { 245 const char *name; 246 247 if (!prop_dictionary_get_cstring_nocopy(aldict, "name", &name)) 248 return NULL; 249 return npf_alg_construct(npf, name); 250 } 251 252 static int __noinline 253 npf_mk_algs(npf_t *npf, prop_array_t alglist, prop_dictionary_t errdict) 254 { 255 prop_object_iterator_t it; 256 prop_dictionary_t nadict; 257 int error = 0; 258 259 it = prop_array_iterator(alglist); 260 while ((nadict = prop_object_iterator_next(it)) != NULL) { 261 if (npf_mk_singlealg(npf, nadict) == NULL) { 262 NPF_ERR_DEBUG(errdict); 263 error = EINVAL; 264 break; 265 } 266 } 267 prop_object_iterator_release(it); 268 return error; 269 } 270 271 static int __noinline 272 npf_mk_code(prop_object_t obj, int type, void **code, size_t *csize, 273 prop_dictionary_t errdict) 274 { 275 const void *cptr; 276 size_t clen; 277 void *bc; 278 279 if (type != NPF_CODE_BPF) { 280 return ENOTSUP; 281 } 282 cptr = prop_data_data_nocopy(obj); 283 if (cptr == NULL || (clen = prop_data_size(obj)) == 0) { 284 NPF_ERR_DEBUG(errdict); 285 return EINVAL; 286 } 287 if (!npf_bpf_validate(cptr, clen)) { 288 NPF_ERR_DEBUG(errdict); 289 return EINVAL; 290 } 291 bc = kmem_alloc(clen, KM_SLEEP); 292 memcpy(bc, cptr, clen); 293 294 *code = bc; 295 *csize = clen; 296 return 0; 297 } 298 299 static int __noinline 300 npf_mk_singlerule(npf_t *npf, prop_dictionary_t rldict, npf_rprocset_t *rpset, 301 npf_rule_t **rlret, prop_dictionary_t errdict) 302 { 303 npf_rule_t *rl; 304 const char *rname; 305 prop_object_t obj; 306 int p, error = 0; 307 308 /* Rule - dictionary. */ 309 if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY) { 310 NPF_ERR_DEBUG(errdict); 311 return EINVAL; 312 } 313 if ((rl = npf_rule_alloc(npf, rldict)) == NULL) { 314 NPF_ERR_DEBUG(errdict); 315 return EINVAL; 316 } 317 318 /* Assign rule procedure, if any. */ 319 if (prop_dictionary_get_cstring_nocopy(rldict, "rproc", &rname)) { 320 npf_rproc_t *rp; 321 322 if (rpset == NULL) { 323 error = EINVAL; 324 goto err; 325 } 326 if ((rp = npf_rprocset_lookup(rpset, rname)) == NULL) { 327 NPF_ERR_DEBUG(errdict); 328 error = EINVAL; 329 goto err; 330 } 331 npf_rule_setrproc(rl, rp); 332 } 333 334 /* Filter code (binary data). */ 335 if ((obj = prop_dictionary_get(rldict, "code")) != NULL) { 336 int type; 337 size_t len; 338 void *code; 339 340 prop_dictionary_get_int32(rldict, "code-type", &type); 341 error = npf_mk_code(obj, type, &code, &len, errdict); 342 if (error) { 343 goto err; 344 } 345 npf_rule_setcode(rl, type, code, len); 346 } 347 348 *rlret = rl; 349 return 0; 350 err: 351 npf_rule_free(rl); 352 prop_dictionary_get_int32(rldict, "prio", &p); /* XXX */ 353 prop_dictionary_set_int64(errdict, "id", p); 354 return error; 355 } 356 357 static int __noinline 358 npf_mk_rules(npf_t *npf, npf_ruleset_t *rlset, prop_array_t rules, 359 npf_rprocset_t *rpset, prop_dictionary_t errdict) 360 { 361 prop_object_iterator_t it; 362 prop_dictionary_t rldict; 363 int error; 364 365 if (prop_object_type(rules) != PROP_TYPE_ARRAY) { 366 NPF_ERR_DEBUG(errdict); 367 return EINVAL; 368 } 369 370 error = 0; 371 it = prop_array_iterator(rules); 372 while ((rldict = prop_object_iterator_next(it)) != NULL) { 373 npf_rule_t *rl = NULL; 374 const char *name; 375 376 error = npf_mk_singlerule(npf, rldict, rpset, &rl, errdict); 377 if (error) { 378 break; 379 } 380 if (prop_dictionary_get_cstring_nocopy(rldict, "name", &name) && 381 npf_ruleset_lookup(rlset, name) != NULL) { 382 NPF_ERR_DEBUG(errdict); 383 npf_rule_free(rl); 384 return EEXIST; 385 } 386 npf_ruleset_insert(rlset, rl); 387 } 388 prop_object_iterator_release(it); 389 /* 390 * Note: in a case of error, caller will free the ruleset. 391 */ 392 return error; 393 } 394 395 static int __noinline 396 npf_mk_natlist(npf_t *npf, npf_ruleset_t *nset, prop_array_t natlist, 397 prop_dictionary_t errdict) 398 { 399 prop_object_iterator_t it; 400 prop_dictionary_t natdict; 401 int error; 402 403 /* NAT policies - array. */ 404 if (prop_object_type(natlist) != PROP_TYPE_ARRAY) { 405 NPF_ERR_DEBUG(errdict); 406 return EINVAL; 407 } 408 409 error = 0; 410 it = prop_array_iterator(natlist); 411 while ((natdict = prop_object_iterator_next(it)) != NULL) { 412 npf_rule_t *rl = NULL; 413 npf_natpolicy_t *np; 414 415 /* NAT policy - dictionary. */ 416 if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) { 417 NPF_ERR_DEBUG(errdict); 418 error = EINVAL; 419 break; 420 } 421 422 /* 423 * NAT policies are standard rules, plus additional 424 * information for translation. Make a rule. 425 */ 426 error = npf_mk_singlerule(npf, natdict, NULL, &rl, errdict); 427 if (error) { 428 break; 429 } 430 npf_ruleset_insert(nset, rl); 431 432 /* If rule is named, it is a group with NAT policies. */ 433 if (prop_dictionary_get(natdict, "name") && 434 prop_dictionary_get(natdict, "subrules")) { 435 continue; 436 } 437 438 /* Allocate a new NAT policy and assign to the rule. */ 439 np = npf_nat_newpolicy(npf, natdict, nset); 440 if (np == NULL) { 441 NPF_ERR_DEBUG(errdict); 442 error = ENOMEM; 443 break; 444 } 445 npf_rule_setnat(rl, np); 446 } 447 prop_object_iterator_release(it); 448 /* 449 * Note: in a case of error, caller will free entire NAT ruleset 450 * with assigned NAT policies. 451 */ 452 return error; 453 } 454 455 /* 456 * npf_mk_connlist: import a list of connections and load them. 457 */ 458 static int __noinline 459 npf_mk_connlist(npf_t *npf, prop_array_t conlist, npf_ruleset_t *natlist, 460 npf_conndb_t **conndb, prop_dictionary_t errdict) 461 { 462 prop_dictionary_t condict; 463 prop_object_iterator_t it; 464 npf_conndb_t *cd; 465 int error = 0; 466 467 /* Connection list - array */ 468 if (prop_object_type(conlist) != PROP_TYPE_ARRAY) { 469 NPF_ERR_DEBUG(errdict); 470 return EINVAL; 471 } 472 473 /* Create a connection database. */ 474 cd = npf_conndb_create(); 475 it = prop_array_iterator(conlist); 476 while ((condict = prop_object_iterator_next(it)) != NULL) { 477 /* Connection - dictionary. */ 478 if (prop_object_type(condict) != PROP_TYPE_DICTIONARY) { 479 NPF_ERR_DEBUG(errdict); 480 error = EINVAL; 481 break; 482 } 483 /* Construct and insert the connection. */ 484 error = npf_conn_import(npf, cd, condict, natlist); 485 if (error) { 486 NPF_ERR_DEBUG(errdict); 487 break; 488 } 489 } 490 prop_object_iterator_release(it); 491 if (error) { 492 npf_conn_gc(npf, cd, true, false); 493 npf_conndb_destroy(cd); 494 } else { 495 *conndb = cd; 496 } 497 return error; 498 } 499 500 #if defined(_NPF_TESTING) || defined(_NPF_STANDALONE) 501 int npfctl_testing; 502 #endif 503 504 /* 505 * npfctl_load: store passed data i.e. update settings, create passed 506 * tables, rules and atomically activate all them. 507 */ 508 int 509 npfctl_load(npf_t *npf, u_long cmd, void *data) 510 { 511 struct plistref *pref = data; 512 prop_dictionary_t npf_dict, errdict; 513 prop_array_t alglist, natlist, tables, rprocs, rules, conlist; 514 npf_tableset_t *tblset = NULL; 515 npf_rprocset_t *rpset = NULL; 516 npf_ruleset_t *rlset = NULL; 517 npf_ruleset_t *nset = NULL; 518 npf_conndb_t *conndb = NULL; 519 uint32_t ver = 0; 520 size_t nitems; 521 bool flush; 522 int error; 523 524 /* Retrieve the dictionary. */ 525 #if defined(_NPF_TESTING) || defined(_NPF_STANDALONE) 526 if (npfctl_testing) 527 npf_dict = (prop_dictionary_t)pref; 528 else 529 #endif 530 { 531 error = prop_dictionary_copyin_ioctl_size(pref, cmd, &npf_dict, 532 4 * 1024 * 1024); 533 if (error) 534 return error; 535 } 536 537 /* Dictionary for error reporting and version check. */ 538 errdict = prop_dictionary_create(); 539 prop_dictionary_get_uint32(npf_dict, "version", &ver); 540 if (ver != NPF_VERSION) { 541 error = EPROGMISMATCH; 542 goto fail; 543 } 544 545 /* ALGs. */ 546 alglist = prop_dictionary_get(npf_dict, "algs"); 547 error = npf_mk_algs(npf, alglist, errdict); 548 if (error) { 549 goto fail; 550 } 551 552 /* NAT policies. */ 553 natlist = prop_dictionary_get(npf_dict, "nat"); 554 if ((nitems = prop_array_count(natlist)) > NPF_MAX_RULES) { 555 error = E2BIG; 556 goto fail; 557 } 558 559 nset = npf_ruleset_create(nitems); 560 error = npf_mk_natlist(npf, nset, natlist, errdict); 561 if (error) { 562 goto fail; 563 } 564 565 /* Tables. */ 566 tables = prop_dictionary_get(npf_dict, "tables"); 567 if ((nitems = prop_array_count(tables)) > NPF_MAX_TABLES) { 568 error = E2BIG; 569 goto fail; 570 } 571 tblset = npf_tableset_create(nitems); 572 error = npf_mk_tables(npf, tblset, tables, errdict); 573 if (error) { 574 goto fail; 575 } 576 577 /* Rule procedures. */ 578 rprocs = prop_dictionary_get(npf_dict, "rprocs"); 579 if ((nitems = prop_array_count(rprocs)) > NPF_MAX_RPROCS) { 580 error = E2BIG; 581 goto fail; 582 } 583 rpset = npf_rprocset_create(); 584 error = npf_mk_rprocs(npf, rpset, rprocs, errdict); 585 if (error) { 586 goto fail; 587 } 588 589 /* Rules. */ 590 rules = prop_dictionary_get(npf_dict, "rules"); 591 if ((nitems = prop_array_count(rules)) > NPF_MAX_RULES) { 592 error = E2BIG; 593 goto fail; 594 } 595 596 rlset = npf_ruleset_create(nitems); 597 error = npf_mk_rules(npf, rlset, rules, rpset, errdict); 598 if (error) { 599 goto fail; 600 } 601 602 /* Connections (if loading any). */ 603 if ((conlist = prop_dictionary_get(npf_dict, "conn-list")) != NULL) { 604 error = npf_mk_connlist(npf, conlist, nset, &conndb, errdict); 605 if (error) { 606 goto fail; 607 } 608 } 609 610 flush = false; 611 prop_dictionary_get_bool(npf_dict, "flush", &flush); 612 613 /* 614 * Finally - perform the load. 615 */ 616 npf_config_load(npf, rlset, tblset, nset, rpset, conndb, flush); 617 618 /* Done. Since data is consumed now, we shall not destroy it. */ 619 tblset = NULL; 620 rpset = NULL; 621 rlset = NULL; 622 nset = NULL; 623 fail: 624 /* 625 * Note: destroy rulesets first, to drop references to the tableset. 626 */ 627 if (nset) { 628 npf_ruleset_destroy(nset); 629 } 630 if (rlset) { 631 npf_ruleset_destroy(rlset); 632 } 633 if (rpset) { 634 npf_rprocset_destroy(rpset); 635 } 636 if (tblset) { 637 npf_tableset_destroy(tblset); 638 } 639 #if defined(_NPF_TESTING) || defined(_NPF_STANDALONE) 640 /* Free only if allocated by prop_dictionary_copyin_ioctl_size. */ 641 if (!npfctl_testing) 642 #endif 643 prop_object_release(npf_dict); 644 645 /* 646 * - _NPF_STANDALONE doesn't require to set prop. 647 * - For _NPF_TESTING, if npfctl_testing, setting prop isn't needed, 648 * otherwise it's needed. 649 */ 650 #ifndef _NPF_STANDALONE 651 #ifdef _NPF_TESTING 652 if (!npfctl_testing) { 653 #endif 654 /* Error report. */ 655 prop_dictionary_set_int32(errdict, "errno", error); 656 prop_dictionary_copyout_ioctl(pref, cmd, errdict); 657 error = 0; 658 #ifdef _NPF_TESTING 659 } 660 #endif 661 #endif /* _NPF_STANDALONE */ 662 prop_object_release(errdict); 663 664 return error; 665 } 666 667 /* 668 * npfctl_save: export the config dictionary as it was submitted, 669 * including the current snapshot of the connections. Additionally, 670 * indicate whether the ruleset is currently active. 671 */ 672 int 673 npfctl_save(npf_t *npf, u_long cmd, void *data) 674 { 675 struct plistref *pref = data; 676 prop_array_t rulelist, natlist, tables, rprocs, conlist; 677 prop_dictionary_t npf_dict = NULL; 678 int error; 679 680 rulelist = prop_array_create(); 681 natlist = prop_array_create(); 682 tables = prop_array_create(); 683 rprocs = prop_array_create(); 684 conlist = prop_array_create(); 685 686 /* 687 * Serialise the connections and NAT policies. 688 */ 689 npf_config_enter(npf); 690 error = npf_conndb_export(npf, conlist); 691 if (error) { 692 goto out; 693 } 694 error = npf_ruleset_export(npf, npf_config_ruleset(npf), rulelist); 695 if (error) { 696 goto out; 697 } 698 error = npf_ruleset_export(npf, npf_config_natset(npf), natlist); 699 if (error) { 700 goto out; 701 } 702 error = npf_tableset_export(npf, npf_config_tableset(npf), tables); 703 if (error) { 704 goto out; 705 } 706 error = npf_rprocset_export(npf_config_rprocs(npf), rprocs); 707 if (error) { 708 goto out; 709 } 710 prop_array_t alglist = npf_alg_export(npf); 711 712 npf_dict = prop_dictionary_create(); 713 prop_dictionary_set_uint32(npf_dict, "version", NPF_VERSION); 714 prop_dictionary_set_and_rel(npf_dict, "algs", alglist); 715 prop_dictionary_set_and_rel(npf_dict, "rules", rulelist); 716 prop_dictionary_set_and_rel(npf_dict, "nat", natlist); 717 prop_dictionary_set_and_rel(npf_dict, "tables", tables); 718 prop_dictionary_set_and_rel(npf_dict, "rprocs", rprocs); 719 prop_dictionary_set_and_rel(npf_dict, "conn-list", conlist); 720 #if !defined(_NPF_STANDALONE) 721 prop_dictionary_set_bool(npf_dict, "active", npf_pfil_registered_p()); 722 error = prop_dictionary_copyout_ioctl(pref, cmd, npf_dict); 723 #else 724 prop_dictionary_set_bool(npf_dict, "active", true); 725 /* Userspace: just copy the pointer of the dictionary. */ 726 CTASSERT(sizeof(prop_dictionary_t) == sizeof(void *)); 727 memcpy(data, npf_dict, sizeof(void *)); 728 #endif 729 out: 730 npf_config_exit(npf); 731 732 if (!npf_dict) { 733 prop_object_release(rulelist); 734 prop_object_release(natlist); 735 prop_object_release(tables); 736 prop_object_release(rprocs); 737 prop_object_release(conlist); 738 } else { 739 #if !defined(_NPF_STANDALONE) 740 prop_object_release(npf_dict); 741 #endif 742 } 743 return error; 744 } 745 746 /* 747 * npfctl_conn_lookup: lookup a connection in the list of connections 748 */ 749 int 750 npfctl_conn_lookup(npf_t *npf, u_long cmd, void *data) 751 { 752 struct plistref *pref = data; 753 prop_dictionary_t conn_data, conn_result; 754 int error; 755 756 #if !defined(_NPF_STANDALONE) 757 error = prop_dictionary_copyin_ioctl(pref, cmd, &conn_data); 758 if (error) { 759 return error; 760 } 761 #else 762 conn_data = (prop_dictionary_t)pref; 763 #endif 764 error = npf_conn_find(npf, conn_data, &conn_result); 765 if (error) { 766 goto out; 767 } 768 #if !defined(_NPF_STANDALONE) 769 prop_dictionary_copyout_ioctl(pref, cmd, conn_result); 770 prop_object_release(conn_result); 771 #else 772 CTASSERT(sizeof(prop_dictionary_t) == sizeof(void *)); 773 memcpy(data, conn_result, sizeof(void *)); 774 #endif 775 out: 776 prop_object_release(conn_data); 777 return error; 778 } 779 780 /* 781 * npfctl_rule: add or remove dynamic rules in the specified ruleset. 782 */ 783 int 784 npfctl_rule(npf_t *npf, u_long cmd, void *data) 785 { 786 struct plistref *pref = data; 787 prop_dictionary_t npf_rule, retdict = NULL; 788 npf_ruleset_t *rlset; 789 npf_rule_t *rl = NULL; 790 const char *ruleset_name; 791 uint32_t rcmd = 0; 792 int error = 0; 793 794 #if !defined(_NPF_STANDALONE) 795 error = prop_dictionary_copyin_ioctl(pref, cmd, &npf_rule); 796 if (error) { 797 return error; 798 } 799 #else 800 npf_rule = (prop_dictionary_t)pref; 801 #endif 802 prop_dictionary_get_uint32(npf_rule, "command", &rcmd); 803 if (!prop_dictionary_get_cstring_nocopy(npf_rule, 804 "ruleset-name", &ruleset_name)) { 805 error = EINVAL; 806 goto out; 807 } 808 809 if (rcmd == NPF_CMD_RULE_ADD) { 810 retdict = prop_dictionary_create(); 811 if (npf_mk_singlerule(npf, npf_rule, NULL, &rl, retdict) != 0) { 812 error = EINVAL; 813 goto out; 814 } 815 } 816 817 npf_config_enter(npf); 818 rlset = npf_config_ruleset(npf); 819 820 switch (rcmd) { 821 case NPF_CMD_RULE_ADD: { 822 if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) { 823 /* Success. */ 824 uint64_t id = npf_rule_getid(rl); 825 prop_dictionary_set_uint64(retdict, "id", id); 826 rl = NULL; 827 } 828 break; 829 } 830 case NPF_CMD_RULE_REMOVE: { 831 uint64_t id; 832 833 if (!prop_dictionary_get_uint64(npf_rule, "id", &id)) { 834 error = EINVAL; 835 break; 836 } 837 error = npf_ruleset_remove(rlset, ruleset_name, id); 838 break; 839 } 840 case NPF_CMD_RULE_REMKEY: { 841 prop_object_t obj = prop_dictionary_get(npf_rule, "key"); 842 const void *key = prop_data_data_nocopy(obj); 843 size_t len = prop_data_size(obj); 844 845 if (len == 0 || len > NPF_RULE_MAXKEYLEN) { 846 error = EINVAL; 847 break; 848 } 849 error = npf_ruleset_remkey(rlset, ruleset_name, key, len); 850 break; 851 } 852 case NPF_CMD_RULE_LIST: { 853 retdict = npf_ruleset_list(npf, rlset, ruleset_name); 854 if (!retdict) { 855 error = ESRCH; 856 } 857 break; 858 } 859 case NPF_CMD_RULE_FLUSH: { 860 error = npf_ruleset_flush(rlset, ruleset_name); 861 break; 862 } 863 default: 864 error = EINVAL; 865 break; 866 } 867 868 /* Destroy any removed rules. */ 869 if (!error && rcmd != NPF_CMD_RULE_ADD && rcmd != NPF_CMD_RULE_LIST) { 870 npf_config_sync(npf); 871 npf_ruleset_gc(rlset); 872 } 873 npf_config_exit(npf); 874 875 if (rl) { 876 KASSERT(error); 877 npf_rule_free(rl); 878 } 879 out: 880 if (retdict) { 881 prop_object_release(npf_rule); 882 #if !defined(_NPF_STANDALONE) 883 prop_dictionary_copyout_ioctl(pref, cmd, retdict); 884 prop_object_release(retdict); 885 #else 886 CTASSERT(sizeof(prop_dictionary_t) == sizeof(void *)); 887 memcpy(data, npf_rule, sizeof(void *)); 888 #endif 889 } 890 return error; 891 } 892 893 /* 894 * npfctl_table: add, remove or query entries in the specified table. 895 * 896 * For maximum performance, interface is avoiding proplib(3)'s overhead. 897 */ 898 int 899 npfctl_table(npf_t *npf, void *data) 900 { 901 const npf_ioctl_table_t *nct = data; 902 char tname[NPF_TABLE_MAXNAMELEN]; 903 npf_tableset_t *ts; 904 npf_table_t *t; 905 int s, error; 906 907 error = copyinstr(nct->nct_name, tname, sizeof(tname), NULL); 908 if (error) { 909 return error; 910 } 911 912 s = npf_config_read_enter(); /* XXX */ 913 ts = npf_config_tableset(npf); 914 if ((t = npf_tableset_getbyname(ts, tname)) == NULL) { 915 npf_config_read_exit(s); 916 return EINVAL; 917 } 918 919 switch (nct->nct_cmd) { 920 case NPF_CMD_TABLE_LOOKUP: 921 error = npf_table_lookup(t, nct->nct_data.ent.alen, 922 &nct->nct_data.ent.addr); 923 break; 924 case NPF_CMD_TABLE_ADD: 925 error = npf_table_insert(t, nct->nct_data.ent.alen, 926 &nct->nct_data.ent.addr, nct->nct_data.ent.mask); 927 break; 928 case NPF_CMD_TABLE_REMOVE: 929 error = npf_table_remove(t, nct->nct_data.ent.alen, 930 &nct->nct_data.ent.addr, nct->nct_data.ent.mask); 931 break; 932 case NPF_CMD_TABLE_LIST: 933 error = npf_table_list(t, nct->nct_data.buf.buf, 934 nct->nct_data.buf.len); 935 break; 936 case NPF_CMD_TABLE_FLUSH: 937 error = npf_table_flush(t); 938 break; 939 default: 940 error = EINVAL; 941 break; 942 } 943 npf_config_read_exit(s); 944 945 return error; 946 } 947