1 /* $NetBSD: npf_ruleset.c,v 1.6 2011/01/18 20:33:46 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 ruleset module. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.6 2011/01/18 20:33:46 rmind Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/kernel.h> 41 42 #include <sys/atomic.h> 43 #include <sys/kmem.h> 44 #include <sys/pool.h> 45 #include <sys/queue.h> 46 #include <sys/types.h> 47 48 #include <net/pfil.h> 49 #include <net/if.h> 50 51 #include "npf_ncode.h" 52 #include "npf_impl.h" 53 54 /* Ruleset structre (queue and default rule). */ 55 struct npf_ruleset { 56 TAILQ_HEAD(, npf_rule) rs_queue; 57 npf_rule_t * rs_default; 58 }; 59 60 /* Rule hook entry. */ 61 struct npf_hook { 62 void (*hk_fn)(npf_cache_t *, nbuf_t *, void *); 63 void * hk_arg; 64 LIST_ENTRY(npf_hook) hk_entry; 65 }; 66 67 /* Rule procedure structure. */ 68 struct npf_rproc { 69 /* Reference count. */ 70 u_int rp_refcnt; 71 uint32_t rp_flags; 72 /* Normalization options. */ 73 bool rp_rnd_ipid; 74 bool rp_no_df; 75 u_int rp_minttl; 76 u_int rp_maxmss; 77 /* Logging interface. */ 78 u_int rp_log_ifid; 79 }; 80 81 /* Rule structure. */ 82 struct npf_rule { 83 TAILQ_ENTRY(npf_rule) r_entry; 84 /* Optional: sub-ruleset, NAT policy. */ 85 npf_ruleset_t r_subset; 86 npf_natpolicy_t * r_natp; 87 /* Rule priority: (highest) 0, 1, 2 ... n (lowest). */ 88 u_int r_priority; 89 /* N-code to process. */ 90 void * r_ncode; 91 size_t r_nc_size; 92 /* Attributes of this rule. */ 93 uint32_t r_attr; 94 /* Interface. */ 95 u_int r_ifid; 96 /* Rule procedure data. */ 97 npf_rproc_t * r_rproc; 98 /* List of hooks to process on match. */ 99 kmutex_t r_hooks_lock; 100 LIST_HEAD(, npf_hook) r_hooks; 101 }; 102 103 npf_ruleset_t * 104 npf_ruleset_create(void) 105 { 106 npf_ruleset_t *rlset; 107 108 rlset = kmem_zalloc(sizeof(npf_ruleset_t), KM_SLEEP); 109 TAILQ_INIT(&rlset->rs_queue); 110 return rlset; 111 } 112 113 void 114 npf_ruleset_destroy(npf_ruleset_t *rlset) 115 { 116 npf_rule_t *rl; 117 118 while ((rl = TAILQ_FIRST(&rlset->rs_queue)) != NULL) { 119 TAILQ_REMOVE(&rlset->rs_queue, rl, r_entry); 120 npf_rule_free(rl); 121 } 122 kmem_free(rlset, sizeof(npf_ruleset_t)); 123 } 124 125 /* 126 * npf_ruleset_insert: insert the rule into the specified ruleset. 127 * 128 * Note: multiple rules at the same priority are allowed. 129 */ 130 void 131 npf_ruleset_insert(npf_ruleset_t *rlset, npf_rule_t *rl) 132 { 133 npf_rule_t *it; 134 135 if (rl->r_attr & NPF_RULE_DEFAULT) { 136 rlset->rs_default = rl; 137 return; 138 } 139 TAILQ_FOREACH(it, &rlset->rs_queue, r_entry) { 140 /* Rule priority: (highest) 0, 1, 2, 4 ... n (lowest). */ 141 if (it->r_priority > rl->r_priority) 142 break; 143 } 144 if (it == NULL) { 145 TAILQ_INSERT_TAIL(&rlset->rs_queue, rl, r_entry); 146 } else { 147 TAILQ_INSERT_BEFORE(it, rl, r_entry); 148 } 149 } 150 151 /* 152 * npf_ruleset_matchnat: find a matching NAT policy in the ruleset. 153 */ 154 npf_rule_t * 155 npf_ruleset_matchnat(npf_ruleset_t *rlset, npf_natpolicy_t *mnp) 156 { 157 npf_rule_t *rl; 158 159 /* Find a matching NAT policy in the old ruleset. */ 160 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) { 161 if (npf_nat_matchpolicy(rl->r_natp, mnp)) 162 break; 163 } 164 return rl; 165 } 166 167 npf_rule_t * 168 npf_ruleset_sharepm(npf_ruleset_t *rlset, npf_natpolicy_t *mnp) 169 { 170 npf_natpolicy_t *np; 171 npf_rule_t *rl; 172 173 /* Find a matching NAT policy in the old ruleset. */ 174 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) { 175 /* 176 * NAT policy might not yet be set during the creation of 177 * the ruleset (in such case, rule is for our policy), or 178 * policies might be equal due to rule exchange on reload. 179 */ 180 np = rl->r_natp; 181 if (np == NULL || np == mnp) 182 continue; 183 if (npf_nat_sharepm(np, mnp)) 184 break; 185 } 186 return rl; 187 } 188 189 /* 190 * npf_ruleset_natreload: minimum reload of NAT policies by maching 191 * two (active and new) NAT rulesets. 192 * 193 * => Active ruleset should be exclusively locked. 194 */ 195 void 196 npf_ruleset_natreload(npf_ruleset_t *nrlset, npf_ruleset_t *arlset) 197 { 198 npf_natpolicy_t *np, *anp; 199 npf_rule_t *rl, *arl; 200 201 KASSERT(npf_core_locked()); 202 203 /* Scan a new NAT ruleset against NAT policies in old ruleset. */ 204 TAILQ_FOREACH(rl, &nrlset->rs_queue, r_entry) { 205 np = rl->r_natp; 206 arl = npf_ruleset_matchnat(arlset, np); 207 if (arl == NULL) { 208 continue; 209 } 210 /* On match - we exchange NAT policies. */ 211 anp = arl->r_natp; 212 rl->r_natp = anp; 213 arl->r_natp = np; 214 /* Update other NAT policies to share portmap. */ 215 (void)npf_ruleset_sharepm(nrlset, anp); 216 } 217 } 218 219 npf_rproc_t * 220 npf_rproc_create(prop_dictionary_t rpdict) 221 { 222 npf_rproc_t *rp; 223 prop_object_t obj; 224 225 rp = kmem_alloc(sizeof(npf_rproc_t), KM_SLEEP); 226 rp->rp_refcnt = 1; 227 228 /* Flags. */ 229 obj = prop_dictionary_get(rpdict, "flags"); 230 rp->rp_flags = prop_number_integer_value(obj); 231 232 /* Logging interface ID (integer). */ 233 obj = prop_dictionary_get(rpdict, "log-interface"); 234 rp->rp_log_ifid = prop_number_integer_value(obj); 235 236 /* Randomize IP ID (bool). */ 237 obj = prop_dictionary_get(rpdict, "randomize-id"); 238 rp->rp_rnd_ipid = prop_bool_true(obj); 239 240 /* IP_DF flag cleansing (bool). */ 241 obj = prop_dictionary_get(rpdict, "no-df"); 242 rp->rp_no_df = prop_bool_true(obj); 243 244 /* Minimum IP TTL (integer). */ 245 obj = prop_dictionary_get(rpdict, "min-ttl"); 246 rp->rp_minttl = prop_number_integer_value(obj); 247 248 /* Maximum TCP MSS (integer). */ 249 obj = prop_dictionary_get(rpdict, "max-mss"); 250 rp->rp_maxmss = prop_number_integer_value(obj); 251 252 return rp; 253 } 254 255 npf_rproc_t * 256 npf_rproc_return(npf_rule_t *rl) 257 { 258 npf_rproc_t *rp = rl->r_rproc; 259 260 KASSERT(npf_core_locked()); 261 if (rp) { 262 atomic_inc_uint(&rp->rp_refcnt); 263 } 264 return rp; 265 } 266 267 void 268 npf_rproc_release(npf_rproc_t *rp) 269 { 270 271 /* Destroy on last reference. */ 272 if (atomic_dec_uint_nv(&rp->rp_refcnt) != 0) { 273 return; 274 } 275 kmem_free(rp, sizeof(npf_rproc_t)); 276 } 277 278 void 279 npf_rproc_run(npf_cache_t *npc, nbuf_t *nbuf, npf_rproc_t *rp) 280 { 281 const uint32_t flags = rp->rp_flags; 282 283 KASSERT(rp->rp_refcnt > 0); 284 285 /* Normalize the packet, if required. */ 286 if (flags & NPF_RPROC_NORMALIZE) { 287 (void)npf_normalize(npc, nbuf, 288 rp->rp_rnd_ipid, rp->rp_no_df, 289 rp->rp_minttl, rp->rp_maxmss); 290 npf_stats_inc(NPF_STAT_RPROC_NORM); 291 } 292 293 /* Log packet, if required. */ 294 if (flags & NPF_RPROC_LOG) { 295 npf_log_packet(npc, nbuf, rp->rp_log_ifid); 296 npf_stats_inc(NPF_STAT_RPROC_LOG); 297 } 298 } 299 300 /* 301 * npf_rule_alloc: allocate a rule and copy n-code from user-space. 302 * 303 * => N-code should be validated by the caller. 304 */ 305 npf_rule_t * 306 npf_rule_alloc(prop_dictionary_t rldict, npf_rproc_t *rp, 307 void *nc, size_t nc_size) 308 { 309 npf_rule_t *rl; 310 prop_object_t obj; 311 int errat; 312 313 /* Allocate a rule structure. */ 314 rl = kmem_alloc(sizeof(npf_rule_t), KM_SLEEP); 315 TAILQ_INIT(&rl->r_subset.rs_queue); 316 mutex_init(&rl->r_hooks_lock, MUTEX_DEFAULT, IPL_SOFTNET); 317 LIST_INIT(&rl->r_hooks); 318 rl->r_natp = NULL; 319 320 /* N-code. */ 321 (void)errat; 322 KASSERT(nc == NULL || npf_ncode_validate(nc, nc_size, &errat) == 0); 323 rl->r_ncode = nc; 324 rl->r_nc_size = nc_size; 325 326 /* Attributes (integer). */ 327 obj = prop_dictionary_get(rldict, "attributes"); 328 rl->r_attr = prop_number_integer_value(obj); 329 330 /* Priority (integer). */ 331 obj = prop_dictionary_get(rldict, "priority"); 332 rl->r_priority = prop_number_integer_value(obj); 333 334 /* Interface ID (integer). */ 335 obj = prop_dictionary_get(rldict, "interface"); 336 rl->r_ifid = prop_number_integer_value(obj); 337 338 /* Rule procedure. */ 339 if (rp) { 340 atomic_inc_uint(&rp->rp_refcnt); 341 } 342 rl->r_rproc = rp; 343 344 return rl; 345 } 346 347 /* 348 * npf_rule_free: free the specified rule. 349 */ 350 void 351 npf_rule_free(npf_rule_t *rl) 352 { 353 npf_natpolicy_t *np = rl->r_natp; 354 npf_rproc_t *rp = rl->r_rproc; 355 356 if (np) { 357 /* Free NAT policy. */ 358 npf_nat_freepolicy(np); 359 } 360 if (rp) { 361 /* Release rule procedure. */ 362 npf_rproc_release(rp); 363 } 364 if (rl->r_ncode) { 365 /* Free n-code. */ 366 npf_ncode_free(rl->r_ncode, rl->r_nc_size); 367 } 368 mutex_destroy(&rl->r_hooks_lock); 369 kmem_free(rl, sizeof(npf_rule_t)); 370 } 371 372 /* 373 * npf_rule_subset: return sub-ruleset, if any. 374 * npf_rule_getnat: get NAT policy assigned to the rule. 375 */ 376 377 npf_ruleset_t * 378 npf_rule_subset(npf_rule_t *rl) 379 { 380 return &rl->r_subset; 381 } 382 383 npf_natpolicy_t * 384 npf_rule_getnat(const npf_rule_t *rl) 385 { 386 return rl->r_natp; 387 } 388 389 /* 390 * npf_rule_setnat: assign NAT policy to the rule and insert into the 391 * NAT policy list in the ruleset. 392 */ 393 void 394 npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np) 395 { 396 397 KASSERT(rl->r_natp == NULL); 398 rl->r_natp = np; 399 } 400 401 /* 402 * npf_hook_register: register action hook in the rule. 403 */ 404 npf_hook_t * 405 npf_hook_register(npf_rule_t *rl, 406 void (*fn)(npf_cache_t *, nbuf_t *, void *), void *arg) 407 { 408 npf_hook_t *hk; 409 410 hk = kmem_alloc(sizeof(npf_hook_t), KM_SLEEP); 411 if (hk != NULL) { 412 hk->hk_fn = fn; 413 hk->hk_arg = arg; 414 mutex_enter(&rl->r_hooks_lock); 415 LIST_INSERT_HEAD(&rl->r_hooks, hk, hk_entry); 416 mutex_exit(&rl->r_hooks_lock); 417 } 418 return hk; 419 } 420 421 /* 422 * npf_hook_unregister: unregister a specified hook. 423 * 424 * => Hook should have been registered in the rule. 425 */ 426 void 427 npf_hook_unregister(npf_rule_t *rl, npf_hook_t *hk) 428 { 429 430 mutex_enter(&rl->r_hooks_lock); 431 LIST_REMOVE(hk, hk_entry); 432 mutex_exit(&rl->r_hooks_lock); 433 kmem_free(hk, sizeof(npf_hook_t)); 434 } 435 436 /* 437 * npf_ruleset_match: inspect the packet against the given ruleset. 438 * 439 * Loop for each rule in the set and run n-code processor of each rule 440 * against the packet (nbuf chain). 441 */ 442 npf_rule_t * 443 npf_ruleset_match(npf_ruleset_t *rlset, npf_cache_t *npc, nbuf_t *nbuf, 444 ifnet_t *ifp, const int di, const int layer) 445 { 446 npf_rule_t *final_rl = NULL, *rl; 447 448 KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0)); 449 450 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) { 451 KASSERT(!final_rl || rl->r_priority >= final_rl->r_priority); 452 453 /* Match the interface. */ 454 if (rl->r_ifid && rl->r_ifid != ifp->if_index) { 455 continue; 456 } 457 /* Match the direction. */ 458 if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) { 459 const int di_mask = 460 (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT; 461 462 if ((rl->r_attr & di_mask) == 0) 463 continue; 464 } 465 /* Process the n-code, if any. */ 466 const void *nc = rl->r_ncode; 467 if (nc && npf_ncode_process(npc, nc, nbuf, layer)) { 468 continue; 469 } 470 /* Set the matching rule and check for "final". */ 471 final_rl = rl; 472 if (rl->r_attr & NPF_RULE_FINAL) { 473 break; 474 } 475 } 476 return final_rl; 477 } 478 479 /* 480 * npf_ruleset_inspect: inspection of the main ruleset for filtering. 481 * If sub-ruleset is found, inspect it. 482 * 483 * => If found, ruleset is kept read-locked. 484 * => Caller should protect the nbuf chain. 485 */ 486 npf_rule_t * 487 npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf, 488 ifnet_t *ifp, const int di, const int layer) 489 { 490 npf_ruleset_t *rlset; 491 npf_rule_t *rl; 492 bool defed; 493 494 defed = false; 495 npf_core_enter(); 496 rlset = npf_core_ruleset(); 497 reinspect: 498 rl = npf_ruleset_match(rlset, npc, nbuf, ifp, di, layer); 499 500 /* If no final rule, then - default. */ 501 if (rl == NULL && !defed) { 502 npf_ruleset_t *mainrlset = npf_core_ruleset(); 503 rl = mainrlset->rs_default; 504 defed = true; 505 } 506 /* Inspect the sub-ruleset, if any. */ 507 if (rl && !TAILQ_EMPTY(&rl->r_subset.rs_queue)) { 508 rlset = &rl->r_subset; 509 goto reinspect; 510 } 511 if (rl == NULL) { 512 npf_core_exit(); 513 } 514 return rl; 515 } 516 517 /* 518 * npf_rule_apply: apply the rule i.e. run hooks and return appropriate value. 519 * 520 * => Returns ENETUNREACH if "block" and 0 if "pass". 521 * => Releases the ruleset lock. 522 */ 523 int 524 npf_rule_apply(npf_cache_t *npc, nbuf_t *nbuf, npf_rule_t *rl, int *retfl) 525 { 526 npf_hook_t *hk; 527 int error; 528 529 KASSERT(npf_core_locked()); 530 531 /* If not passing - drop the packet. */ 532 if ((rl->r_attr & NPF_RULE_PASS) == 0) { 533 error = ENETUNREACH; 534 goto done; 535 } 536 error = 0; 537 538 /* Passing. Run the hooks. */ 539 LIST_FOREACH(hk, &rl->r_hooks, hk_entry) { 540 KASSERT(hk->hk_fn != NULL); 541 (*hk->hk_fn)(npc, nbuf, hk->hk_arg); 542 } 543 done: 544 *retfl = rl->r_attr; 545 npf_core_exit(); 546 return error; 547 } 548 549 #if defined(DDB) || defined(_NPF_TESTING) 550 551 void 552 npf_rulenc_dump(npf_rule_t *rl) 553 { 554 uint32_t *op = rl->r_ncode; 555 size_t n = rl->r_nc_size; 556 557 while (n) { 558 printf("\t> |0x%02x|\n", (uint32_t)*op); 559 op++; 560 n -= sizeof(*op); 561 } 562 printf("-> %s\n", (rl->r_attr & NPF_RULE_PASS) ? "pass" : "block"); 563 } 564 565 #endif 566