ip_frag.c revision 145579
1330569Sgordon/* $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_frag.c 145579 2005-04-27 03:48:10Z darrenr $ */ 2330569Sgordon 3330569Sgordon/* 4330569Sgordon * Copyright (C) 1993-2003 by Darren Reed. 5330569Sgordon * 6330569Sgordon * See the IPFILTER.LICENCE file for details on licencing. 7330569Sgordon */ 8330569Sgordon#if defined(KERNEL) || defined(_KERNEL) 9330569Sgordon# undef KERNEL 10330569Sgordon# undef _KERNEL 11330569Sgordon# define KERNEL 1 12330569Sgordon# define _KERNEL 1 13330569Sgordon#endif 14330569Sgordon#include <sys/errno.h> 15330569Sgordon#include <sys/types.h> 16330569Sgordon#include <sys/param.h> 17330569Sgordon#include <sys/time.h> 18330569Sgordon#include <sys/file.h> 19330569Sgordon#ifdef __hpux 20330569Sgordon# include <sys/timeout.h> 21330569Sgordon#endif 22330569Sgordon#if !defined(_KERNEL) 23330569Sgordon# include <stdio.h> 24330569Sgordon# include <string.h> 25330569Sgordon# include <stdlib.h> 26330569Sgordon# define _KERNEL 27330569Sgordon# ifdef __OpenBSD__ 28330569Sgordonstruct file; 29330569Sgordon# endif 30330569Sgordon# include <sys/uio.h> 31330569Sgordon# undef _KERNEL 32330569Sgordon#endif 33330569Sgordon#if defined(_KERNEL) && (__FreeBSD_version >= 220000) 34330569Sgordon# include <sys/filio.h> 35330569Sgordon# include <sys/fcntl.h> 36330569Sgordon#else 37330569Sgordon# include <sys/ioctl.h> 38330569Sgordon#endif 39330569Sgordon#if !defined(linux) 40330569Sgordon# include <sys/protosw.h> 41330569Sgordon#endif 42330569Sgordon#include <sys/socket.h> 43330569Sgordon#if defined(_KERNEL) 44330569Sgordon# include <sys/systm.h> 45330569Sgordon# if !defined(__SVR4) && !defined(__svr4__) 46330569Sgordon# include <sys/mbuf.h> 47330569Sgordon# endif 48330569Sgordon#endif 49330569Sgordon#if !defined(__SVR4) && !defined(__svr4__) 50330569Sgordon# if defined(_KERNEL) && !defined(__sgi) 51330569Sgordon# include <sys/kernel.h> 52330569Sgordon# endif 53330569Sgordon#else 54330569Sgordon# include <sys/byteorder.h> 55330569Sgordon# ifdef _KERNEL 56330569Sgordon# include <sys/dditypes.h> 57330569Sgordon# endif 58330569Sgordon# 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 145579 2005-04-27 03:48:10Z darrenr $"; 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 ((ip->ip_off & IP_OFFMASK) != 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 if ((fra->ipfr_rule = fin->fin_fr) != NULL) 282 fin->fin_fr->fr_ref++; 283 284 /* 285 * Insert the fragment into the fragment table, copy the struct used 286 * in the search using bcopy rather than reassign each field. 287 * Set the ttl to the default. 288 */ 289 if ((fra->ipfr_hnext = table[idx]) != NULL) 290 table[idx]->ipfr_hprev = &fra->ipfr_hnext; 291 fra->ipfr_hprev = table + idx; 292 fra->ipfr_data = NULL; 293 table[idx] = fra; 294 bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); 295 fra->ipfr_ttl = fr_ticks + fr_ipfrttl; 296 297 /* 298 * Compute the offset of the expected start of the next packet. 299 */ 300 off = ip->ip_off & IP_OFFMASK; 301 if (off == 0) 302 fra->ipfr_seen0 = 1; 303 fra->ipfr_off = off + (fin->fin_dlen >> 3); 304 fra->ipfr_pass = pass; 305 ipfr_stats.ifs_new++; 306 ipfr_inuse++; 307 return fra; 308} 309 310 311/* ------------------------------------------------------------------------ */ 312/* Function: fr_newfrag */ 313/* Returns: int - 0 == success, -1 == error */ 314/* Parameters: fin(I) - pointer to packet information */ 315/* */ 316/* Add a new entry to the fragment cache table based on the current packet */ 317/* ------------------------------------------------------------------------ */ 318int fr_newfrag(fin, pass) 319u_32_t pass; 320fr_info_t *fin; 321{ 322 ipfr_t *fra; 323 324 if ((fin->fin_v != 4) || (fr_frag_lock != 0)) 325 return -1; 326 327 WRITE_ENTER(&ipf_frag); 328 fra = ipfr_newfrag(fin, pass, ipfr_heads); 329 if (fra != NULL) { 330 *ipfr_tail = fra; 331 fra->ipfr_prev = ipfr_tail; 332 ipfr_tail = &fra->ipfr_next; 333 if (ipfr_list == NULL) 334 ipfr_list = fra; 335 fra->ipfr_next = NULL; 336 } 337 RWLOCK_EXIT(&ipf_frag); 338 return fra ? 0 : -1; 339} 340 341 342/* ------------------------------------------------------------------------ */ 343/* Function: fr_nat_newfrag */ 344/* Returns: int - 0 == success, -1 == error */ 345/* Parameters: fin(I) - pointer to packet information */ 346/* nat(I) - pointer to NAT structure */ 347/* */ 348/* Create a new NAT fragment cache entry based on the current packet and */ 349/* the NAT structure for this "session". */ 350/* ------------------------------------------------------------------------ */ 351int fr_nat_newfrag(fin, pass, nat) 352fr_info_t *fin; 353u_32_t pass; 354nat_t *nat; 355{ 356 ipfr_t *fra; 357 358 if ((fin->fin_v != 4) || (fr_frag_lock != 0)) 359 return 0; 360 361 WRITE_ENTER(&ipf_natfrag); 362 fra = ipfr_newfrag(fin, pass, ipfr_nattab); 363 if (fra != NULL) { 364 fra->ipfr_data = nat; 365 nat->nat_data = fra; 366 *ipfr_nattail = fra; 367 fra->ipfr_prev = ipfr_nattail; 368 ipfr_nattail = &fra->ipfr_next; 369 fra->ipfr_next = NULL; 370 } 371 RWLOCK_EXIT(&ipf_natfrag); 372 return fra ? 0 : -1; 373} 374 375 376/* ------------------------------------------------------------------------ */ 377/* Function: fr_ipid_newfrag */ 378/* Returns: int - 0 == success, -1 == error */ 379/* Parameters: fin(I) - pointer to packet information */ 380/* ipid(I) - new IP ID for this fragmented packet */ 381/* */ 382/* Create a new fragment cache entry for this packet and store, as a data */ 383/* pointer, the new IP ID value. */ 384/* ------------------------------------------------------------------------ */ 385int fr_ipid_newfrag(fin, ipid) 386fr_info_t *fin; 387u_32_t ipid; 388{ 389 ipfr_t *fra; 390 391 if ((fin->fin_v != 4) || (fr_frag_lock)) 392 return 0; 393 394 WRITE_ENTER(&ipf_ipidfrag); 395 fra = ipfr_newfrag(fin, 0, ipfr_ipidtab); 396 if (fra != NULL) { 397 fra->ipfr_data = (void *)ipid; 398 *ipfr_ipidtail = fra; 399 fra->ipfr_prev = ipfr_ipidtail; 400 ipfr_ipidtail = &fra->ipfr_next; 401 fra->ipfr_next = NULL; 402 } 403 RWLOCK_EXIT(&ipf_ipidfrag); 404 return fra ? 0 : -1; 405} 406 407 408/* ------------------------------------------------------------------------ */ 409/* Function: fr_fraglookup */ 410/* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */ 411/* matching entry in the frag table, else NULL */ 412/* Parameters: fin(I) - pointer to packet information */ 413/* table(I) - pointer to fragment cache table to search */ 414/* */ 415/* Check the fragment cache to see if there is already a record of this */ 416/* packet with its filter result known. */ 417/* ------------------------------------------------------------------------ */ 418static ipfr_t *fr_fraglookup(fin, table) 419fr_info_t *fin; 420ipfr_t *table[]; 421{ 422 ipfr_t *f, frag; 423 u_int idx; 424 ip_t *ip; 425 426 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) 427 return NULL; 428 429 /* 430 * For fragments, we record protocol, packet id, TOS and both IP#'s 431 * (these should all be the same for all fragments of a packet). 432 * 433 * build up a hash value to index the table with. 434 */ 435 ip = fin->fin_ip; 436 frag.ipfr_p = ip->ip_p; 437 idx = ip->ip_p; 438 frag.ipfr_id = ip->ip_id; 439 idx += ip->ip_id; 440 frag.ipfr_tos = ip->ip_tos; 441 frag.ipfr_src.s_addr = ip->ip_src.s_addr; 442 idx += ip->ip_src.s_addr; 443 frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; 444 idx += ip->ip_dst.s_addr; 445 frag.ipfr_ifp = fin->fin_ifp; 446 idx *= 127; 447 idx %= IPFT_SIZE; 448 449 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 450 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 451 frag.ipfr_auth = fin->fin_fi.fi_auth; 452 453 /* 454 * check the table, careful to only compare the right amount of data 455 */ 456 for (f = table[idx]; f; f = f->ipfr_hnext) 457 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, 458 IPFR_CMPSZ)) { 459 u_short off; 460 461 /* 462 * We don't want to let short packets match because 463 * they could be compromising the security of other 464 * rules that want to match on layer 4 fields (and 465 * can't because they have been fragmented off.) 466 * Why do this check here? The counter acts as an 467 * indicator of this kind of attack, whereas if it was 468 * elsewhere, it wouldn't know if other matching 469 * packets had been seen. 470 */ 471 if (fin->fin_flx & FI_SHORT) { 472 ATOMIC_INCL(ipfr_stats.ifs_short); 473 continue; 474 } 475 476 /* 477 * XXX - We really need to be guarding against the 478 * retransmission of (src,dst,id,offset-range) here 479 * because a fragmented packet is never resent with 480 * the same IP ID# (or shouldn't). 481 */ 482 off = ip->ip_off & IP_OFFMASK; 483 if (f->ipfr_seen0) { 484 if (off == 0) { 485 ATOMIC_INCL(ipfr_stats.ifs_retrans0); 486 continue; 487 } 488 } else if (off == 0) 489 f->ipfr_seen0 = 1; 490 491 if (f != table[idx]) { 492 ipfr_t **fp; 493 494 /* 495 * Move fragment info. to the top of the list 496 * to speed up searches. First, delink... 497 */ 498 fp = f->ipfr_hprev; 499 (*fp) = f->ipfr_hnext; 500 if (f->ipfr_hnext != NULL) 501 f->ipfr_hnext->ipfr_hprev = fp; 502 /* 503 * Then put back at the top of the chain. 504 */ 505 f->ipfr_hnext = table[idx]; 506 table[idx]->ipfr_hprev = &f->ipfr_hnext; 507 f->ipfr_hprev = table + idx; 508 table[idx] = f; 509 } 510 511 /* 512 * If we've follwed the fragments, and this is the 513 * last (in order), shrink expiration time. 514 */ 515 if (off == f->ipfr_off) { 516 if (!(ip->ip_off & IP_MF)) 517 f->ipfr_ttl = fr_ticks + 1; 518 f->ipfr_off = (fin->fin_dlen >> 3) + off; 519 } else if (f->ipfr_pass & FR_FRSTRICT) 520 continue; 521 ATOMIC_INCL(ipfr_stats.ifs_hits); 522 return f; 523 } 524 return NULL; 525} 526 527 528/* ------------------------------------------------------------------------ */ 529/* Function: fr_nat_knownfrag */ 530/* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ 531/* match found, else NULL */ 532/* Parameters: fin(I) - pointer to packet information */ 533/* */ 534/* Functional interface for NAT lookups of the NAT fragment cache */ 535/* ------------------------------------------------------------------------ */ 536nat_t *fr_nat_knownfrag(fin) 537fr_info_t *fin; 538{ 539 nat_t *nat; 540 ipfr_t *ipf; 541 542 if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_natlist) 543 return NULL; 544 READ_ENTER(&ipf_natfrag); 545 ipf = fr_fraglookup(fin, ipfr_nattab); 546 if (ipf != NULL) { 547 nat = ipf->ipfr_data; 548 /* 549 * This is the last fragment for this packet. 550 */ 551 if ((ipf->ipfr_ttl == fr_ticks + 1) && (nat != NULL)) { 552 nat->nat_data = NULL; 553 ipf->ipfr_data = NULL; 554 } 555 } else 556 nat = NULL; 557 RWLOCK_EXIT(&ipf_natfrag); 558 return nat; 559} 560 561 562/* ------------------------------------------------------------------------ */ 563/* Function: fr_ipid_knownfrag */ 564/* Returns: u_32_t - IPv4 ID for this packet if match found, else */ 565/* return 0xfffffff to indicate no match. */ 566/* Parameters: fin(I) - pointer to packet information */ 567/* */ 568/* Functional interface for IP ID lookups of the IP ID fragment cache */ 569/* ------------------------------------------------------------------------ */ 570u_32_t fr_ipid_knownfrag(fin) 571fr_info_t *fin; 572{ 573 ipfr_t *ipf; 574 u_32_t id; 575 576 if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_ipidlist) 577 return 0xffffffff; 578 579 READ_ENTER(&ipf_ipidfrag); 580 ipf = fr_fraglookup(fin, ipfr_ipidtab); 581 if (ipf != NULL) 582 id = (u_32_t)ipf->ipfr_data; 583 else 584 id = 0xffffffff; 585 RWLOCK_EXIT(&ipf_ipidfrag); 586 return id; 587} 588 589 590/* ------------------------------------------------------------------------ */ 591/* Function: fr_knownfrag */ 592/* Returns: frentry_t* - pointer to filter rule if a match is found in */ 593/* the frag cache table, else NULL. */ 594/* Parameters: fin(I) - pointer to packet information */ 595/* passp(O) - pointer to where to store rule flags resturned */ 596/* */ 597/* Functional interface for normal lookups of the fragment cache. If a */ 598/* match is found, return the rule pointer and flags from the rule, except */ 599/* that if FR_LOGFIRST is set, reset FR_LOG. */ 600/* ------------------------------------------------------------------------ */ 601frentry_t *fr_knownfrag(fin, passp) 602fr_info_t *fin; 603u_32_t *passp; 604{ 605 frentry_t *fr = NULL; 606 ipfr_t *fra; 607 u_32_t pass; 608 609 if ((fin->fin_v != 4) || (fr_frag_lock) || (ipfr_list == NULL)) 610 return NULL; 611 612 READ_ENTER(&ipf_frag); 613 fra = fr_fraglookup(fin, ipfr_heads); 614 if (fra != NULL) { 615 fr = fra->ipfr_rule; 616 fin->fin_fr = fr; 617 if (fr != NULL) { 618 pass = fr->fr_flags; 619 if ((pass & FR_LOGFIRST) != 0) 620 pass &= ~(FR_LOGFIRST|FR_LOG); 621 *passp = pass; 622 } 623 } 624 RWLOCK_EXIT(&ipf_frag); 625 return fr; 626} 627 628 629/* ------------------------------------------------------------------------ */ 630/* Function: fr_forget */ 631/* Returns: Nil */ 632/* Parameters: ptr(I) - pointer to data structure */ 633/* */ 634/* Search through all of the fragment cache entries and wherever a pointer */ 635/* is found to match ptr, reset it to NULL. */ 636/* ------------------------------------------------------------------------ */ 637void fr_forget(ptr) 638void *ptr; 639{ 640 ipfr_t *fr; 641 642 WRITE_ENTER(&ipf_frag); 643 for (fr = ipfr_list; fr; fr = fr->ipfr_next) 644 if (fr->ipfr_data == ptr) 645 fr->ipfr_data = NULL; 646 RWLOCK_EXIT(&ipf_frag); 647} 648 649 650/* ------------------------------------------------------------------------ */ 651/* Function: fr_forgetnat */ 652/* Returns: Nil */ 653/* Parameters: ptr(I) - pointer to data structure */ 654/* */ 655/* Search through all of the fragment cache entries for NAT and wherever a */ 656/* pointer is found to match ptr, reset it to NULL. */ 657/* ------------------------------------------------------------------------ */ 658void fr_forgetnat(ptr) 659void *ptr; 660{ 661 ipfr_t *fr; 662 663 WRITE_ENTER(&ipf_natfrag); 664 for (fr = ipfr_natlist; fr; fr = fr->ipfr_next) 665 if (fr->ipfr_data == ptr) 666 fr->ipfr_data = NULL; 667 RWLOCK_EXIT(&ipf_natfrag); 668} 669 670 671/* ------------------------------------------------------------------------ */ 672/* Function: fr_fragdelete */ 673/* Returns: Nil */ 674/* Parameters: fra(I) - pointer to fragment structure to delete */ 675/* tail(IO) - pointer to the pointer to the tail of the frag */ 676/* list */ 677/* */ 678/* Remove a fragment cache table entry from the table & list. Also free */ 679/* the filter rule it is associated with it if it is no longer used as a */ 680/* result of decreasing the reference count. */ 681/* ------------------------------------------------------------------------ */ 682static void fr_fragdelete(fra, tail) 683ipfr_t *fra, ***tail; 684{ 685 frentry_t *fr; 686 687 fr = fra->ipfr_rule; 688 if (fr != NULL) 689 (void)fr_derefrule(&fr); 690 691 if (fra->ipfr_next) 692 fra->ipfr_next->ipfr_prev = fra->ipfr_prev; 693 *fra->ipfr_prev = fra->ipfr_next; 694 if (*tail == &fra->ipfr_next) 695 *tail = fra->ipfr_prev; 696 697 if (fra->ipfr_hnext) 698 fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev; 699 *fra->ipfr_hprev = fra->ipfr_hnext; 700 KFREE(fra); 701} 702 703 704/* ------------------------------------------------------------------------ */ 705/* Function: fr_fragclear */ 706/* Returns: Nil */ 707/* Parameters: Nil */ 708/* */ 709/* Free memory in use by fragment state information kept. Do the normal */ 710/* fragment state stuff first and then the NAT-fragment table. */ 711/* ------------------------------------------------------------------------ */ 712void fr_fragclear() 713{ 714 ipfr_t *fra; 715 nat_t *nat; 716 717 WRITE_ENTER(&ipf_frag); 718 while ((fra = ipfr_list) != NULL) 719 fr_fragdelete(fra, &ipfr_tail); 720 ipfr_tail = &ipfr_list; 721 RWLOCK_EXIT(&ipf_frag); 722 723 WRITE_ENTER(&ipf_nat); 724 WRITE_ENTER(&ipf_natfrag); 725 while ((fra = ipfr_natlist) != NULL) { 726 nat = fra->ipfr_data; 727 if (nat != NULL) { 728 if (nat->nat_data == fra) 729 nat->nat_data = NULL; 730 } 731 fr_fragdelete(fra, &ipfr_nattail); 732 } 733 ipfr_nattail = &ipfr_natlist; 734 RWLOCK_EXIT(&ipf_natfrag); 735 RWLOCK_EXIT(&ipf_nat); 736} 737 738 739/* ------------------------------------------------------------------------ */ 740/* Function: fr_fragexpire */ 741/* Returns: Nil */ 742/* Parameters: Nil */ 743/* */ 744/* Expire entries in the fragment cache table that have been there too long */ 745/* ------------------------------------------------------------------------ */ 746void fr_fragexpire() 747{ 748 ipfr_t **fp, *fra; 749 nat_t *nat; 750#if defined(USE_SPL) && defined(_KERNEL) 751 int s; 752#endif 753 754 if (fr_frag_lock) 755 return; 756 757 SPL_NET(s); 758 WRITE_ENTER(&ipf_frag); 759 /* 760 * Go through the entire table, looking for entries to expire, 761 * which is indicated by the ttl being less than or equal to fr_ticks. 762 */ 763 for (fp = &ipfr_list; ((fra = *fp) != NULL); ) { 764 if (fra->ipfr_ttl > fr_ticks) 765 break; 766 fr_fragdelete(fra, &ipfr_tail); 767 ipfr_stats.ifs_expire++; 768 ipfr_inuse--; 769 } 770 RWLOCK_EXIT(&ipf_frag); 771 772 WRITE_ENTER(&ipf_ipidfrag); 773 for (fp = &ipfr_ipidlist; ((fra = *fp) != NULL); ) { 774 if (fra->ipfr_ttl > fr_ticks) 775 break; 776 fr_fragdelete(fra, &ipfr_ipidtail); 777 ipfr_stats.ifs_expire++; 778 ipfr_inuse--; 779 } 780 RWLOCK_EXIT(&ipf_ipidfrag); 781 782 /* 783 * Same again for the NAT table, except that if the structure also 784 * still points to a NAT structure, and the NAT structure points back 785 * at the one to be free'd, NULL the reference from the NAT struct. 786 * NOTE: We need to grab both mutex's early, and in this order so as 787 * to prevent a deadlock if both try to expire at the same time. 788 */ 789 WRITE_ENTER(&ipf_nat); 790 WRITE_ENTER(&ipf_natfrag); 791 for (fp = &ipfr_natlist; ((fra = *fp) != NULL); ) { 792 if (fra->ipfr_ttl > fr_ticks) 793 break; 794 nat = fra->ipfr_data; 795 if (nat != NULL) { 796 if (nat->nat_data == fra) 797 nat->nat_data = NULL; 798 } 799 fr_fragdelete(fra, &ipfr_nattail); 800 ipfr_stats.ifs_expire++; 801 ipfr_inuse--; 802 } 803 RWLOCK_EXIT(&ipf_natfrag); 804 RWLOCK_EXIT(&ipf_nat); 805 SPL_X(s); 806} 807 808 809/* ------------------------------------------------------------------------ */ 810/* Function: fr_slowtimer */ 811/* Returns: Nil */ 812/* Parameters: Nil */ 813/* */ 814/* Slowly expire held state for fragments. Timeouts are set * in */ 815/* expectation of this being called twice per second. */ 816/* ------------------------------------------------------------------------ */ 817#if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \ 818 !defined(__osf__)) 819# if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi)) 820void fr_slowtimer __P((void *ptr)) 821# else 822int fr_slowtimer() 823# endif 824{ 825 READ_ENTER(&ipf_global); 826 827 fr_fragexpire(); 828 fr_timeoutstate(); 829 fr_natexpire(); 830 fr_authexpire(); 831 fr_ticks++; 832 if (fr_running <= 0) 833 goto done; 834# ifdef _KERNEL 835# if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000) 836 callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL); 837# else 838# if defined(__OpenBSD__) 839 timeout_add(&fr_slowtimer_ch, hz/2); 840# else 841# if (__FreeBSD_version >= 300000) 842 fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2); 843# else 844# ifdef linux 845 ; 846# else 847 timeout(fr_slowtimer, NULL, hz/2); 848# endif 849# endif /* FreeBSD */ 850# endif /* OpenBSD */ 851# endif /* NetBSD */ 852# endif 853done: 854 RWLOCK_EXIT(&ipf_global); 855# if (BSD < 199103) || !defined(_KERNEL) 856 return 0; 857# endif 858} 859#endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */ 860