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