diff options
Diffstat (limited to 'sys/contrib/ipfilter/netinet/ip_frag.c')
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_frag.c | 374 |
1 files changed, 233 insertions, 141 deletions
diff --git a/sys/contrib/ipfilter/netinet/ip_frag.c b/sys/contrib/ipfilter/netinet/ip_frag.c index 923f685..3e0a7f3 100644 --- a/sys/contrib/ipfilter/netinet/ip_frag.c +++ b/sys/contrib/ipfilter/netinet/ip_frag.c @@ -1,59 +1,68 @@ /* - * Copyright (C) 1993-1997 by Darren Reed. + * Copyright (C) 1993-2000 by Darren Reed. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. */ #if !defined(lint) -static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ip_frag.c,v 2.0.2.19.2.1 1997/11/12 10:50:21 darrenr Exp $"; +static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$Id: ip_frag.c,v 2.10.2.3 2000/05/05 15:10:23 darrenr Exp $"; #endif -#if !defined(_KERNEL) && !defined(KERNEL) -# include <string.h> -# include <stdlib.h> +#if defined(KERNEL) && !defined(_KERNEL) +# define _KERNEL #endif + #include <sys/errno.h> #include <sys/types.h> #include <sys/param.h> #include <sys/time.h> #include <sys/file.h> -#if defined(KERNEL) && (__FreeBSD_version >= 220000) -#include <sys/filio.h> -#include <sys/fcntl.h> +#if !defined(_KERNEL) && !defined(KERNEL) +# include <stdio.h> +# include <string.h> +# include <stdlib.h> +#endif +#if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000) +# include <sys/filio.h> +# include <sys/fcntl.h> #else -#include <sys/ioctl.h> +# include <sys/ioctl.h> #endif #include <sys/uio.h> #ifndef linux -#include <sys/protosw.h> +# include <sys/protosw.h> #endif #include <sys/socket.h> #if defined(_KERNEL) && !defined(linux) # include <sys/systm.h> #endif #if !defined(__SVR4) && !defined(__svr4__) +# if defined(_KERNEL) && !defined(__sgi) +# include <sys/kernel.h> +# endif # ifndef linux # include <sys/mbuf.h> # endif #else # include <sys/byteorder.h> -# include <sys/dditypes.h> +# ifdef _KERNEL +# include <sys/dditypes.h> +# endif # include <sys/stream.h> # include <sys/kmem.h> #endif - #include <net/if.h> #ifdef sun -#include <net/af.h> +# include <net/af.h> #endif #include <net/route.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #ifndef linux -#include <netinet/ip_var.h> +# include <netinet/ip_var.h> #endif #include <netinet/tcp.h> #include <netinet/udp.h> @@ -66,24 +75,51 @@ static const char rcsid[] = "@(#)$Id: ip_frag.c,v 2.0.2.19.2.1 1997/11/12 10:50: #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_auth.h" +#if (__FreeBSD_version >= 300000) +# include <sys/malloc.h> +# if (defined(KERNEL) || defined(_KERNEL)) +# ifndef IPFILTER_LKM +# include <sys/libkern.h> +# include <sys/systm.h> +# endif +extern struct callout_handle ipfr_slowtimer_ch; +# endif +#endif +#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) +# include <sys/callout.h> +extern struct callout ipfr_slowtimer_ch; +#endif + + +static ipfr_t *ipfr_heads[IPFT_SIZE]; +static ipfr_t *ipfr_nattab[IPFT_SIZE]; +static ipfrstat_t ipfr_stats; +static int ipfr_inuse = 0; + +int fr_ipfrttl = 120; /* 60 seconds */ +int fr_frag_lock = 0; -ipfr_t *ipfr_heads[IPFT_SIZE]; -ipfr_t *ipfr_nattab[IPFT_SIZE]; -ipfrstat_t ipfr_stats; -int ipfr_inuse = 0, - fr_ipfrttl = 120; /* 60 seconds */ #ifdef _KERNEL +# if SOLARIS2 >= 7 +extern timeout_id_t ipfr_timer_id; +# else extern int ipfr_timer_id; +# endif #endif #if (SOLARIS || defined(__sgi)) && defined(_KERNEL) -extern kmutex_t ipf_frag; -extern kmutex_t ipf_natfrag; -extern kmutex_t ipf_nat; +extern KRWLOCK_T ipf_frag, ipf_natfrag, ipf_nat, ipf_mutex; +# if SOLARIS +extern KRWLOCK_T ipf_solaris; +# else +KRWLOCK_T ipf_solaris; +# endif +extern kmutex_t ipf_rw; #endif -static ipfr_t *ipfr_new __P((ip_t *, fr_info_t *, int, ipfr_t **)); +static ipfr_t *ipfr_new __P((ip_t *, fr_info_t *, u_int, ipfr_t **)); static ipfr_t *ipfr_lookup __P((ip_t *, fr_info_t *, ipfr_t **)); +static void ipfr_delete __P((ipfr_t *)); ipfrstat_t *ipfr_fragstats() @@ -102,10 +138,10 @@ ipfrstat_t *ipfr_fragstats() static ipfr_t *ipfr_new(ip, fin, pass, table) ip_t *ip; fr_info_t *fin; -int pass; +u_int pass; ipfr_t *table[]; { - ipfr_t **fp, *fr, frag; + ipfr_t **fp, *fra, frag; u_int idx; frag.ipfr_p = ip->ip_p; @@ -123,10 +159,10 @@ ipfr_t *table[]; /* * first, make sure it isn't already there... */ - for (fp = &table[idx]; (fr = *fp); fp = &fr->ipfr_next) - if (!bcmp((char *)&frag.ipfr_src, (char *)&fr->ipfr_src, + for (fp = &table[idx]; (fra = *fp); fp = &fra->ipfr_next) + if (!bcmp((char *)&frag.ipfr_src, (char *)&fra->ipfr_src, IPFR_CMPSZ)) { - ipfr_stats.ifs_exists++; + ATOMIC_INCL(ipfr_stats.ifs_exists); return NULL; } @@ -134,45 +170,51 @@ ipfr_t *table[]; * allocate some memory, if possible, if not, just record that we * failed to do so. */ - KMALLOC(fr, ipfr_t *, sizeof(*fr)); - if (fr == NULL) { - ipfr_stats.ifs_nomem++; + KMALLOC(fra, ipfr_t *); + if (fra == NULL) { + ATOMIC_INCL(ipfr_stats.ifs_nomem); return NULL; } + if ((fra->ipfr_rule = fin->fin_fr) != NULL) { + ATOMIC_INC32(fin->fin_fr->fr_ref); + } + + /* * Instert the fragment into the fragment table, copy the struct used * in the search using bcopy rather than reassign each field. * Set the ttl to the default and mask out logging from "pass" */ - if ((fr->ipfr_next = table[idx])) - table[idx]->ipfr_prev = fr; - fr->ipfr_prev = NULL; - fr->ipfr_data = NULL; - table[idx] = fr; - bcopy((char *)&frag.ipfr_src, (char *)&fr->ipfr_src, IPFR_CMPSZ); - fr->ipfr_ttl = fr_ipfrttl; - fr->ipfr_pass = pass & ~(FR_LOGFIRST|FR_LOG); + if ((fra->ipfr_next = table[idx])) + table[idx]->ipfr_prev = fra; + fra->ipfr_prev = NULL; + fra->ipfr_data = NULL; + table[idx] = fra; + bcopy((char *)&frag.ipfr_src, (char *)&fra->ipfr_src, IPFR_CMPSZ); + fra->ipfr_ttl = fr_ipfrttl; /* * Compute the offset of the expected start of the next packet. */ - fr->ipfr_off = (ip->ip_off & 0x1fff) + (fin->fin_dlen >> 3); - ipfr_stats.ifs_new++; - ipfr_inuse++; - return fr; + fra->ipfr_off = (ip->ip_off & IP_OFFMASK) + (fin->fin_dlen >> 3); + ATOMIC_INCL(ipfr_stats.ifs_new); + ATOMIC_INC32(ipfr_inuse); + return fra; } int ipfr_newfrag(ip, fin, pass) ip_t *ip; fr_info_t *fin; -int pass; +u_int pass; { ipfr_t *ipf; - MUTEX_ENTER(&ipf_frag); + if ((ip->ip_v != 4) || (fr_frag_lock)) + return NULL; + WRITE_ENTER(&ipf_frag); ipf = ipfr_new(ip, fin, pass, ipfr_heads); - MUTEX_EXIT(&ipf_frag); + RWLOCK_EXIT(&ipf_frag); return ipf ? 0 : -1; } @@ -180,17 +222,20 @@ int pass; int ipfr_nat_newfrag(ip, fin, pass, nat) ip_t *ip; fr_info_t *fin; -int pass; +u_int pass; nat_t *nat; { ipfr_t *ipf; - MUTEX_ENTER(&ipf_natfrag); - if ((ipf = ipfr_new(ip, fin, pass, ipfr_nattab))) { + if ((ip->ip_v != 4) || (fr_frag_lock)) + return NULL; + WRITE_ENTER(&ipf_natfrag); + ipf = ipfr_new(ip, fin, pass, ipfr_nattab); + if (ipf != NULL) { ipf->ipfr_data = nat; nat->nat_data = ipf; } - MUTEX_EXIT(&ipf_natfrag); + RWLOCK_EXIT(&ipf_natfrag); return ipf ? 0 : -1; } @@ -245,19 +290,19 @@ ipfr_t *table[]; f->ipfr_prev = NULL; table[idx] = f; } - off = ip->ip_off; + off = ip->ip_off & IP_OFFMASK; atoff = off + (fin->fin_dlen >> 3); /* * If we've follwed the fragments, and this is the * last (in order), shrink expiration time. */ - if ((off & 0x1fff) == f->ipfr_off) { - if (!(off & IP_MF)) + if (off == f->ipfr_off) { + if (!(ip->ip_off & IP_MF)) f->ipfr_ttl = 1; else f->ipfr_off = atoff; } - ipfr_stats.ifs_hits++; + ATOMIC_INCL(ipfr_stats.ifs_hits); return f; } return NULL; @@ -274,20 +319,22 @@ fr_info_t *fin; nat_t *nat; ipfr_t *ipf; - MUTEX_ENTER(&ipf_natfrag); + if ((ip->ip_v != 4) || (fr_frag_lock)) + return NULL; + READ_ENTER(&ipf_natfrag); ipf = ipfr_lookup(ip, fin, ipfr_nattab); - if (ipf) { + if (ipf != NULL) { nat = ipf->ipfr_data; /* * This is the last fragment for this packet. */ - if (ipf->ipfr_ttl == 1) { + if ((ipf->ipfr_ttl == 1) && (nat != NULL)) { nat->nat_data = NULL; ipf->ipfr_data = NULL; } } else nat = NULL; - MUTEX_EXIT(&ipf_natfrag); + RWLOCK_EXIT(&ipf_natfrag); return nat; } @@ -295,18 +342,21 @@ fr_info_t *fin; /* * functional interface for normal lookups of the fragment cache */ -int ipfr_knownfrag(ip, fin) +frentry_t *ipfr_knownfrag(ip, fin) ip_t *ip; fr_info_t *fin; { - int ret; - ipfr_t *ipf; + frentry_t *fr = NULL; + ipfr_t *fra; - MUTEX_ENTER(&ipf_frag); - ipf = ipfr_lookup(ip, fin, ipfr_heads); - ret = ipf ? ipf->ipfr_pass : 0; - MUTEX_EXIT(&ipf_frag); - return ret; + if ((ip->ip_v != 4) || (fr_frag_lock)) + return NULL; + READ_ENTER(&ipf_frag); + fra = ipfr_lookup(ip, fin, ipfr_heads); + if (fra != NULL) + fr = fra->ipfr_rule; + RWLOCK_EXIT(&ipf_frag); + return fr; } @@ -319,13 +369,32 @@ void *nat; ipfr_t *fr; int idx; - MUTEX_ENTER(&ipf_natfrag); + WRITE_ENTER(&ipf_natfrag); for (idx = IPFT_SIZE - 1; idx >= 0; idx--) for (fr = ipfr_heads[idx]; fr; fr = fr->ipfr_next) if (fr->ipfr_data == nat) fr->ipfr_data = NULL; - MUTEX_EXIT(&ipf_natfrag); + RWLOCK_EXIT(&ipf_natfrag); +} + + +static void ipfr_delete(fra) +ipfr_t *fra; +{ + frentry_t *fr; + + fr = fra->ipfr_rule; + if (fr != NULL) { + ATOMIC_DEC32(fr->fr_ref); + if (fr->fr_ref == 0) + KFREE(fr); + } + if (fra->ipfr_prev) + fra->ipfr_prev->ipfr_next = fra->ipfr_next; + if (fra->ipfr_next) + fra->ipfr_next->ipfr_prev = fra->ipfr_prev; + KFREE(fra); } @@ -334,55 +403,52 @@ void *nat; */ void ipfr_unload() { - ipfr_t **fp, *fr; + ipfr_t **fp, *fra; nat_t *nat; int idx; - MUTEX_ENTER(&ipf_frag); + WRITE_ENTER(&ipf_frag); for (idx = IPFT_SIZE - 1; idx >= 0; idx--) - for (fp = &ipfr_heads[idx]; (fr = *fp); ) { - *fp = fr->ipfr_next; - KFREE(fr); + for (fp = &ipfr_heads[idx]; (fra = *fp); ) { + *fp = fra->ipfr_next; + ipfr_delete(fra); } - MUTEX_EXIT(&ipf_frag); + RWLOCK_EXIT(&ipf_frag); - MUTEX_ENTER(&ipf_nat); - MUTEX_ENTER(&ipf_natfrag); + WRITE_ENTER(&ipf_nat); + WRITE_ENTER(&ipf_natfrag); for (idx = IPFT_SIZE - 1; idx >= 0; idx--) - for (fp = &ipfr_nattab[idx]; (fr = *fp); ) { - *fp = fr->ipfr_next; - if ((nat = (nat_t *)fr->ipfr_data)) { - if (nat->nat_data == fr) + for (fp = &ipfr_nattab[idx]; (fra = *fp); ) { + *fp = fra->ipfr_next; + nat = fra->ipfr_data; + if (nat != NULL) { + if (nat->nat_data == fra) nat->nat_data = NULL; } - KFREE(fr); + ipfr_delete(fra); } - MUTEX_EXIT(&ipf_natfrag); - MUTEX_EXIT(&ipf_nat); + RWLOCK_EXIT(&ipf_natfrag); + RWLOCK_EXIT(&ipf_nat); } #ifdef _KERNEL -/* - * Slowly expire held state for fragments. Timeouts are set * in expectation - * of this being called twice per second. - */ -# if (BSD >= 199306) || SOLARIS || defined(__sgi) -void ipfr_slowtimer() -# else -int ipfr_slowtimer() -# endif +void ipfr_fragexpire() { - ipfr_t **fp, *fr; + ipfr_t **fp, *fra; nat_t *nat; - int s, idx; - -#ifdef __sgi - ipfilter_sgi_intfsync(); + int idx; +#if defined(_KERNEL) +# if !SOLARIS + int s; +# endif #endif + if (fr_frag_lock) + return; + SPL_NET(s); - MUTEX_ENTER(&ipf_frag); + WRITE_ENTER(&ipf_frag); /* * Go through the entire table, looking for entries to expire, @@ -390,23 +456,17 @@ int ipfr_slowtimer() * remove it from the chain and free it. */ for (idx = IPFT_SIZE - 1; idx >= 0; idx--) - for (fp = &ipfr_heads[idx]; (fr = *fp); ) { - --fr->ipfr_ttl; - if (fr->ipfr_ttl == 0) { - if (fr->ipfr_prev) - fr->ipfr_prev->ipfr_next = - fr->ipfr_next; - if (fr->ipfr_next) - fr->ipfr_next->ipfr_prev = - fr->ipfr_prev; - *fp = fr->ipfr_next; - ipfr_stats.ifs_expire++; - ipfr_inuse--; - KFREE(fr); + for (fp = &ipfr_heads[idx]; (fra = *fp); ) { + --fra->ipfr_ttl; + if (fra->ipfr_ttl == 0) { + *fp = fra->ipfr_next; + ipfr_delete(fra); + ATOMIC_INCL(ipfr_stats.ifs_expire); + ATOMIC_DEC32(ipfr_inuse); } else - fp = &fr->ipfr_next; + fp = &fra->ipfr_next; } - MUTEX_EXIT(&ipf_frag); + RWLOCK_EXIT(&ipf_frag); /* * Same again for the NAT table, except that if the structure also @@ -415,44 +475,76 @@ int ipfr_slowtimer() * NOTE: We need to grab both mutex's early, and in this order so as * to prevent a deadlock if both try to expire at the same time. */ - MUTEX_ENTER(&ipf_nat); - MUTEX_ENTER(&ipf_natfrag); + WRITE_ENTER(&ipf_nat); + WRITE_ENTER(&ipf_natfrag); for (idx = IPFT_SIZE - 1; idx >= 0; idx--) - for (fp = &ipfr_nattab[idx]; (fr = *fp); ) { - --fr->ipfr_ttl; - if (fr->ipfr_ttl == 0) { - if (fr->ipfr_prev) - fr->ipfr_prev->ipfr_next = - fr->ipfr_next; - if (fr->ipfr_next) - fr->ipfr_next->ipfr_prev = - fr->ipfr_prev; - *fp = fr->ipfr_next; - ipfr_stats.ifs_expire++; - ipfr_inuse--; - if ((nat = (nat_t *)fr->ipfr_data)) { - if (nat->nat_data == fr) + for (fp = &ipfr_nattab[idx]; (fra = *fp); ) { + --fra->ipfr_ttl; + if (fra->ipfr_ttl == 0) { + ATOMIC_INCL(ipfr_stats.ifs_expire); + ATOMIC_DEC32(ipfr_inuse); + nat = fra->ipfr_data; + if (nat != NULL) { + if (nat->nat_data == fra) nat->nat_data = NULL; } - KFREE(fr); + *fp = fra->ipfr_next; + ipfr_delete(fra); } else - fp = &fr->ipfr_next; + fp = &fra->ipfr_next; } - MUTEX_EXIT(&ipf_natfrag); - MUTEX_EXIT(&ipf_nat); + RWLOCK_EXIT(&ipf_natfrag); + RWLOCK_EXIT(&ipf_nat); SPL_X(s); +} + + +/* + * Slowly expire held state for fragments. Timeouts are set * in expectation + * of this being called twice per second. + */ +# if (BSD >= 199306) || SOLARIS || defined(__sgi) +# if defined(SOLARIS2) && (SOLARIS2 < 7) +void ipfr_slowtimer() +# else +void ipfr_slowtimer __P((void *ptr)) +# endif +# else +int ipfr_slowtimer() +# endif +{ +#if defined(_KERNEL) && SOLARIS + extern int fr_running; + + if (fr_running <= 0) + return; +#endif + + READ_ENTER(&ipf_solaris); +#ifdef __sgi + ipfilter_sgi_intfsync(); +#endif + + ipfr_fragexpire(); fr_timeoutstate(); ip_natexpire(); fr_authexpire(); -# if SOLARIS +# if SOLARIS ipfr_timer_id = timeout(ipfr_slowtimer, NULL, drv_usectohz(500000)); + RWLOCK_EXIT(&ipf_solaris); # else -# ifndef linux - ip_slowtimo(); -# endif -# if (BSD < 199306) && !defined(__sgi) +# if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000) + callout_reset(&ipfr_slowtimer_ch, hz / 2, ipfr_slowtimer, NULL); +# else +# if (__FreeBSD_version >= 300000) + ipfr_slowtimer_ch = timeout(ipfr_slowtimer, NULL, hz/2); +# else + timeout(ipfr_slowtimer, NULL, hz/2); +# endif +# if (BSD < 199306) && !defined(__sgi) return 0; -# endif -# endif +# endif /* FreeBSD */ +# endif /* NetBSD */ +# endif /* SOLARIS */ } #endif /* defined(_KERNEL) */ |