1145522Sdarrenr/* $FreeBSD: stable/10/sys/contrib/ipfilter/netinet/ip_frag.c 317241 2017-04-21 01:51:49Z cy $ */ 2145522Sdarrenr 353642Sguido/* 4255332Scy * Copyright (C) 2012 by Darren Reed. 553642Sguido * 680482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 753642Sguido */ 8145522Sdarrenr#if defined(KERNEL) || defined(_KERNEL) 9145522Sdarrenr# undef KERNEL 10145522Sdarrenr# undef _KERNEL 11145522Sdarrenr# define KERNEL 1 12145522Sdarrenr# define _KERNEL 1 1353642Sguido#endif 1453642Sguido#include <sys/errno.h> 1553642Sguido#include <sys/types.h> 1653642Sguido#include <sys/param.h> 1753642Sguido#include <sys/time.h> 1853642Sguido#include <sys/file.h> 19145522Sdarrenr#ifdef __hpux 20145522Sdarrenr# include <sys/timeout.h> 21145522Sdarrenr#endif 22145522Sdarrenr#if !defined(_KERNEL) 2353642Sguido# include <stdio.h> 2453642Sguido# include <string.h> 2553642Sguido# include <stdlib.h> 26145522Sdarrenr# define _KERNEL 27145522Sdarrenr# ifdef __OpenBSD__ 28145522Sdarrenrstruct file; 29145522Sdarrenr# endif 30145522Sdarrenr# include <sys/uio.h> 31145522Sdarrenr# undef _KERNEL 3253642Sguido#endif 33255332Scy#if defined(_KERNEL) && \ 34255332Scy defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) 3553642Sguido# include <sys/filio.h> 3653642Sguido# include <sys/fcntl.h> 3753642Sguido#else 3853642Sguido# include <sys/ioctl.h> 3953642Sguido#endif 40145522Sdarrenr#if !defined(linux) 4153642Sguido# include <sys/protosw.h> 4253642Sguido#endif 4353642Sguido#include <sys/socket.h> 44145522Sdarrenr#if defined(_KERNEL) 4553642Sguido# include <sys/systm.h> 46145522Sdarrenr# if !defined(__SVR4) && !defined(__svr4__) 47145522Sdarrenr# include <sys/mbuf.h> 48145522Sdarrenr# endif 4953642Sguido#endif 5053642Sguido#if !defined(__SVR4) && !defined(__svr4__) 51153876Sguido# if defined(_KERNEL) && !defined(__sgi) && !defined(AIX) 5253642Sguido# include <sys/kernel.h> 5353642Sguido# endif 5453642Sguido#else 5553642Sguido# include <sys/byteorder.h> 5653642Sguido# ifdef _KERNEL 5753642Sguido# include <sys/dditypes.h> 5853642Sguido# endif 5953642Sguido# include <sys/stream.h> 6053642Sguido# include <sys/kmem.h> 6153642Sguido#endif 6253642Sguido#include <net/if.h> 6353642Sguido#ifdef sun 6453642Sguido# include <net/af.h> 6553642Sguido#endif 6653642Sguido#include <netinet/in.h> 6753642Sguido#include <netinet/in_systm.h> 6853642Sguido#include <netinet/ip.h> 69145522Sdarrenr#if !defined(linux) 7053642Sguido# include <netinet/ip_var.h> 7153642Sguido#endif 7253642Sguido#include <netinet/tcp.h> 7353642Sguido#include <netinet/udp.h> 7453642Sguido#include <netinet/ip_icmp.h> 7553642Sguido#include "netinet/ip_compat.h" 7653642Sguido#include <netinet/tcpip.h> 7753642Sguido#include "netinet/ip_fil.h" 7853642Sguido#include "netinet/ip_nat.h" 7953642Sguido#include "netinet/ip_frag.h" 8053642Sguido#include "netinet/ip_state.h" 8153642Sguido#include "netinet/ip_auth.h" 82255332Scy#include "netinet/ip_lookup.h" 83145522Sdarrenr#include "netinet/ip_proxy.h" 84255332Scy#include "netinet/ip_sync.h" 85145522Sdarrenr/* END OF INCLUDES */ 8653642Sguido 8780482Sdarrenr#if !defined(lint) 8880482Sdarrenrstatic const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed"; 8980482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: stable/10/sys/contrib/ipfilter/netinet/ip_frag.c 317241 2017-04-21 01:51:49Z cy $"; 90172776Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_frag.c,v 2.77.2.12 2007/09/20 12:51:51 darrenr Exp $"; */ 9180482Sdarrenr#endif 9253642Sguido 9380482Sdarrenr 94255332Scy#ifdef USE_MUTEXES 95255332Scystatic ipfr_t *ipfr_frag_new __P((ipf_main_softc_t *, ipf_frag_softc_t *, 96255332Scy fr_info_t *, u_32_t, ipfr_t **, 97255332Scy ipfrwlock_t *)); 98255332Scystatic ipfr_t *ipf_frag_lookup __P((ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **, ipfrwlock_t *)); 99255332Scystatic void ipf_frag_deref __P((void *, ipfr_t **, ipfrwlock_t *)); 100255332Scystatic int ipf_frag_next __P((ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, 101255332Scy ipfr_t **, ipfrwlock_t *)); 102255332Scy#else 103255332Scystatic ipfr_t *ipfr_frag_new __P((ipf_main_softc_t *, ipf_frag_softc_t *, 104255332Scy fr_info_t *, u_32_t, ipfr_t **)); 105255332Scystatic ipfr_t *ipf_frag_lookup __P((ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **)); 106255332Scystatic void ipf_frag_deref __P((void *, ipfr_t **)); 107255332Scystatic int ipf_frag_next __P((ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, 108255332Scy ipfr_t **)); 109255332Scy#endif 110255332Scystatic void ipf_frag_delete __P((ipf_main_softc_t *, ipfr_t *, ipfr_t ***)); 111255332Scystatic void ipf_frag_free __P((ipf_frag_softc_t *, ipfr_t *)); 112170268Sdarrenr 113255332Scystatic frentry_t ipfr_block; 114145522Sdarrenr 115302015Scystatic ipftuneable_t ipf_frag_tuneables[] = { 116255332Scy { { (void *)offsetof(ipf_frag_softc_t, ipfr_size) }, 117255332Scy "frag_size", 1, 0x7fffffff, 118255332Scy stsizeof(ipf_frag_softc_t, ipfr_size), 119255332Scy IPFT_WRDISABLED, NULL, NULL }, 120255332Scy { { (void *)offsetof(ipf_frag_softc_t, ipfr_ttl) }, 121255332Scy "frag_ttl", 1, 0x7fffffff, 122255332Scy stsizeof(ipf_frag_softc_t, ipfr_ttl), 123255332Scy 0, NULL, NULL }, 124255332Scy { { NULL }, 125255332Scy NULL, 0, 0, 126255332Scy 0, 127255332Scy 0, NULL, NULL } 128255332Scy}; 12960855Sdarrenr 130255332Scy#define FBUMP(x) softf->ipfr_stats.x++ 131255332Scy#define FBUMPD(x) do { softf->ipfr_stats.x++; DT(x); } while (0) 13260855Sdarrenr 13353642Sguido 134255332Scy/* ------------------------------------------------------------------------ */ 135255332Scy/* Function: ipf_frag_main_load */ 136255332Scy/* Returns: int - 0 == success, -1 == error */ 137255332Scy/* Parameters: Nil */ 138255332Scy/* */ 139255332Scy/* Initialise the filter rule associted with blocked packets - everyone can */ 140255332Scy/* use it. */ 141255332Scy/* ------------------------------------------------------------------------ */ 142255332Scyint 143255332Scyipf_frag_main_load() 144255332Scy{ 145255332Scy bzero((char *)&ipfr_block, sizeof(ipfr_block)); 146255332Scy ipfr_block.fr_flags = FR_BLOCK|FR_QUICK; 147255332Scy ipfr_block.fr_ref = 1; 14853642Sguido 149255332Scy return 0; 150255332Scy} 15153642Sguido 152255332Scy 153145522Sdarrenr/* ------------------------------------------------------------------------ */ 154255332Scy/* Function: ipf_frag_main_unload */ 155145522Sdarrenr/* Returns: int - 0 == success, -1 == error */ 156145522Sdarrenr/* Parameters: Nil */ 157145522Sdarrenr/* */ 158255332Scy/* A null-op function that exists as a placeholder so that the flow in */ 159255332Scy/* other functions is obvious. */ 160255332Scy/* ------------------------------------------------------------------------ */ 161255332Scyint 162255332Scyipf_frag_main_unload() 163255332Scy{ 164255332Scy return 0; 165255332Scy} 166255332Scy 167255332Scy 168255332Scy/* ------------------------------------------------------------------------ */ 169255332Scy/* Function: ipf_frag_soft_create */ 170255332Scy/* Returns: void * - NULL = failure, else pointer to local context */ 171255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 172255332Scy/* */ 173255332Scy/* Allocate a new soft context structure to track fragment related info. */ 174255332Scy/* ------------------------------------------------------------------------ */ 175255332Scy/*ARGSUSED*/ 176255332Scyvoid * 177255332Scyipf_frag_soft_create(softc) 178255332Scy ipf_main_softc_t *softc; 179255332Scy{ 180255332Scy ipf_frag_softc_t *softf; 181255332Scy 182255332Scy KMALLOC(softf, ipf_frag_softc_t *); 183255332Scy if (softf == NULL) 184255332Scy return NULL; 185255332Scy 186255332Scy bzero((char *)softf, sizeof(*softf)); 187255332Scy 188255332Scy RWLOCK_INIT(&softf->ipfr_ipidfrag, "frag ipid lock"); 189255332Scy RWLOCK_INIT(&softf->ipfr_frag, "ipf fragment rwlock"); 190255332Scy RWLOCK_INIT(&softf->ipfr_natfrag, "ipf NAT fragment rwlock"); 191255332Scy 192302015Scy softf->ipf_frag_tune = ipf_tune_array_copy(softf, 193302015Scy sizeof(ipf_frag_tuneables), 194302015Scy ipf_frag_tuneables); 195302015Scy if (softf->ipf_frag_tune == NULL) { 196302015Scy ipf_frag_soft_destroy(softc, softf); 197302015Scy return NULL; 198302015Scy } 199302015Scy if (ipf_tune_array_link(softc, softf->ipf_frag_tune) == -1) { 200302015Scy ipf_frag_soft_destroy(softc, softf); 201302015Scy return NULL; 202302015Scy } 203302015Scy 204255332Scy softf->ipfr_size = IPFT_SIZE; 205255332Scy softf->ipfr_ttl = IPF_TTLVAL(60); 206255332Scy softf->ipfr_lock = 1; 207255332Scy softf->ipfr_tail = &softf->ipfr_list; 208255332Scy softf->ipfr_nattail = &softf->ipfr_natlist; 209255332Scy softf->ipfr_ipidtail = &softf->ipfr_ipidlist; 210255332Scy 211255332Scy return softf; 212255332Scy} 213255332Scy 214255332Scy 215255332Scy/* ------------------------------------------------------------------------ */ 216255332Scy/* Function: ipf_frag_soft_destroy */ 217255332Scy/* Returns: Nil */ 218255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 219255332Scy/* arg(I) - pointer to local context to use */ 220255332Scy/* */ 221145522Sdarrenr/* Initialise the hash tables for the fragment cache lookups. */ 222145522Sdarrenr/* ------------------------------------------------------------------------ */ 223255332Scyvoid 224255332Scyipf_frag_soft_destroy(softc, arg) 225255332Scy ipf_main_softc_t *softc; 226255332Scy void *arg; 227145522Sdarrenr{ 228255332Scy ipf_frag_softc_t *softf = arg; 22953642Sguido 230255332Scy RW_DESTROY(&softf->ipfr_ipidfrag); 231255332Scy RW_DESTROY(&softf->ipfr_frag); 232255332Scy RW_DESTROY(&softf->ipfr_natfrag); 233145522Sdarrenr 234302015Scy if (softf->ipf_frag_tune != NULL) { 235302015Scy ipf_tune_array_unlink(softc, softf->ipf_frag_tune); 236302015Scy KFREES(softf->ipf_frag_tune, sizeof(ipf_frag_tuneables)); 237302015Scy softf->ipf_frag_tune = NULL; 238302015Scy } 239302015Scy 240255332Scy KFREE(softf); 241255332Scy} 242255332Scy 243255332Scy 244255332Scy/* ------------------------------------------------------------------------ */ 245255332Scy/* Function: ipf_frag_soft_init */ 246255332Scy/* Returns: int - 0 == success, -1 == error */ 247255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 248255332Scy/* arg(I) - pointer to local context to use */ 249255332Scy/* */ 250255332Scy/* Initialise the hash tables for the fragment cache lookups. */ 251255332Scy/* ------------------------------------------------------------------------ */ 252255332Scy/*ARGSUSED*/ 253255332Scyint 254255332Scyipf_frag_soft_init(softc, arg) 255255332Scy ipf_main_softc_t *softc; 256255332Scy void *arg; 257255332Scy{ 258255332Scy ipf_frag_softc_t *softf = arg; 259255332Scy 260255332Scy KMALLOCS(softf->ipfr_heads, ipfr_t **, 261255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 262255332Scy if (softf->ipfr_heads == NULL) 263145522Sdarrenr return -1; 264145522Sdarrenr 265255332Scy bzero((char *)softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); 266145522Sdarrenr 267255332Scy KMALLOCS(softf->ipfr_nattab, ipfr_t **, 268255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 269255332Scy if (softf->ipfr_nattab == NULL) 270255332Scy return -2; 271255332Scy 272255332Scy bzero((char *)softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); 273255332Scy 274255332Scy KMALLOCS(softf->ipfr_ipidtab, ipfr_t **, 275255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 276255332Scy if (softf->ipfr_ipidtab == NULL) 277255332Scy return -3; 278255332Scy 279255332Scy bzero((char *)softf->ipfr_ipidtab, 280255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 281255332Scy 282255332Scy softf->ipfr_lock = 0; 283255332Scy softf->ipfr_inited = 1; 284255332Scy 285145522Sdarrenr return 0; 286145522Sdarrenr} 287145522Sdarrenr 288145522Sdarrenr 289145522Sdarrenr/* ------------------------------------------------------------------------ */ 290255332Scy/* Function: ipf_frag_soft_fini */ 291255332Scy/* Returns: int - 0 == success, -1 == error */ 292255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 293255332Scy/* arg(I) - pointer to local context to use */ 294145522Sdarrenr/* */ 295145522Sdarrenr/* Free all memory allocated whilst running and from initialisation. */ 296145522Sdarrenr/* ------------------------------------------------------------------------ */ 297255332Scyint 298255332Scyipf_frag_soft_fini(softc, arg) 299255332Scy ipf_main_softc_t *softc; 300255332Scy void *arg; 30153642Sguido{ 302255332Scy ipf_frag_softc_t *softf = arg; 303145522Sdarrenr 304255332Scy softf->ipfr_lock = 1; 305255332Scy 306255332Scy if (softf->ipfr_inited == 1) { 307255332Scy ipf_frag_clear(softc); 308255332Scy 309255332Scy softf->ipfr_inited = 0; 310145522Sdarrenr } 311145522Sdarrenr 312255332Scy if (softf->ipfr_heads != NULL) 313255332Scy KFREES(softf->ipfr_heads, 314255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 315255332Scy softf->ipfr_heads = NULL; 316145522Sdarrenr 317255332Scy if (softf->ipfr_nattab != NULL) 318255332Scy KFREES(softf->ipfr_nattab, 319255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 320255332Scy softf->ipfr_nattab = NULL; 321145522Sdarrenr 322255332Scy if (softf->ipfr_ipidtab != NULL) 323255332Scy KFREES(softf->ipfr_ipidtab, 324255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 325255332Scy softf->ipfr_ipidtab = NULL; 326255332Scy 327255332Scy return 0; 328145522Sdarrenr} 329145522Sdarrenr 330145522Sdarrenr 331145522Sdarrenr/* ------------------------------------------------------------------------ */ 332255332Scy/* Function: ipf_frag_set_lock */ 333255332Scy/* Returns: Nil */ 334255332Scy/* Parameters: arg(I) - pointer to local context to use */ 335255332Scy/* tmp(I) - new value for lock */ 336255332Scy/* */ 337255332Scy/* Stub function that allows for external manipulation of ipfr_lock */ 338255332Scy/* ------------------------------------------------------------------------ */ 339255332Scyvoid 340255332Scyipf_frag_setlock(arg, tmp) 341255332Scy void *arg; 342255332Scy int tmp; 343255332Scy{ 344255332Scy ipf_frag_softc_t *softf = arg; 345255332Scy 346255332Scy softf->ipfr_lock = tmp; 347255332Scy} 348255332Scy 349255332Scy 350255332Scy/* ------------------------------------------------------------------------ */ 351255332Scy/* Function: ipf_frag_stats */ 352145522Sdarrenr/* Returns: ipfrstat_t* - pointer to struct with current frag stats */ 353255332Scy/* Parameters: arg(I) - pointer to local context to use */ 354145522Sdarrenr/* */ 355145522Sdarrenr/* Updates ipfr_stats with current information and returns a pointer to it */ 356145522Sdarrenr/* ------------------------------------------------------------------------ */ 357255332Scyipfrstat_t * 358255332Scyipf_frag_stats(arg) 359255332Scy void *arg; 360145522Sdarrenr{ 361255332Scy ipf_frag_softc_t *softf = arg; 362255332Scy 363255332Scy softf->ipfr_stats.ifs_table = softf->ipfr_heads; 364255332Scy softf->ipfr_stats.ifs_nattab = softf->ipfr_nattab; 365255332Scy return &softf->ipfr_stats; 36653642Sguido} 36753642Sguido 36853642Sguido 369145522Sdarrenr/* ------------------------------------------------------------------------ */ 370255332Scy/* Function: ipfr_frag_new */ 371145522Sdarrenr/* Returns: ipfr_t * - pointer to fragment cache state info or NULL */ 372145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 373145522Sdarrenr/* table(I) - pointer to frag table to add to */ 374255332Scy/* lock(I) - pointer to lock to get a write hold of */ 375145522Sdarrenr/* */ 376145522Sdarrenr/* Add a new entry to the fragment cache, registering it as having come */ 377145522Sdarrenr/* through this box, with the result of the filter operation. */ 378255332Scy/* */ 379255332Scy/* If this function succeeds, it returns with a write lock held on "lock". */ 380255332Scy/* If it fails, no lock is held on return. */ 381145522Sdarrenr/* ------------------------------------------------------------------------ */ 382255332Scystatic ipfr_t * 383255332Scyipfr_frag_new(softc, softf, fin, pass, table 384255332Scy#ifdef USE_MUTEXES 385255332Scy, lock 386255332Scy#endif 387255332Scy) 388255332Scy ipf_main_softc_t *softc; 389255332Scy ipf_frag_softc_t *softf; 390255332Scy fr_info_t *fin; 391255332Scy u_32_t pass; 392255332Scy ipfr_t *table[]; 393255332Scy#ifdef USE_MUTEXES 394255332Scy ipfrwlock_t *lock; 395255332Scy#endif 39653642Sguido{ 397255332Scy ipfr_t *fra, frag, *fran; 39875262Sdarrenr u_int idx, off; 399161356Sguido frentry_t *fr; 40053642Sguido 401255332Scy if (softf->ipfr_stats.ifs_inuse >= softf->ipfr_size) { 402255332Scy FBUMPD(ifs_maximum); 40363523Sdarrenr return NULL; 404255332Scy } 40563523Sdarrenr 406255332Scy if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) { 407255332Scy FBUMPD(ifs_newbad); 40875262Sdarrenr return NULL; 409255332Scy } 41075262Sdarrenr 411255332Scy if (pass & FR_FRSTRICT) { 412255332Scy if (fin->fin_off != 0) { 413255332Scy FBUMPD(ifs_newrestrictnot0); 414145522Sdarrenr return NULL; 415255332Scy } 416255332Scy } 417145522Sdarrenr 418255332Scy frag.ipfr_v = fin->fin_v; 419255332Scy idx = fin->fin_v; 420255332Scy frag.ipfr_p = fin->fin_p; 421255332Scy idx += fin->fin_p; 422255332Scy frag.ipfr_id = fin->fin_id; 423255332Scy idx += fin->fin_id; 424255332Scy frag.ipfr_source = fin->fin_fi.fi_src; 425255332Scy idx += frag.ipfr_src.s_addr; 426255332Scy frag.ipfr_dest = fin->fin_fi.fi_dst; 427255332Scy idx += frag.ipfr_dst.s_addr; 42872006Sdarrenr frag.ipfr_ifp = fin->fin_ifp; 42953642Sguido idx *= 127; 430255332Scy idx %= softf->ipfr_size; 43153642Sguido 43280482Sdarrenr frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 43380482Sdarrenr frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 43480482Sdarrenr frag.ipfr_auth = fin->fin_fi.fi_auth; 43580482Sdarrenr 436255332Scy off = fin->fin_off >> 3; 437255332Scy if (off == 0) { 438255332Scy char *ptr; 439255332Scy int end; 440255332Scy 441255332Scy#ifdef USE_INET6 442255332Scy if (fin->fin_v == 6) { 443255332Scy 444255332Scy ptr = (char *)fin->fin_fraghdr + 445255332Scy sizeof(struct ip6_frag); 446255332Scy } else 447255332Scy#endif 448255332Scy { 449255332Scy ptr = fin->fin_dp; 450255332Scy } 451255332Scy end = fin->fin_plen - (ptr - (char *)fin->fin_ip); 452255332Scy frag.ipfr_firstend = end >> 3; 453255332Scy } else { 454255332Scy frag.ipfr_firstend = 0; 455255332Scy } 456255332Scy 45753642Sguido /* 458255332Scy * allocate some memory, if possible, if not, just record that we 459255332Scy * failed to do so. 460255332Scy */ 461255332Scy KMALLOC(fran, ipfr_t *); 462255332Scy if (fran == NULL) { 463255332Scy FBUMPD(ifs_nomem); 464255332Scy return NULL; 465255332Scy } 466255332Scy 467255332Scy WRITE_ENTER(lock); 468255332Scy 469255332Scy /* 47053642Sguido * first, make sure it isn't already there... 47153642Sguido */ 472145522Sdarrenr for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext) 473145522Sdarrenr if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, 47453642Sguido IPFR_CMPSZ)) { 475255332Scy RWLOCK_EXIT(lock); 476255332Scy FBUMPD(ifs_exists); 477317241Scy KFREE(fran); 47853642Sguido return NULL; 47953642Sguido } 48053642Sguido 481255332Scy fra = fran; 482255332Scy fran = NULL; 483161356Sguido fr = fin->fin_fr; 484161356Sguido fra->ipfr_rule = fr; 485161356Sguido if (fr != NULL) { 486153876Sguido MUTEX_ENTER(&fr->fr_lock); 487153876Sguido fr->fr_ref++; 488153876Sguido MUTEX_EXIT(&fr->fr_lock); 489153876Sguido } 490153876Sguido 49153642Sguido /* 492130886Sdarrenr * Insert the fragment into the fragment table, copy the struct used 49353642Sguido * in the search using bcopy rather than reassign each field. 494102520Sdarrenr * Set the ttl to the default. 49553642Sguido */ 496145522Sdarrenr if ((fra->ipfr_hnext = table[idx]) != NULL) 497145522Sdarrenr table[idx]->ipfr_hprev = &fra->ipfr_hnext; 498145522Sdarrenr fra->ipfr_hprev = table + idx; 49953642Sguido fra->ipfr_data = NULL; 50053642Sguido table[idx] = fra; 501145522Sdarrenr bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); 502255332Scy fra->ipfr_v = fin->fin_v; 503255332Scy fra->ipfr_ttl = softc->ipf_ticks + softf->ipfr_ttl; 504255332Scy fra->ipfr_firstend = frag.ipfr_firstend; 505145522Sdarrenr 50653642Sguido /* 50753642Sguido * Compute the offset of the expected start of the next packet. 50853642Sguido */ 509145522Sdarrenr if (off == 0) 51075262Sdarrenr fra->ipfr_seen0 = 1; 51175262Sdarrenr fra->ipfr_off = off + (fin->fin_dlen >> 3); 512145522Sdarrenr fra->ipfr_pass = pass; 513170268Sdarrenr fra->ipfr_ref = 1; 514255332Scy fra->ipfr_pkts = 1; 515255332Scy fra->ipfr_bytes = fin->fin_plen; 516255332Scy FBUMP(ifs_inuse); 517255332Scy FBUMP(ifs_new); 51853642Sguido return fra; 51953642Sguido} 52053642Sguido 52153642Sguido 522145522Sdarrenr/* ------------------------------------------------------------------------ */ 523255332Scy/* Function: ipf_frag_new */ 524145522Sdarrenr/* Returns: int - 0 == success, -1 == error */ 525145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 526145522Sdarrenr/* */ 527145522Sdarrenr/* Add a new entry to the fragment cache table based on the current packet */ 528145522Sdarrenr/* ------------------------------------------------------------------------ */ 529255332Scyint 530255332Scyipf_frag_new(softc, fin, pass) 531255332Scy ipf_main_softc_t *softc; 532255332Scy u_32_t pass; 533255332Scy fr_info_t *fin; 53453642Sguido{ 535255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 536145522Sdarrenr ipfr_t *fra; 53753642Sguido 538255332Scy if (softf->ipfr_lock != 0) 53967614Sdarrenr return -1; 540145522Sdarrenr 541255332Scy#ifdef USE_MUTEXES 542255332Scy fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads, &softc->ipf_frag); 543255332Scy#else 544255332Scy fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads); 545255332Scy#endif 546145522Sdarrenr if (fra != NULL) { 547255332Scy *softf->ipfr_tail = fra; 548255332Scy fra->ipfr_prev = softf->ipfr_tail; 549255332Scy softf->ipfr_tail = &fra->ipfr_next; 550145522Sdarrenr fra->ipfr_next = NULL; 551255332Scy RWLOCK_EXIT(&softc->ipf_frag); 552145522Sdarrenr } 553145522Sdarrenr return fra ? 0 : -1; 55453642Sguido} 55553642Sguido 55653642Sguido 557145522Sdarrenr/* ------------------------------------------------------------------------ */ 558255332Scy/* Function: ipf_frag_natnew */ 559145522Sdarrenr/* Returns: int - 0 == success, -1 == error */ 560145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 561145522Sdarrenr/* nat(I) - pointer to NAT structure */ 562145522Sdarrenr/* */ 563145522Sdarrenr/* Create a new NAT fragment cache entry based on the current packet and */ 564145522Sdarrenr/* the NAT structure for this "session". */ 565145522Sdarrenr/* ------------------------------------------------------------------------ */ 566255332Scyint 567255332Scyipf_frag_natnew(softc, fin, pass, nat) 568255332Scy ipf_main_softc_t *softc; 569255332Scy fr_info_t *fin; 570255332Scy u_32_t pass; 571255332Scy nat_t *nat; 57253642Sguido{ 573255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 574145522Sdarrenr ipfr_t *fra; 57553642Sguido 576255332Scy if (softf->ipfr_lock != 0) 577145522Sdarrenr return 0; 57880482Sdarrenr 579255332Scy#ifdef USE_MUTEXES 580255332Scy fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab, 581255332Scy &softf->ipfr_natfrag); 582255332Scy#else 583255332Scy fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab); 584255332Scy#endif 585145522Sdarrenr if (fra != NULL) { 586145522Sdarrenr fra->ipfr_data = nat; 587145522Sdarrenr nat->nat_data = fra; 588255332Scy *softf->ipfr_nattail = fra; 589255332Scy fra->ipfr_prev = softf->ipfr_nattail; 590255332Scy softf->ipfr_nattail = &fra->ipfr_next; 591145522Sdarrenr fra->ipfr_next = NULL; 592255332Scy RWLOCK_EXIT(&softf->ipfr_natfrag); 593255332Scy return 0; 59453642Sguido } 595255332Scy return -1; 59653642Sguido} 59753642Sguido 59853642Sguido 599145522Sdarrenr/* ------------------------------------------------------------------------ */ 600255332Scy/* Function: ipf_frag_ipidnew */ 601145522Sdarrenr/* Returns: int - 0 == success, -1 == error */ 602145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 603145522Sdarrenr/* ipid(I) - new IP ID for this fragmented packet */ 604145522Sdarrenr/* */ 605145522Sdarrenr/* Create a new fragment cache entry for this packet and store, as a data */ 606145522Sdarrenr/* pointer, the new IP ID value. */ 607145522Sdarrenr/* ------------------------------------------------------------------------ */ 608255332Scyint 609255332Scyipf_frag_ipidnew(fin, ipid) 610255332Scy fr_info_t *fin; 611255332Scy u_32_t ipid; 612145522Sdarrenr{ 613255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 614255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 615145522Sdarrenr ipfr_t *fra; 616145522Sdarrenr 617255332Scy if (softf->ipfr_lock) 618145522Sdarrenr return 0; 619145522Sdarrenr 620255332Scy#ifdef USE_MUTEXES 621255332Scy fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); 622255332Scy#else 623255332Scy fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab); 624255332Scy#endif 625145522Sdarrenr if (fra != NULL) { 626255332Scy fra->ipfr_data = (void *)(intptr_t)ipid; 627255332Scy *softf->ipfr_ipidtail = fra; 628255332Scy fra->ipfr_prev = softf->ipfr_ipidtail; 629255332Scy softf->ipfr_ipidtail = &fra->ipfr_next; 630145522Sdarrenr fra->ipfr_next = NULL; 631255332Scy RWLOCK_EXIT(&softf->ipfr_ipidfrag); 632145522Sdarrenr } 633145522Sdarrenr return fra ? 0 : -1; 634145522Sdarrenr} 635145522Sdarrenr 636145522Sdarrenr 637145522Sdarrenr/* ------------------------------------------------------------------------ */ 638255332Scy/* Function: ipf_frag_lookup */ 639145522Sdarrenr/* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */ 640145522Sdarrenr/* matching entry in the frag table, else NULL */ 641145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 642145522Sdarrenr/* table(I) - pointer to fragment cache table to search */ 643145522Sdarrenr/* */ 644145522Sdarrenr/* Check the fragment cache to see if there is already a record of this */ 645145522Sdarrenr/* packet with its filter result known. */ 646255332Scy/* */ 647255332Scy/* If this function succeeds, it returns with a write lock held on "lock". */ 648255332Scy/* If it fails, no lock is held on return. */ 649145522Sdarrenr/* ------------------------------------------------------------------------ */ 650255332Scystatic ipfr_t * 651255332Scyipf_frag_lookup(softc, softf, fin, table 652255332Scy#ifdef USE_MUTEXES 653255332Scy, lock 654255332Scy#endif 655255332Scy) 656255332Scy ipf_main_softc_t *softc; 657255332Scy ipf_frag_softc_t *softf; 658255332Scy fr_info_t *fin; 659255332Scy ipfr_t *table[]; 660255332Scy#ifdef USE_MUTEXES 661255332Scy ipfrwlock_t *lock; 662255332Scy#endif 66353642Sguido{ 664145522Sdarrenr ipfr_t *f, frag; 665145522Sdarrenr u_int idx; 66653642Sguido 667255332Scy /* 668255332Scy * We don't want to let short packets match because they could be 669255332Scy * compromising the security of other rules that want to match on 670255332Scy * layer 4 fields (and can't because they have been fragmented off.) 671255332Scy * Why do this check here? The counter acts as an indicator of this 672255332Scy * kind of attack, whereas if it was elsewhere, it wouldn't know if 673255332Scy * other matching packets had been seen. 674255332Scy */ 675255332Scy if (fin->fin_flx & FI_SHORT) { 676255332Scy FBUMPD(ifs_short); 677145522Sdarrenr return NULL; 678255332Scy } 679145522Sdarrenr 680255332Scy if ((fin->fin_flx & FI_BAD) != 0) { 681255332Scy FBUMPD(ifs_bad); 682255332Scy return NULL; 683255332Scy } 684255332Scy 68553642Sguido /* 68653642Sguido * For fragments, we record protocol, packet id, TOS and both IP#'s 68753642Sguido * (these should all be the same for all fragments of a packet). 68853642Sguido * 68953642Sguido * build up a hash value to index the table with. 69053642Sguido */ 691255332Scy frag.ipfr_v = fin->fin_v; 692255332Scy idx = fin->fin_v; 693255332Scy frag.ipfr_p = fin->fin_p; 694255332Scy idx += fin->fin_p; 695255332Scy frag.ipfr_id = fin->fin_id; 696255332Scy idx += fin->fin_id; 697255332Scy frag.ipfr_source = fin->fin_fi.fi_src; 698255332Scy idx += frag.ipfr_src.s_addr; 699255332Scy frag.ipfr_dest = fin->fin_fi.fi_dst; 700255332Scy idx += frag.ipfr_dst.s_addr; 70172006Sdarrenr frag.ipfr_ifp = fin->fin_ifp; 70253642Sguido idx *= 127; 703255332Scy idx %= softf->ipfr_size; 70453642Sguido 70580482Sdarrenr frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 70680482Sdarrenr frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 70780482Sdarrenr frag.ipfr_auth = fin->fin_fi.fi_auth; 70880482Sdarrenr 709255332Scy READ_ENTER(lock); 710255332Scy 71153642Sguido /* 71253642Sguido * check the table, careful to only compare the right amount of data 71353642Sguido */ 714255332Scy for (f = table[idx]; f; f = f->ipfr_hnext) { 715145522Sdarrenr if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, 71653642Sguido IPFR_CMPSZ)) { 717145522Sdarrenr u_short off; 71853642Sguido 719145522Sdarrenr /* 72075262Sdarrenr * XXX - We really need to be guarding against the 72175262Sdarrenr * retransmission of (src,dst,id,offset-range) here 72275262Sdarrenr * because a fragmented packet is never resent with 723145522Sdarrenr * the same IP ID# (or shouldn't). 72475262Sdarrenr */ 725255332Scy off = fin->fin_off >> 3; 72675262Sdarrenr if (f->ipfr_seen0) { 727145522Sdarrenr if (off == 0) { 728255332Scy FBUMPD(ifs_retrans0); 72975262Sdarrenr continue; 730145522Sdarrenr } 731255332Scy 732255332Scy /* 733255332Scy * Case 3. See comment for frpr_fragment6. 734255332Scy */ 735255332Scy if ((f->ipfr_firstend != 0) && 736255332Scy (off < f->ipfr_firstend)) { 737255332Scy FBUMP(ifs_overlap); 738255332Scy DT2(ifs_overlap, u_short, off, 739255332Scy ipfr_t *, f); 740255332Scy fin->fin_flx |= FI_BAD; 741255332Scy break; 742255332Scy } 743145522Sdarrenr } else if (off == 0) 74475262Sdarrenr f->ipfr_seen0 = 1; 74575262Sdarrenr 746314251Scy if (f != table[idx] && MUTEX_TRY_UPGRADE(lock)) { 747145522Sdarrenr ipfr_t **fp; 748145522Sdarrenr 74953642Sguido /* 750145522Sdarrenr * Move fragment info. to the top of the list 751145522Sdarrenr * to speed up searches. First, delink... 75253642Sguido */ 753145522Sdarrenr fp = f->ipfr_hprev; 754145522Sdarrenr (*fp) = f->ipfr_hnext; 755145522Sdarrenr if (f->ipfr_hnext != NULL) 756145522Sdarrenr f->ipfr_hnext->ipfr_hprev = fp; 757145522Sdarrenr /* 758145522Sdarrenr * Then put back at the top of the chain. 759145522Sdarrenr */ 760145522Sdarrenr f->ipfr_hnext = table[idx]; 761145522Sdarrenr table[idx]->ipfr_hprev = &f->ipfr_hnext; 762145522Sdarrenr f->ipfr_hprev = table + idx; 76353642Sguido table[idx] = f; 764314251Scy MUTEX_DOWNGRADE(lock); 76553642Sguido } 766145522Sdarrenr 76753642Sguido /* 76853642Sguido * If we've follwed the fragments, and this is the 76953642Sguido * last (in order), shrink expiration time. 77053642Sguido */ 77155929Sguido if (off == f->ipfr_off) { 772145522Sdarrenr f->ipfr_off = (fin->fin_dlen >> 3) + off; 773255332Scy 774255332Scy /* 775255332Scy * Well, we could shrink the expiration time 776255332Scy * but only if every fragment has been seen 777255332Scy * in order upto this, the last. ipfr_badorder 778255332Scy * is used here to count those out of order 779255332Scy * and if it equals 0 when we get to the last 780255332Scy * fragment then we can assume all of the 781255332Scy * fragments have been seen and in order. 782255332Scy */ 783255332Scy#if 0 784255332Scy /* 785255332Scy * Doing this properly requires moving it to 786255332Scy * the head of the list which is infesible. 787255332Scy */ 788255332Scy if ((more == 0) && (f->ipfr_badorder == 0)) 789255332Scy f->ipfr_ttl = softc->ipf_ticks + 1; 790255332Scy#endif 791255332Scy } else { 792255332Scy f->ipfr_badorder++; 793255332Scy FBUMPD(ifs_unordered); 794255332Scy if (f->ipfr_pass & FR_FRSTRICT) { 795255332Scy FBUMPD(ifs_strict); 796255332Scy continue; 797255332Scy } 798255332Scy } 799255332Scy f->ipfr_pkts++; 800255332Scy f->ipfr_bytes += fin->fin_plen; 801255332Scy FBUMP(ifs_hits); 80253642Sguido return f; 80353642Sguido } 804255332Scy } 805255332Scy 806255332Scy RWLOCK_EXIT(lock); 807255332Scy FBUMP(ifs_miss); 80853642Sguido return NULL; 80953642Sguido} 81053642Sguido 81153642Sguido 812145522Sdarrenr/* ------------------------------------------------------------------------ */ 813255332Scy/* Function: ipf_frag_natknown */ 814145522Sdarrenr/* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ 815145522Sdarrenr/* match found, else NULL */ 816145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 817145522Sdarrenr/* */ 818145522Sdarrenr/* Functional interface for NAT lookups of the NAT fragment cache */ 819145522Sdarrenr/* ------------------------------------------------------------------------ */ 820255332Scynat_t * 821255332Scyipf_frag_natknown(fin) 822255332Scy fr_info_t *fin; 82353642Sguido{ 824255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 825255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 826145522Sdarrenr nat_t *nat; 827145522Sdarrenr ipfr_t *ipf; 82853642Sguido 829255332Scy if ((softf->ipfr_lock) || !softf->ipfr_natlist) 83060855Sdarrenr return NULL; 831255332Scy#ifdef USE_MUTEXES 832255332Scy ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab, 833255332Scy &softf->ipfr_natfrag); 834255332Scy#else 835255332Scy ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab); 836255332Scy#endif 83753642Sguido if (ipf != NULL) { 83853642Sguido nat = ipf->ipfr_data; 83972006Sdarrenr /* 84072006Sdarrenr * This is the last fragment for this packet. 84172006Sdarrenr */ 842255332Scy if ((ipf->ipfr_ttl == softc->ipf_ticks + 1) && (nat != NULL)) { 84372006Sdarrenr nat->nat_data = NULL; 84472006Sdarrenr ipf->ipfr_data = NULL; 84572006Sdarrenr } 846255332Scy RWLOCK_EXIT(&softf->ipfr_natfrag); 84753642Sguido } else 84853642Sguido nat = NULL; 84953642Sguido return nat; 85053642Sguido} 85153642Sguido 85253642Sguido 853145522Sdarrenr/* ------------------------------------------------------------------------ */ 854255332Scy/* Function: ipf_frag_ipidknown */ 855145522Sdarrenr/* Returns: u_32_t - IPv4 ID for this packet if match found, else */ 856145522Sdarrenr/* return 0xfffffff to indicate no match. */ 857145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 858145522Sdarrenr/* */ 859145522Sdarrenr/* Functional interface for IP ID lookups of the IP ID fragment cache */ 860145522Sdarrenr/* ------------------------------------------------------------------------ */ 861255332Scyu_32_t 862255332Scyipf_frag_ipidknown(fin) 863255332Scy fr_info_t *fin; 86453642Sguido{ 865255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 866255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 867145522Sdarrenr ipfr_t *ipf; 868145522Sdarrenr u_32_t id; 86953642Sguido 870255332Scy if (softf->ipfr_lock || !softf->ipfr_ipidlist) 871145522Sdarrenr return 0xffffffff; 87280482Sdarrenr 873255332Scy#ifdef USE_MUTEXES 874255332Scy ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab, 875255332Scy &softf->ipfr_ipidfrag); 876255332Scy#else 877255332Scy ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab); 878255332Scy#endif 879255332Scy if (ipf != NULL) { 880255332Scy id = (u_32_t)(intptr_t)ipf->ipfr_data; 881255332Scy RWLOCK_EXIT(&softf->ipfr_ipidfrag); 882255332Scy } else 883145522Sdarrenr id = 0xffffffff; 884145522Sdarrenr return id; 885145522Sdarrenr} 886145522Sdarrenr 887145522Sdarrenr 888145522Sdarrenr/* ------------------------------------------------------------------------ */ 889255332Scy/* Function: ipf_frag_known */ 890145522Sdarrenr/* Returns: frentry_t* - pointer to filter rule if a match is found in */ 891145522Sdarrenr/* the frag cache table, else NULL. */ 892145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 893145522Sdarrenr/* passp(O) - pointer to where to store rule flags resturned */ 894145522Sdarrenr/* */ 895145522Sdarrenr/* Functional interface for normal lookups of the fragment cache. If a */ 896145522Sdarrenr/* match is found, return the rule pointer and flags from the rule, except */ 897145522Sdarrenr/* that if FR_LOGFIRST is set, reset FR_LOG. */ 898145522Sdarrenr/* ------------------------------------------------------------------------ */ 899255332Scyfrentry_t * 900255332Scyipf_frag_known(fin, passp) 901255332Scy fr_info_t *fin; 902255332Scy u_32_t *passp; 903145522Sdarrenr{ 904255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 905255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 906145522Sdarrenr frentry_t *fr = NULL; 907145522Sdarrenr ipfr_t *fra; 908145522Sdarrenr u_32_t pass; 909145522Sdarrenr 910255332Scy if ((softf->ipfr_lock) || (softf->ipfr_list == NULL)) 91180482Sdarrenr return NULL; 91280482Sdarrenr 913255332Scy#ifdef USE_MUTEXES 914255332Scy fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads, 915255332Scy &softc->ipf_frag); 916255332Scy#else 917255332Scy fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads); 918255332Scy#endif 919145522Sdarrenr if (fra != NULL) { 920255332Scy if (fin->fin_flx & FI_BAD) { 921255332Scy fr = &ipfr_block; 922255332Scy fin->fin_reason = FRB_BADFRAG; 923255332Scy } else { 924255332Scy fr = fra->ipfr_rule; 925255332Scy } 926145522Sdarrenr fin->fin_fr = fr; 927145522Sdarrenr if (fr != NULL) { 928145522Sdarrenr pass = fr->fr_flags; 929255332Scy if ((pass & FR_KEEPSTATE) != 0) { 930255332Scy fin->fin_flx |= FI_STATE; 931255332Scy /* 932255332Scy * Reset the keep state flag here so that we 933255332Scy * don't try and add a new state entry because 934255332Scy * of a match here. That leads to blocking of 935255332Scy * the packet later because the add fails. 936255332Scy */ 937255332Scy pass &= ~FR_KEEPSTATE; 938255332Scy } 939145522Sdarrenr if ((pass & FR_LOGFIRST) != 0) 940145522Sdarrenr pass &= ~(FR_LOGFIRST|FR_LOG); 941145522Sdarrenr *passp = pass; 942145522Sdarrenr } 943255332Scy RWLOCK_EXIT(&softc->ipf_frag); 944145522Sdarrenr } 94553642Sguido return fr; 94653642Sguido} 94753642Sguido 94853642Sguido 949145522Sdarrenr/* ------------------------------------------------------------------------ */ 950255332Scy/* Function: ipf_frag_natforget */ 951145522Sdarrenr/* Returns: Nil */ 952272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 953272993Scy/* ptr(I) - pointer to data structure */ 954145522Sdarrenr/* */ 955145522Sdarrenr/* Search through all of the fragment cache entries for NAT and wherever a */ 956145522Sdarrenr/* pointer is found to match ptr, reset it to NULL. */ 957145522Sdarrenr/* ------------------------------------------------------------------------ */ 958255332Scyvoid 959255332Scyipf_frag_natforget(softc, ptr) 960255332Scy ipf_main_softc_t *softc; 961255332Scy void *ptr; 96253642Sguido{ 963255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 96453642Sguido ipfr_t *fr; 96553642Sguido 966255332Scy WRITE_ENTER(&softf->ipfr_natfrag); 967255332Scy for (fr = softf->ipfr_natlist; fr; fr = fr->ipfr_next) 968145522Sdarrenr if (fr->ipfr_data == ptr) 969145522Sdarrenr fr->ipfr_data = NULL; 970255332Scy RWLOCK_EXIT(&softf->ipfr_natfrag); 97153642Sguido} 97253642Sguido 97353642Sguido 974145522Sdarrenr/* ------------------------------------------------------------------------ */ 975255332Scy/* Function: ipf_frag_delete */ 976145522Sdarrenr/* Returns: Nil */ 977272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 978272993Scy/* fra(I) - pointer to fragment structure to delete */ 979145522Sdarrenr/* tail(IO) - pointer to the pointer to the tail of the frag */ 980145522Sdarrenr/* list */ 981145522Sdarrenr/* */ 982145522Sdarrenr/* Remove a fragment cache table entry from the table & list. Also free */ 983145522Sdarrenr/* the filter rule it is associated with it if it is no longer used as a */ 984145522Sdarrenr/* result of decreasing the reference count. */ 985145522Sdarrenr/* ------------------------------------------------------------------------ */ 986255332Scystatic void 987255332Scyipf_frag_delete(softc, fra, tail) 988255332Scy ipf_main_softc_t *softc; 989255332Scy ipfr_t *fra, ***tail; 99053642Sguido{ 991255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 99253642Sguido 99353642Sguido if (fra->ipfr_next) 99453642Sguido fra->ipfr_next->ipfr_prev = fra->ipfr_prev; 995145522Sdarrenr *fra->ipfr_prev = fra->ipfr_next; 996145522Sdarrenr if (*tail == &fra->ipfr_next) 997145522Sdarrenr *tail = fra->ipfr_prev; 998145522Sdarrenr 999145522Sdarrenr if (fra->ipfr_hnext) 1000145522Sdarrenr fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev; 1001145522Sdarrenr *fra->ipfr_hprev = fra->ipfr_hnext; 1002170268Sdarrenr 1003170268Sdarrenr if (fra->ipfr_rule != NULL) { 1004255332Scy (void) ipf_derefrule(softc, &fra->ipfr_rule); 1005170268Sdarrenr } 1006170268Sdarrenr 1007170268Sdarrenr if (fra->ipfr_ref <= 0) 1008255332Scy ipf_frag_free(softf, fra); 1009170268Sdarrenr} 1010170268Sdarrenr 1011170268Sdarrenr 1012170268Sdarrenr/* ------------------------------------------------------------------------ */ 1013255332Scy/* Function: ipf_frag_free */ 1014170268Sdarrenr/* Returns: Nil */ 1015272993Scy/* Parameters: softf(I) - pointer to fragment context information */ 1016272993Scy/* fra(I) - pointer to fragment structure to free */ 1017170268Sdarrenr/* */ 1018272993Scy/* Free up a fragment cache entry and bump relevent statistics. */ 1019170268Sdarrenr/* ------------------------------------------------------------------------ */ 1020255332Scystatic void 1021255332Scyipf_frag_free(softf, fra) 1022255332Scy ipf_frag_softc_t *softf; 1023255332Scy ipfr_t *fra; 1024170268Sdarrenr{ 102553642Sguido KFREE(fra); 1026255332Scy FBUMP(ifs_expire); 1027255332Scy softf->ipfr_stats.ifs_inuse--; 102853642Sguido} 102953642Sguido 103053642Sguido 1031145522Sdarrenr/* ------------------------------------------------------------------------ */ 1032255332Scy/* Function: ipf_frag_clear */ 1033145522Sdarrenr/* Returns: Nil */ 1034272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1035145522Sdarrenr/* */ 1036145522Sdarrenr/* Free memory in use by fragment state information kept. Do the normal */ 1037145522Sdarrenr/* fragment state stuff first and then the NAT-fragment table. */ 1038145522Sdarrenr/* ------------------------------------------------------------------------ */ 1039255332Scyvoid 1040255332Scyipf_frag_clear(softc) 1041255332Scy ipf_main_softc_t *softc; 104253642Sguido{ 1043255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1044145522Sdarrenr ipfr_t *fra; 104553642Sguido nat_t *nat; 104653642Sguido 1047255332Scy WRITE_ENTER(&softc->ipf_frag); 1048255332Scy while ((fra = softf->ipfr_list) != NULL) { 1049170268Sdarrenr fra->ipfr_ref--; 1050255332Scy ipf_frag_delete(softc, fra, &softf->ipfr_tail); 1051170268Sdarrenr } 1052255332Scy softf->ipfr_tail = &softf->ipfr_list; 1053255332Scy RWLOCK_EXIT(&softc->ipf_frag); 105453642Sguido 1055255332Scy WRITE_ENTER(&softc->ipf_nat); 1056255332Scy WRITE_ENTER(&softf->ipfr_natfrag); 1057255332Scy while ((fra = softf->ipfr_natlist) != NULL) { 1058145522Sdarrenr nat = fra->ipfr_data; 1059145522Sdarrenr if (nat != NULL) { 1060145522Sdarrenr if (nat->nat_data == fra) 1061145522Sdarrenr nat->nat_data = NULL; 106253642Sguido } 1063170268Sdarrenr fra->ipfr_ref--; 1064255332Scy ipf_frag_delete(softc, fra, &softf->ipfr_nattail); 1065145522Sdarrenr } 1066255332Scy softf->ipfr_nattail = &softf->ipfr_natlist; 1067255332Scy RWLOCK_EXIT(&softf->ipfr_natfrag); 1068255332Scy RWLOCK_EXIT(&softc->ipf_nat); 106953642Sguido} 107053642Sguido 107153642Sguido 1072145522Sdarrenr/* ------------------------------------------------------------------------ */ 1073255332Scy/* Function: ipf_frag_expire */ 1074145522Sdarrenr/* Returns: Nil */ 1075272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1076145522Sdarrenr/* */ 1077145522Sdarrenr/* Expire entries in the fragment cache table that have been there too long */ 1078145522Sdarrenr/* ------------------------------------------------------------------------ */ 1079255332Scyvoid 1080255332Scyipf_frag_expire(softc) 1081255332Scy ipf_main_softc_t *softc; 108253642Sguido{ 1083255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 108453642Sguido ipfr_t **fp, *fra; 108553642Sguido nat_t *nat; 1086153876Sguido SPL_INT(s); 108753642Sguido 1088255332Scy if (softf->ipfr_lock) 108960855Sdarrenr return; 109053642Sguido 109153642Sguido SPL_NET(s); 1092255332Scy WRITE_ENTER(&softc->ipf_frag); 109353642Sguido /* 109453642Sguido * Go through the entire table, looking for entries to expire, 1095255332Scy * which is indicated by the ttl being less than or equal to ipf_ticks. 109653642Sguido */ 1097255332Scy for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) { 1098255332Scy if (fra->ipfr_ttl > softc->ipf_ticks) 1099145522Sdarrenr break; 1100170268Sdarrenr fra->ipfr_ref--; 1101255332Scy ipf_frag_delete(softc, fra, &softf->ipfr_tail); 1102145522Sdarrenr } 1103255332Scy RWLOCK_EXIT(&softc->ipf_frag); 110453642Sguido 1105255332Scy WRITE_ENTER(&softf->ipfr_ipidfrag); 1106255332Scy for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) { 1107255332Scy if (fra->ipfr_ttl > softc->ipf_ticks) 1108145522Sdarrenr break; 1109170268Sdarrenr fra->ipfr_ref--; 1110255332Scy ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail); 1111145522Sdarrenr } 1112255332Scy RWLOCK_EXIT(&softf->ipfr_ipidfrag); 1113145522Sdarrenr 111453642Sguido /* 111553642Sguido * Same again for the NAT table, except that if the structure also 111653642Sguido * still points to a NAT structure, and the NAT structure points back 111753642Sguido * at the one to be free'd, NULL the reference from the NAT struct. 111853642Sguido * NOTE: We need to grab both mutex's early, and in this order so as 111953642Sguido * to prevent a deadlock if both try to expire at the same time. 1120170268Sdarrenr * The extra if() statement here is because it locks out all NAT 1121170268Sdarrenr * operations - no need to do that if there are no entries in this 1122170268Sdarrenr * list, right? 112353642Sguido */ 1124255332Scy if (softf->ipfr_natlist != NULL) { 1125255332Scy WRITE_ENTER(&softc->ipf_nat); 1126255332Scy WRITE_ENTER(&softf->ipfr_natfrag); 1127255332Scy for (fp = &softf->ipfr_natlist; ((fra = *fp) != NULL); ) { 1128255332Scy if (fra->ipfr_ttl > softc->ipf_ticks) 1129170268Sdarrenr break; 1130170268Sdarrenr nat = fra->ipfr_data; 1131170268Sdarrenr if (nat != NULL) { 1132170268Sdarrenr if (nat->nat_data == fra) 1133170268Sdarrenr nat->nat_data = NULL; 1134170268Sdarrenr } 1135170268Sdarrenr fra->ipfr_ref--; 1136255332Scy ipf_frag_delete(softc, fra, &softf->ipfr_nattail); 113753642Sguido } 1138255332Scy RWLOCK_EXIT(&softf->ipfr_natfrag); 1139255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1140145522Sdarrenr } 114153642Sguido SPL_X(s); 114260855Sdarrenr} 114360855Sdarrenr 114460855Sdarrenr 1145145522Sdarrenr/* ------------------------------------------------------------------------ */ 1146255332Scy/* Function: ipf_frag_pkt_next */ 1147272993Scy/* Returns: int - 0 == success, else error */ 1148272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1149272993Scy/* token(I) - pointer to token information for this caller */ 1150272993Scy/* itp(I) - pointer to generic iterator from caller */ 1151272993Scy/* */ 1152272993Scy/* This function is used to step through the fragment cache list used for */ 1153272993Scy/* filter rules. The hard work is done by the more generic ipf_frag_next. */ 1154145522Sdarrenr/* ------------------------------------------------------------------------ */ 1155255332Scyint 1156255332Scyipf_frag_pkt_next(softc, token, itp) 1157255332Scy ipf_main_softc_t *softc; 1158255332Scy ipftoken_t *token; 1159255332Scy ipfgeniter_t *itp; 116060855Sdarrenr{ 1161255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 116260855Sdarrenr 1163255332Scy#ifdef USE_MUTEXES 1164255332Scy return ipf_frag_next(softc, token, itp, &softf->ipfr_list, 1165255332Scy &softf->ipfr_frag); 1166255332Scy#else 1167255332Scy return ipf_frag_next(softc, token, itp, &softf->ipfr_list); 1168255332Scy#endif 116953642Sguido} 1170170268Sdarrenr 1171170268Sdarrenr 1172170268Sdarrenr/* ------------------------------------------------------------------------ */ 1173255332Scy/* Function: ipf_frag_nat_next */ 1174272993Scy/* Returns: int - 0 == success, else error */ 1175272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1176272993Scy/* token(I) - pointer to token information for this caller */ 1177272993Scy/* itp(I) - pointer to generic iterator from caller */ 1178272993Scy/* */ 1179272993Scy/* This function is used to step through the fragment cache list used for */ 1180272993Scy/* NAT. The hard work is done by the more generic ipf_frag_next. */ 1181255332Scy/* ------------------------------------------------------------------------ */ 1182255332Scyint 1183255332Scyipf_frag_nat_next(softc, token, itp) 1184255332Scy ipf_main_softc_t *softc; 1185255332Scy ipftoken_t *token; 1186255332Scy ipfgeniter_t *itp; 1187255332Scy{ 1188255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft;; 1189255332Scy 1190255332Scy#ifdef USE_MUTEXES 1191255332Scy return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist, 1192255332Scy &softf->ipfr_natfrag); 1193255332Scy#else 1194255332Scy return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist); 1195255332Scy#endif 1196255332Scy} 1197255332Scy 1198255332Scy/* ------------------------------------------------------------------------ */ 1199255332Scy/* Function: ipf_frag_next */ 1200170268Sdarrenr/* Returns: int - 0 == success, else error */ 1201272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1202272993Scy/* token(I) - pointer to token information for this caller */ 1203170268Sdarrenr/* itp(I) - pointer to generic iterator from caller */ 1204170268Sdarrenr/* top(I) - top of the fragment list */ 1205170268Sdarrenr/* lock(I) - fragment cache lock */ 1206170268Sdarrenr/* */ 1207170268Sdarrenr/* This function is used to interate through the list of entries in the */ 1208170268Sdarrenr/* fragment cache. It increases the reference count on the one currently */ 1209170268Sdarrenr/* being returned so that the caller can come back and resume from it later.*/ 1210170268Sdarrenr/* */ 1211170268Sdarrenr/* This function is used for both the NAT fragment cache as well as the ipf */ 1212255332Scy/* fragment cache - hence the reason for passing in top and lock. */ 1213170268Sdarrenr/* ------------------------------------------------------------------------ */ 1214255332Scystatic int 1215255332Scyipf_frag_next(softc, token, itp, top 1216170268Sdarrenr#ifdef USE_MUTEXES 1217170268Sdarrenr, lock 1218170268Sdarrenr#endif 1219170268Sdarrenr) 1220255332Scy ipf_main_softc_t *softc; 1221255332Scy ipftoken_t *token; 1222255332Scy ipfgeniter_t *itp; 1223255332Scy ipfr_t **top; 1224170268Sdarrenr#ifdef USE_MUTEXES 1225255332Scy ipfrwlock_t *lock; 1226170268Sdarrenr#endif 1227170268Sdarrenr{ 1228170268Sdarrenr ipfr_t *frag, *next, zero; 1229170268Sdarrenr int error = 0; 1230170268Sdarrenr 1231255332Scy if (itp->igi_data == NULL) { 1232255332Scy IPFERROR(20001); 1233255332Scy return EFAULT; 1234170268Sdarrenr } 1235170268Sdarrenr 1236255332Scy if (itp->igi_nitems != 1) { 1237255332Scy IPFERROR(20003); 1238255332Scy return EFAULT; 1239255332Scy } 1240255332Scy 1241255332Scy frag = token->ipt_data; 1242255332Scy 1243170268Sdarrenr READ_ENTER(lock); 1244255332Scy 1245170268Sdarrenr if (frag == NULL) 1246170268Sdarrenr next = *top; 1247170268Sdarrenr else 1248170268Sdarrenr next = frag->ipfr_next; 1249170268Sdarrenr 1250170268Sdarrenr if (next != NULL) { 1251170268Sdarrenr ATOMIC_INC(next->ipfr_ref); 1252170268Sdarrenr token->ipt_data = next; 1253170268Sdarrenr } else { 1254170268Sdarrenr bzero(&zero, sizeof(zero)); 1255170268Sdarrenr next = &zero; 1256172776Sdarrenr token->ipt_data = NULL; 1257170268Sdarrenr } 1258255332Scy if (next->ipfr_next == NULL) 1259255332Scy ipf_token_mark_complete(token); 1260255332Scy 1261170268Sdarrenr RWLOCK_EXIT(lock); 1262170268Sdarrenr 1263255332Scy error = COPYOUT(next, itp->igi_data, sizeof(*next)); 1264255332Scy if (error != 0) 1265255332Scy IPFERROR(20002); 1266255332Scy 1267255332Scy if (frag != NULL) { 1268172776Sdarrenr#ifdef USE_MUTEXES 1269255332Scy ipf_frag_deref(softc, &frag, lock); 1270172776Sdarrenr#else 1271255332Scy ipf_frag_deref(softc, &frag); 1272172776Sdarrenr#endif 1273255332Scy } 1274255332Scy return error; 1275255332Scy} 1276170268Sdarrenr 1277170268Sdarrenr 1278255332Scy/* ------------------------------------------------------------------------ */ 1279255332Scy/* Function: ipf_frag_pkt_deref */ 1280255332Scy/* Returns: Nil */ 1281272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1282272993Scy/* data(I) - pointer to frag cache pointer */ 1283255332Scy/* */ 1284272993Scy/* This function is the external interface for dropping a reference to a */ 1285272993Scy/* fragment cache entry used by filter rules. */ 1286255332Scy/* ------------------------------------------------------------------------ */ 1287255332Scyvoid 1288255332Scyipf_frag_pkt_deref(softc, data) 1289255332Scy ipf_main_softc_t *softc; 1290255332Scy void *data; 1291255332Scy{ 1292255332Scy ipfr_t **frp = data; 1293255332Scy 1294255332Scy#ifdef USE_MUTEXES 1295255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1296255332Scy 1297255332Scy ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_frag); 1298255332Scy#else 1299255332Scy ipf_frag_deref(softc->ipf_frag_soft, frp); 1300255332Scy#endif 1301170268Sdarrenr} 1302170268Sdarrenr 1303170268Sdarrenr 1304170268Sdarrenr/* ------------------------------------------------------------------------ */ 1305255332Scy/* Function: ipf_frag_nat_deref */ 1306170268Sdarrenr/* Returns: Nil */ 1307272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1308272993Scy/* data(I) - pointer to frag cache pointer */ 1309255332Scy/* */ 1310272993Scy/* This function is the external interface for dropping a reference to a */ 1311272993Scy/* fragment cache entry used by NAT table entries. */ 1312255332Scy/* ------------------------------------------------------------------------ */ 1313255332Scyvoid 1314255332Scyipf_frag_nat_deref(softc, data) 1315255332Scy ipf_main_softc_t *softc; 1316255332Scy void *data; 1317255332Scy{ 1318255332Scy ipfr_t **frp = data; 1319255332Scy 1320255332Scy#ifdef USE_MUTEXES 1321255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1322255332Scy 1323255332Scy ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_natfrag); 1324255332Scy#else 1325255332Scy ipf_frag_deref(softc->ipf_frag_soft, frp); 1326255332Scy#endif 1327255332Scy} 1328255332Scy 1329255332Scy 1330255332Scy/* ------------------------------------------------------------------------ */ 1331255332Scy/* Function: ipf_frag_deref */ 1332255332Scy/* Returns: Nil */ 1333170268Sdarrenr/* Parameters: frp(IO) - pointer to fragment structure to deference */ 1334170268Sdarrenr/* lock(I) - lock associated with the fragment */ 1335170268Sdarrenr/* */ 1336170268Sdarrenr/* This function dereferences a fragment structure (ipfr_t). The pointer */ 1337170268Sdarrenr/* passed in will always be reset back to NULL, even if the structure is */ 1338170268Sdarrenr/* not freed, to enforce the notion that the caller is no longer entitled */ 1339170268Sdarrenr/* to use the pointer it is dropping the reference to. */ 1340170268Sdarrenr/* ------------------------------------------------------------------------ */ 1341255332Scystatic void 1342255332Scyipf_frag_deref(arg, frp 1343170268Sdarrenr#ifdef USE_MUTEXES 1344170268Sdarrenr, lock 1345170268Sdarrenr#endif 1346170268Sdarrenr) 1347255332Scy void *arg; 1348255332Scy ipfr_t **frp; 1349170268Sdarrenr#ifdef USE_MUTEXES 1350255332Scy ipfrwlock_t *lock; 1351170268Sdarrenr#endif 1352170268Sdarrenr{ 1353255332Scy ipf_frag_softc_t *softf = arg; 1354170268Sdarrenr ipfr_t *fra; 1355170268Sdarrenr 1356170268Sdarrenr fra = *frp; 1357170268Sdarrenr *frp = NULL; 1358170268Sdarrenr 1359170268Sdarrenr WRITE_ENTER(lock); 1360170268Sdarrenr fra->ipfr_ref--; 1361170268Sdarrenr if (fra->ipfr_ref <= 0) 1362255332Scy ipf_frag_free(softf, fra); 1363170268Sdarrenr RWLOCK_EXIT(lock); 1364170268Sdarrenr} 1365