1145522Sdarrenr/* $FreeBSD: releng/10.3/sys/contrib/ipfilter/netinet/ip_frag.c 317487 2017-04-27 06:52:30Z delphij $ */ 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: releng/10.3/sys/contrib/ipfilter/netinet/ip_frag.c 317487 2017-04-27 06:52:30Z delphij $"; 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 115275690Scyconst ipftuneable_t ipf_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 192255332Scy softf->ipfr_size = IPFT_SIZE; 193255332Scy softf->ipfr_ttl = IPF_TTLVAL(60); 194255332Scy softf->ipfr_lock = 1; 195255332Scy softf->ipfr_tail = &softf->ipfr_list; 196255332Scy softf->ipfr_nattail = &softf->ipfr_natlist; 197255332Scy softf->ipfr_ipidtail = &softf->ipfr_ipidlist; 198255332Scy 199255332Scy return softf; 200255332Scy} 201255332Scy 202255332Scy 203255332Scy/* ------------------------------------------------------------------------ */ 204255332Scy/* Function: ipf_frag_soft_destroy */ 205255332Scy/* Returns: Nil */ 206255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 207255332Scy/* arg(I) - pointer to local context to use */ 208255332Scy/* */ 209145522Sdarrenr/* Initialise the hash tables for the fragment cache lookups. */ 210145522Sdarrenr/* ------------------------------------------------------------------------ */ 211255332Scyvoid 212255332Scyipf_frag_soft_destroy(softc, arg) 213255332Scy ipf_main_softc_t *softc; 214255332Scy void *arg; 215145522Sdarrenr{ 216255332Scy ipf_frag_softc_t *softf = arg; 21753642Sguido 218255332Scy RW_DESTROY(&softf->ipfr_ipidfrag); 219255332Scy RW_DESTROY(&softf->ipfr_frag); 220255332Scy RW_DESTROY(&softf->ipfr_natfrag); 221145522Sdarrenr 222255332Scy KFREE(softf); 223255332Scy} 224255332Scy 225255332Scy 226255332Scy/* ------------------------------------------------------------------------ */ 227255332Scy/* Function: ipf_frag_soft_init */ 228255332Scy/* Returns: int - 0 == success, -1 == error */ 229255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 230255332Scy/* arg(I) - pointer to local context to use */ 231255332Scy/* */ 232255332Scy/* Initialise the hash tables for the fragment cache lookups. */ 233255332Scy/* ------------------------------------------------------------------------ */ 234255332Scy/*ARGSUSED*/ 235255332Scyint 236255332Scyipf_frag_soft_init(softc, arg) 237255332Scy ipf_main_softc_t *softc; 238255332Scy void *arg; 239255332Scy{ 240255332Scy ipf_frag_softc_t *softf = arg; 241255332Scy 242255332Scy KMALLOCS(softf->ipfr_heads, ipfr_t **, 243255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 244255332Scy if (softf->ipfr_heads == NULL) 245145522Sdarrenr return -1; 246145522Sdarrenr 247255332Scy bzero((char *)softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); 248145522Sdarrenr 249255332Scy KMALLOCS(softf->ipfr_nattab, ipfr_t **, 250255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 251255332Scy if (softf->ipfr_nattab == NULL) 252255332Scy return -2; 253255332Scy 254255332Scy bzero((char *)softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); 255255332Scy 256255332Scy KMALLOCS(softf->ipfr_ipidtab, ipfr_t **, 257255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 258255332Scy if (softf->ipfr_ipidtab == NULL) 259255332Scy return -3; 260255332Scy 261255332Scy bzero((char *)softf->ipfr_ipidtab, 262255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 263255332Scy 264255332Scy softf->ipfr_lock = 0; 265255332Scy softf->ipfr_inited = 1; 266255332Scy 267145522Sdarrenr return 0; 268145522Sdarrenr} 269145522Sdarrenr 270145522Sdarrenr 271145522Sdarrenr/* ------------------------------------------------------------------------ */ 272255332Scy/* Function: ipf_frag_soft_fini */ 273255332Scy/* Returns: int - 0 == success, -1 == error */ 274255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 275255332Scy/* arg(I) - pointer to local context to use */ 276145522Sdarrenr/* */ 277145522Sdarrenr/* Free all memory allocated whilst running and from initialisation. */ 278145522Sdarrenr/* ------------------------------------------------------------------------ */ 279255332Scyint 280255332Scyipf_frag_soft_fini(softc, arg) 281255332Scy ipf_main_softc_t *softc; 282255332Scy void *arg; 28353642Sguido{ 284255332Scy ipf_frag_softc_t *softf = arg; 285145522Sdarrenr 286255332Scy softf->ipfr_lock = 1; 287255332Scy 288255332Scy if (softf->ipfr_inited == 1) { 289255332Scy ipf_frag_clear(softc); 290255332Scy 291255332Scy softf->ipfr_inited = 0; 292145522Sdarrenr } 293145522Sdarrenr 294255332Scy if (softf->ipfr_heads != NULL) 295255332Scy KFREES(softf->ipfr_heads, 296255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 297255332Scy softf->ipfr_heads = NULL; 298145522Sdarrenr 299255332Scy if (softf->ipfr_nattab != NULL) 300255332Scy KFREES(softf->ipfr_nattab, 301255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 302255332Scy softf->ipfr_nattab = NULL; 303145522Sdarrenr 304255332Scy if (softf->ipfr_ipidtab != NULL) 305255332Scy KFREES(softf->ipfr_ipidtab, 306255332Scy softf->ipfr_size * sizeof(ipfr_t *)); 307255332Scy softf->ipfr_ipidtab = NULL; 308255332Scy 309255332Scy return 0; 310145522Sdarrenr} 311145522Sdarrenr 312145522Sdarrenr 313145522Sdarrenr/* ------------------------------------------------------------------------ */ 314255332Scy/* Function: ipf_frag_set_lock */ 315255332Scy/* Returns: Nil */ 316255332Scy/* Parameters: arg(I) - pointer to local context to use */ 317255332Scy/* tmp(I) - new value for lock */ 318255332Scy/* */ 319255332Scy/* Stub function that allows for external manipulation of ipfr_lock */ 320255332Scy/* ------------------------------------------------------------------------ */ 321255332Scyvoid 322255332Scyipf_frag_setlock(arg, tmp) 323255332Scy void *arg; 324255332Scy int tmp; 325255332Scy{ 326255332Scy ipf_frag_softc_t *softf = arg; 327255332Scy 328255332Scy softf->ipfr_lock = tmp; 329255332Scy} 330255332Scy 331255332Scy 332255332Scy/* ------------------------------------------------------------------------ */ 333255332Scy/* Function: ipf_frag_stats */ 334145522Sdarrenr/* Returns: ipfrstat_t* - pointer to struct with current frag stats */ 335255332Scy/* Parameters: arg(I) - pointer to local context to use */ 336145522Sdarrenr/* */ 337145522Sdarrenr/* Updates ipfr_stats with current information and returns a pointer to it */ 338145522Sdarrenr/* ------------------------------------------------------------------------ */ 339255332Scyipfrstat_t * 340255332Scyipf_frag_stats(arg) 341255332Scy void *arg; 342145522Sdarrenr{ 343255332Scy ipf_frag_softc_t *softf = arg; 344255332Scy 345255332Scy softf->ipfr_stats.ifs_table = softf->ipfr_heads; 346255332Scy softf->ipfr_stats.ifs_nattab = softf->ipfr_nattab; 347255332Scy return &softf->ipfr_stats; 34853642Sguido} 34953642Sguido 35053642Sguido 351145522Sdarrenr/* ------------------------------------------------------------------------ */ 352255332Scy/* Function: ipfr_frag_new */ 353145522Sdarrenr/* Returns: ipfr_t * - pointer to fragment cache state info or NULL */ 354145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 355145522Sdarrenr/* table(I) - pointer to frag table to add to */ 356255332Scy/* lock(I) - pointer to lock to get a write hold of */ 357145522Sdarrenr/* */ 358145522Sdarrenr/* Add a new entry to the fragment cache, registering it as having come */ 359145522Sdarrenr/* through this box, with the result of the filter operation. */ 360255332Scy/* */ 361255332Scy/* If this function succeeds, it returns with a write lock held on "lock". */ 362255332Scy/* If it fails, no lock is held on return. */ 363145522Sdarrenr/* ------------------------------------------------------------------------ */ 364255332Scystatic ipfr_t * 365255332Scyipfr_frag_new(softc, softf, fin, pass, table 366255332Scy#ifdef USE_MUTEXES 367255332Scy, lock 368255332Scy#endif 369255332Scy) 370255332Scy ipf_main_softc_t *softc; 371255332Scy ipf_frag_softc_t *softf; 372255332Scy fr_info_t *fin; 373255332Scy u_32_t pass; 374255332Scy ipfr_t *table[]; 375255332Scy#ifdef USE_MUTEXES 376255332Scy ipfrwlock_t *lock; 377255332Scy#endif 37853642Sguido{ 379255332Scy ipfr_t *fra, frag, *fran; 38075262Sdarrenr u_int idx, off; 381161356Sguido frentry_t *fr; 38253642Sguido 383255332Scy if (softf->ipfr_stats.ifs_inuse >= softf->ipfr_size) { 384255332Scy FBUMPD(ifs_maximum); 38563523Sdarrenr return NULL; 386255332Scy } 38763523Sdarrenr 388255332Scy if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) { 389255332Scy FBUMPD(ifs_newbad); 39075262Sdarrenr return NULL; 391255332Scy } 39275262Sdarrenr 393255332Scy if (pass & FR_FRSTRICT) { 394255332Scy if (fin->fin_off != 0) { 395255332Scy FBUMPD(ifs_newrestrictnot0); 396145522Sdarrenr return NULL; 397255332Scy } 398255332Scy } 399145522Sdarrenr 400255332Scy frag.ipfr_v = fin->fin_v; 401255332Scy idx = fin->fin_v; 402255332Scy frag.ipfr_p = fin->fin_p; 403255332Scy idx += fin->fin_p; 404255332Scy frag.ipfr_id = fin->fin_id; 405255332Scy idx += fin->fin_id; 406255332Scy frag.ipfr_source = fin->fin_fi.fi_src; 407255332Scy idx += frag.ipfr_src.s_addr; 408255332Scy frag.ipfr_dest = fin->fin_fi.fi_dst; 409255332Scy idx += frag.ipfr_dst.s_addr; 41072006Sdarrenr frag.ipfr_ifp = fin->fin_ifp; 41153642Sguido idx *= 127; 412255332Scy idx %= softf->ipfr_size; 41353642Sguido 41480482Sdarrenr frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 41580482Sdarrenr frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 41680482Sdarrenr frag.ipfr_auth = fin->fin_fi.fi_auth; 41780482Sdarrenr 418255332Scy off = fin->fin_off >> 3; 419255332Scy if (off == 0) { 420255332Scy char *ptr; 421255332Scy int end; 422255332Scy 423255332Scy#ifdef USE_INET6 424255332Scy if (fin->fin_v == 6) { 425255332Scy 426255332Scy ptr = (char *)fin->fin_fraghdr + 427255332Scy sizeof(struct ip6_frag); 428255332Scy } else 429255332Scy#endif 430255332Scy { 431255332Scy ptr = fin->fin_dp; 432255332Scy } 433255332Scy end = fin->fin_plen - (ptr - (char *)fin->fin_ip); 434255332Scy frag.ipfr_firstend = end >> 3; 435255332Scy } else { 436255332Scy frag.ipfr_firstend = 0; 437255332Scy } 438255332Scy 43953642Sguido /* 440255332Scy * allocate some memory, if possible, if not, just record that we 441255332Scy * failed to do so. 442255332Scy */ 443255332Scy KMALLOC(fran, ipfr_t *); 444255332Scy if (fran == NULL) { 445255332Scy FBUMPD(ifs_nomem); 446255332Scy return NULL; 447255332Scy } 448255332Scy 449255332Scy WRITE_ENTER(lock); 450255332Scy 451255332Scy /* 45253642Sguido * first, make sure it isn't already there... 45353642Sguido */ 454145522Sdarrenr for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext) 455145522Sdarrenr if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, 45653642Sguido IPFR_CMPSZ)) { 457255332Scy RWLOCK_EXIT(lock); 458255332Scy FBUMPD(ifs_exists); 459317487Sdelphij KFREE(fran); 46053642Sguido return NULL; 46153642Sguido } 46253642Sguido 463255332Scy fra = fran; 464255332Scy fran = NULL; 465161356Sguido fr = fin->fin_fr; 466161356Sguido fra->ipfr_rule = fr; 467161356Sguido if (fr != NULL) { 468153876Sguido MUTEX_ENTER(&fr->fr_lock); 469153876Sguido fr->fr_ref++; 470153876Sguido MUTEX_EXIT(&fr->fr_lock); 471153876Sguido } 472153876Sguido 47353642Sguido /* 474130886Sdarrenr * Insert the fragment into the fragment table, copy the struct used 47553642Sguido * in the search using bcopy rather than reassign each field. 476102520Sdarrenr * Set the ttl to the default. 47753642Sguido */ 478145522Sdarrenr if ((fra->ipfr_hnext = table[idx]) != NULL) 479145522Sdarrenr table[idx]->ipfr_hprev = &fra->ipfr_hnext; 480145522Sdarrenr fra->ipfr_hprev = table + idx; 48153642Sguido fra->ipfr_data = NULL; 48253642Sguido table[idx] = fra; 483145522Sdarrenr bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); 484255332Scy fra->ipfr_v = fin->fin_v; 485255332Scy fra->ipfr_ttl = softc->ipf_ticks + softf->ipfr_ttl; 486255332Scy fra->ipfr_firstend = frag.ipfr_firstend; 487145522Sdarrenr 48853642Sguido /* 48953642Sguido * Compute the offset of the expected start of the next packet. 49053642Sguido */ 491145522Sdarrenr if (off == 0) 49275262Sdarrenr fra->ipfr_seen0 = 1; 49375262Sdarrenr fra->ipfr_off = off + (fin->fin_dlen >> 3); 494145522Sdarrenr fra->ipfr_pass = pass; 495170268Sdarrenr fra->ipfr_ref = 1; 496255332Scy fra->ipfr_pkts = 1; 497255332Scy fra->ipfr_bytes = fin->fin_plen; 498255332Scy FBUMP(ifs_inuse); 499255332Scy FBUMP(ifs_new); 50053642Sguido return fra; 50153642Sguido} 50253642Sguido 50353642Sguido 504145522Sdarrenr/* ------------------------------------------------------------------------ */ 505255332Scy/* Function: ipf_frag_new */ 506145522Sdarrenr/* Returns: int - 0 == success, -1 == error */ 507145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 508145522Sdarrenr/* */ 509145522Sdarrenr/* Add a new entry to the fragment cache table based on the current packet */ 510145522Sdarrenr/* ------------------------------------------------------------------------ */ 511255332Scyint 512255332Scyipf_frag_new(softc, fin, pass) 513255332Scy ipf_main_softc_t *softc; 514255332Scy u_32_t pass; 515255332Scy fr_info_t *fin; 51653642Sguido{ 517255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 518145522Sdarrenr ipfr_t *fra; 51953642Sguido 520255332Scy if (softf->ipfr_lock != 0) 52167614Sdarrenr return -1; 522145522Sdarrenr 523255332Scy#ifdef USE_MUTEXES 524255332Scy fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads, &softc->ipf_frag); 525255332Scy#else 526255332Scy fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads); 527255332Scy#endif 528145522Sdarrenr if (fra != NULL) { 529255332Scy *softf->ipfr_tail = fra; 530255332Scy fra->ipfr_prev = softf->ipfr_tail; 531255332Scy softf->ipfr_tail = &fra->ipfr_next; 532145522Sdarrenr fra->ipfr_next = NULL; 533255332Scy RWLOCK_EXIT(&softc->ipf_frag); 534145522Sdarrenr } 535145522Sdarrenr return fra ? 0 : -1; 53653642Sguido} 53753642Sguido 53853642Sguido 539145522Sdarrenr/* ------------------------------------------------------------------------ */ 540255332Scy/* Function: ipf_frag_natnew */ 541145522Sdarrenr/* Returns: int - 0 == success, -1 == error */ 542145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 543145522Sdarrenr/* nat(I) - pointer to NAT structure */ 544145522Sdarrenr/* */ 545145522Sdarrenr/* Create a new NAT fragment cache entry based on the current packet and */ 546145522Sdarrenr/* the NAT structure for this "session". */ 547145522Sdarrenr/* ------------------------------------------------------------------------ */ 548255332Scyint 549255332Scyipf_frag_natnew(softc, fin, pass, nat) 550255332Scy ipf_main_softc_t *softc; 551255332Scy fr_info_t *fin; 552255332Scy u_32_t pass; 553255332Scy nat_t *nat; 55453642Sguido{ 555255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 556145522Sdarrenr ipfr_t *fra; 55753642Sguido 558255332Scy if (softf->ipfr_lock != 0) 559145522Sdarrenr return 0; 56080482Sdarrenr 561255332Scy#ifdef USE_MUTEXES 562255332Scy fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab, 563255332Scy &softf->ipfr_natfrag); 564255332Scy#else 565255332Scy fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab); 566255332Scy#endif 567145522Sdarrenr if (fra != NULL) { 568145522Sdarrenr fra->ipfr_data = nat; 569145522Sdarrenr nat->nat_data = fra; 570255332Scy *softf->ipfr_nattail = fra; 571255332Scy fra->ipfr_prev = softf->ipfr_nattail; 572255332Scy softf->ipfr_nattail = &fra->ipfr_next; 573145522Sdarrenr fra->ipfr_next = NULL; 574255332Scy RWLOCK_EXIT(&softf->ipfr_natfrag); 575255332Scy return 0; 57653642Sguido } 577255332Scy return -1; 57853642Sguido} 57953642Sguido 58053642Sguido 581145522Sdarrenr/* ------------------------------------------------------------------------ */ 582255332Scy/* Function: ipf_frag_ipidnew */ 583145522Sdarrenr/* Returns: int - 0 == success, -1 == error */ 584145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 585145522Sdarrenr/* ipid(I) - new IP ID for this fragmented packet */ 586145522Sdarrenr/* */ 587145522Sdarrenr/* Create a new fragment cache entry for this packet and store, as a data */ 588145522Sdarrenr/* pointer, the new IP ID value. */ 589145522Sdarrenr/* ------------------------------------------------------------------------ */ 590255332Scyint 591255332Scyipf_frag_ipidnew(fin, ipid) 592255332Scy fr_info_t *fin; 593255332Scy u_32_t ipid; 594145522Sdarrenr{ 595255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 596255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 597145522Sdarrenr ipfr_t *fra; 598145522Sdarrenr 599255332Scy if (softf->ipfr_lock) 600145522Sdarrenr return 0; 601145522Sdarrenr 602255332Scy#ifdef USE_MUTEXES 603255332Scy fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); 604255332Scy#else 605255332Scy fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab); 606255332Scy#endif 607145522Sdarrenr if (fra != NULL) { 608255332Scy fra->ipfr_data = (void *)(intptr_t)ipid; 609255332Scy *softf->ipfr_ipidtail = fra; 610255332Scy fra->ipfr_prev = softf->ipfr_ipidtail; 611255332Scy softf->ipfr_ipidtail = &fra->ipfr_next; 612145522Sdarrenr fra->ipfr_next = NULL; 613255332Scy RWLOCK_EXIT(&softf->ipfr_ipidfrag); 614145522Sdarrenr } 615145522Sdarrenr return fra ? 0 : -1; 616145522Sdarrenr} 617145522Sdarrenr 618145522Sdarrenr 619145522Sdarrenr/* ------------------------------------------------------------------------ */ 620255332Scy/* Function: ipf_frag_lookup */ 621145522Sdarrenr/* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */ 622145522Sdarrenr/* matching entry in the frag table, else NULL */ 623145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 624145522Sdarrenr/* table(I) - pointer to fragment cache table to search */ 625145522Sdarrenr/* */ 626145522Sdarrenr/* Check the fragment cache to see if there is already a record of this */ 627145522Sdarrenr/* packet with its filter result known. */ 628255332Scy/* */ 629255332Scy/* If this function succeeds, it returns with a write lock held on "lock". */ 630255332Scy/* If it fails, no lock is held on return. */ 631145522Sdarrenr/* ------------------------------------------------------------------------ */ 632255332Scystatic ipfr_t * 633255332Scyipf_frag_lookup(softc, softf, fin, table 634255332Scy#ifdef USE_MUTEXES 635255332Scy, lock 636255332Scy#endif 637255332Scy) 638255332Scy ipf_main_softc_t *softc; 639255332Scy ipf_frag_softc_t *softf; 640255332Scy fr_info_t *fin; 641255332Scy ipfr_t *table[]; 642255332Scy#ifdef USE_MUTEXES 643255332Scy ipfrwlock_t *lock; 644255332Scy#endif 64553642Sguido{ 646145522Sdarrenr ipfr_t *f, frag; 647145522Sdarrenr u_int idx; 64853642Sguido 649255332Scy /* 650255332Scy * We don't want to let short packets match because they could be 651255332Scy * compromising the security of other rules that want to match on 652255332Scy * layer 4 fields (and can't because they have been fragmented off.) 653255332Scy * Why do this check here? The counter acts as an indicator of this 654255332Scy * kind of attack, whereas if it was elsewhere, it wouldn't know if 655255332Scy * other matching packets had been seen. 656255332Scy */ 657255332Scy if (fin->fin_flx & FI_SHORT) { 658255332Scy FBUMPD(ifs_short); 659145522Sdarrenr return NULL; 660255332Scy } 661145522Sdarrenr 662255332Scy if ((fin->fin_flx & FI_BAD) != 0) { 663255332Scy FBUMPD(ifs_bad); 664255332Scy return NULL; 665255332Scy } 666255332Scy 66753642Sguido /* 66853642Sguido * For fragments, we record protocol, packet id, TOS and both IP#'s 66953642Sguido * (these should all be the same for all fragments of a packet). 67053642Sguido * 67153642Sguido * build up a hash value to index the table with. 67253642Sguido */ 673255332Scy frag.ipfr_v = fin->fin_v; 674255332Scy idx = fin->fin_v; 675255332Scy frag.ipfr_p = fin->fin_p; 676255332Scy idx += fin->fin_p; 677255332Scy frag.ipfr_id = fin->fin_id; 678255332Scy idx += fin->fin_id; 679255332Scy frag.ipfr_source = fin->fin_fi.fi_src; 680255332Scy idx += frag.ipfr_src.s_addr; 681255332Scy frag.ipfr_dest = fin->fin_fi.fi_dst; 682255332Scy idx += frag.ipfr_dst.s_addr; 68372006Sdarrenr frag.ipfr_ifp = fin->fin_ifp; 68453642Sguido idx *= 127; 685255332Scy idx %= softf->ipfr_size; 68653642Sguido 68780482Sdarrenr frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 68880482Sdarrenr frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 68980482Sdarrenr frag.ipfr_auth = fin->fin_fi.fi_auth; 69080482Sdarrenr 691255332Scy READ_ENTER(lock); 692255332Scy 69353642Sguido /* 69453642Sguido * check the table, careful to only compare the right amount of data 69553642Sguido */ 696255332Scy for (f = table[idx]; f; f = f->ipfr_hnext) { 697145522Sdarrenr if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, 69853642Sguido IPFR_CMPSZ)) { 699145522Sdarrenr u_short off; 70053642Sguido 701145522Sdarrenr /* 70275262Sdarrenr * XXX - We really need to be guarding against the 70375262Sdarrenr * retransmission of (src,dst,id,offset-range) here 70475262Sdarrenr * because a fragmented packet is never resent with 705145522Sdarrenr * the same IP ID# (or shouldn't). 70675262Sdarrenr */ 707255332Scy off = fin->fin_off >> 3; 70875262Sdarrenr if (f->ipfr_seen0) { 709145522Sdarrenr if (off == 0) { 710255332Scy FBUMPD(ifs_retrans0); 71175262Sdarrenr continue; 712145522Sdarrenr } 713255332Scy 714255332Scy /* 715255332Scy * Case 3. See comment for frpr_fragment6. 716255332Scy */ 717255332Scy if ((f->ipfr_firstend != 0) && 718255332Scy (off < f->ipfr_firstend)) { 719255332Scy FBUMP(ifs_overlap); 720255332Scy DT2(ifs_overlap, u_short, off, 721255332Scy ipfr_t *, f); 722255332Scy fin->fin_flx |= FI_BAD; 723255332Scy break; 724255332Scy } 725145522Sdarrenr } else if (off == 0) 72675262Sdarrenr f->ipfr_seen0 = 1; 72775262Sdarrenr 72853642Sguido if (f != table[idx]) { 729145522Sdarrenr ipfr_t **fp; 730145522Sdarrenr 73153642Sguido /* 732145522Sdarrenr * Move fragment info. to the top of the list 733145522Sdarrenr * to speed up searches. First, delink... 73453642Sguido */ 735145522Sdarrenr fp = f->ipfr_hprev; 736145522Sdarrenr (*fp) = f->ipfr_hnext; 737145522Sdarrenr if (f->ipfr_hnext != NULL) 738145522Sdarrenr f->ipfr_hnext->ipfr_hprev = fp; 739145522Sdarrenr /* 740145522Sdarrenr * Then put back at the top of the chain. 741145522Sdarrenr */ 742145522Sdarrenr f->ipfr_hnext = table[idx]; 743145522Sdarrenr table[idx]->ipfr_hprev = &f->ipfr_hnext; 744145522Sdarrenr f->ipfr_hprev = table + idx; 74553642Sguido table[idx] = f; 74653642Sguido } 747145522Sdarrenr 74853642Sguido /* 74953642Sguido * If we've follwed the fragments, and this is the 75053642Sguido * last (in order), shrink expiration time. 75153642Sguido */ 75255929Sguido if (off == f->ipfr_off) { 753145522Sdarrenr f->ipfr_off = (fin->fin_dlen >> 3) + off; 754255332Scy 755255332Scy /* 756255332Scy * Well, we could shrink the expiration time 757255332Scy * but only if every fragment has been seen 758255332Scy * in order upto this, the last. ipfr_badorder 759255332Scy * is used here to count those out of order 760255332Scy * and if it equals 0 when we get to the last 761255332Scy * fragment then we can assume all of the 762255332Scy * fragments have been seen and in order. 763255332Scy */ 764255332Scy#if 0 765255332Scy /* 766255332Scy * Doing this properly requires moving it to 767255332Scy * the head of the list which is infesible. 768255332Scy */ 769255332Scy if ((more == 0) && (f->ipfr_badorder == 0)) 770255332Scy f->ipfr_ttl = softc->ipf_ticks + 1; 771255332Scy#endif 772255332Scy } else { 773255332Scy f->ipfr_badorder++; 774255332Scy FBUMPD(ifs_unordered); 775255332Scy if (f->ipfr_pass & FR_FRSTRICT) { 776255332Scy FBUMPD(ifs_strict); 777255332Scy continue; 778255332Scy } 779255332Scy } 780255332Scy f->ipfr_pkts++; 781255332Scy f->ipfr_bytes += fin->fin_plen; 782255332Scy FBUMP(ifs_hits); 78353642Sguido return f; 78453642Sguido } 785255332Scy } 786255332Scy 787255332Scy RWLOCK_EXIT(lock); 788255332Scy FBUMP(ifs_miss); 78953642Sguido return NULL; 79053642Sguido} 79153642Sguido 79253642Sguido 793145522Sdarrenr/* ------------------------------------------------------------------------ */ 794255332Scy/* Function: ipf_frag_natknown */ 795145522Sdarrenr/* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ 796145522Sdarrenr/* match found, else NULL */ 797145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 798145522Sdarrenr/* */ 799145522Sdarrenr/* Functional interface for NAT lookups of the NAT fragment cache */ 800145522Sdarrenr/* ------------------------------------------------------------------------ */ 801255332Scynat_t * 802255332Scyipf_frag_natknown(fin) 803255332Scy fr_info_t *fin; 80453642Sguido{ 805255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 806255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 807145522Sdarrenr nat_t *nat; 808145522Sdarrenr ipfr_t *ipf; 80953642Sguido 810255332Scy if ((softf->ipfr_lock) || !softf->ipfr_natlist) 81160855Sdarrenr return NULL; 812255332Scy#ifdef USE_MUTEXES 813255332Scy ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab, 814255332Scy &softf->ipfr_natfrag); 815255332Scy#else 816255332Scy ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab); 817255332Scy#endif 81853642Sguido if (ipf != NULL) { 81953642Sguido nat = ipf->ipfr_data; 82072006Sdarrenr /* 82172006Sdarrenr * This is the last fragment for this packet. 82272006Sdarrenr */ 823255332Scy if ((ipf->ipfr_ttl == softc->ipf_ticks + 1) && (nat != NULL)) { 82472006Sdarrenr nat->nat_data = NULL; 82572006Sdarrenr ipf->ipfr_data = NULL; 82672006Sdarrenr } 827255332Scy RWLOCK_EXIT(&softf->ipfr_natfrag); 82853642Sguido } else 82953642Sguido nat = NULL; 83053642Sguido return nat; 83153642Sguido} 83253642Sguido 83353642Sguido 834145522Sdarrenr/* ------------------------------------------------------------------------ */ 835255332Scy/* Function: ipf_frag_ipidknown */ 836145522Sdarrenr/* Returns: u_32_t - IPv4 ID for this packet if match found, else */ 837145522Sdarrenr/* return 0xfffffff to indicate no match. */ 838145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 839145522Sdarrenr/* */ 840145522Sdarrenr/* Functional interface for IP ID lookups of the IP ID fragment cache */ 841145522Sdarrenr/* ------------------------------------------------------------------------ */ 842255332Scyu_32_t 843255332Scyipf_frag_ipidknown(fin) 844255332Scy fr_info_t *fin; 84553642Sguido{ 846255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 847255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 848145522Sdarrenr ipfr_t *ipf; 849145522Sdarrenr u_32_t id; 85053642Sguido 851255332Scy if (softf->ipfr_lock || !softf->ipfr_ipidlist) 852145522Sdarrenr return 0xffffffff; 85380482Sdarrenr 854255332Scy#ifdef USE_MUTEXES 855255332Scy ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab, 856255332Scy &softf->ipfr_ipidfrag); 857255332Scy#else 858255332Scy ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab); 859255332Scy#endif 860255332Scy if (ipf != NULL) { 861255332Scy id = (u_32_t)(intptr_t)ipf->ipfr_data; 862255332Scy RWLOCK_EXIT(&softf->ipfr_ipidfrag); 863255332Scy } else 864145522Sdarrenr id = 0xffffffff; 865145522Sdarrenr return id; 866145522Sdarrenr} 867145522Sdarrenr 868145522Sdarrenr 869145522Sdarrenr/* ------------------------------------------------------------------------ */ 870255332Scy/* Function: ipf_frag_known */ 871145522Sdarrenr/* Returns: frentry_t* - pointer to filter rule if a match is found in */ 872145522Sdarrenr/* the frag cache table, else NULL. */ 873145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 874145522Sdarrenr/* passp(O) - pointer to where to store rule flags resturned */ 875145522Sdarrenr/* */ 876145522Sdarrenr/* Functional interface for normal lookups of the fragment cache. If a */ 877145522Sdarrenr/* match is found, return the rule pointer and flags from the rule, except */ 878145522Sdarrenr/* that if FR_LOGFIRST is set, reset FR_LOG. */ 879145522Sdarrenr/* ------------------------------------------------------------------------ */ 880255332Scyfrentry_t * 881255332Scyipf_frag_known(fin, passp) 882255332Scy fr_info_t *fin; 883255332Scy u_32_t *passp; 884145522Sdarrenr{ 885255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 886255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 887145522Sdarrenr frentry_t *fr = NULL; 888145522Sdarrenr ipfr_t *fra; 889145522Sdarrenr u_32_t pass; 890145522Sdarrenr 891255332Scy if ((softf->ipfr_lock) || (softf->ipfr_list == NULL)) 89280482Sdarrenr return NULL; 89380482Sdarrenr 894255332Scy#ifdef USE_MUTEXES 895255332Scy fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads, 896255332Scy &softc->ipf_frag); 897255332Scy#else 898255332Scy fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads); 899255332Scy#endif 900145522Sdarrenr if (fra != NULL) { 901255332Scy if (fin->fin_flx & FI_BAD) { 902255332Scy fr = &ipfr_block; 903255332Scy fin->fin_reason = FRB_BADFRAG; 904255332Scy } else { 905255332Scy fr = fra->ipfr_rule; 906255332Scy } 907145522Sdarrenr fin->fin_fr = fr; 908145522Sdarrenr if (fr != NULL) { 909145522Sdarrenr pass = fr->fr_flags; 910255332Scy if ((pass & FR_KEEPSTATE) != 0) { 911255332Scy fin->fin_flx |= FI_STATE; 912255332Scy /* 913255332Scy * Reset the keep state flag here so that we 914255332Scy * don't try and add a new state entry because 915255332Scy * of a match here. That leads to blocking of 916255332Scy * the packet later because the add fails. 917255332Scy */ 918255332Scy pass &= ~FR_KEEPSTATE; 919255332Scy } 920145522Sdarrenr if ((pass & FR_LOGFIRST) != 0) 921145522Sdarrenr pass &= ~(FR_LOGFIRST|FR_LOG); 922145522Sdarrenr *passp = pass; 923145522Sdarrenr } 924255332Scy RWLOCK_EXIT(&softc->ipf_frag); 925145522Sdarrenr } 92653642Sguido return fr; 92753642Sguido} 92853642Sguido 92953642Sguido 930145522Sdarrenr/* ------------------------------------------------------------------------ */ 931255332Scy/* Function: ipf_frag_natforget */ 932145522Sdarrenr/* Returns: Nil */ 933272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 934272993Scy/* ptr(I) - pointer to data structure */ 935145522Sdarrenr/* */ 936145522Sdarrenr/* Search through all of the fragment cache entries for NAT and wherever a */ 937145522Sdarrenr/* pointer is found to match ptr, reset it to NULL. */ 938145522Sdarrenr/* ------------------------------------------------------------------------ */ 939255332Scyvoid 940255332Scyipf_frag_natforget(softc, ptr) 941255332Scy ipf_main_softc_t *softc; 942255332Scy void *ptr; 94353642Sguido{ 944255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 94553642Sguido ipfr_t *fr; 94653642Sguido 947255332Scy WRITE_ENTER(&softf->ipfr_natfrag); 948255332Scy for (fr = softf->ipfr_natlist; fr; fr = fr->ipfr_next) 949145522Sdarrenr if (fr->ipfr_data == ptr) 950145522Sdarrenr fr->ipfr_data = NULL; 951255332Scy RWLOCK_EXIT(&softf->ipfr_natfrag); 95253642Sguido} 95353642Sguido 95453642Sguido 955145522Sdarrenr/* ------------------------------------------------------------------------ */ 956255332Scy/* Function: ipf_frag_delete */ 957145522Sdarrenr/* Returns: Nil */ 958272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 959272993Scy/* fra(I) - pointer to fragment structure to delete */ 960145522Sdarrenr/* tail(IO) - pointer to the pointer to the tail of the frag */ 961145522Sdarrenr/* list */ 962145522Sdarrenr/* */ 963145522Sdarrenr/* Remove a fragment cache table entry from the table & list. Also free */ 964145522Sdarrenr/* the filter rule it is associated with it if it is no longer used as a */ 965145522Sdarrenr/* result of decreasing the reference count. */ 966145522Sdarrenr/* ------------------------------------------------------------------------ */ 967255332Scystatic void 968255332Scyipf_frag_delete(softc, fra, tail) 969255332Scy ipf_main_softc_t *softc; 970255332Scy ipfr_t *fra, ***tail; 97153642Sguido{ 972255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 97353642Sguido 97453642Sguido if (fra->ipfr_next) 97553642Sguido fra->ipfr_next->ipfr_prev = fra->ipfr_prev; 976145522Sdarrenr *fra->ipfr_prev = fra->ipfr_next; 977145522Sdarrenr if (*tail == &fra->ipfr_next) 978145522Sdarrenr *tail = fra->ipfr_prev; 979145522Sdarrenr 980145522Sdarrenr if (fra->ipfr_hnext) 981145522Sdarrenr fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev; 982145522Sdarrenr *fra->ipfr_hprev = fra->ipfr_hnext; 983170268Sdarrenr 984170268Sdarrenr if (fra->ipfr_rule != NULL) { 985255332Scy (void) ipf_derefrule(softc, &fra->ipfr_rule); 986170268Sdarrenr } 987170268Sdarrenr 988170268Sdarrenr if (fra->ipfr_ref <= 0) 989255332Scy ipf_frag_free(softf, fra); 990170268Sdarrenr} 991170268Sdarrenr 992170268Sdarrenr 993170268Sdarrenr/* ------------------------------------------------------------------------ */ 994255332Scy/* Function: ipf_frag_free */ 995170268Sdarrenr/* Returns: Nil */ 996272993Scy/* Parameters: softf(I) - pointer to fragment context information */ 997272993Scy/* fra(I) - pointer to fragment structure to free */ 998170268Sdarrenr/* */ 999272993Scy/* Free up a fragment cache entry and bump relevent statistics. */ 1000170268Sdarrenr/* ------------------------------------------------------------------------ */ 1001255332Scystatic void 1002255332Scyipf_frag_free(softf, fra) 1003255332Scy ipf_frag_softc_t *softf; 1004255332Scy ipfr_t *fra; 1005170268Sdarrenr{ 100653642Sguido KFREE(fra); 1007255332Scy FBUMP(ifs_expire); 1008255332Scy softf->ipfr_stats.ifs_inuse--; 100953642Sguido} 101053642Sguido 101153642Sguido 1012145522Sdarrenr/* ------------------------------------------------------------------------ */ 1013255332Scy/* Function: ipf_frag_clear */ 1014145522Sdarrenr/* Returns: Nil */ 1015272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1016145522Sdarrenr/* */ 1017145522Sdarrenr/* Free memory in use by fragment state information kept. Do the normal */ 1018145522Sdarrenr/* fragment state stuff first and then the NAT-fragment table. */ 1019145522Sdarrenr/* ------------------------------------------------------------------------ */ 1020255332Scyvoid 1021255332Scyipf_frag_clear(softc) 1022255332Scy ipf_main_softc_t *softc; 102353642Sguido{ 1024255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1025145522Sdarrenr ipfr_t *fra; 102653642Sguido nat_t *nat; 102753642Sguido 1028255332Scy WRITE_ENTER(&softc->ipf_frag); 1029255332Scy while ((fra = softf->ipfr_list) != NULL) { 1030170268Sdarrenr fra->ipfr_ref--; 1031255332Scy ipf_frag_delete(softc, fra, &softf->ipfr_tail); 1032170268Sdarrenr } 1033255332Scy softf->ipfr_tail = &softf->ipfr_list; 1034255332Scy RWLOCK_EXIT(&softc->ipf_frag); 103553642Sguido 1036255332Scy WRITE_ENTER(&softc->ipf_nat); 1037255332Scy WRITE_ENTER(&softf->ipfr_natfrag); 1038255332Scy while ((fra = softf->ipfr_natlist) != NULL) { 1039145522Sdarrenr nat = fra->ipfr_data; 1040145522Sdarrenr if (nat != NULL) { 1041145522Sdarrenr if (nat->nat_data == fra) 1042145522Sdarrenr nat->nat_data = NULL; 104353642Sguido } 1044170268Sdarrenr fra->ipfr_ref--; 1045255332Scy ipf_frag_delete(softc, fra, &softf->ipfr_nattail); 1046145522Sdarrenr } 1047255332Scy softf->ipfr_nattail = &softf->ipfr_natlist; 1048255332Scy RWLOCK_EXIT(&softf->ipfr_natfrag); 1049255332Scy RWLOCK_EXIT(&softc->ipf_nat); 105053642Sguido} 105153642Sguido 105253642Sguido 1053145522Sdarrenr/* ------------------------------------------------------------------------ */ 1054255332Scy/* Function: ipf_frag_expire */ 1055145522Sdarrenr/* Returns: Nil */ 1056272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1057145522Sdarrenr/* */ 1058145522Sdarrenr/* Expire entries in the fragment cache table that have been there too long */ 1059145522Sdarrenr/* ------------------------------------------------------------------------ */ 1060255332Scyvoid 1061255332Scyipf_frag_expire(softc) 1062255332Scy ipf_main_softc_t *softc; 106353642Sguido{ 1064255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 106553642Sguido ipfr_t **fp, *fra; 106653642Sguido nat_t *nat; 1067153876Sguido SPL_INT(s); 106853642Sguido 1069255332Scy if (softf->ipfr_lock) 107060855Sdarrenr return; 107153642Sguido 107253642Sguido SPL_NET(s); 1073255332Scy WRITE_ENTER(&softc->ipf_frag); 107453642Sguido /* 107553642Sguido * Go through the entire table, looking for entries to expire, 1076255332Scy * which is indicated by the ttl being less than or equal to ipf_ticks. 107753642Sguido */ 1078255332Scy for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) { 1079255332Scy if (fra->ipfr_ttl > softc->ipf_ticks) 1080145522Sdarrenr break; 1081170268Sdarrenr fra->ipfr_ref--; 1082255332Scy ipf_frag_delete(softc, fra, &softf->ipfr_tail); 1083145522Sdarrenr } 1084255332Scy RWLOCK_EXIT(&softc->ipf_frag); 108553642Sguido 1086255332Scy WRITE_ENTER(&softf->ipfr_ipidfrag); 1087255332Scy for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) { 1088255332Scy if (fra->ipfr_ttl > softc->ipf_ticks) 1089145522Sdarrenr break; 1090170268Sdarrenr fra->ipfr_ref--; 1091255332Scy ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail); 1092145522Sdarrenr } 1093255332Scy RWLOCK_EXIT(&softf->ipfr_ipidfrag); 1094145522Sdarrenr 109553642Sguido /* 109653642Sguido * Same again for the NAT table, except that if the structure also 109753642Sguido * still points to a NAT structure, and the NAT structure points back 109853642Sguido * at the one to be free'd, NULL the reference from the NAT struct. 109953642Sguido * NOTE: We need to grab both mutex's early, and in this order so as 110053642Sguido * to prevent a deadlock if both try to expire at the same time. 1101170268Sdarrenr * The extra if() statement here is because it locks out all NAT 1102170268Sdarrenr * operations - no need to do that if there are no entries in this 1103170268Sdarrenr * list, right? 110453642Sguido */ 1105255332Scy if (softf->ipfr_natlist != NULL) { 1106255332Scy WRITE_ENTER(&softc->ipf_nat); 1107255332Scy WRITE_ENTER(&softf->ipfr_natfrag); 1108255332Scy for (fp = &softf->ipfr_natlist; ((fra = *fp) != NULL); ) { 1109255332Scy if (fra->ipfr_ttl > softc->ipf_ticks) 1110170268Sdarrenr break; 1111170268Sdarrenr nat = fra->ipfr_data; 1112170268Sdarrenr if (nat != NULL) { 1113170268Sdarrenr if (nat->nat_data == fra) 1114170268Sdarrenr nat->nat_data = NULL; 1115170268Sdarrenr } 1116170268Sdarrenr fra->ipfr_ref--; 1117255332Scy ipf_frag_delete(softc, fra, &softf->ipfr_nattail); 111853642Sguido } 1119255332Scy RWLOCK_EXIT(&softf->ipfr_natfrag); 1120255332Scy RWLOCK_EXIT(&softc->ipf_nat); 1121145522Sdarrenr } 112253642Sguido SPL_X(s); 112360855Sdarrenr} 112460855Sdarrenr 112560855Sdarrenr 1126145522Sdarrenr/* ------------------------------------------------------------------------ */ 1127255332Scy/* Function: ipf_frag_pkt_next */ 1128272993Scy/* Returns: int - 0 == success, else error */ 1129272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1130272993Scy/* token(I) - pointer to token information for this caller */ 1131272993Scy/* itp(I) - pointer to generic iterator from caller */ 1132272993Scy/* */ 1133272993Scy/* This function is used to step through the fragment cache list used for */ 1134272993Scy/* filter rules. The hard work is done by the more generic ipf_frag_next. */ 1135145522Sdarrenr/* ------------------------------------------------------------------------ */ 1136255332Scyint 1137255332Scyipf_frag_pkt_next(softc, token, itp) 1138255332Scy ipf_main_softc_t *softc; 1139255332Scy ipftoken_t *token; 1140255332Scy ipfgeniter_t *itp; 114160855Sdarrenr{ 1142255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 114360855Sdarrenr 1144255332Scy#ifdef USE_MUTEXES 1145255332Scy return ipf_frag_next(softc, token, itp, &softf->ipfr_list, 1146255332Scy &softf->ipfr_frag); 1147255332Scy#else 1148255332Scy return ipf_frag_next(softc, token, itp, &softf->ipfr_list); 1149255332Scy#endif 115053642Sguido} 1151170268Sdarrenr 1152170268Sdarrenr 1153170268Sdarrenr/* ------------------------------------------------------------------------ */ 1154255332Scy/* Function: ipf_frag_nat_next */ 1155272993Scy/* Returns: int - 0 == success, else error */ 1156272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1157272993Scy/* token(I) - pointer to token information for this caller */ 1158272993Scy/* itp(I) - pointer to generic iterator from caller */ 1159272993Scy/* */ 1160272993Scy/* This function is used to step through the fragment cache list used for */ 1161272993Scy/* NAT. The hard work is done by the more generic ipf_frag_next. */ 1162255332Scy/* ------------------------------------------------------------------------ */ 1163255332Scyint 1164255332Scyipf_frag_nat_next(softc, token, itp) 1165255332Scy ipf_main_softc_t *softc; 1166255332Scy ipftoken_t *token; 1167255332Scy ipfgeniter_t *itp; 1168255332Scy{ 1169255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft;; 1170255332Scy 1171255332Scy#ifdef USE_MUTEXES 1172255332Scy return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist, 1173255332Scy &softf->ipfr_natfrag); 1174255332Scy#else 1175255332Scy return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist); 1176255332Scy#endif 1177255332Scy} 1178255332Scy 1179255332Scy/* ------------------------------------------------------------------------ */ 1180255332Scy/* Function: ipf_frag_next */ 1181170268Sdarrenr/* Returns: int - 0 == success, else error */ 1182272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1183272993Scy/* token(I) - pointer to token information for this caller */ 1184170268Sdarrenr/* itp(I) - pointer to generic iterator from caller */ 1185170268Sdarrenr/* top(I) - top of the fragment list */ 1186170268Sdarrenr/* lock(I) - fragment cache lock */ 1187170268Sdarrenr/* */ 1188170268Sdarrenr/* This function is used to interate through the list of entries in the */ 1189170268Sdarrenr/* fragment cache. It increases the reference count on the one currently */ 1190170268Sdarrenr/* being returned so that the caller can come back and resume from it later.*/ 1191170268Sdarrenr/* */ 1192170268Sdarrenr/* This function is used for both the NAT fragment cache as well as the ipf */ 1193255332Scy/* fragment cache - hence the reason for passing in top and lock. */ 1194170268Sdarrenr/* ------------------------------------------------------------------------ */ 1195255332Scystatic int 1196255332Scyipf_frag_next(softc, token, itp, top 1197170268Sdarrenr#ifdef USE_MUTEXES 1198170268Sdarrenr, lock 1199170268Sdarrenr#endif 1200170268Sdarrenr) 1201255332Scy ipf_main_softc_t *softc; 1202255332Scy ipftoken_t *token; 1203255332Scy ipfgeniter_t *itp; 1204255332Scy ipfr_t **top; 1205170268Sdarrenr#ifdef USE_MUTEXES 1206255332Scy ipfrwlock_t *lock; 1207170268Sdarrenr#endif 1208170268Sdarrenr{ 1209170268Sdarrenr ipfr_t *frag, *next, zero; 1210170268Sdarrenr int error = 0; 1211170268Sdarrenr 1212255332Scy if (itp->igi_data == NULL) { 1213255332Scy IPFERROR(20001); 1214255332Scy return EFAULT; 1215170268Sdarrenr } 1216170268Sdarrenr 1217255332Scy if (itp->igi_nitems != 1) { 1218255332Scy IPFERROR(20003); 1219255332Scy return EFAULT; 1220255332Scy } 1221255332Scy 1222255332Scy frag = token->ipt_data; 1223255332Scy 1224170268Sdarrenr READ_ENTER(lock); 1225255332Scy 1226170268Sdarrenr if (frag == NULL) 1227170268Sdarrenr next = *top; 1228170268Sdarrenr else 1229170268Sdarrenr next = frag->ipfr_next; 1230170268Sdarrenr 1231170268Sdarrenr if (next != NULL) { 1232170268Sdarrenr ATOMIC_INC(next->ipfr_ref); 1233170268Sdarrenr token->ipt_data = next; 1234170268Sdarrenr } else { 1235170268Sdarrenr bzero(&zero, sizeof(zero)); 1236170268Sdarrenr next = &zero; 1237172776Sdarrenr token->ipt_data = NULL; 1238170268Sdarrenr } 1239255332Scy if (next->ipfr_next == NULL) 1240255332Scy ipf_token_mark_complete(token); 1241255332Scy 1242170268Sdarrenr RWLOCK_EXIT(lock); 1243170268Sdarrenr 1244255332Scy error = COPYOUT(next, itp->igi_data, sizeof(*next)); 1245255332Scy if (error != 0) 1246255332Scy IPFERROR(20002); 1247255332Scy 1248255332Scy if (frag != NULL) { 1249172776Sdarrenr#ifdef USE_MUTEXES 1250255332Scy ipf_frag_deref(softc, &frag, lock); 1251172776Sdarrenr#else 1252255332Scy ipf_frag_deref(softc, &frag); 1253172776Sdarrenr#endif 1254255332Scy } 1255255332Scy return error; 1256255332Scy} 1257170268Sdarrenr 1258170268Sdarrenr 1259255332Scy/* ------------------------------------------------------------------------ */ 1260255332Scy/* Function: ipf_frag_pkt_deref */ 1261255332Scy/* Returns: Nil */ 1262272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1263272993Scy/* data(I) - pointer to frag cache pointer */ 1264255332Scy/* */ 1265272993Scy/* This function is the external interface for dropping a reference to a */ 1266272993Scy/* fragment cache entry used by filter rules. */ 1267255332Scy/* ------------------------------------------------------------------------ */ 1268255332Scyvoid 1269255332Scyipf_frag_pkt_deref(softc, data) 1270255332Scy ipf_main_softc_t *softc; 1271255332Scy void *data; 1272255332Scy{ 1273255332Scy ipfr_t **frp = data; 1274255332Scy 1275255332Scy#ifdef USE_MUTEXES 1276255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1277255332Scy 1278255332Scy ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_frag); 1279255332Scy#else 1280255332Scy ipf_frag_deref(softc->ipf_frag_soft, frp); 1281255332Scy#endif 1282170268Sdarrenr} 1283170268Sdarrenr 1284170268Sdarrenr 1285170268Sdarrenr/* ------------------------------------------------------------------------ */ 1286255332Scy/* Function: ipf_frag_nat_deref */ 1287170268Sdarrenr/* Returns: Nil */ 1288272993Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1289272993Scy/* data(I) - pointer to frag cache pointer */ 1290255332Scy/* */ 1291272993Scy/* This function is the external interface for dropping a reference to a */ 1292272993Scy/* fragment cache entry used by NAT table entries. */ 1293255332Scy/* ------------------------------------------------------------------------ */ 1294255332Scyvoid 1295255332Scyipf_frag_nat_deref(softc, data) 1296255332Scy ipf_main_softc_t *softc; 1297255332Scy void *data; 1298255332Scy{ 1299255332Scy ipfr_t **frp = data; 1300255332Scy 1301255332Scy#ifdef USE_MUTEXES 1302255332Scy ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1303255332Scy 1304255332Scy ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_natfrag); 1305255332Scy#else 1306255332Scy ipf_frag_deref(softc->ipf_frag_soft, frp); 1307255332Scy#endif 1308255332Scy} 1309255332Scy 1310255332Scy 1311255332Scy/* ------------------------------------------------------------------------ */ 1312255332Scy/* Function: ipf_frag_deref */ 1313255332Scy/* Returns: Nil */ 1314170268Sdarrenr/* Parameters: frp(IO) - pointer to fragment structure to deference */ 1315170268Sdarrenr/* lock(I) - lock associated with the fragment */ 1316170268Sdarrenr/* */ 1317170268Sdarrenr/* This function dereferences a fragment structure (ipfr_t). The pointer */ 1318170268Sdarrenr/* passed in will always be reset back to NULL, even if the structure is */ 1319170268Sdarrenr/* not freed, to enforce the notion that the caller is no longer entitled */ 1320170268Sdarrenr/* to use the pointer it is dropping the reference to. */ 1321170268Sdarrenr/* ------------------------------------------------------------------------ */ 1322255332Scystatic void 1323255332Scyipf_frag_deref(arg, frp 1324170268Sdarrenr#ifdef USE_MUTEXES 1325170268Sdarrenr, lock 1326170268Sdarrenr#endif 1327170268Sdarrenr) 1328255332Scy void *arg; 1329255332Scy ipfr_t **frp; 1330170268Sdarrenr#ifdef USE_MUTEXES 1331255332Scy ipfrwlock_t *lock; 1332170268Sdarrenr#endif 1333170268Sdarrenr{ 1334255332Scy ipf_frag_softc_t *softf = arg; 1335170268Sdarrenr ipfr_t *fra; 1336170268Sdarrenr 1337170268Sdarrenr fra = *frp; 1338170268Sdarrenr *frp = NULL; 1339170268Sdarrenr 1340170268Sdarrenr WRITE_ENTER(lock); 1341170268Sdarrenr fra->ipfr_ref--; 1342170268Sdarrenr if (fra->ipfr_ref <= 0) 1343255332Scy ipf_frag_free(softf, fra); 1344170268Sdarrenr RWLOCK_EXIT(lock); 1345170268Sdarrenr} 1346