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