1 /* $NetBSD: ip_frag.c,v 1.3 2012/07/22 14:27:51 darrenr 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.3 2012/07/22 14:27:51 darrenr 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(fra); 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 (f != table[idx]) { 730 ipfr_t **fp; 731 732 /* 733 * Move fragment info. to the top of the list 734 * to speed up searches. First, delink... 735 */ 736 fp = f->ipfr_hprev; 737 (*fp) = f->ipfr_hnext; 738 if (f->ipfr_hnext != NULL) 739 f->ipfr_hnext->ipfr_hprev = fp; 740 /* 741 * Then put back at the top of the chain. 742 */ 743 f->ipfr_hnext = table[idx]; 744 table[idx]->ipfr_hprev = &f->ipfr_hnext; 745 f->ipfr_hprev = table + idx; 746 table[idx] = f; 747 } 748 749 /* 750 * If we've follwed the fragments, and this is the 751 * last (in order), shrink expiration time. 752 */ 753 if (off == f->ipfr_off) { 754 f->ipfr_off = (fin->fin_dlen >> 3) + off; 755 756 /* 757 * Well, we could shrink the expiration time 758 * but only if every fragment has been seen 759 * in order upto this, the last. ipfr_badorder 760 * is used here to count those out of order 761 * and if it equals 0 when we get to the last 762 * fragment then we can assume all of the 763 * fragments have been seen and in order. 764 */ 765 #if 0 766 /* 767 * Doing this properly requires moving it to 768 * the head of the list which is infesible. 769 */ 770 if ((more == 0) && (f->ipfr_badorder == 0)) 771 f->ipfr_ttl = softc->ipf_ticks + 1; 772 #endif 773 } else { 774 f->ipfr_badorder++; 775 FBUMPD(ifs_unordered); 776 if (f->ipfr_pass & FR_FRSTRICT) { 777 FBUMPD(ifs_strict); 778 continue; 779 } 780 } 781 f->ipfr_pkts++; 782 f->ipfr_bytes += fin->fin_plen; 783 FBUMP(ifs_hits); 784 return f; 785 } 786 } 787 788 RWLOCK_EXIT(lock); 789 FBUMP(ifs_miss); 790 return NULL; 791 } 792 793 794 /* ------------------------------------------------------------------------ */ 795 /* Function: ipf_frag_natknown */ 796 /* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ 797 /* match found, else NULL */ 798 /* Parameters: fin(I) - pointer to packet information */ 799 /* */ 800 /* Functional interface for NAT lookups of the NAT fragment cache */ 801 /* ------------------------------------------------------------------------ */ 802 nat_t * 803 ipf_frag_natknown(fr_info_t *fin) 804 { 805 ipf_main_softc_t *softc = fin->fin_main_soft; 806 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 807 nat_t *nat; 808 ipfr_t *ipf; 809 810 if ((softf->ipfr_lock) || !softf->ipfr_natlist) 811 return NULL; 812 #ifdef USE_MUTEXES 813 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab, 814 &softf->ipfr_natfrag); 815 #else 816 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab); 817 #endif 818 if (ipf != NULL) { 819 nat = ipf->ipfr_data; 820 /* 821 * This is the last fragment for this packet. 822 */ 823 if ((ipf->ipfr_ttl == softc->ipf_ticks + 1) && (nat != NULL)) { 824 nat->nat_data = NULL; 825 ipf->ipfr_data = NULL; 826 } 827 RWLOCK_EXIT(&softf->ipfr_natfrag); 828 } else 829 nat = NULL; 830 return nat; 831 } 832 833 834 /* ------------------------------------------------------------------------ */ 835 /* Function: ipf_frag_ipidknown */ 836 /* Returns: u_32_t - IPv4 ID for this packet if match found, else */ 837 /* return 0xfffffff to indicate no match. */ 838 /* Parameters: fin(I) - pointer to packet information */ 839 /* */ 840 /* Functional interface for IP ID lookups of the IP ID fragment cache */ 841 /* ------------------------------------------------------------------------ */ 842 u_32_t 843 ipf_frag_ipidknown(fr_info_t *fin) 844 { 845 ipf_main_softc_t *softc = fin->fin_main_soft; 846 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 847 ipfr_t *ipf; 848 u_32_t id; 849 850 if (softf->ipfr_lock || !softf->ipfr_ipidlist) 851 return 0xffffffff; 852 853 #ifdef USE_MUTEXES 854 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab, 855 &softf->ipfr_ipidfrag); 856 #else 857 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab); 858 #endif 859 if (ipf != NULL) { 860 id = (u_32_t)(intptr_t)ipf->ipfr_data; 861 RWLOCK_EXIT(&softf->ipfr_ipidfrag); 862 } else 863 id = 0xffffffff; 864 return id; 865 } 866 867 868 /* ------------------------------------------------------------------------ */ 869 /* Function: ipf_frag_known */ 870 /* Returns: frentry_t* - pointer to filter rule if a match is found in */ 871 /* the frag cache table, else NULL. */ 872 /* Parameters: fin(I) - pointer to packet information */ 873 /* passp(O) - pointer to where to store rule flags resturned */ 874 /* */ 875 /* Functional interface for normal lookups of the fragment cache. If a */ 876 /* match is found, return the rule pointer and flags from the rule, except */ 877 /* that if FR_LOGFIRST is set, reset FR_LOG. */ 878 /* ------------------------------------------------------------------------ */ 879 frentry_t * 880 ipf_frag_known(fr_info_t *fin, u_32_t *passp) 881 { 882 ipf_main_softc_t *softc = fin->fin_main_soft; 883 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 884 frentry_t *fr = NULL; 885 ipfr_t *fra; 886 u_32_t pass; 887 888 if ((softf->ipfr_lock) || (softf->ipfr_list == NULL)) 889 return NULL; 890 891 #ifdef USE_MUTEXES 892 fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads, 893 &softc->ipf_frag); 894 #else 895 fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads); 896 #endif 897 if (fra != NULL) { 898 if (fin->fin_flx & FI_BAD) { 899 fr = &ipfr_block; 900 fin->fin_reason = FRB_BADFRAG; 901 } else { 902 fr = fra->ipfr_rule; 903 } 904 fin->fin_fr = fr; 905 if (fr != NULL) { 906 pass = fr->fr_flags; 907 if ((pass & FR_KEEPSTATE) != 0) { 908 fin->fin_flx |= FI_STATE; 909 /* 910 * Reset the keep state flag here so that we 911 * don't try and add a new state entry because 912 * of a match here. That leads to blocking of 913 * the packet later because the add fails. 914 */ 915 pass &= ~FR_KEEPSTATE; 916 } 917 if ((pass & FR_LOGFIRST) != 0) 918 pass &= ~(FR_LOGFIRST|FR_LOG); 919 *passp = pass; 920 } 921 RWLOCK_EXIT(&softc->ipf_frag); 922 } 923 return fr; 924 } 925 926 927 /* ------------------------------------------------------------------------ */ 928 /* Function: ipf_frag_natforget */ 929 /* Returns: Nil */ 930 /* Parameters: ptr(I) - pointer to data structure */ 931 /* */ 932 /* Search through all of the fragment cache entries for NAT and wherever a */ 933 /* pointer is found to match ptr, reset it to NULL. */ 934 /* ------------------------------------------------------------------------ */ 935 void 936 ipf_frag_natforget(ipf_main_softc_t *softc, void *ptr) 937 { 938 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 939 ipfr_t *fr; 940 941 WRITE_ENTER(&softf->ipfr_natfrag); 942 for (fr = softf->ipfr_natlist; fr; fr = fr->ipfr_next) 943 if (fr->ipfr_data == ptr) 944 fr->ipfr_data = NULL; 945 RWLOCK_EXIT(&softf->ipfr_natfrag); 946 } 947 948 949 /* ------------------------------------------------------------------------ */ 950 /* Function: ipf_frag_delete */ 951 /* Returns: Nil */ 952 /* Parameters: fra(I) - pointer to fragment structure to delete */ 953 /* tail(IO) - pointer to the pointer to the tail of the frag */ 954 /* list */ 955 /* */ 956 /* Remove a fragment cache table entry from the table & list. Also free */ 957 /* the filter rule it is associated with it if it is no longer used as a */ 958 /* result of decreasing the reference count. */ 959 /* ------------------------------------------------------------------------ */ 960 static void 961 ipf_frag_delete(ipf_main_softc_t *softc, ipfr_t *fra, ipfr_t ***tail) 962 { 963 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 964 965 if (fra->ipfr_next) 966 fra->ipfr_next->ipfr_prev = fra->ipfr_prev; 967 *fra->ipfr_prev = fra->ipfr_next; 968 if (*tail == &fra->ipfr_next) 969 *tail = fra->ipfr_prev; 970 971 if (fra->ipfr_hnext) 972 fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev; 973 *fra->ipfr_hprev = fra->ipfr_hnext; 974 975 if (fra->ipfr_rule != NULL) { 976 (void) ipf_derefrule(softc, &fra->ipfr_rule); 977 } 978 979 if (fra->ipfr_ref <= 0) 980 ipf_frag_free(softf, fra); 981 } 982 983 984 /* ------------------------------------------------------------------------ */ 985 /* Function: ipf_frag_free */ 986 /* Returns: Nil */ 987 /* */ 988 /* ------------------------------------------------------------------------ */ 989 static void 990 ipf_frag_free(ipf_frag_softc_t *softf, ipfr_t *fra) 991 { 992 KFREE(fra); 993 FBUMP(ifs_expire); 994 softf->ipfr_stats.ifs_inuse--; 995 } 996 997 998 /* ------------------------------------------------------------------------ */ 999 /* Function: ipf_frag_clear */ 1000 /* Returns: Nil */ 1001 /* Parameters: Nil */ 1002 /* */ 1003 /* Free memory in use by fragment state information kept. Do the normal */ 1004 /* fragment state stuff first and then the NAT-fragment table. */ 1005 /* ------------------------------------------------------------------------ */ 1006 void 1007 ipf_frag_clear(ipf_main_softc_t *softc) 1008 { 1009 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1010 ipfr_t *fra; 1011 nat_t *nat; 1012 1013 WRITE_ENTER(&softc->ipf_frag); 1014 while ((fra = softf->ipfr_list) != NULL) { 1015 fra->ipfr_ref--; 1016 ipf_frag_delete(softc, fra, &softf->ipfr_tail); 1017 } 1018 softf->ipfr_tail = &softf->ipfr_list; 1019 RWLOCK_EXIT(&softc->ipf_frag); 1020 1021 WRITE_ENTER(&softc->ipf_nat); 1022 WRITE_ENTER(&softf->ipfr_natfrag); 1023 while ((fra = softf->ipfr_natlist) != NULL) { 1024 nat = fra->ipfr_data; 1025 if (nat != NULL) { 1026 if (nat->nat_data == fra) 1027 nat->nat_data = NULL; 1028 } 1029 fra->ipfr_ref--; 1030 ipf_frag_delete(softc, fra, &softf->ipfr_nattail); 1031 } 1032 softf->ipfr_nattail = &softf->ipfr_natlist; 1033 RWLOCK_EXIT(&softf->ipfr_natfrag); 1034 RWLOCK_EXIT(&softc->ipf_nat); 1035 } 1036 1037 1038 /* ------------------------------------------------------------------------ */ 1039 /* Function: ipf_frag_expire */ 1040 /* Returns: Nil */ 1041 /* Parameters: Nil */ 1042 /* */ 1043 /* Expire entries in the fragment cache table that have been there too long */ 1044 /* ------------------------------------------------------------------------ */ 1045 void 1046 ipf_frag_expire(ipf_main_softc_t *softc) 1047 { 1048 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1049 ipfr_t **fp, *fra; 1050 nat_t *nat; 1051 SPL_INT(s); 1052 1053 if (softf->ipfr_lock) 1054 return; 1055 1056 SPL_NET(s); 1057 WRITE_ENTER(&softc->ipf_frag); 1058 /* 1059 * Go through the entire table, looking for entries to expire, 1060 * which is indicated by the ttl being less than or equal to ipf_ticks. 1061 */ 1062 for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) { 1063 if (fra->ipfr_ttl > softc->ipf_ticks) 1064 break; 1065 fra->ipfr_ref--; 1066 ipf_frag_delete(softc, fra, &softf->ipfr_tail); 1067 } 1068 RWLOCK_EXIT(&softc->ipf_frag); 1069 1070 WRITE_ENTER(&softf->ipfr_ipidfrag); 1071 for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) { 1072 if (fra->ipfr_ttl > softc->ipf_ticks) 1073 break; 1074 fra->ipfr_ref--; 1075 ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail); 1076 } 1077 RWLOCK_EXIT(&softf->ipfr_ipidfrag); 1078 1079 /* 1080 * Same again for the NAT table, except that if the structure also 1081 * still points to a NAT structure, and the NAT structure points back 1082 * at the one to be free'd, NULL the reference from the NAT struct. 1083 * NOTE: We need to grab both mutex's early, and in this order so as 1084 * to prevent a deadlock if both try to expire at the same time. 1085 * The extra if() statement here is because it locks out all NAT 1086 * operations - no need to do that if there are no entries in this 1087 * list, right? 1088 */ 1089 if (softf->ipfr_natlist != NULL) { 1090 WRITE_ENTER(&softc->ipf_nat); 1091 WRITE_ENTER(&softf->ipfr_natfrag); 1092 for (fp = &softf->ipfr_natlist; ((fra = *fp) != NULL); ) { 1093 if (fra->ipfr_ttl > softc->ipf_ticks) 1094 break; 1095 nat = fra->ipfr_data; 1096 if (nat != NULL) { 1097 if (nat->nat_data == fra) 1098 nat->nat_data = NULL; 1099 } 1100 fra->ipfr_ref--; 1101 ipf_frag_delete(softc, fra, &softf->ipfr_nattail); 1102 } 1103 RWLOCK_EXIT(&softf->ipfr_natfrag); 1104 RWLOCK_EXIT(&softc->ipf_nat); 1105 } 1106 SPL_X(s); 1107 } 1108 1109 1110 /* ------------------------------------------------------------------------ */ 1111 /* Function: ipf_frag_pkt_next */ 1112 /* ------------------------------------------------------------------------ */ 1113 int 1114 ipf_frag_pkt_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) 1115 { 1116 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1117 1118 #ifdef USE_MUTEXES 1119 return ipf_frag_next(softc, token, itp, &softf->ipfr_list, 1120 &softf->ipfr_frag); 1121 #else 1122 return ipf_frag_next(softc, token, itp, &softf->ipfr_list); 1123 #endif 1124 } 1125 1126 1127 /* ------------------------------------------------------------------------ */ 1128 /* Function: ipf_frag_nat_next */ 1129 /* ------------------------------------------------------------------------ */ 1130 int 1131 ipf_frag_nat_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) 1132 { 1133 ipf_frag_softc_t *softf = softc->ipf_frag_soft;; 1134 1135 #ifdef USE_MUTEXES 1136 return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist, 1137 &softf->ipfr_natfrag); 1138 #else 1139 return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist); 1140 #endif 1141 } 1142 1143 /* ------------------------------------------------------------------------ */ 1144 /* Function: ipf_frag_next */ 1145 /* Returns: int - 0 == success, else error */ 1146 /* Parameters: token(I) - pointer to token information for this caller */ 1147 /* itp(I) - pointer to generic iterator from caller */ 1148 /* top(I) - top of the fragment list */ 1149 /* lock(I) - fragment cache lock */ 1150 /* */ 1151 /* This function is used to interate through the list of entries in the */ 1152 /* fragment cache. It increases the reference count on the one currently */ 1153 /* being returned so that the caller can come back and resume from it later.*/ 1154 /* */ 1155 /* This function is used for both the NAT fragment cache as well as the ipf */ 1156 /* fragment cache - hence the reason for passing in top and lock. */ 1157 /* ------------------------------------------------------------------------ */ 1158 static int 1159 ipf_frag_next( 1160 ipf_main_softc_t *softc, 1161 ipftoken_t *token, 1162 ipfgeniter_t *itp, 1163 ipfr_t **top 1164 #ifdef USE_MUTEXES 1165 , ipfrwlock_t *lock 1166 #endif 1167 ) 1168 { 1169 ipfr_t *frag, *next, zero; 1170 int error = 0; 1171 1172 if (itp->igi_data == NULL) { 1173 IPFERROR(20001); 1174 return EFAULT; 1175 } 1176 1177 if (itp->igi_nitems != 1) { 1178 IPFERROR(20003); 1179 return EFAULT; 1180 } 1181 1182 frag = token->ipt_data; 1183 1184 READ_ENTER(lock); 1185 1186 if (frag == NULL) 1187 next = *top; 1188 else 1189 next = frag->ipfr_next; 1190 1191 if (next != NULL) { 1192 ATOMIC_INC(next->ipfr_ref); 1193 token->ipt_data = next; 1194 } else { 1195 bzero(&zero, sizeof(zero)); 1196 next = &zero; 1197 token->ipt_data = NULL; 1198 } 1199 if (next->ipfr_next == NULL) 1200 ipf_token_mark_complete(token); 1201 1202 RWLOCK_EXIT(lock); 1203 1204 error = COPYOUT(next, itp->igi_data, sizeof(*next)); 1205 if (error != 0) 1206 IPFERROR(20002); 1207 1208 if (frag != NULL) { 1209 #ifdef USE_MUTEXES 1210 ipf_frag_deref(softc, &frag, lock); 1211 #else 1212 ipf_frag_deref(softc, &frag); 1213 #endif 1214 } 1215 return error; 1216 } 1217 1218 1219 /* ------------------------------------------------------------------------ */ 1220 /* Function: ipf_frag_pkt_deref */ 1221 /* Returns: Nil */ 1222 /* */ 1223 /* ------------------------------------------------------------------------ */ 1224 void 1225 ipf_frag_pkt_deref(ipf_main_softc_t *softc, void *data) 1226 { 1227 ipfr_t **frp = data; 1228 1229 #ifdef USE_MUTEXES 1230 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1231 1232 ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_frag); 1233 #else 1234 ipf_frag_deref(softc->ipf_frag_soft, frp); 1235 #endif 1236 } 1237 1238 1239 /* ------------------------------------------------------------------------ */ 1240 /* Function: ipf_frag_nat_deref */ 1241 /* Returns: Nil */ 1242 /* */ 1243 /* ------------------------------------------------------------------------ */ 1244 void 1245 ipf_frag_nat_deref(ipf_main_softc_t *softc, void *data) 1246 { 1247 ipfr_t **frp = data; 1248 1249 #ifdef USE_MUTEXES 1250 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1251 1252 ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_natfrag); 1253 #else 1254 ipf_frag_deref(softc->ipf_frag_soft, frp); 1255 #endif 1256 } 1257 1258 1259 /* ------------------------------------------------------------------------ */ 1260 /* Function: ipf_frag_deref */ 1261 /* Returns: Nil */ 1262 /* Parameters: frp(IO) - pointer to fragment structure to deference */ 1263 /* lock(I) - lock associated with the fragment */ 1264 /* */ 1265 /* This function dereferences a fragment structure (ipfr_t). The pointer */ 1266 /* passed in will always be reset back to NULL, even if the structure is */ 1267 /* not freed, to enforce the notion that the caller is no longer entitled */ 1268 /* to use the pointer it is dropping the reference to. */ 1269 /* ------------------------------------------------------------------------ */ 1270 static void 1271 ipf_frag_deref(void *arg, ipfr_t **frp 1272 #ifdef USE_MUTEXES 1273 , ipfrwlock_t *lock 1274 #endif 1275 ) 1276 { 1277 ipf_frag_softc_t *softf = arg; 1278 ipfr_t *fra; 1279 1280 fra = *frp; 1281 *frp = NULL; 1282 1283 WRITE_ENTER(lock); 1284 fra->ipfr_ref--; 1285 if (fra->ipfr_ref <= 0) 1286 ipf_frag_free(softf, fra); 1287 RWLOCK_EXIT(lock); 1288 } 1289