1 /* $NetBSD: npf_ctl.c,v 1.14 2012/03/11 18:27:59 rmind Exp $ */ 2 3 /*- 4 * Copyright (c) 2009-2012 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.14 2012/03/11 18:27:59 rmind Exp $"); 41 42 #include <sys/param.h> 43 #include <sys/conf.h> 44 45 #include <prop/proplib.h> 46 47 #include "npf_ncode.h" 48 #include "npf_impl.h" 49 50 #if defined(DEBUG) || defined(DIAGNOSTIC) 51 #define NPF_ERR_DEBUG(e) \ 52 prop_dictionary_set_cstring_nocopy((e), "source-file", __FILE__); \ 53 prop_dictionary_set_uint32((e), "source-line", __LINE__); 54 #else 55 #define NPF_ERR_DEBUG(e) 56 #endif 57 58 /* 59 * npfctl_switch: enable or disable packet inspection. 60 */ 61 int 62 npfctl_switch(void *data) 63 { 64 const bool onoff = *(int *)data ? true : false; 65 int error; 66 67 if (onoff) { 68 /* Enable: add pfil hooks. */ 69 error = npf_pfil_register(); 70 } else { 71 /* Disable: remove pfil hooks. */ 72 npf_pfil_unregister(); 73 error = 0; 74 } 75 return error; 76 } 77 78 static int __noinline 79 npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables, 80 prop_dictionary_t errdict) 81 { 82 prop_object_iterator_t it; 83 prop_dictionary_t tbldict; 84 int error = 0; 85 86 /* Tables - array. */ 87 if (prop_object_type(tables) != PROP_TYPE_ARRAY) { 88 NPF_ERR_DEBUG(errdict); 89 return EINVAL; 90 } 91 92 it = prop_array_iterator(tables); 93 while ((tbldict = prop_object_iterator_next(it)) != NULL) { 94 prop_dictionary_t ent; 95 prop_object_iterator_t eit; 96 prop_array_t entries; 97 npf_table_t *t; 98 u_int tid; 99 int type; 100 101 /* Table - dictionary. */ 102 if (prop_object_type(tbldict) != PROP_TYPE_DICTIONARY) { 103 NPF_ERR_DEBUG(errdict); 104 error = EINVAL; 105 break; 106 } 107 108 /* Table ID and type. */ 109 prop_dictionary_get_uint32(tbldict, "id", &tid); 110 prop_dictionary_get_int32(tbldict, "type", &type); 111 112 /* Validate them, check for duplicate IDs. */ 113 error = npf_table_check(tblset, tid, type); 114 if (error) 115 break; 116 117 /* Create and insert the table. */ 118 t = npf_table_create(tid, type, 1024); /* XXX */ 119 if (t == NULL) { 120 NPF_ERR_DEBUG(errdict); 121 error = ENOMEM; 122 break; 123 } 124 error = npf_tableset_insert(tblset, t); 125 KASSERT(error == 0); 126 127 /* Entries. */ 128 entries = prop_dictionary_get(tbldict, "entries"); 129 if (prop_object_type(entries) != PROP_TYPE_ARRAY) { 130 NPF_ERR_DEBUG(errdict); 131 error = EINVAL; 132 break; 133 } 134 eit = prop_array_iterator(entries); 135 while ((ent = prop_object_iterator_next(eit)) != NULL) { 136 const npf_addr_t *addr; 137 npf_netmask_t mask; 138 139 /* Get address and mask. Add a table entry. */ 140 addr = (const npf_addr_t *)prop_data_data_nocopy( 141 prop_dictionary_get(ent, "addr")); 142 prop_dictionary_get_uint8(ent, "mask", &mask); 143 error = npf_table_add_cidr(tblset, tid, addr, mask); 144 if (error) 145 break; 146 } 147 prop_object_iterator_release(eit); 148 if (error) 149 break; 150 } 151 prop_object_iterator_release(it); 152 /* 153 * Note: in a case of error, caller will free the tableset. 154 */ 155 return error; 156 } 157 158 static npf_rproc_t * 159 npf_mk_rproc(prop_array_t rprocs, const char *rpname) 160 { 161 prop_object_iterator_t it; 162 prop_dictionary_t rpdict; 163 npf_rproc_t *rp; 164 165 it = prop_array_iterator(rprocs); 166 while ((rpdict = prop_object_iterator_next(it)) != NULL) { 167 const char *iname; 168 prop_dictionary_get_cstring_nocopy(rpdict, "name", &iname); 169 KASSERT(iname != NULL); 170 if (strcmp(rpname, iname) == 0) 171 break; 172 } 173 prop_object_iterator_release(it); 174 if (rpdict == NULL) { 175 return NULL; 176 } 177 CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t)); 178 if (!prop_dictionary_get_uint64(rpdict, "rproc-ptr", (uint64_t *)&rp)) { 179 rp = npf_rproc_create(rpdict); 180 prop_dictionary_set_uint64(rpdict, "rproc-ptr", 181 (uint64_t)(uintptr_t)rp); 182 } 183 return rp; 184 } 185 186 static int __noinline 187 npf_mk_ncode(prop_object_t obj, void **code, size_t *csize, 188 prop_dictionary_t errdict) 189 { 190 const void *ncptr; 191 int nc_err, errat; 192 size_t nc_size; 193 void *nc; 194 195 /* 196 * Allocate, copy and validate n-code. XXX: Inefficient. 197 */ 198 ncptr = prop_data_data_nocopy(obj); 199 nc_size = prop_data_size(obj); 200 if (ncptr == NULL || nc_size > NPF_NCODE_LIMIT) { 201 NPF_ERR_DEBUG(errdict); 202 return ERANGE; 203 } 204 nc = npf_ncode_alloc(nc_size); 205 if (nc == NULL) { 206 NPF_ERR_DEBUG(errdict); 207 return ENOMEM; 208 } 209 memcpy(nc, ncptr, nc_size); 210 nc_err = npf_ncode_validate(nc, nc_size, &errat); 211 if (nc_err) { 212 npf_ncode_free(nc, nc_size); 213 prop_dictionary_set_int32(errdict, "ncode-error", nc_err); 214 prop_dictionary_set_int32(errdict, "ncode-errat", errat); 215 return EINVAL; 216 } 217 *code = nc; 218 *csize = nc_size; 219 return 0; 220 } 221 222 static int __noinline 223 npf_mk_singlerule(prop_dictionary_t rldict, prop_array_t rps, npf_rule_t **rl, 224 prop_dictionary_t errdict) 225 { 226 const char *rnm; 227 npf_rproc_t *rp; 228 prop_object_t obj; 229 size_t nc_size; 230 void *nc; 231 int p, error; 232 233 /* Rule - dictionary. */ 234 if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY) { 235 NPF_ERR_DEBUG(errdict); 236 return EINVAL; 237 } 238 239 error = 0; 240 obj = prop_dictionary_get(rldict, "ncode"); 241 if (obj) { 242 /* N-code (binary data). */ 243 error = npf_mk_ncode(obj, &nc, &nc_size, errdict); 244 if (error) { 245 goto err; 246 } 247 } else { 248 /* No n-code. */ 249 nc = NULL; 250 nc_size = 0; 251 } 252 253 /* Check for rule procedure. */ 254 if (rps && prop_dictionary_get_cstring_nocopy(rldict, "rproc", &rnm)) { 255 rp = npf_mk_rproc(rps, rnm); 256 if (rp == NULL) { 257 if (nc) { 258 npf_ncode_free(nc, nc_size); /* XXX */ 259 } 260 NPF_ERR_DEBUG(errdict); 261 error = EINVAL; 262 goto err; 263 } 264 } else { 265 rp = NULL; 266 } 267 268 /* Finally, allocate and return the rule. */ 269 *rl = npf_rule_alloc(rldict, rp, nc, nc_size); 270 KASSERT(*rl != NULL); 271 return 0; 272 err: 273 prop_dictionary_get_int32(rldict, "priority", &p); /* XXX */ 274 prop_dictionary_set_int32(errdict, "id", p); 275 return error; 276 } 277 278 static int __noinline 279 npf_mk_subrules(npf_ruleset_t *rlset, prop_array_t rules, prop_array_t rprocs, 280 prop_dictionary_t errdict) 281 { 282 prop_object_iterator_t it; 283 prop_dictionary_t rldict; 284 int error = 0; 285 286 if (prop_object_type(rules) != PROP_TYPE_ARRAY) { 287 NPF_ERR_DEBUG(errdict); 288 return EINVAL; 289 } 290 it = prop_array_iterator(rules); 291 while ((rldict = prop_object_iterator_next(it)) != NULL) { 292 npf_rule_t *rl; 293 error = npf_mk_singlerule(rldict, rprocs, &rl, errdict); 294 if (error) { 295 break; 296 } 297 npf_ruleset_insert(rlset, rl); 298 } 299 prop_object_iterator_release(it); 300 return error; 301 } 302 303 static int __noinline 304 npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules, prop_array_t rprocs, 305 prop_dictionary_t errdict) 306 { 307 prop_object_iterator_t it; 308 prop_dictionary_t rldict, rpdict; 309 int error; 310 311 /* Rule procedures and the ruleset - arrays. */ 312 if (prop_object_type(rprocs) != PROP_TYPE_ARRAY || 313 prop_object_type(rules) != PROP_TYPE_ARRAY) { 314 NPF_ERR_DEBUG(errdict); 315 return EINVAL; 316 } 317 318 it = prop_array_iterator(rprocs); 319 while ((rpdict = prop_object_iterator_next(it)) != NULL) { 320 if (prop_dictionary_get(rpdict, "rproc-ptr")) { 321 prop_object_iterator_release(it); 322 NPF_ERR_DEBUG(errdict); 323 return EINVAL; 324 } 325 } 326 prop_object_iterator_release(it); 327 328 error = 0; 329 it = prop_array_iterator(rules); 330 while ((rldict = prop_object_iterator_next(it)) != NULL) { 331 prop_array_t subrules; 332 npf_ruleset_t *rlsetsub; 333 npf_rule_t *rl; 334 335 /* Generate a single rule. */ 336 error = npf_mk_singlerule(rldict, rprocs, &rl, errdict); 337 if (error) { 338 break; 339 } 340 npf_ruleset_insert(rlset, rl); 341 342 /* Check for sub-rules and generate, if any. */ 343 subrules = prop_dictionary_get(rldict, "subrules"); 344 if (subrules == NULL) { 345 /* No subrules, next.. */ 346 continue; 347 } 348 rlsetsub = npf_rule_subset(rl); 349 error = npf_mk_subrules(rlsetsub, subrules, rprocs, errdict); 350 if (error) 351 break; 352 } 353 prop_object_iterator_release(it); 354 /* 355 * Note: in a case of error, caller will free the ruleset. 356 */ 357 return error; 358 } 359 360 static int __noinline 361 npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist, 362 prop_dictionary_t errdict) 363 { 364 prop_object_iterator_t it; 365 prop_dictionary_t natdict; 366 int error; 367 368 /* NAT policies - array. */ 369 if (prop_object_type(natlist) != PROP_TYPE_ARRAY) { 370 NPF_ERR_DEBUG(errdict); 371 return EINVAL; 372 } 373 374 error = 0; 375 it = prop_array_iterator(natlist); 376 while ((natdict = prop_object_iterator_next(it)) != NULL) { 377 npf_natpolicy_t *np; 378 npf_rule_t *rl; 379 380 /* NAT policy - dictionary. */ 381 if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) { 382 NPF_ERR_DEBUG(errdict); 383 error = EINVAL; 384 break; 385 } 386 387 /* 388 * NAT policies are standard rules, plus additional 389 * information for translation. Make a rule. 390 */ 391 error = npf_mk_singlerule(natdict, NULL, &rl, errdict); 392 if (error) { 393 break; 394 } 395 npf_ruleset_insert(nset, rl); 396 397 /* If rule is named, it is a group with NAT policies. */ 398 if (prop_dictionary_get(natdict, "name") && 399 prop_dictionary_get(natdict, "subrules")) { 400 continue; 401 } 402 403 /* Allocate a new NAT policy and assign to the rule. */ 404 np = npf_nat_newpolicy(natdict, nset); 405 if (np == NULL) { 406 NPF_ERR_DEBUG(errdict); 407 error = ENOMEM; 408 break; 409 } 410 npf_rule_setnat(rl, np); 411 } 412 prop_object_iterator_release(it); 413 /* 414 * Note: in a case of error, caller will free entire NAT ruleset 415 * with assigned NAT policies. 416 */ 417 return error; 418 } 419 420 /* 421 * npfctl_reload: store passed data i.e. update settings, create passed 422 * tables, rules and atomically activate all them. 423 */ 424 int 425 npfctl_reload(u_long cmd, void *data) 426 { 427 struct plistref *pref = data; 428 prop_dictionary_t npf_dict, errdict; 429 prop_array_t natlist, tables, rprocs, rules; 430 npf_tableset_t *tblset = NULL; 431 npf_ruleset_t *rlset = NULL; 432 npf_ruleset_t *nset = NULL; 433 bool flush; 434 int error; 435 436 /* Retrieve the dictionary. */ 437 #ifdef _KERNEL 438 error = prop_dictionary_copyin_ioctl(pref, cmd, &npf_dict); 439 if (error) 440 return error; 441 #else 442 npf_dict = prop_dictionary_internalize_from_file(data); 443 if (npf_dict == NULL) 444 return EINVAL; 445 #endif 446 /* Dictionary for error reporting. */ 447 errdict = prop_dictionary_create(); 448 449 /* NAT policies. */ 450 nset = npf_ruleset_create(); 451 natlist = prop_dictionary_get(npf_dict, "translation"); 452 error = npf_mk_natlist(nset, natlist, errdict); 453 if (error) { 454 goto fail; 455 } 456 457 /* Tables. */ 458 tblset = npf_tableset_create(); 459 tables = prop_dictionary_get(npf_dict, "tables"); 460 error = npf_mk_tables(tblset, tables, errdict); 461 if (error) { 462 goto fail; 463 } 464 465 /* Rules and rule procedures. */ 466 rlset = npf_ruleset_create(); 467 rprocs = prop_dictionary_get(npf_dict, "rprocs"); 468 rules = prop_dictionary_get(npf_dict, "rules"); 469 error = npf_mk_rules(rlset, rules, rprocs, errdict); 470 if (error) { 471 goto fail; 472 } 473 474 flush = false; 475 prop_dictionary_get_bool(npf_dict, "flush", &flush); 476 477 /* 478 * Finally - reload ruleset, tableset and NAT policies. 479 * Operation will be performed as a single transaction. 480 */ 481 npf_reload(npf_dict, rlset, tblset, nset, flush); 482 483 /* Turn on/off session tracking accordingly. */ 484 npf_session_tracking(!flush); 485 486 /* Done. Since data is consumed now, we shall not destroy it. */ 487 tblset = NULL; 488 rlset = NULL; 489 nset = NULL; 490 fail: 491 /* 492 * Note: destroy rulesets first, to drop references to the tableset. 493 */ 494 KASSERT(error == 0 || (nset || rlset || tblset)); 495 if (nset) { 496 npf_ruleset_destroy(nset); 497 } 498 if (rlset) { 499 npf_ruleset_destroy(rlset); 500 } 501 if (tblset) { 502 npf_tableset_destroy(tblset); 503 } 504 if (error) { 505 prop_object_release(npf_dict); 506 } 507 508 /* Error report. */ 509 prop_dictionary_set_int32(errdict, "errno", error); 510 #ifdef _KERNEL 511 prop_dictionary_copyout_ioctl(pref, cmd, errdict); 512 #endif 513 prop_object_release(errdict); 514 return 0; 515 } 516 517 int 518 npfctl_getconf(u_long cmd, void *data) 519 { 520 struct plistref *pref = data; 521 prop_dictionary_t npf_dict; 522 int error; 523 524 npf_core_enter(); 525 npf_dict = npf_core_dict(); 526 prop_dictionary_set_bool(npf_dict, "active", npf_pfil_registered_p()); 527 error = prop_dictionary_copyout_ioctl(pref, cmd, npf_dict); 528 npf_core_exit(); 529 530 return error; 531 } 532 533 /* 534 * npfctl_update_rule: reload a specific rule identified by the name. 535 */ 536 int 537 npfctl_update_rule(u_long cmd, void *data) 538 { 539 struct plistref *pref = data; 540 prop_dictionary_t dict, errdict; 541 prop_array_t subrules; 542 prop_object_t obj; 543 npf_ruleset_t *rlset; 544 const char *name; 545 int error; 546 547 #ifdef _KERNEL 548 /* Retrieve and construct the rule. */ 549 error = prop_dictionary_copyin_ioctl(pref, cmd, &dict); 550 if (error) { 551 return error; 552 } 553 #else 554 dict = prop_dictionary_internalize_from_file(data); 555 if (dict == NULL) 556 return EINVAL; 557 #endif 558 559 /* Dictionary for error reporting. */ 560 errdict = prop_dictionary_create(); 561 562 /* Create the ruleset and construct sub-rules. */ 563 rlset = npf_ruleset_create(); 564 subrules = prop_dictionary_get(dict, "subrules"); 565 error = npf_mk_subrules(rlset, subrules, NULL, errdict); 566 if (error) { 567 goto out; 568 } 569 570 /* Lookup the rule by name, and replace its subset (sub-rules). */ 571 obj = prop_dictionary_get(dict, "name"); 572 name = prop_string_cstring_nocopy(obj); 573 if (npf_ruleset_replace(name, rlset) == NULL) { 574 /* Not found. */ 575 error = ENOENT; 576 out: /* Error path. */ 577 npf_ruleset_destroy(rlset); 578 } 579 prop_object_release(dict); 580 581 /* Error report. */ 582 prop_dictionary_set_int32(errdict, "errno", error); 583 #ifdef _KERNEL 584 prop_dictionary_copyout_ioctl(pref, cmd, errdict); 585 #endif 586 prop_object_release(errdict); 587 return error; 588 } 589 590 /* 591 * npfctl_sessions_save: construct a list of sessions and export for saving. 592 */ 593 int 594 npfctl_sessions_save(u_long cmd, void *data) 595 { 596 struct plistref *pref = data; 597 prop_dictionary_t sesdict; 598 prop_array_t selist, nplist; 599 int error; 600 601 /* Create a dictionary and two lists. */ 602 sesdict = prop_dictionary_create(); 603 selist = prop_array_create(); 604 nplist = prop_array_create(); 605 606 /* Save the sessions. */ 607 error = npf_session_save(selist, nplist); 608 if (error) { 609 goto fail; 610 } 611 612 /* Set the session list, NAT policy list and export the dictionary. */ 613 prop_dictionary_set(sesdict, "session-list", selist); 614 prop_dictionary_set(sesdict, "nat-policy-list", nplist); 615 #ifdef _KERNEL 616 error = prop_dictionary_copyout_ioctl(pref, cmd, sesdict); 617 #else 618 error = prop_dictionary_externalize_to_file(sesdict, data) ? 0 : errno; 619 #endif 620 fail: 621 prop_object_release(sesdict); 622 return error; 623 } 624 625 /* 626 * npfctl_sessions_load: import a list of sessions, reconstruct them and load. 627 */ 628 int 629 npfctl_sessions_load(u_long cmd, void *data) 630 { 631 const struct plistref *pref = data; 632 npf_sehash_t *sehasht = NULL; 633 prop_dictionary_t sesdict, sedict; 634 prop_object_iterator_t it; 635 prop_array_t selist; 636 int error; 637 638 /* Retrieve the dictionary containing session and NAT policy lists. */ 639 #ifdef _KERNEL 640 error = prop_dictionary_copyin_ioctl(pref, cmd, &sesdict); 641 if (error) 642 return error; 643 #else 644 sesdict = prop_dictionary_internalize_from_file(data); 645 if (sesdict == NULL) 646 return EINVAL; 647 #endif 648 /* 649 * Note: session objects contain the references to the NAT policy 650 * entries. Therefore, no need to directly access it. 651 */ 652 selist = prop_dictionary_get(sesdict, "session-list"); 653 if (prop_object_type(selist) != PROP_TYPE_ARRAY) { 654 error = EINVAL; 655 goto fail; 656 } 657 658 /* Create a session hash table. */ 659 sehasht = sess_htable_create(); 660 if (sehasht == NULL) { 661 error = ENOMEM; 662 goto fail; 663 } 664 665 /* 666 * Iterate through and construct each session. 667 */ 668 error = 0; 669 it = prop_array_iterator(selist); 670 npf_core_enter(); 671 while ((sedict = prop_object_iterator_next(it)) != NULL) { 672 /* Session - dictionary. */ 673 if (prop_object_type(sedict) != PROP_TYPE_DICTIONARY) { 674 error = EINVAL; 675 goto fail; 676 } 677 /* Construct and insert real session structure. */ 678 error = npf_session_restore(sehasht, sedict); 679 if (error) { 680 goto fail; 681 } 682 } 683 npf_core_exit(); 684 sess_htable_reload(sehasht); 685 fail: 686 prop_object_release(selist); 687 if (error && sehasht) { 688 /* Destroy session table. */ 689 sess_htable_destroy(sehasht); 690 } 691 return error; 692 } 693 694 /* 695 * npfctl_table: add, remove or query entries in the specified table. 696 * 697 * For maximum performance, interface is avoiding proplib(3)'s overhead. 698 */ 699 int 700 npfctl_table(void *data) 701 { 702 npf_ioctl_table_t *nct = data; 703 npf_tableset_t *tblset; 704 int error; 705 706 npf_core_enter(); /* XXXSMP */ 707 tblset = npf_core_tableset(); 708 switch (nct->nct_action) { 709 case NPF_IOCTL_TBLENT_ADD: 710 error = npf_table_add_cidr(tblset, nct->nct_tid, 711 &nct->nct_addr, nct->nct_mask); 712 break; 713 case NPF_IOCTL_TBLENT_REM: 714 error = npf_table_rem_cidr(tblset, nct->nct_tid, 715 &nct->nct_addr, nct->nct_mask); 716 break; 717 default: 718 error = npf_table_match_addr(tblset, nct->nct_tid, 719 &nct->nct_addr); 720 } 721 npf_core_exit(); /* XXXSMP */ 722 return error; 723 } 724