1 /* $NetBSD: ip_frag.c,v 1.5 2017/04/23 19:09:29 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2012 by Darren Reed. 5 * 6 * See the IPFILTER.LICENCE file for details on licencing. 7 */ 8 #if defined(KERNEL) || defined(_KERNEL) 9 # undef KERNEL 10 # undef _KERNEL 11 # define KERNEL 1 12 # define _KERNEL 1 13 #endif 14 #include <sys/errno.h> 15 #include <sys/types.h> 16 #include <sys/param.h> 17 #include <sys/time.h> 18 #include <sys/file.h> 19 #ifdef __hpux 20 # include <sys/timeout.h> 21 #endif 22 #if !defined(_KERNEL) 23 # include <stdio.h> 24 # include <string.h> 25 # include <stdlib.h> 26 # define _KERNEL 27 # ifdef __OpenBSD__ 28 struct file; 29 # endif 30 # include <sys/uio.h> 31 # undef _KERNEL 32 #endif 33 #if defined(_KERNEL) && \ 34 defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) 35 # include <sys/filio.h> 36 # include <sys/fcntl.h> 37 #else 38 # include <sys/ioctl.h> 39 #endif 40 #if !defined(linux) 41 # include <sys/protosw.h> 42 #endif 43 #include <sys/socket.h> 44 #if defined(_KERNEL) 45 # include <sys/systm.h> 46 # if !defined(__SVR4) && !defined(__svr4__) 47 # include <sys/mbuf.h> 48 # endif 49 #endif 50 #if !defined(__SVR4) && !defined(__svr4__) 51 # if defined(_KERNEL) && !defined(__sgi) && !defined(AIX) 52 # include <sys/kernel.h> 53 # endif 54 #else 55 # include <sys/byteorder.h> 56 # ifdef _KERNEL 57 # include <sys/dditypes.h> 58 # endif 59 # include <sys/stream.h> 60 # include <sys/kmem.h> 61 #endif 62 #include <net/if.h> 63 #ifdef sun 64 # include <net/af.h> 65 #endif 66 #include <netinet/in.h> 67 #include <netinet/in_systm.h> 68 #include <netinet/ip.h> 69 #if !defined(linux) 70 # include <netinet/ip_var.h> 71 #endif 72 #include <netinet/tcp.h> 73 #include <netinet/udp.h> 74 #include <netinet/ip_icmp.h> 75 #include "netinet/ip_compat.h" 76 #include <netinet/tcpip.h> 77 #include "netinet/ip_fil.h" 78 #include "netinet/ip_nat.h" 79 #include "netinet/ip_frag.h" 80 #include "netinet/ip_state.h" 81 #include "netinet/ip_auth.h" 82 #include "netinet/ip_lookup.h" 83 #include "netinet/ip_proxy.h" 84 #include "netinet/ip_sync.h" 85 /* END OF INCLUDES */ 86 87 #if !defined(lint) 88 #if defined(__NetBSD__) 89 #include <sys/cdefs.h> 90 __KERNEL_RCSID(0, "$NetBSD: ip_frag.c,v 1.5 2017/04/23 19:09:29 christos Exp $"); 91 #else 92 static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed"; 93 static const char rcsid[] = "@(#)Id: ip_frag.c,v 1.1.1.2 2012/07/22 13:45:17 darrenr Exp"; 94 #endif 95 #endif 96 97 98 typedef struct ipf_frag_softc_s { 99 ipfrwlock_t ipfr_ipidfrag; 100 ipfrwlock_t ipfr_frag; 101 ipfrwlock_t ipfr_natfrag; 102 int ipfr_size; 103 int ipfr_ttl; 104 int ipfr_lock; 105 int ipfr_inited; 106 ipfr_t *ipfr_list; 107 ipfr_t **ipfr_tail; 108 ipfr_t *ipfr_natlist; 109 ipfr_t **ipfr_nattail; 110 ipfr_t *ipfr_ipidlist; 111 ipfr_t **ipfr_ipidtail; 112 ipfr_t **ipfr_heads; 113 ipfr_t **ipfr_nattab; 114 ipfr_t **ipfr_ipidtab; 115 ipfrstat_t ipfr_stats; 116 } ipf_frag_softc_t; 117 118 119 #ifdef USE_MUTEXES 120 static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *, 121 fr_info_t *, u_32_t, ipfr_t **, 122 ipfrwlock_t *); 123 static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **, ipfrwlock_t *); 124 static void ipf_frag_deref(void *, ipfr_t **, ipfrwlock_t *); 125 static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, 126 ipfr_t **, ipfrwlock_t *); 127 #else 128 static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *, 129 fr_info_t *, u_32_t, ipfr_t **); 130 static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **); 131 static void ipf_frag_deref(void *, ipfr_t **); 132 static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, 133 ipfr_t **); 134 #endif 135 static void ipf_frag_delete(ipf_main_softc_t *, ipfr_t *, ipfr_t ***); 136 static void ipf_frag_free(ipf_frag_softc_t *, ipfr_t *); 137 138 static frentry_t ipfr_block; 139 140 ipftuneable_t ipf_tuneables[] = { 141 { { (void *)offsetof(ipf_frag_softc_t, ipfr_size) }, 142 "frag_size", 1, 0x7fffffff, 143 stsizeof(ipf_frag_softc_t, ipfr_size), 144 IPFT_WRDISABLED, NULL, NULL }, 145 { { (void *)offsetof(ipf_frag_softc_t, ipfr_ttl) }, 146 "frag_ttl", 1, 0x7fffffff, 147 stsizeof(ipf_frag_softc_t, ipfr_ttl), 148 0, NULL, NULL }, 149 { { NULL }, 150 NULL, 0, 0, 151 0, 152 0, NULL, NULL } 153 }; 154 155 #define FBUMP(x) softf->ipfr_stats.x++ 156 #define FBUMPD(x) do { softf->ipfr_stats.x++; DT(x); } while (0) 157 158 159 /* ------------------------------------------------------------------------ */ 160 /* Function: ipf_frag_main_load */ 161 /* Returns: int - 0 == success, -1 == error */ 162 /* Parameters: Nil */ 163 /* */ 164 /* Initialise the filter rule associted with blocked packets - everyone can */ 165 /* use it. */ 166 /* ------------------------------------------------------------------------ */ 167 int 168 ipf_frag_main_load(void) 169 { 170 bzero((char *)&ipfr_block, sizeof(ipfr_block)); 171 ipfr_block.fr_flags = FR_BLOCK|FR_QUICK; 172 ipfr_block.fr_ref = 1; 173 174 return 0; 175 } 176 177 178 /* ------------------------------------------------------------------------ */ 179 /* Function: ipf_frag_main_unload */ 180 /* Returns: int - 0 == success, -1 == error */ 181 /* Parameters: Nil */ 182 /* */ 183 /* A null-op function that exists as a placeholder so that the flow in */ 184 /* other functions is obvious. */ 185 /* ------------------------------------------------------------------------ */ 186 int 187 ipf_frag_main_unload(void) 188 { 189 return 0; 190 } 191 192 193 /* ------------------------------------------------------------------------ */ 194 /* Function: ipf_frag_soft_create */ 195 /* Returns: void * - NULL = failure, else pointer to local context */ 196 /* Parameters: softc(I) - pointer to soft context main structure */ 197 /* */ 198 /* Allocate a new soft context structure to track fragment related info. */ 199 /* ------------------------------------------------------------------------ */ 200 /*ARGSUSED*/ 201 void * 202 ipf_frag_soft_create(ipf_main_softc_t *softc) 203 { 204 ipf_frag_softc_t *softf; 205 206 KMALLOC(softf, ipf_frag_softc_t *); 207 if (softf == NULL) 208 return NULL; 209 210 bzero((char *)softf, sizeof(*softf)); 211 212 RWLOCK_INIT(&softf->ipfr_ipidfrag, "frag ipid lock"); 213 RWLOCK_INIT(&softf->ipfr_frag, "ipf fragment rwlock"); 214 RWLOCK_INIT(&softf->ipfr_natfrag, "ipf NAT fragment rwlock"); 215 216 softf->ipfr_size = IPFT_SIZE; 217 softf->ipfr_ttl = IPF_TTLVAL(60); 218 softf->ipfr_lock = 1; 219 softf->ipfr_tail = &softf->ipfr_list; 220 softf->ipfr_nattail = &softf->ipfr_natlist; 221 softf->ipfr_ipidtail = &softf->ipfr_ipidlist; 222 223 return softf; 224 } 225 226 227 /* ------------------------------------------------------------------------ */ 228 /* Function: ipf_frag_soft_destroy */ 229 /* Returns: Nil */ 230 /* Parameters: softc(I) - pointer to soft context main structure */ 231 /* arg(I) - pointer to local context to use */ 232 /* */ 233 /* Initialise the hash tables for the fragment cache lookups. */ 234 /* ------------------------------------------------------------------------ */ 235 void 236 ipf_frag_soft_destroy(ipf_main_softc_t *softc, void *arg) 237 { 238 ipf_frag_softc_t *softf = arg; 239 240 RW_DESTROY(&softf->ipfr_ipidfrag); 241 RW_DESTROY(&softf->ipfr_frag); 242 RW_DESTROY(&softf->ipfr_natfrag); 243 244 KFREE(softf); 245 } 246 247 248 /* ------------------------------------------------------------------------ */ 249 /* Function: ipf_frag_soft_init */ 250 /* Returns: int - 0 == success, -1 == error */ 251 /* Parameters: softc(I) - pointer to soft context main structure */ 252 /* arg(I) - pointer to local context to use */ 253 /* */ 254 /* Initialise the hash tables for the fragment cache lookups. */ 255 /* ------------------------------------------------------------------------ */ 256 /*ARGSUSED*/ 257 int 258 ipf_frag_soft_init(ipf_main_softc_t *softc, void *arg) 259 { 260 ipf_frag_softc_t *softf = arg; 261 262 KMALLOCS(softf->ipfr_heads, ipfr_t **, 263 softf->ipfr_size * sizeof(ipfr_t *)); 264 if (softf->ipfr_heads == NULL) 265 return -1; 266 267 bzero((char *)softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); 268 269 KMALLOCS(softf->ipfr_nattab, ipfr_t **, 270 softf->ipfr_size * sizeof(ipfr_t *)); 271 if (softf->ipfr_nattab == NULL) 272 return -2; 273 274 bzero((char *)softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); 275 276 KMALLOCS(softf->ipfr_ipidtab, ipfr_t **, 277 softf->ipfr_size * sizeof(ipfr_t *)); 278 if (softf->ipfr_ipidtab == NULL) 279 return -3; 280 281 bzero((char *)softf->ipfr_ipidtab, 282 softf->ipfr_size * sizeof(ipfr_t *)); 283 284 softf->ipfr_lock = 0; 285 softf->ipfr_inited = 1; 286 287 return 0; 288 } 289 290 291 /* ------------------------------------------------------------------------ */ 292 /* Function: ipf_frag_soft_fini */ 293 /* Returns: int - 0 == success, -1 == error */ 294 /* Parameters: softc(I) - pointer to soft context main structure */ 295 /* arg(I) - pointer to local context to use */ 296 /* */ 297 /* Free all memory allocated whilst running and from initialisation. */ 298 /* ------------------------------------------------------------------------ */ 299 int 300 ipf_frag_soft_fini(ipf_main_softc_t *softc, void *arg) 301 { 302 ipf_frag_softc_t *softf = arg; 303 304 softf->ipfr_lock = 1; 305 306 if (softf->ipfr_inited == 1) { 307 ipf_frag_clear(softc); 308 309 softf->ipfr_inited = 0; 310 } 311 312 if (softf->ipfr_heads != NULL) 313 KFREES(softf->ipfr_heads, 314 softf->ipfr_size * sizeof(ipfr_t *)); 315 softf->ipfr_heads = NULL; 316 317 if (softf->ipfr_nattab != NULL) 318 KFREES(softf->ipfr_nattab, 319 softf->ipfr_size * sizeof(ipfr_t *)); 320 softf->ipfr_nattab = NULL; 321 322 if (softf->ipfr_ipidtab != NULL) 323 KFREES(softf->ipfr_ipidtab, 324 softf->ipfr_size * sizeof(ipfr_t *)); 325 softf->ipfr_ipidtab = NULL; 326 327 return 0; 328 } 329 330 331 /* ------------------------------------------------------------------------ */ 332 /* Function: ipf_frag_set_lock */ 333 /* Returns: Nil */ 334 /* Parameters: arg(I) - pointer to local context to use */ 335 /* tmp(I) - new value for lock */ 336 /* */ 337 /* Stub function that allows for external manipulation of ipfr_lock */ 338 /* ------------------------------------------------------------------------ */ 339 void 340 ipf_frag_setlock(void *arg, int tmp) 341 { 342 ipf_frag_softc_t *softf = arg; 343 344 softf->ipfr_lock = tmp; 345 } 346 347 348 /* ------------------------------------------------------------------------ */ 349 /* Function: ipf_frag_stats */ 350 /* Returns: ipfrstat_t* - pointer to struct with current frag stats */ 351 /* Parameters: arg(I) - pointer to local context to use */ 352 /* */ 353 /* Updates ipfr_stats with current information and returns a pointer to it */ 354 /* ------------------------------------------------------------------------ */ 355 ipfrstat_t * 356 ipf_frag_stats(void *arg) 357 { 358 ipf_frag_softc_t *softf = arg; 359 360 softf->ipfr_stats.ifs_table = softf->ipfr_heads; 361 softf->ipfr_stats.ifs_nattab = softf->ipfr_nattab; 362 return &softf->ipfr_stats; 363 } 364 365 366 /* ------------------------------------------------------------------------ */ 367 /* Function: ipfr_frag_new */ 368 /* Returns: ipfr_t * - pointer to fragment cache state info or NULL */ 369 /* Parameters: fin(I) - pointer to packet information */ 370 /* table(I) - pointer to frag table to add to */ 371 /* lock(I) - pointer to lock to get a write hold of */ 372 /* */ 373 /* Add a new entry to the fragment cache, registering it as having come */ 374 /* through this box, with the result of the filter operation. */ 375 /* */ 376 /* If this function succeeds, it returns with a write lock held on "lock". */ 377 /* If it fails, no lock is held on return. */ 378 /* ------------------------------------------------------------------------ */ 379 static ipfr_t * 380 ipfr_frag_new( 381 ipf_main_softc_t *softc, 382 ipf_frag_softc_t *softf, 383 fr_info_t *fin, 384 u_32_t pass, 385 ipfr_t *table[] 386 #ifdef USE_MUTEXES 387 , ipfrwlock_t *lock 388 #endif 389 ) 390 { 391 ipfr_t *fra, frag, *fran; 392 u_int idx, off; 393 frentry_t *fr; 394 395 if (softf->ipfr_stats.ifs_inuse >= softf->ipfr_size) { 396 FBUMPD(ifs_maximum); 397 return NULL; 398 } 399 400 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) { 401 FBUMPD(ifs_newbad); 402 return NULL; 403 } 404 405 if (pass & FR_FRSTRICT) { 406 if (fin->fin_off != 0) { 407 FBUMPD(ifs_newrestrictnot0); 408 return NULL; 409 } 410 } 411 412 frag.ipfr_v = fin->fin_v; 413 idx = fin->fin_v; 414 frag.ipfr_p = fin->fin_p; 415 idx += fin->fin_p; 416 frag.ipfr_id = fin->fin_id; 417 idx += fin->fin_id; 418 frag.ipfr_source = fin->fin_fi.fi_src; 419 idx += frag.ipfr_src.s_addr; 420 frag.ipfr_dest = fin->fin_fi.fi_dst; 421 idx += frag.ipfr_dst.s_addr; 422 frag.ipfr_ifp = fin->fin_ifp; 423 idx *= 127; 424 idx %= softf->ipfr_size; 425 426 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 427 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 428 frag.ipfr_auth = fin->fin_fi.fi_auth; 429 430 off = fin->fin_off >> 3; 431 if (off == 0) { 432 char *ptr; 433 int end; 434 435 #ifdef USE_INET6 436 if (fin->fin_v == 6) { 437 438 ptr = (char *)fin->fin_fraghdr + 439 sizeof(struct ip6_frag); 440 } else 441 #endif 442 { 443 ptr = fin->fin_dp; 444 } 445 end = fin->fin_plen - (ptr - (char *)fin->fin_ip); 446 frag.ipfr_firstend = end >> 3; 447 } else { 448 frag.ipfr_firstend = 0; 449 } 450 451 /* 452 * allocate some memory, if possible, if not, just record that we 453 * failed to do so. 454 */ 455 KMALLOC(fran, ipfr_t *); 456 if (fran == NULL) { 457 FBUMPD(ifs_nomem); 458 return NULL; 459 } 460 461 WRITE_ENTER(lock); 462 463 /* 464 * first, make sure it isn't already there... 465 */ 466 for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext) 467 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, 468 IPFR_CMPSZ)) { 469 RWLOCK_EXIT(lock); 470 FBUMPD(ifs_exists); 471 KFREE(fran); 472 return NULL; 473 } 474 475 fra = fran; 476 fran = NULL; 477 fr = fin->fin_fr; 478 fra->ipfr_rule = fr; 479 if (fr != NULL) { 480 MUTEX_ENTER(&fr->fr_lock); 481 fr->fr_ref++; 482 MUTEX_EXIT(&fr->fr_lock); 483 } 484 485 /* 486 * Insert the fragment into the fragment table, copy the struct used 487 * in the search using bcopy rather than reassign each field. 488 * Set the ttl to the default. 489 */ 490 if ((fra->ipfr_hnext = table[idx]) != NULL) 491 table[idx]->ipfr_hprev = &fra->ipfr_hnext; 492 fra->ipfr_hprev = table + idx; 493 fra->ipfr_data = NULL; 494 table[idx] = fra; 495 bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); 496 fra->ipfr_v = fin->fin_v; 497 fra->ipfr_ttl = softc->ipf_ticks + softf->ipfr_ttl; 498 fra->ipfr_firstend = frag.ipfr_firstend; 499 500 /* 501 * Compute the offset of the expected start of the next packet. 502 */ 503 if (off == 0) 504 fra->ipfr_seen0 = 1; 505 fra->ipfr_off = off + (fin->fin_dlen >> 3); 506 fra->ipfr_pass = pass; 507 fra->ipfr_ref = 1; 508 fra->ipfr_pkts = 1; 509 fra->ipfr_bytes = fin->fin_plen; 510 FBUMP(ifs_inuse); 511 FBUMP(ifs_new); 512 return fra; 513 } 514 515 516 /* ------------------------------------------------------------------------ */ 517 /* Function: ipf_frag_new */ 518 /* Returns: int - 0 == success, -1 == error */ 519 /* Parameters: fin(I) - pointer to packet information */ 520 /* */ 521 /* Add a new entry to the fragment cache table based on the current packet */ 522 /* ------------------------------------------------------------------------ */ 523 int 524 ipf_frag_new(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass) 525 { 526 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 527 ipfr_t *fra; 528 529 if (softf->ipfr_lock != 0) 530 return -1; 531 532 #ifdef USE_MUTEXES 533 fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads, &softc->ipf_frag); 534 #else 535 fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads); 536 #endif 537 if (fra != NULL) { 538 *softf->ipfr_tail = fra; 539 fra->ipfr_prev = softf->ipfr_tail; 540 softf->ipfr_tail = &fra->ipfr_next; 541 fra->ipfr_next = NULL; 542 RWLOCK_EXIT(&softc->ipf_frag); 543 } 544 return fra ? 0 : -1; 545 } 546 547 548 /* ------------------------------------------------------------------------ */ 549 /* Function: ipf_frag_natnew */ 550 /* Returns: int - 0 == success, -1 == error */ 551 /* Parameters: fin(I) - pointer to packet information */ 552 /* nat(I) - pointer to NAT structure */ 553 /* */ 554 /* Create a new NAT fragment cache entry based on the current packet and */ 555 /* the NAT structure for this "session". */ 556 /* ------------------------------------------------------------------------ */ 557 int 558 ipf_frag_natnew(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass, 559 nat_t *nat) 560 { 561 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 562 ipfr_t *fra; 563 564 if (softf->ipfr_lock != 0) 565 return 0; 566 567 #ifdef USE_MUTEXES 568 fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab, 569 &softf->ipfr_natfrag); 570 #else 571 fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab); 572 #endif 573 if (fra != NULL) { 574 fra->ipfr_data = nat; 575 nat->nat_data = fra; 576 *softf->ipfr_nattail = fra; 577 fra->ipfr_prev = softf->ipfr_nattail; 578 softf->ipfr_nattail = &fra->ipfr_next; 579 fra->ipfr_next = NULL; 580 RWLOCK_EXIT(&softf->ipfr_natfrag); 581 return 0; 582 } 583 return -1; 584 } 585 586 587 /* ------------------------------------------------------------------------ */ 588 /* Function: ipf_frag_ipidnew */ 589 /* Returns: int - 0 == success, -1 == error */ 590 /* Parameters: fin(I) - pointer to packet information */ 591 /* ipid(I) - new IP ID for this fragmented packet */ 592 /* */ 593 /* Create a new fragment cache entry for this packet and store, as a data */ 594 /* pointer, the new IP ID value. */ 595 /* ------------------------------------------------------------------------ */ 596 int 597 ipf_frag_ipidnew(fr_info_t *fin, u_32_t ipid) 598 { 599 ipf_main_softc_t *softc = fin->fin_main_soft; 600 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 601 ipfr_t *fra; 602 603 if (softf->ipfr_lock) 604 return 0; 605 606 #ifdef USE_MUTEXES 607 fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); 608 #else 609 fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab); 610 #endif 611 if (fra != NULL) { 612 fra->ipfr_data = (void *)(intptr_t)ipid; 613 *softf->ipfr_ipidtail = fra; 614 fra->ipfr_prev = softf->ipfr_ipidtail; 615 softf->ipfr_ipidtail = &fra->ipfr_next; 616 fra->ipfr_next = NULL; 617 RWLOCK_EXIT(&softf->ipfr_ipidfrag); 618 } 619 return fra ? 0 : -1; 620 } 621 622 623 /* ------------------------------------------------------------------------ */ 624 /* Function: ipf_frag_lookup */ 625 /* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */ 626 /* matching entry in the frag table, else NULL */ 627 /* Parameters: fin(I) - pointer to packet information */ 628 /* table(I) - pointer to fragment cache table to search */ 629 /* */ 630 /* Check the fragment cache to see if there is already a record of this */ 631 /* packet with its filter result known. */ 632 /* */ 633 /* If this function succeeds, it returns with a write lock held on "lock". */ 634 /* If it fails, no lock is held on return. */ 635 /* ------------------------------------------------------------------------ */ 636 static ipfr_t * 637 ipf_frag_lookup( 638 ipf_main_softc_t *softc, 639 ipf_frag_softc_t *softf, 640 fr_info_t *fin, 641 ipfr_t *table[] 642 #ifdef USE_MUTEXES 643 , ipfrwlock_t *lock 644 #endif 645 ) 646 { 647 ipfr_t *f, frag; 648 u_int idx; 649 650 /* 651 * We don't want to let short packets match because they could be 652 * compromising the security of other rules that want to match on 653 * layer 4 fields (and can't because they have been fragmented off.) 654 * Why do this check here? The counter acts as an indicator of this 655 * kind of attack, whereas if it was elsewhere, it wouldn't know if 656 * other matching packets had been seen. 657 */ 658 if (fin->fin_flx & FI_SHORT) { 659 FBUMPD(ifs_short); 660 return NULL; 661 } 662 663 if ((fin->fin_flx & FI_BAD) != 0) { 664 FBUMPD(ifs_bad); 665 return NULL; 666 } 667 668 /* 669 * For fragments, we record protocol, packet id, TOS and both IP#'s 670 * (these should all be the same for all fragments of a packet). 671 * 672 * build up a hash value to index the table with. 673 */ 674 frag.ipfr_v = fin->fin_v; 675 idx = fin->fin_v; 676 frag.ipfr_p = fin->fin_p; 677 idx += fin->fin_p; 678 frag.ipfr_id = fin->fin_id; 679 idx += fin->fin_id; 680 frag.ipfr_source = fin->fin_fi.fi_src; 681 idx += frag.ipfr_src.s_addr; 682 frag.ipfr_dest = fin->fin_fi.fi_dst; 683 idx += frag.ipfr_dst.s_addr; 684 frag.ipfr_ifp = fin->fin_ifp; 685 idx *= 127; 686 idx %= softf->ipfr_size; 687 688 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 689 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 690 frag.ipfr_auth = fin->fin_fi.fi_auth; 691 692 READ_ENTER(lock); 693 694 /* 695 * check the table, careful to only compare the right amount of data 696 */ 697 for (f = table[idx]; f; f = f->ipfr_hnext) { 698 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, 699 IPFR_CMPSZ)) { 700 u_short off; 701 702 /* 703 * XXX - We really need to be guarding against the 704 * retransmission of (src,dst,id,offset-range) here 705 * because a fragmented packet is never resent with 706 * the same IP ID# (or shouldn't). 707 */ 708 off = fin->fin_off >> 3; 709 if (f->ipfr_seen0) { 710 if (off == 0) { 711 FBUMPD(ifs_retrans0); 712 continue; 713 } 714 715 /* 716 * Case 3. See comment for frpr_fragment6. 717 */ 718 if ((f->ipfr_firstend != 0) && 719 (off < f->ipfr_firstend)) { 720 FBUMP(ifs_overlap); 721 DT2(ifs_overlap, u_short, off, 722 ipfr_t *, f); 723 fin->fin_flx |= FI_BAD; 724 break; 725 } 726 } else if (off == 0) 727 f->ipfr_seen0 = 1; 728 729 #if 0 730 /* We can't do this, since we only have a read lock! */ 731 if (f != table[idx]) { 732 ipfr_t **fp; 733 734 /* 735 * Move fragment info. to the top of the list 736 * to speed up searches. First, delink... 737 */ 738 fp = f->ipfr_hprev; 739 (*fp) = f->ipfr_hnext; 740 if (f->ipfr_hnext != NULL) 741 f->ipfr_hnext->ipfr_hprev = fp; 742 /* 743 * Then put back at the top of the chain. 744 */ 745 f->ipfr_hnext = table[idx]; 746 table[idx]->ipfr_hprev = &f->ipfr_hnext; 747 f->ipfr_hprev = table + idx; 748 table[idx] = f; 749 } 750 #endif 751 752 /* 753 * If we've followed the fragments, and this is the 754 * last (in order), shrink expiration time. 755 */ 756 if (off == f->ipfr_off) { 757 f->ipfr_off = (fin->fin_dlen >> 3) + off; 758 759 /* 760 * Well, we could shrink the expiration time 761 * but only if every fragment has been seen 762 * in order upto this, the last. ipfr_badorder 763 * is used here to count those out of order 764 * and if it equals 0 when we get to the last 765 * fragment then we can assume all of the 766 * fragments have been seen and in order. 767 */ 768 #if 0 769 /* 770 * Doing this properly requires moving it to 771 * the head of the list which is infesible. 772 */ 773 if ((more == 0) && (f->ipfr_badorder == 0)) 774 f->ipfr_ttl = softc->ipf_ticks + 1; 775 #endif 776 } else { 777 f->ipfr_badorder++; 778 FBUMPD(ifs_unordered); 779 if (f->ipfr_pass & FR_FRSTRICT) { 780 FBUMPD(ifs_strict); 781 continue; 782 } 783 } 784 f->ipfr_pkts++; 785 f->ipfr_bytes += fin->fin_plen; 786 FBUMP(ifs_hits); 787 return f; 788 } 789 } 790 791 RWLOCK_EXIT(lock); 792 FBUMP(ifs_miss); 793 return NULL; 794 } 795 796 797 /* ------------------------------------------------------------------------ */ 798 /* Function: ipf_frag_natknown */ 799 /* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ 800 /* match found, else NULL */ 801 /* Parameters: fin(I) - pointer to packet information */ 802 /* */ 803 /* Functional interface for NAT lookups of the NAT fragment cache */ 804 /* ------------------------------------------------------------------------ */ 805 nat_t * 806 ipf_frag_natknown(fr_info_t *fin) 807 { 808 ipf_main_softc_t *softc = fin->fin_main_soft; 809 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 810 nat_t *nat; 811 ipfr_t *ipf; 812 813 if ((softf->ipfr_lock) || !softf->ipfr_natlist) 814 return NULL; 815 #ifdef USE_MUTEXES 816 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab, 817 &softf->ipfr_natfrag); 818 #else 819 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab); 820 #endif 821 if (ipf != NULL) { 822 nat = ipf->ipfr_data; 823 /* 824 * This is the last fragment for this packet. 825 */ 826 if ((ipf->ipfr_ttl == softc->ipf_ticks + 1) && (nat != NULL)) { 827 nat->nat_data = NULL; 828 ipf->ipfr_data = NULL; 829 } 830 RWLOCK_EXIT(&softf->ipfr_natfrag); 831 } else 832 nat = NULL; 833 return nat; 834 } 835 836 837 /* ------------------------------------------------------------------------ */ 838 /* Function: ipf_frag_ipidknown */ 839 /* Returns: u_32_t - IPv4 ID for this packet if match found, else */ 840 /* return 0xfffffff to indicate no match. */ 841 /* Parameters: fin(I) - pointer to packet information */ 842 /* */ 843 /* Functional interface for IP ID lookups of the IP ID fragment cache */ 844 /* ------------------------------------------------------------------------ */ 845 u_32_t 846 ipf_frag_ipidknown(fr_info_t *fin) 847 { 848 ipf_main_softc_t *softc = fin->fin_main_soft; 849 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 850 ipfr_t *ipf; 851 u_32_t id; 852 853 if (softf->ipfr_lock || !softf->ipfr_ipidlist) 854 return 0xffffffff; 855 856 #ifdef USE_MUTEXES 857 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab, 858 &softf->ipfr_ipidfrag); 859 #else 860 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab); 861 #endif 862 if (ipf != NULL) { 863 id = (u_32_t)(intptr_t)ipf->ipfr_data; 864 RWLOCK_EXIT(&softf->ipfr_ipidfrag); 865 } else 866 id = 0xffffffff; 867 return id; 868 } 869 870 871 /* ------------------------------------------------------------------------ */ 872 /* Function: ipf_frag_known */ 873 /* Returns: frentry_t* - pointer to filter rule if a match is found in */ 874 /* the frag cache table, else NULL. */ 875 /* Parameters: fin(I) - pointer to packet information */ 876 /* passp(O) - pointer to where to store rule flags resturned */ 877 /* */ 878 /* Functional interface for normal lookups of the fragment cache. If a */ 879 /* match is found, return the rule pointer and flags from the rule, except */ 880 /* that if FR_LOGFIRST is set, reset FR_LOG. */ 881 /* ------------------------------------------------------------------------ */ 882 frentry_t * 883 ipf_frag_known(fr_info_t *fin, u_32_t *passp) 884 { 885 ipf_main_softc_t *softc = fin->fin_main_soft; 886 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 887 frentry_t *fr = NULL; 888 ipfr_t *fra; 889 u_32_t pass; 890 891 if ((softf->ipfr_lock) || (softf->ipfr_list == NULL)) 892 return NULL; 893 894 #ifdef USE_MUTEXES 895 fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads, 896 &softc->ipf_frag); 897 #else 898 fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads); 899 #endif 900 if (fra != NULL) { 901 if (fin->fin_flx & FI_BAD) { 902 fr = &ipfr_block; 903 fin->fin_reason = FRB_BADFRAG; 904 } else { 905 fr = fra->ipfr_rule; 906 } 907 fin->fin_fr = fr; 908 if (fr != NULL) { 909 pass = fr->fr_flags; 910 if ((pass & FR_KEEPSTATE) != 0) { 911 fin->fin_flx |= FI_STATE; 912 /* 913 * Reset the keep state flag here so that we 914 * don't try and add a new state entry because 915 * of a match here. That leads to blocking of 916 * the packet later because the add fails. 917 */ 918 pass &= ~FR_KEEPSTATE; 919 } 920 if ((pass & FR_LOGFIRST) != 0) 921 pass &= ~(FR_LOGFIRST|FR_LOG); 922 *passp = pass; 923 } 924 RWLOCK_EXIT(&softc->ipf_frag); 925 } 926 return fr; 927 } 928 929 930 /* ------------------------------------------------------------------------ */ 931 /* Function: ipf_frag_natforget */ 932 /* Returns: Nil */ 933 /* Parameters: ptr(I) - pointer to data structure */ 934 /* */ 935 /* Search through all of the fragment cache entries for NAT and wherever a */ 936 /* pointer is found to match ptr, reset it to NULL. */ 937 /* ------------------------------------------------------------------------ */ 938 void 939 ipf_frag_natforget(ipf_main_softc_t *softc, void *ptr) 940 { 941 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 942 ipfr_t *fr; 943 944 WRITE_ENTER(&softf->ipfr_natfrag); 945 for (fr = softf->ipfr_natlist; fr; fr = fr->ipfr_next) 946 if (fr->ipfr_data == ptr) 947 fr->ipfr_data = NULL; 948 RWLOCK_EXIT(&softf->ipfr_natfrag); 949 } 950 951 952 /* ------------------------------------------------------------------------ */ 953 /* Function: ipf_frag_delete */ 954 /* Returns: Nil */ 955 /* Parameters: fra(I) - pointer to fragment structure to delete */ 956 /* tail(IO) - pointer to the pointer to the tail of the frag */ 957 /* list */ 958 /* */ 959 /* Remove a fragment cache table entry from the table & list. Also free */ 960 /* the filter rule it is associated with it if it is no longer used as a */ 961 /* result of decreasing the reference count. */ 962 /* ------------------------------------------------------------------------ */ 963 static void 964 ipf_frag_delete(ipf_main_softc_t *softc, ipfr_t *fra, ipfr_t ***tail) 965 { 966 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 967 968 if (fra->ipfr_next) 969 fra->ipfr_next->ipfr_prev = fra->ipfr_prev; 970 *fra->ipfr_prev = fra->ipfr_next; 971 if (*tail == &fra->ipfr_next) 972 *tail = fra->ipfr_prev; 973 974 if (fra->ipfr_hnext) 975 fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev; 976 *fra->ipfr_hprev = fra->ipfr_hnext; 977 978 if (fra->ipfr_rule != NULL) { 979 (void) ipf_derefrule(softc, &fra->ipfr_rule); 980 } 981 982 if (fra->ipfr_ref <= 0) 983 ipf_frag_free(softf, fra); 984 } 985 986 987 /* ------------------------------------------------------------------------ */ 988 /* Function: ipf_frag_free */ 989 /* Returns: Nil */ 990 /* */ 991 /* ------------------------------------------------------------------------ */ 992 static void 993 ipf_frag_free(ipf_frag_softc_t *softf, ipfr_t *fra) 994 { 995 KFREE(fra); 996 FBUMP(ifs_expire); 997 softf->ipfr_stats.ifs_inuse--; 998 } 999 1000 1001 /* ------------------------------------------------------------------------ */ 1002 /* Function: ipf_frag_clear */ 1003 /* Returns: Nil */ 1004 /* Parameters: Nil */ 1005 /* */ 1006 /* Free memory in use by fragment state information kept. Do the normal */ 1007 /* fragment state stuff first and then the NAT-fragment table. */ 1008 /* ------------------------------------------------------------------------ */ 1009 void 1010 ipf_frag_clear(ipf_main_softc_t *softc) 1011 { 1012 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1013 ipfr_t *fra; 1014 nat_t *nat; 1015 1016 WRITE_ENTER(&softc->ipf_frag); 1017 while ((fra = softf->ipfr_list) != NULL) { 1018 fra->ipfr_ref--; 1019 ipf_frag_delete(softc, fra, &softf->ipfr_tail); 1020 } 1021 softf->ipfr_tail = &softf->ipfr_list; 1022 RWLOCK_EXIT(&softc->ipf_frag); 1023 1024 WRITE_ENTER(&softc->ipf_nat); 1025 WRITE_ENTER(&softf->ipfr_natfrag); 1026 while ((fra = softf->ipfr_natlist) != NULL) { 1027 nat = fra->ipfr_data; 1028 if (nat != NULL) { 1029 if (nat->nat_data == fra) 1030 nat->nat_data = NULL; 1031 } 1032 fra->ipfr_ref--; 1033 ipf_frag_delete(softc, fra, &softf->ipfr_nattail); 1034 } 1035 softf->ipfr_nattail = &softf->ipfr_natlist; 1036 RWLOCK_EXIT(&softf->ipfr_natfrag); 1037 RWLOCK_EXIT(&softc->ipf_nat); 1038 } 1039 1040 1041 /* ------------------------------------------------------------------------ */ 1042 /* Function: ipf_frag_expire */ 1043 /* Returns: Nil */ 1044 /* Parameters: Nil */ 1045 /* */ 1046 /* Expire entries in the fragment cache table that have been there too long */ 1047 /* ------------------------------------------------------------------------ */ 1048 void 1049 ipf_frag_expire(ipf_main_softc_t *softc) 1050 { 1051 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1052 ipfr_t **fp, *fra; 1053 nat_t *nat; 1054 SPL_INT(s); 1055 1056 if (softf->ipfr_lock) 1057 return; 1058 1059 SPL_NET(s); 1060 WRITE_ENTER(&softc->ipf_frag); 1061 /* 1062 * Go through the entire table, looking for entries to expire, 1063 * which is indicated by the ttl being less than or equal to ipf_ticks. 1064 */ 1065 for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) { 1066 if (fra->ipfr_ttl > softc->ipf_ticks) 1067 break; 1068 fra->ipfr_ref--; 1069 ipf_frag_delete(softc, fra, &softf->ipfr_tail); 1070 } 1071 RWLOCK_EXIT(&softc->ipf_frag); 1072 1073 WRITE_ENTER(&softf->ipfr_ipidfrag); 1074 for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) { 1075 if (fra->ipfr_ttl > softc->ipf_ticks) 1076 break; 1077 fra->ipfr_ref--; 1078 ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail); 1079 } 1080 RWLOCK_EXIT(&softf->ipfr_ipidfrag); 1081 1082 /* 1083 * Same again for the NAT table, except that if the structure also 1084 * still points to a NAT structure, and the NAT structure points back 1085 * at the one to be free'd, NULL the reference from the NAT struct. 1086 * NOTE: We need to grab both mutex's early, and in this order so as 1087 * to prevent a deadlock if both try to expire at the same time. 1088 * The extra if() statement here is because it locks out all NAT 1089 * operations - no need to do that if there are no entries in this 1090 * list, right? 1091 */ 1092 if (softf->ipfr_natlist != NULL) { 1093 WRITE_ENTER(&softc->ipf_nat); 1094 WRITE_ENTER(&softf->ipfr_natfrag); 1095 for (fp = &softf->ipfr_natlist; ((fra = *fp) != NULL); ) { 1096 if (fra->ipfr_ttl > softc->ipf_ticks) 1097 break; 1098 nat = fra->ipfr_data; 1099 if (nat != NULL) { 1100 if (nat->nat_data == fra) 1101 nat->nat_data = NULL; 1102 } 1103 fra->ipfr_ref--; 1104 ipf_frag_delete(softc, fra, &softf->ipfr_nattail); 1105 } 1106 RWLOCK_EXIT(&softf->ipfr_natfrag); 1107 RWLOCK_EXIT(&softc->ipf_nat); 1108 } 1109 SPL_X(s); 1110 } 1111 1112 1113 /* ------------------------------------------------------------------------ */ 1114 /* Function: ipf_frag_pkt_next */ 1115 /* ------------------------------------------------------------------------ */ 1116 int 1117 ipf_frag_pkt_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) 1118 { 1119 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1120 1121 #ifdef USE_MUTEXES 1122 return ipf_frag_next(softc, token, itp, &softf->ipfr_list, 1123 &softf->ipfr_frag); 1124 #else 1125 return ipf_frag_next(softc, token, itp, &softf->ipfr_list); 1126 #endif 1127 } 1128 1129 1130 /* ------------------------------------------------------------------------ */ 1131 /* Function: ipf_frag_nat_next */ 1132 /* ------------------------------------------------------------------------ */ 1133 int 1134 ipf_frag_nat_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) 1135 { 1136 ipf_frag_softc_t *softf = softc->ipf_frag_soft;; 1137 1138 #ifdef USE_MUTEXES 1139 return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist, 1140 &softf->ipfr_natfrag); 1141 #else 1142 return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist); 1143 #endif 1144 } 1145 1146 /* ------------------------------------------------------------------------ */ 1147 /* Function: ipf_frag_next */ 1148 /* Returns: int - 0 == success, else error */ 1149 /* Parameters: token(I) - pointer to token information for this caller */ 1150 /* itp(I) - pointer to generic iterator from caller */ 1151 /* top(I) - top of the fragment list */ 1152 /* lock(I) - fragment cache lock */ 1153 /* */ 1154 /* This function is used to interate through the list of entries in the */ 1155 /* fragment cache. It increases the reference count on the one currently */ 1156 /* being returned so that the caller can come back and resume from it later.*/ 1157 /* */ 1158 /* This function is used for both the NAT fragment cache as well as the ipf */ 1159 /* fragment cache - hence the reason for passing in top and lock. */ 1160 /* ------------------------------------------------------------------------ */ 1161 static int 1162 ipf_frag_next( 1163 ipf_main_softc_t *softc, 1164 ipftoken_t *token, 1165 ipfgeniter_t *itp, 1166 ipfr_t **top 1167 #ifdef USE_MUTEXES 1168 , ipfrwlock_t *lock 1169 #endif 1170 ) 1171 { 1172 ipfr_t *frag, *next, zero; 1173 int error = 0; 1174 1175 if (itp->igi_data == NULL) { 1176 IPFERROR(20001); 1177 return EFAULT; 1178 } 1179 1180 if (itp->igi_nitems != 1) { 1181 IPFERROR(20003); 1182 return EFAULT; 1183 } 1184 1185 frag = token->ipt_data; 1186 1187 READ_ENTER(lock); 1188 1189 if (frag == NULL) 1190 next = *top; 1191 else 1192 next = frag->ipfr_next; 1193 1194 if (next != NULL) { 1195 ATOMIC_INC(next->ipfr_ref); 1196 token->ipt_data = next; 1197 } else { 1198 bzero(&zero, sizeof(zero)); 1199 next = &zero; 1200 token->ipt_data = NULL; 1201 } 1202 if (next->ipfr_next == NULL) 1203 ipf_token_mark_complete(token); 1204 1205 RWLOCK_EXIT(lock); 1206 1207 error = COPYOUT(next, itp->igi_data, sizeof(*next)); 1208 if (error != 0) 1209 IPFERROR(20002); 1210 1211 if (frag != NULL) { 1212 #ifdef USE_MUTEXES 1213 ipf_frag_deref(softc, &frag, lock); 1214 #else 1215 ipf_frag_deref(softc, &frag); 1216 #endif 1217 } 1218 return error; 1219 } 1220 1221 1222 /* ------------------------------------------------------------------------ */ 1223 /* Function: ipf_frag_pkt_deref */ 1224 /* Returns: Nil */ 1225 /* */ 1226 /* ------------------------------------------------------------------------ */ 1227 void 1228 ipf_frag_pkt_deref(ipf_main_softc_t *softc, void *data) 1229 { 1230 ipfr_t **frp = data; 1231 1232 #ifdef USE_MUTEXES 1233 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1234 1235 ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_frag); 1236 #else 1237 ipf_frag_deref(softc->ipf_frag_soft, frp); 1238 #endif 1239 } 1240 1241 1242 /* ------------------------------------------------------------------------ */ 1243 /* Function: ipf_frag_nat_deref */ 1244 /* Returns: Nil */ 1245 /* */ 1246 /* ------------------------------------------------------------------------ */ 1247 void 1248 ipf_frag_nat_deref(ipf_main_softc_t *softc, void *data) 1249 { 1250 ipfr_t **frp = data; 1251 1252 #ifdef USE_MUTEXES 1253 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1254 1255 ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_natfrag); 1256 #else 1257 ipf_frag_deref(softc->ipf_frag_soft, frp); 1258 #endif 1259 } 1260 1261 1262 /* ------------------------------------------------------------------------ */ 1263 /* Function: ipf_frag_deref */ 1264 /* Returns: Nil */ 1265 /* Parameters: frp(IO) - pointer to fragment structure to deference */ 1266 /* lock(I) - lock associated with the fragment */ 1267 /* */ 1268 /* This function dereferences a fragment structure (ipfr_t). The pointer */ 1269 /* passed in will always be reset back to NULL, even if the structure is */ 1270 /* not freed, to enforce the notion that the caller is no longer entitled */ 1271 /* to use the pointer it is dropping the reference to. */ 1272 /* ------------------------------------------------------------------------ */ 1273 static void 1274 ipf_frag_deref(void *arg, ipfr_t **frp 1275 #ifdef USE_MUTEXES 1276 , ipfrwlock_t *lock 1277 #endif 1278 ) 1279 { 1280 ipf_frag_softc_t *softf = arg; 1281 ipfr_t *fra; 1282 1283 fra = *frp; 1284 *frp = NULL; 1285 1286 WRITE_ENTER(lock); 1287 fra->ipfr_ref--; 1288 if (fra->ipfr_ref <= 0) 1289 ipf_frag_free(softf, fra); 1290 RWLOCK_EXIT(lock); 1291 } 1292