1 /* $NetBSD: npf_ruleset.c,v 1.7 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 ruleset module. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.7 2011/02/02 02:20:25 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; 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 (void)errat; 320 KASSERT(nc == NULL || npf_ncode_validate(nc, nc_size, &errat) == 0); 321 rl->r_ncode = nc; 322 rl->r_nc_size = nc_size; 323 324 /* Name (string, optional) */ 325 if (prop_dictionary_get_cstring_nocopy(rldict, "name", &rname)) { 326 strlcpy(rl->r_name, rname, NPF_RNAME_LEN); 327 } else { 328 rl->r_name[0] = '\0'; 329 } 330 331 /* Attributes, priority and interface ID. */ 332 prop_dictionary_get_uint32(rldict, "attributes", &rl->r_attr); 333 prop_dictionary_get_int32(rldict, "priority", &rl->r_priority); 334 prop_dictionary_get_uint32(rldict, "interface", &rl->r_ifid); 335 336 /* Rule procedure. */ 337 if (rp) { 338 atomic_inc_uint(&rp->rp_refcnt); 339 } 340 rl->r_rproc = rp; 341 342 return rl; 343 } 344 345 /* 346 * npf_rule_free: free the specified rule. 347 */ 348 void 349 npf_rule_free(npf_rule_t *rl) 350 { 351 npf_natpolicy_t *np = rl->r_natp; 352 npf_rproc_t *rp = rl->r_rproc; 353 354 if (np) { 355 /* Free NAT policy. */ 356 npf_nat_freepolicy(np); 357 } 358 if (rp) { 359 /* Release rule procedure. */ 360 npf_rproc_release(rp); 361 } 362 if (rl->r_ncode) { 363 /* Free n-code. */ 364 npf_ncode_free(rl->r_ncode, rl->r_nc_size); 365 } 366 mutex_destroy(&rl->r_hooks_lock); 367 kmem_free(rl, sizeof(npf_rule_t)); 368 } 369 370 /* 371 * npf_rule_subset: return sub-ruleset, if any. 372 * npf_rule_getnat: get NAT policy assigned to the rule. 373 */ 374 375 npf_ruleset_t * 376 npf_rule_subset(npf_rule_t *rl) 377 { 378 return &rl->r_subset; 379 } 380 381 npf_natpolicy_t * 382 npf_rule_getnat(const npf_rule_t *rl) 383 { 384 return rl->r_natp; 385 } 386 387 /* 388 * npf_rule_setnat: assign NAT policy to the rule and insert into the 389 * NAT policy list in the ruleset. 390 */ 391 void 392 npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np) 393 { 394 395 KASSERT(rl->r_natp == NULL); 396 rl->r_natp = np; 397 } 398 399 #if 0 400 /* 401 * npf_hook_register: register action hook in the rule. 402 */ 403 npf_hook_t * 404 npf_hook_register(npf_rule_t *rl, 405 void (*fn)(npf_cache_t *, nbuf_t *, void *), void *arg) 406 { 407 npf_hook_t *hk; 408 409 hk = kmem_alloc(sizeof(npf_hook_t), KM_SLEEP); 410 if (hk != NULL) { 411 hk->hk_fn = fn; 412 hk->hk_arg = arg; 413 mutex_enter(&rl->r_hooks_lock); 414 LIST_INSERT_HEAD(&rl->r_hooks, hk, hk_entry); 415 mutex_exit(&rl->r_hooks_lock); 416 } 417 return hk; 418 } 419 420 /* 421 * npf_hook_unregister: unregister a specified hook. 422 * 423 * => Hook should have been registered in the rule. 424 */ 425 void 426 npf_hook_unregister(npf_rule_t *rl, npf_hook_t *hk) 427 { 428 429 mutex_enter(&rl->r_hooks_lock); 430 LIST_REMOVE(hk, hk_entry); 431 mutex_exit(&rl->r_hooks_lock); 432 kmem_free(hk, sizeof(npf_hook_t)); 433 } 434 #endif 435 436 npf_rule_t * 437 npf_ruleset_replace(const char *name, npf_ruleset_t *rlset) 438 { 439 npf_ruleset_t orlset; 440 npf_rule_t *rl; 441 442 npf_core_enter(); /* XXX */ 443 rlset = npf_core_ruleset(); 444 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) { 445 if (rl->r_name[0] == '\0') 446 continue; 447 if (strncmp(rl->r_name, name, NPF_RNAME_LEN)) 448 continue; 449 memcpy(&orlset, &rl->r_subset, sizeof(npf_ruleset_t)); 450 break; 451 } 452 npf_core_exit(); 453 return rl; 454 } 455 456 /* 457 * npf_ruleset_inspect: inspect the packet against the given ruleset. 458 * 459 * Loop through the rules in the set and run n-code processor of each rule 460 * against the packet (nbuf chain). If sub-ruleset is found, inspect it. 461 * 462 * => If not found, core ruleset lock is released. 463 * => Caller should protect the nbuf chain. 464 */ 465 npf_rule_t * 466 npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf, npf_ruleset_t *mainrlset, 467 ifnet_t *ifp, const int di, const int layer) 468 { 469 const int di_mask = (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT; 470 npf_ruleset_t *rlset = mainrlset; 471 npf_rule_t *final_rl = NULL, *rl; 472 bool defed = false; 473 474 KASSERT(npf_core_locked()); 475 KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0)); 476 again: 477 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) { 478 KASSERT(!final_rl || rl->r_priority >= final_rl->r_priority); 479 480 /* Match the interface. */ 481 if (rl->r_ifid && rl->r_ifid != ifp->if_index) { 482 continue; 483 } 484 /* Match the direction. */ 485 if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) { 486 if ((rl->r_attr & di_mask) == 0) 487 continue; 488 } 489 /* Process the n-code, if any. */ 490 const void *nc = rl->r_ncode; 491 if (nc && npf_ncode_process(npc, nc, nbuf, layer)) { 492 continue; 493 } 494 /* Set the matching rule and check for "final". */ 495 final_rl = rl; 496 if (rl->r_attr & NPF_RULE_FINAL) { 497 break; 498 } 499 } 500 501 /* If no final rule, then - default. */ 502 if (final_rl == NULL && !defed) { 503 final_rl = mainrlset->rs_default; 504 defed = true; 505 } 506 /* Inspect the sub-ruleset, if any. */ 507 if (final_rl && !TAILQ_EMPTY(&final_rl->r_subset.rs_queue)) { 508 rlset = &final_rl->r_subset; 509 final_rl = NULL; 510 goto again; 511 } 512 if (final_rl == NULL) { 513 npf_core_exit(); 514 } 515 return final_rl; 516 } 517 518 /* 519 * npf_rule_apply: apply the rule i.e. run hooks and return appropriate value. 520 * 521 * => Returns ENETUNREACH if "block" and 0 if "pass". 522 * => Releases the ruleset lock. 523 */ 524 int 525 npf_rule_apply(npf_cache_t *npc, nbuf_t *nbuf, npf_rule_t *rl, int *retfl) 526 { 527 npf_hook_t *hk; 528 int error; 529 530 KASSERT(npf_core_locked()); 531 532 /* If not passing - drop the packet. */ 533 if ((rl->r_attr & NPF_RULE_PASS) == 0) { 534 error = ENETUNREACH; 535 goto done; 536 } 537 error = 0; 538 539 /* Passing. Run the hooks. */ 540 LIST_FOREACH(hk, &rl->r_hooks, hk_entry) { 541 KASSERT(hk->hk_fn != NULL); 542 (*hk->hk_fn)(npc, nbuf, hk->hk_arg); 543 } 544 done: 545 *retfl = rl->r_attr; 546 npf_core_exit(); 547 return error; 548 } 549 550 #if defined(DDB) || defined(_NPF_TESTING) 551 552 void 553 npf_rulenc_dump(npf_rule_t *rl) 554 { 555 uint32_t *op = rl->r_ncode; 556 size_t n = rl->r_nc_size; 557 558 while (n) { 559 printf("\t> |0x%02x|\n", (uint32_t)*op); 560 op++; 561 n -= sizeof(*op); 562 } 563 printf("-> %s\n", (rl->r_attr & NPF_RULE_PASS) ? "pass" : "block"); 564 } 565 566 #endif 567