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