ip_frag.c revision 153876
1/* $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_frag.c 153876 2005-12-30 11:32:23Z guido $ */ 2 3/* 4 * Copyright (C) 1993-2003 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) && (__FreeBSD_version >= 220000) 34# include <sys/filio.h> 35# include <sys/fcntl.h> 36#else 37# include <sys/ioctl.h> 38#endif 39#if !defined(linux) 40# include <sys/protosw.h> 41#endif 42#include <sys/socket.h> 43#if defined(_KERNEL) 44# include <sys/systm.h> 45# if !defined(__SVR4) && !defined(__svr4__) 46# include <sys/mbuf.h> 47# endif 48#endif 49#if !defined(__SVR4) && !defined(__svr4__) 50# if defined(_KERNEL) && !defined(__sgi) && !defined(AIX) 51# include <sys/kernel.h> 52# endif 53#else 54# include <sys/byteorder.h> 55# ifdef _KERNEL 56# include <sys/dditypes.h> 57# endif 58# include <sys/stream.h> 59# include <sys/kmem.h> 60#endif 61#include <net/if.h> 62#ifdef sun 63# include <net/af.h> 64#endif 65#include <net/route.h> 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_proxy.h" 83#if (__FreeBSD_version >= 300000) 84# include <sys/malloc.h> 85# if defined(_KERNEL) 86# ifndef IPFILTER_LKM 87# include <sys/libkern.h> 88# include <sys/systm.h> 89# endif 90extern struct callout_handle fr_slowtimer_ch; 91# endif 92#endif 93#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) 94# include <sys/callout.h> 95extern struct callout fr_slowtimer_ch; 96#endif 97#if defined(__OpenBSD__) 98# include <sys/timeout.h> 99extern struct timeout fr_slowtimer_ch; 100#endif 101/* END OF INCLUDES */ 102 103#if !defined(lint) 104static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed"; 105static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_frag.c 153876 2005-12-30 11:32:23Z guido $"; 106/* static const char rcsid[] = "@(#)Id: ip_frag.c,v 2.77 2004/01/27 00:24:54 darrenr Exp"; */ 107#endif 108 109 110static ipfr_t *ipfr_list = NULL; 111static ipfr_t **ipfr_tail = &ipfr_list; 112static ipfr_t **ipfr_heads; 113 114static ipfr_t *ipfr_natlist = NULL; 115static ipfr_t **ipfr_nattail = &ipfr_natlist; 116static ipfr_t **ipfr_nattab; 117 118static ipfr_t *ipfr_ipidlist = NULL; 119static ipfr_t **ipfr_ipidtail = &ipfr_ipidlist; 120static ipfr_t **ipfr_ipidtab; 121 122static ipfrstat_t ipfr_stats; 123static int ipfr_inuse = 0; 124int ipfr_size = IPFT_SIZE; 125 126int fr_ipfrttl = 120; /* 60 seconds */ 127int fr_frag_lock = 0; 128int fr_frag_init = 0; 129u_long fr_ticks = 0; 130 131 132static ipfr_t *ipfr_newfrag __P((fr_info_t *, u_32_t, ipfr_t **)); 133static ipfr_t *fr_fraglookup __P((fr_info_t *, ipfr_t **)); 134static void fr_fragdelete __P((ipfr_t *, ipfr_t ***)); 135 136 137/* ------------------------------------------------------------------------ */ 138/* Function: fr_fraginit */ 139/* Returns: int - 0 == success, -1 == error */ 140/* Parameters: Nil */ 141/* */ 142/* Initialise the hash tables for the fragment cache lookups. */ 143/* ------------------------------------------------------------------------ */ 144int fr_fraginit() 145{ 146 KMALLOCS(ipfr_heads, ipfr_t **, ipfr_size * sizeof(ipfr_t *)); 147 if (ipfr_heads == NULL) 148 return -1; 149 bzero((char *)ipfr_heads, ipfr_size * sizeof(ipfr_t *)); 150 151 KMALLOCS(ipfr_nattab, ipfr_t **, ipfr_size * sizeof(ipfr_t *)); 152 if (ipfr_nattab == NULL) 153 return -1; 154 bzero((char *)ipfr_nattab, ipfr_size * sizeof(ipfr_t *)); 155 156 KMALLOCS(ipfr_ipidtab, ipfr_t **, ipfr_size * sizeof(ipfr_t *)); 157 if (ipfr_ipidtab == NULL) 158 return -1; 159 bzero((char *)ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *)); 160 161 RWLOCK_INIT(&ipf_frag, "ipf fragment rwlock"); 162 fr_frag_init = 1; 163 164 return 0; 165} 166 167 168/* ------------------------------------------------------------------------ */ 169/* Function: fr_fragunload */ 170/* Returns: Nil */ 171/* Parameters: Nil */ 172/* */ 173/* Free all memory allocated whilst running and from initialisation. */ 174/* ------------------------------------------------------------------------ */ 175void fr_fragunload() 176{ 177 if (fr_frag_init == 1) { 178 fr_fragclear(); 179 180 RW_DESTROY(&ipf_frag); 181 fr_frag_init = 0; 182 } 183 184 if (ipfr_heads != NULL) 185 KFREES(ipfr_heads, ipfr_size * sizeof(ipfr_t *)); 186 ipfr_heads = NULL; 187 188 if (ipfr_nattab != NULL) 189 KFREES(ipfr_nattab, ipfr_size * sizeof(ipfr_t *)); 190 ipfr_nattab = NULL; 191 192 if (ipfr_ipidtab != NULL) 193 KFREES(ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *)); 194 ipfr_ipidtab = NULL; 195} 196 197 198/* ------------------------------------------------------------------------ */ 199/* Function: fr_fragstats */ 200/* Returns: ipfrstat_t* - pointer to struct with current frag stats */ 201/* Parameters: Nil */ 202/* */ 203/* Updates ipfr_stats with current information and returns a pointer to it */ 204/* ------------------------------------------------------------------------ */ 205ipfrstat_t *fr_fragstats() 206{ 207 ipfr_stats.ifs_table = ipfr_heads; 208 ipfr_stats.ifs_nattab = ipfr_nattab; 209 ipfr_stats.ifs_inuse = ipfr_inuse; 210 return &ipfr_stats; 211} 212 213 214/* ------------------------------------------------------------------------ */ 215/* Function: ipfr_newfrag */ 216/* Returns: ipfr_t * - pointer to fragment cache state info or NULL */ 217/* Parameters: fin(I) - pointer to packet information */ 218/* table(I) - pointer to frag table to add to */ 219/* */ 220/* Add a new entry to the fragment cache, registering it as having come */ 221/* through this box, with the result of the filter operation. */ 222/* ------------------------------------------------------------------------ */ 223static ipfr_t *ipfr_newfrag(fin, pass, table) 224fr_info_t *fin; 225u_32_t pass; 226ipfr_t *table[]; 227{ 228 ipfr_t *fra, frag; 229 u_int idx, off; 230 ip_t *ip; 231 232 if (ipfr_inuse >= IPFT_SIZE) 233 return NULL; 234 235 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) 236 return NULL; 237 238 ip = fin->fin_ip; 239 240 if (pass & FR_FRSTRICT) 241 if (fin->fin_off != 0) 242 return NULL; 243 244 frag.ipfr_p = ip->ip_p; 245 idx = ip->ip_p; 246 frag.ipfr_id = ip->ip_id; 247 idx += ip->ip_id; 248 frag.ipfr_tos = ip->ip_tos; 249 frag.ipfr_src.s_addr = ip->ip_src.s_addr; 250 idx += ip->ip_src.s_addr; 251 frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; 252 idx += ip->ip_dst.s_addr; 253 frag.ipfr_ifp = fin->fin_ifp; 254 idx *= 127; 255 idx %= IPFT_SIZE; 256 257 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 258 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 259 frag.ipfr_auth = fin->fin_fi.fi_auth; 260 261 /* 262 * first, make sure it isn't already there... 263 */ 264 for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext) 265 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, 266 IPFR_CMPSZ)) { 267 ipfr_stats.ifs_exists++; 268 return NULL; 269 } 270 271 /* 272 * allocate some memory, if possible, if not, just record that we 273 * failed to do so. 274 */ 275 KMALLOC(fra, ipfr_t *); 276 if (fra == NULL) { 277 ipfr_stats.ifs_nomem++; 278 return NULL; 279 } 280 281 fra->ipfr_rule = fin->fin_fr; 282 if (fra->ipfr_rule != NULL) { 283 284 frentry_t *fr; 285 286 fr = fin->fin_fr; 287 MUTEX_ENTER(&fr->fr_lock); 288 fr->fr_ref++; 289 MUTEX_EXIT(&fr->fr_lock); 290 } 291 292 /* 293 * Insert the fragment into the fragment table, copy the struct used 294 * in the search using bcopy rather than reassign each field. 295 * Set the ttl to the default. 296 */ 297 if ((fra->ipfr_hnext = table[idx]) != NULL) 298 table[idx]->ipfr_hprev = &fra->ipfr_hnext; 299 fra->ipfr_hprev = table + idx; 300 fra->ipfr_data = NULL; 301 table[idx] = fra; 302 bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); 303 fra->ipfr_ttl = fr_ticks + fr_ipfrttl; 304 305 /* 306 * Compute the offset of the expected start of the next packet. 307 */ 308 off = ip->ip_off & IP_OFFMASK; 309 if (off == 0) 310 fra->ipfr_seen0 = 1; 311 fra->ipfr_off = off + (fin->fin_dlen >> 3); 312 fra->ipfr_pass = pass; 313 ipfr_stats.ifs_new++; 314 ipfr_inuse++; 315 return fra; 316} 317 318 319/* ------------------------------------------------------------------------ */ 320/* Function: fr_newfrag */ 321/* Returns: int - 0 == success, -1 == error */ 322/* Parameters: fin(I) - pointer to packet information */ 323/* */ 324/* Add a new entry to the fragment cache table based on the current packet */ 325/* ------------------------------------------------------------------------ */ 326int fr_newfrag(fin, pass) 327u_32_t pass; 328fr_info_t *fin; 329{ 330 ipfr_t *fra; 331 332 if ((fin->fin_v != 4) || (fr_frag_lock != 0)) 333 return -1; 334 335 WRITE_ENTER(&ipf_frag); 336 fra = ipfr_newfrag(fin, pass, ipfr_heads); 337 if (fra != NULL) { 338 *ipfr_tail = fra; 339 fra->ipfr_prev = ipfr_tail; 340 ipfr_tail = &fra->ipfr_next; 341 if (ipfr_list == NULL) 342 ipfr_list = fra; 343 fra->ipfr_next = NULL; 344 } 345 RWLOCK_EXIT(&ipf_frag); 346 return fra ? 0 : -1; 347} 348 349 350/* ------------------------------------------------------------------------ */ 351/* Function: fr_nat_newfrag */ 352/* Returns: int - 0 == success, -1 == error */ 353/* Parameters: fin(I) - pointer to packet information */ 354/* nat(I) - pointer to NAT structure */ 355/* */ 356/* Create a new NAT fragment cache entry based on the current packet and */ 357/* the NAT structure for this "session". */ 358/* ------------------------------------------------------------------------ */ 359int fr_nat_newfrag(fin, pass, nat) 360fr_info_t *fin; 361u_32_t pass; 362nat_t *nat; 363{ 364 ipfr_t *fra; 365 366 if ((fin->fin_v != 4) || (fr_frag_lock != 0)) 367 return 0; 368 369 WRITE_ENTER(&ipf_natfrag); 370 fra = ipfr_newfrag(fin, pass, ipfr_nattab); 371 if (fra != NULL) { 372 fra->ipfr_data = nat; 373 nat->nat_data = fra; 374 *ipfr_nattail = fra; 375 fra->ipfr_prev = ipfr_nattail; 376 ipfr_nattail = &fra->ipfr_next; 377 fra->ipfr_next = NULL; 378 } 379 RWLOCK_EXIT(&ipf_natfrag); 380 return fra ? 0 : -1; 381} 382 383 384/* ------------------------------------------------------------------------ */ 385/* Function: fr_ipid_newfrag */ 386/* Returns: int - 0 == success, -1 == error */ 387/* Parameters: fin(I) - pointer to packet information */ 388/* ipid(I) - new IP ID for this fragmented packet */ 389/* */ 390/* Create a new fragment cache entry for this packet and store, as a data */ 391/* pointer, the new IP ID value. */ 392/* ------------------------------------------------------------------------ */ 393int fr_ipid_newfrag(fin, ipid) 394fr_info_t *fin; 395u_32_t ipid; 396{ 397 ipfr_t *fra; 398 399 if ((fin->fin_v != 4) || (fr_frag_lock)) 400 return 0; 401 402 WRITE_ENTER(&ipf_ipidfrag); 403 fra = ipfr_newfrag(fin, 0, ipfr_ipidtab); 404 if (fra != NULL) { 405 fra->ipfr_data = (void *)(uintptr_t)ipid; 406 *ipfr_ipidtail = fra; 407 fra->ipfr_prev = ipfr_ipidtail; 408 ipfr_ipidtail = &fra->ipfr_next; 409 fra->ipfr_next = NULL; 410 } 411 RWLOCK_EXIT(&ipf_ipidfrag); 412 return fra ? 0 : -1; 413} 414 415 416/* ------------------------------------------------------------------------ */ 417/* Function: fr_fraglookup */ 418/* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */ 419/* matching entry in the frag table, else NULL */ 420/* Parameters: fin(I) - pointer to packet information */ 421/* table(I) - pointer to fragment cache table to search */ 422/* */ 423/* Check the fragment cache to see if there is already a record of this */ 424/* packet with its filter result known. */ 425/* ------------------------------------------------------------------------ */ 426static ipfr_t *fr_fraglookup(fin, table) 427fr_info_t *fin; 428ipfr_t *table[]; 429{ 430 ipfr_t *f, frag; 431 u_int idx; 432 ip_t *ip; 433 434 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) 435 return NULL; 436 437 /* 438 * For fragments, we record protocol, packet id, TOS and both IP#'s 439 * (these should all be the same for all fragments of a packet). 440 * 441 * build up a hash value to index the table with. 442 */ 443 ip = fin->fin_ip; 444 frag.ipfr_p = ip->ip_p; 445 idx = ip->ip_p; 446 frag.ipfr_id = ip->ip_id; 447 idx += ip->ip_id; 448 frag.ipfr_tos = ip->ip_tos; 449 frag.ipfr_src.s_addr = ip->ip_src.s_addr; 450 idx += ip->ip_src.s_addr; 451 frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; 452 idx += ip->ip_dst.s_addr; 453 frag.ipfr_ifp = fin->fin_ifp; 454 idx *= 127; 455 idx %= IPFT_SIZE; 456 457 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 458 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 459 frag.ipfr_auth = fin->fin_fi.fi_auth; 460 461 /* 462 * check the table, careful to only compare the right amount of data 463 */ 464 for (f = table[idx]; f; f = f->ipfr_hnext) 465 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, 466 IPFR_CMPSZ)) { 467 u_short off; 468 469 /* 470 * We don't want to let short packets match because 471 * they could be compromising the security of other 472 * rules that want to match on layer 4 fields (and 473 * can't because they have been fragmented off.) 474 * Why do this check here? The counter acts as an 475 * indicator of this kind of attack, whereas if it was 476 * elsewhere, it wouldn't know if other matching 477 * packets had been seen. 478 */ 479 if (fin->fin_flx & FI_SHORT) { 480 ATOMIC_INCL(ipfr_stats.ifs_short); 481 continue; 482 } 483 484 /* 485 * XXX - We really need to be guarding against the 486 * retransmission of (src,dst,id,offset-range) here 487 * because a fragmented packet is never resent with 488 * the same IP ID# (or shouldn't). 489 */ 490 off = ip->ip_off & IP_OFFMASK; 491 if (f->ipfr_seen0) { 492 if (off == 0) { 493 ATOMIC_INCL(ipfr_stats.ifs_retrans0); 494 continue; 495 } 496 } else if (off == 0) 497 f->ipfr_seen0 = 1; 498 499 if (f != table[idx]) { 500 ipfr_t **fp; 501 502 /* 503 * Move fragment info. to the top of the list 504 * to speed up searches. First, delink... 505 */ 506 fp = f->ipfr_hprev; 507 (*fp) = f->ipfr_hnext; 508 if (f->ipfr_hnext != NULL) 509 f->ipfr_hnext->ipfr_hprev = fp; 510 /* 511 * Then put back at the top of the chain. 512 */ 513 f->ipfr_hnext = table[idx]; 514 table[idx]->ipfr_hprev = &f->ipfr_hnext; 515 f->ipfr_hprev = table + idx; 516 table[idx] = f; 517 } 518 519 /* 520 * If we've follwed the fragments, and this is the 521 * last (in order), shrink expiration time. 522 */ 523 if (off == f->ipfr_off) { 524 if (!(ip->ip_off & IP_MF)) 525 f->ipfr_ttl = fr_ticks + 1; 526 f->ipfr_off = (fin->fin_dlen >> 3) + off; 527 } else if (f->ipfr_pass & FR_FRSTRICT) 528 continue; 529 ATOMIC_INCL(ipfr_stats.ifs_hits); 530 return f; 531 } 532 return NULL; 533} 534 535 536/* ------------------------------------------------------------------------ */ 537/* Function: fr_nat_knownfrag */ 538/* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ 539/* match found, else NULL */ 540/* Parameters: fin(I) - pointer to packet information */ 541/* */ 542/* Functional interface for NAT lookups of the NAT fragment cache */ 543/* ------------------------------------------------------------------------ */ 544nat_t *fr_nat_knownfrag(fin) 545fr_info_t *fin; 546{ 547 nat_t *nat; 548 ipfr_t *ipf; 549 550 if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_natlist) 551 return NULL; 552 READ_ENTER(&ipf_natfrag); 553 ipf = fr_fraglookup(fin, ipfr_nattab); 554 if (ipf != NULL) { 555 nat = ipf->ipfr_data; 556 /* 557 * This is the last fragment for this packet. 558 */ 559 if ((ipf->ipfr_ttl == fr_ticks + 1) && (nat != NULL)) { 560 nat->nat_data = NULL; 561 ipf->ipfr_data = NULL; 562 } 563 } else 564 nat = NULL; 565 RWLOCK_EXIT(&ipf_natfrag); 566 return nat; 567} 568 569 570/* ------------------------------------------------------------------------ */ 571/* Function: fr_ipid_knownfrag */ 572/* Returns: u_32_t - IPv4 ID for this packet if match found, else */ 573/* return 0xfffffff to indicate no match. */ 574/* Parameters: fin(I) - pointer to packet information */ 575/* */ 576/* Functional interface for IP ID lookups of the IP ID fragment cache */ 577/* ------------------------------------------------------------------------ */ 578u_32_t fr_ipid_knownfrag(fin) 579fr_info_t *fin; 580{ 581 ipfr_t *ipf; 582 u_32_t id; 583 584 if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_ipidlist) 585 return 0xffffffff; 586 587 READ_ENTER(&ipf_ipidfrag); 588 ipf = fr_fraglookup(fin, ipfr_ipidtab); 589 if (ipf != NULL) 590 id = (u_32_t)(uintptr_t)ipf->ipfr_data; 591 else 592 id = 0xffffffff; 593 RWLOCK_EXIT(&ipf_ipidfrag); 594 return id; 595} 596 597 598/* ------------------------------------------------------------------------ */ 599/* Function: fr_knownfrag */ 600/* Returns: frentry_t* - pointer to filter rule if a match is found in */ 601/* the frag cache table, else NULL. */ 602/* Parameters: fin(I) - pointer to packet information */ 603/* passp(O) - pointer to where to store rule flags resturned */ 604/* */ 605/* Functional interface for normal lookups of the fragment cache. If a */ 606/* match is found, return the rule pointer and flags from the rule, except */ 607/* that if FR_LOGFIRST is set, reset FR_LOG. */ 608/* ------------------------------------------------------------------------ */ 609frentry_t *fr_knownfrag(fin, passp) 610fr_info_t *fin; 611u_32_t *passp; 612{ 613 frentry_t *fr = NULL; 614 ipfr_t *fra; 615 u_32_t pass; 616 617 if ((fin->fin_v != 4) || (fr_frag_lock) || (ipfr_list == NULL)) 618 return NULL; 619 620 READ_ENTER(&ipf_frag); 621 fra = fr_fraglookup(fin, ipfr_heads); 622 if (fra != NULL) { 623 fr = fra->ipfr_rule; 624 fin->fin_fr = fr; 625 if (fr != NULL) { 626 pass = fr->fr_flags; 627 if ((pass & FR_LOGFIRST) != 0) 628 pass &= ~(FR_LOGFIRST|FR_LOG); 629 *passp = pass; 630 } 631 } 632 RWLOCK_EXIT(&ipf_frag); 633 return fr; 634} 635 636 637/* ------------------------------------------------------------------------ */ 638/* Function: fr_forget */ 639/* Returns: Nil */ 640/* Parameters: ptr(I) - pointer to data structure */ 641/* */ 642/* Search through all of the fragment cache entries and wherever a pointer */ 643/* is found to match ptr, reset it to NULL. */ 644/* ------------------------------------------------------------------------ */ 645void fr_forget(ptr) 646void *ptr; 647{ 648 ipfr_t *fr; 649 650 WRITE_ENTER(&ipf_frag); 651 for (fr = ipfr_list; fr; fr = fr->ipfr_next) 652 if (fr->ipfr_data == ptr) 653 fr->ipfr_data = NULL; 654 RWLOCK_EXIT(&ipf_frag); 655} 656 657 658/* ------------------------------------------------------------------------ */ 659/* Function: fr_forgetnat */ 660/* Returns: Nil */ 661/* Parameters: ptr(I) - pointer to data structure */ 662/* */ 663/* Search through all of the fragment cache entries for NAT and wherever a */ 664/* pointer is found to match ptr, reset it to NULL. */ 665/* ------------------------------------------------------------------------ */ 666void fr_forgetnat(ptr) 667void *ptr; 668{ 669 ipfr_t *fr; 670 671 WRITE_ENTER(&ipf_natfrag); 672 for (fr = ipfr_natlist; fr; fr = fr->ipfr_next) 673 if (fr->ipfr_data == ptr) 674 fr->ipfr_data = NULL; 675 RWLOCK_EXIT(&ipf_natfrag); 676} 677 678 679/* ------------------------------------------------------------------------ */ 680/* Function: fr_fragdelete */ 681/* Returns: Nil */ 682/* Parameters: fra(I) - pointer to fragment structure to delete */ 683/* tail(IO) - pointer to the pointer to the tail of the frag */ 684/* list */ 685/* */ 686/* Remove a fragment cache table entry from the table & list. Also free */ 687/* the filter rule it is associated with it if it is no longer used as a */ 688/* result of decreasing the reference count. */ 689/* ------------------------------------------------------------------------ */ 690static void fr_fragdelete(fra, tail) 691ipfr_t *fra, ***tail; 692{ 693 frentry_t *fr; 694 695 fr = fra->ipfr_rule; 696 if (fr != NULL) 697 (void)fr_derefrule(&fr); 698 699 if (fra->ipfr_next) 700 fra->ipfr_next->ipfr_prev = fra->ipfr_prev; 701 *fra->ipfr_prev = fra->ipfr_next; 702 if (*tail == &fra->ipfr_next) 703 *tail = fra->ipfr_prev; 704 705 if (fra->ipfr_hnext) 706 fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev; 707 *fra->ipfr_hprev = fra->ipfr_hnext; 708 KFREE(fra); 709} 710 711 712/* ------------------------------------------------------------------------ */ 713/* Function: fr_fragclear */ 714/* Returns: Nil */ 715/* Parameters: Nil */ 716/* */ 717/* Free memory in use by fragment state information kept. Do the normal */ 718/* fragment state stuff first and then the NAT-fragment table. */ 719/* ------------------------------------------------------------------------ */ 720void fr_fragclear() 721{ 722 ipfr_t *fra; 723 nat_t *nat; 724 725 WRITE_ENTER(&ipf_frag); 726 while ((fra = ipfr_list) != NULL) 727 fr_fragdelete(fra, &ipfr_tail); 728 ipfr_tail = &ipfr_list; 729 RWLOCK_EXIT(&ipf_frag); 730 731 WRITE_ENTER(&ipf_nat); 732 WRITE_ENTER(&ipf_natfrag); 733 while ((fra = ipfr_natlist) != NULL) { 734 nat = fra->ipfr_data; 735 if (nat != NULL) { 736 if (nat->nat_data == fra) 737 nat->nat_data = NULL; 738 } 739 fr_fragdelete(fra, &ipfr_nattail); 740 } 741 ipfr_nattail = &ipfr_natlist; 742 RWLOCK_EXIT(&ipf_natfrag); 743 RWLOCK_EXIT(&ipf_nat); 744} 745 746 747/* ------------------------------------------------------------------------ */ 748/* Function: fr_fragexpire */ 749/* Returns: Nil */ 750/* Parameters: Nil */ 751/* */ 752/* Expire entries in the fragment cache table that have been there too long */ 753/* ------------------------------------------------------------------------ */ 754void fr_fragexpire() 755{ 756 ipfr_t **fp, *fra; 757 nat_t *nat; 758 SPL_INT(s); 759 760 if (fr_frag_lock) 761 return; 762 763 SPL_NET(s); 764 WRITE_ENTER(&ipf_frag); 765 /* 766 * Go through the entire table, looking for entries to expire, 767 * which is indicated by the ttl being less than or equal to fr_ticks. 768 */ 769 for (fp = &ipfr_list; ((fra = *fp) != NULL); ) { 770 if (fra->ipfr_ttl > fr_ticks) 771 break; 772 fr_fragdelete(fra, &ipfr_tail); 773 ipfr_stats.ifs_expire++; 774 ipfr_inuse--; 775 } 776 RWLOCK_EXIT(&ipf_frag); 777 778 WRITE_ENTER(&ipf_ipidfrag); 779 for (fp = &ipfr_ipidlist; ((fra = *fp) != NULL); ) { 780 if (fra->ipfr_ttl > fr_ticks) 781 break; 782 fr_fragdelete(fra, &ipfr_ipidtail); 783 ipfr_stats.ifs_expire++; 784 ipfr_inuse--; 785 } 786 RWLOCK_EXIT(&ipf_ipidfrag); 787 788 /* 789 * Same again for the NAT table, except that if the structure also 790 * still points to a NAT structure, and the NAT structure points back 791 * at the one to be free'd, NULL the reference from the NAT struct. 792 * NOTE: We need to grab both mutex's early, and in this order so as 793 * to prevent a deadlock if both try to expire at the same time. 794 */ 795 WRITE_ENTER(&ipf_nat); 796 WRITE_ENTER(&ipf_natfrag); 797 for (fp = &ipfr_natlist; ((fra = *fp) != NULL); ) { 798 if (fra->ipfr_ttl > fr_ticks) 799 break; 800 nat = fra->ipfr_data; 801 if (nat != NULL) { 802 if (nat->nat_data == fra) 803 nat->nat_data = NULL; 804 } 805 fr_fragdelete(fra, &ipfr_nattail); 806 ipfr_stats.ifs_expire++; 807 ipfr_inuse--; 808 } 809 RWLOCK_EXIT(&ipf_natfrag); 810 RWLOCK_EXIT(&ipf_nat); 811 SPL_X(s); 812} 813 814 815/* ------------------------------------------------------------------------ */ 816/* Function: fr_slowtimer */ 817/* Returns: Nil */ 818/* Parameters: Nil */ 819/* */ 820/* Slowly expire held state for fragments. Timeouts are set * in */ 821/* expectation of this being called twice per second. */ 822/* ------------------------------------------------------------------------ */ 823#if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \ 824 !defined(__osf__) && !defined(linux)) 825# if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi)) 826void fr_slowtimer __P((void *ptr)) 827# else 828int fr_slowtimer() 829# endif 830{ 831 READ_ENTER(&ipf_global); 832 833 fr_fragexpire(); 834 fr_timeoutstate(); 835 fr_natexpire(); 836 fr_authexpire(); 837 fr_ticks++; 838 if (fr_running <= 0) 839 goto done; 840# ifdef _KERNEL 841# if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000) 842 callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL); 843# else 844# if defined(__OpenBSD__) 845 timeout_add(&fr_slowtimer_ch, hz/2); 846# else 847# if (__FreeBSD_version >= 300000) 848 fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2); 849# else 850# ifdef linux 851 ; 852# else 853 timeout(fr_slowtimer, NULL, hz/2); 854# endif 855# endif /* FreeBSD */ 856# endif /* OpenBSD */ 857# endif /* NetBSD */ 858# endif 859done: 860 RWLOCK_EXIT(&ipf_global); 861# if (BSD < 199103) || !defined(_KERNEL) 862 return 0; 863# endif 864} 865#endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */ 866