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