1 /* $NetBSD: npf_ruleset.c,v 1.11 2012/02/20 00:18:20 rmind Exp $ */ 2 3 /*- 4 * Copyright (c) 2009-2012 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.11 2012/02/20 00:18:20 rmind Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/types.h> 41 42 #include <sys/kmem.h> 43 #include <sys/queue.h> 44 #include <sys/types.h> 45 46 #include <net/pfil.h> 47 #include <net/if.h> 48 49 #include "npf_ncode.h" 50 #include "npf_impl.h" 51 52 /* Ruleset structre (queue and default rule). */ 53 struct npf_ruleset { 54 TAILQ_HEAD(, npf_rule) rs_queue; 55 npf_rule_t * rs_default; 56 }; 57 58 #define NPF_RNAME_LEN 16 59 60 /* Rule structure. */ 61 struct npf_rule { 62 /* Rule name (optional) and list entry. */ 63 char r_name[NPF_RNAME_LEN]; 64 TAILQ_ENTRY(npf_rule) r_entry; 65 /* Optional: sub-ruleset, NAT policy. */ 66 npf_ruleset_t r_subset; 67 npf_natpolicy_t * r_natp; 68 /* Rule priority: (highest) 0, 1, 2 ... n (lowest). */ 69 pri_t r_priority; 70 /* N-code to process. */ 71 void * r_ncode; 72 size_t r_nc_size; 73 /* Attributes of this rule. */ 74 uint32_t r_attr; 75 /* Interface. */ 76 u_int r_ifid; 77 /* Rule procedure data. */ 78 npf_rproc_t * r_rproc; 79 }; 80 81 npf_ruleset_t * 82 npf_ruleset_create(void) 83 { 84 npf_ruleset_t *rlset; 85 86 rlset = kmem_zalloc(sizeof(npf_ruleset_t), KM_SLEEP); 87 TAILQ_INIT(&rlset->rs_queue); 88 return rlset; 89 } 90 91 void 92 npf_ruleset_destroy(npf_ruleset_t *rlset) 93 { 94 npf_rule_t *rl; 95 96 while ((rl = TAILQ_FIRST(&rlset->rs_queue)) != NULL) { 97 TAILQ_REMOVE(&rlset->rs_queue, rl, r_entry); 98 npf_rule_free(rl); 99 } 100 kmem_free(rlset, sizeof(npf_ruleset_t)); 101 } 102 103 /* 104 * npf_ruleset_insert: insert the rule into the specified ruleset. 105 * 106 * Note: multiple rules at the same priority are allowed. 107 */ 108 void 109 npf_ruleset_insert(npf_ruleset_t *rlset, npf_rule_t *rl) 110 { 111 npf_rule_t *it; 112 113 if (rl->r_attr & NPF_RULE_DEFAULT) { 114 rlset->rs_default = rl; 115 return; 116 } 117 TAILQ_FOREACH(it, &rlset->rs_queue, r_entry) { 118 /* Rule priority: (highest) 0, 1, 2, 4 ... n (lowest). */ 119 if (it->r_priority > rl->r_priority) 120 break; 121 } 122 if (it == NULL) { 123 TAILQ_INSERT_TAIL(&rlset->rs_queue, rl, r_entry); 124 } else { 125 TAILQ_INSERT_BEFORE(it, rl, r_entry); 126 } 127 } 128 129 /* 130 * npf_ruleset_matchnat: find a matching NAT policy in the ruleset. 131 */ 132 npf_rule_t * 133 npf_ruleset_matchnat(npf_ruleset_t *rlset, npf_natpolicy_t *mnp) 134 { 135 npf_rule_t *rl; 136 137 /* Find a matching NAT policy in the old ruleset. */ 138 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) { 139 if (npf_nat_matchpolicy(rl->r_natp, mnp)) 140 break; 141 } 142 return rl; 143 } 144 145 npf_rule_t * 146 npf_ruleset_sharepm(npf_ruleset_t *rlset, npf_natpolicy_t *mnp) 147 { 148 npf_natpolicy_t *np; 149 npf_rule_t *rl; 150 151 /* Find a matching NAT policy in the old ruleset. */ 152 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) { 153 /* 154 * NAT policy might not yet be set during the creation of 155 * the ruleset (in such case, rule is for our policy), or 156 * policies might be equal due to rule exchange on reload. 157 */ 158 np = rl->r_natp; 159 if (np == NULL || np == mnp) 160 continue; 161 if (npf_nat_sharepm(np, mnp)) 162 break; 163 } 164 return rl; 165 } 166 167 /* 168 * npf_ruleset_natreload: minimum reload of NAT policies by maching 169 * two (active and new) NAT rulesets. 170 * 171 * => Active ruleset should be exclusively locked. 172 */ 173 void 174 npf_ruleset_natreload(npf_ruleset_t *nrlset, npf_ruleset_t *arlset) 175 { 176 npf_natpolicy_t *np, *anp; 177 npf_rule_t *rl, *arl; 178 179 KASSERT(npf_core_locked()); 180 181 /* Scan a new NAT ruleset against NAT policies in old ruleset. */ 182 TAILQ_FOREACH(rl, &nrlset->rs_queue, r_entry) { 183 np = rl->r_natp; 184 arl = npf_ruleset_matchnat(arlset, np); 185 if (arl == NULL) { 186 continue; 187 } 188 /* On match - we exchange NAT policies. */ 189 anp = arl->r_natp; 190 rl->r_natp = anp; 191 arl->r_natp = np; 192 /* Update other NAT policies to share portmap. */ 193 (void)npf_ruleset_sharepm(nrlset, anp); 194 } 195 } 196 197 /* 198 * npf_rule_alloc: allocate a rule and copy n-code from user-space. 199 * 200 * => N-code should be validated by the caller. 201 */ 202 npf_rule_t * 203 npf_rule_alloc(prop_dictionary_t rldict, npf_rproc_t *rp, 204 void *nc, size_t nc_size) 205 { 206 npf_rule_t *rl; 207 const char *rname; 208 int errat __unused; 209 210 /* Allocate a rule structure. */ 211 rl = kmem_zalloc(sizeof(npf_rule_t), KM_SLEEP); 212 TAILQ_INIT(&rl->r_subset.rs_queue); 213 rl->r_natp = NULL; 214 215 /* N-code. */ 216 KASSERT(nc == NULL || npf_ncode_validate(nc, nc_size, &errat) == 0); 217 rl->r_ncode = nc; 218 rl->r_nc_size = nc_size; 219 220 /* Name (optional) */ 221 if (prop_dictionary_get_cstring_nocopy(rldict, "name", &rname)) { 222 strlcpy(rl->r_name, rname, NPF_RNAME_LEN); 223 } else { 224 rl->r_name[0] = '\0'; 225 } 226 227 /* Attributes, priority and interface ID (optional). */ 228 prop_dictionary_get_uint32(rldict, "attributes", &rl->r_attr); 229 prop_dictionary_get_int32(rldict, "priority", &rl->r_priority); 230 prop_dictionary_get_uint32(rldict, "interface", &rl->r_ifid); 231 232 /* Rule procedure. */ 233 if (rp) { 234 npf_rproc_acquire(rp); 235 } 236 rl->r_rproc = rp; 237 238 return rl; 239 } 240 241 /* 242 * npf_rule_free: free the specified rule. 243 */ 244 void 245 npf_rule_free(npf_rule_t *rl) 246 { 247 npf_natpolicy_t *np = rl->r_natp; 248 npf_rproc_t *rp = rl->r_rproc; 249 250 if (np) { 251 /* Free NAT policy. */ 252 npf_nat_freepolicy(np); 253 } 254 if (rp) { 255 /* Release rule procedure. */ 256 npf_rproc_release(rp); 257 } 258 if (rl->r_ncode) { 259 /* Free n-code. */ 260 npf_ncode_free(rl->r_ncode, rl->r_nc_size); 261 } 262 kmem_free(rl, sizeof(npf_rule_t)); 263 } 264 265 /* 266 * npf_rule_subset: return sub-ruleset, if any. 267 * npf_rule_getrproc: acquire a reference and return rule procedure, if any. 268 * npf_rule_getnat: get NAT policy assigned to the rule. 269 */ 270 271 npf_ruleset_t * 272 npf_rule_subset(npf_rule_t *rl) 273 { 274 return &rl->r_subset; 275 } 276 277 npf_rproc_t * 278 npf_rule_getrproc(npf_rule_t *rl) 279 { 280 npf_rproc_t *rp = rl->r_rproc; 281 282 KASSERT(npf_core_locked()); 283 if (rp) { 284 npf_rproc_acquire(rp); 285 } 286 return rp; 287 } 288 289 npf_natpolicy_t * 290 npf_rule_getnat(const npf_rule_t *rl) 291 { 292 return rl->r_natp; 293 } 294 295 /* 296 * npf_rule_setnat: assign NAT policy to the rule and insert into the 297 * NAT policy list in the ruleset. 298 */ 299 void 300 npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np) 301 { 302 303 KASSERT(rl->r_natp == NULL); 304 rl->r_natp = np; 305 } 306 307 npf_rule_t * 308 npf_ruleset_replace(const char *name, npf_ruleset_t *rlset) 309 { 310 npf_ruleset_t orlset; 311 npf_rule_t *rl; 312 313 npf_core_enter(); /* XXX */ 314 rlset = npf_core_ruleset(); 315 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) { 316 if (rl->r_name[0] == '\0') 317 continue; 318 if (strncmp(rl->r_name, name, NPF_RNAME_LEN)) 319 continue; 320 memcpy(&orlset, &rl->r_subset, sizeof(npf_ruleset_t)); 321 break; 322 } 323 npf_core_exit(); 324 return rl; 325 } 326 327 /* 328 * npf_ruleset_inspect: inspect the packet against the given ruleset. 329 * 330 * Loop through the rules in the set and run n-code processor of each rule 331 * against the packet (nbuf chain). If sub-ruleset is found, inspect it. 332 * 333 * => Caller is responsible for nbuf chain protection. 334 */ 335 npf_rule_t * 336 npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf, npf_ruleset_t *mainrlset, 337 ifnet_t *ifp, const int di, const int layer) 338 { 339 const int di_mask = (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT; 340 npf_ruleset_t *rlset = mainrlset; 341 npf_rule_t *final_rl = NULL, *rl; 342 bool defed = false; 343 344 KASSERT(npf_core_locked()); 345 KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0)); 346 again: 347 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) { 348 KASSERT(!final_rl || rl->r_priority >= final_rl->r_priority); 349 350 /* Match the interface. */ 351 if (rl->r_ifid && rl->r_ifid != ifp->if_index) { 352 continue; 353 } 354 /* Match the direction. */ 355 if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) { 356 if ((rl->r_attr & di_mask) == 0) 357 continue; 358 } 359 /* Process the n-code, if any. */ 360 const void *nc = rl->r_ncode; 361 if (nc && npf_ncode_process(npc, nc, nbuf, layer)) { 362 continue; 363 } 364 /* Set the matching rule and check for "final". */ 365 final_rl = rl; 366 if (rl->r_attr & NPF_RULE_FINAL) { 367 break; 368 } 369 } 370 371 /* If no final rule, then - default. */ 372 if (final_rl == NULL && !defed) { 373 final_rl = mainrlset->rs_default; 374 defed = true; 375 } 376 /* Inspect the sub-ruleset, if any. */ 377 if (final_rl && !TAILQ_EMPTY(&final_rl->r_subset.rs_queue)) { 378 rlset = &final_rl->r_subset; 379 final_rl = NULL; 380 goto again; 381 } 382 return final_rl; 383 } 384 385 /* 386 * npf_rule_apply: apply the rule and return appropriate value. 387 * 388 * => Returns ENETUNREACH if "block" and 0 if "pass". 389 * => Releases the ruleset lock. 390 */ 391 int 392 npf_rule_apply(npf_cache_t *npc, nbuf_t *nbuf, npf_rule_t *rl, int *retfl) 393 { 394 int error; 395 396 KASSERT(npf_core_locked()); 397 398 /* If not passing - drop the packet. */ 399 error = (rl->r_attr & NPF_RULE_PASS) ? 0 : ENETUNREACH; 400 401 *retfl = rl->r_attr; 402 npf_core_exit(); 403 404 return error; 405 } 406 407 #if defined(DDB) || defined(_NPF_TESTING) 408 409 void 410 npf_rulenc_dump(npf_rule_t *rl) 411 { 412 uint32_t *op = rl->r_ncode; 413 size_t n = rl->r_nc_size; 414 415 while (n) { 416 printf("\t> |0x%02x|\n", (uint32_t)*op); 417 op++; 418 n -= sizeof(*op); 419 } 420 printf("-> %s\n", (rl->r_attr & NPF_RULE_PASS) ? "pass" : "block"); 421 } 422 423 #endif 424