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