From aa253ab58590afa73cf198c524f0255f45700d8b Mon Sep 17 00:00:00 2001 From: darrenr Date: Thu, 25 Apr 2002 03:31:39 +0000 Subject: bring in changes from 3.4.26. --- sys/contrib/ipfilter/netinet/IPFILTER.LICENCE | 5 +- sys/contrib/ipfilter/netinet/QNX_OCL.txt | 2 + sys/contrib/ipfilter/netinet/fil.c | 7 +- sys/contrib/ipfilter/netinet/ip_auth.c | 6 +- sys/contrib/ipfilter/netinet/ip_compat.h | 80 +++++----- sys/contrib/ipfilter/netinet/ip_fil.c | 5 +- sys/contrib/ipfilter/netinet/ip_fil.h | 25 +-- sys/contrib/ipfilter/netinet/ip_frag.c | 2 +- sys/contrib/ipfilter/netinet/ip_log.c | 1 + sys/contrib/ipfilter/netinet/ip_nat.c | 120 ++++++++++---- sys/contrib/ipfilter/netinet/ip_nat.h | 2 + sys/contrib/ipfilter/netinet/ip_state.c | 216 +++++++++++++++++++------- sys/contrib/ipfilter/netinet/ip_state.h | 25 +-- sys/contrib/ipfilter/netinet/ipl.h | 2 +- 14 files changed, 338 insertions(+), 160 deletions(-) diff --git a/sys/contrib/ipfilter/netinet/IPFILTER.LICENCE b/sys/contrib/ipfilter/netinet/IPFILTER.LICENCE index dfb199c..1ee473d 100644 --- a/sys/contrib/ipfilter/netinet/IPFILTER.LICENCE +++ b/sys/contrib/ipfilter/netinet/IPFILTER.LICENCE @@ -1,7 +1,7 @@ -Copyright (C) 1993-2001 by Darren Reed. - $FreeBSD$ +Copyright (C) 1993-2002 by Darren Reed. + The author accepts no responsibility for the use of this software and provides it on an ``as is'' basis without express or implied warranty. @@ -27,3 +27,4 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. I hate legalese, don't you ? + diff --git a/sys/contrib/ipfilter/netinet/QNX_OCL.txt b/sys/contrib/ipfilter/netinet/QNX_OCL.txt index 6aa33ea..b623776 100644 --- a/sys/contrib/ipfilter/netinet/QNX_OCL.txt +++ b/sys/contrib/ipfilter/netinet/QNX_OCL.txt @@ -1,3 +1,5 @@ +$FreeBSD$ + End User License Certificate (EULA) End User License Certificate (EULA) Support Support diff --git a/sys/contrib/ipfilter/netinet/fil.c b/sys/contrib/ipfilter/netinet/fil.c index dd5cad1..bffb4b2 100644 --- a/sys/contrib/ipfilter/netinet/fil.c +++ b/sys/contrib/ipfilter/netinet/fil.c @@ -213,9 +213,7 @@ fr_info_t *fin; fin->fin_data[1] = 0; fin->fin_rule = -1; fin->fin_group = -1; -#ifdef _KERNEL fin->fin_icode = ipl_unreach; -#endif v = fin->fin_v; fi->fi_v = v; fin->fin_hlen = hlen; @@ -265,6 +263,7 @@ fr_info_t *fin; fin->fin_off = off; fin->fin_plen = plen; fin->fin_dp = (char *)tcp; + fin->fin_misc = 0; off <<= 3; switch (p) @@ -297,7 +296,7 @@ fr_info_t *fin; } } - if (!(plen >= hlen + minicmpsz)) + if (!(plen >= minicmpsz)) fi->fi_fl |= FI_SHORT; break; @@ -1512,7 +1511,7 @@ nodata: * SUCH DAMAGE. * * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 - * $Id: fil.c,v 2.35.2.58 2002/03/13 02:23:13 darrenr Exp $ + * $Id: fil.c,v 2.35.2.59 2002/03/25 11:07:37 darrenr Exp $ */ /* * Copy data from an mbuf chain starting "off" bytes from the beginning, diff --git a/sys/contrib/ipfilter/netinet/ip_auth.c b/sys/contrib/ipfilter/netinet/ip_auth.c index ee3a7d7..cdbfb92 100644 --- a/sys/contrib/ipfilter/netinet/ip_auth.c +++ b/sys/contrib/ipfilter/netinet/ip_auth.c @@ -410,6 +410,7 @@ fr_authioctlloop: RWLOCK_EXIT(&ipf_auth); return 0; } + RWLOCK_EXIT(&ipf_auth); #ifdef _KERNEL # if SOLARIS mutex_enter(&ipf_authmx); @@ -422,7 +423,6 @@ fr_authioctlloop: error = SLEEP(&fr_authnext, "fr_authnext"); # endif #endif - RWLOCK_EXIT(&ipf_auth); if (!error) goto fr_authioctlloop; break; @@ -452,7 +452,7 @@ fr_authioctlloop: #ifdef _KERNEL if (m && au->fra_info.fin_out) { # if SOLARIS - error = fr_qout(fra->fra_q, m); + error = (fr_qout(fra->fra_q, m) == 0) ? EINVAL : 0; # else /* SOLARIS */ struct route ro; @@ -474,7 +474,7 @@ fr_authioctlloop: fr_authstats.fas_sendok++; } else if (m) { # if SOLARIS - error = fr_qin(fra->fra_q, m); + error = (fr_qin(fra->fra_q, m) == 0) ? EINVAL : 0; # else /* SOLARIS */ if (! IF_HANDOFF(&ipintrq, m, NULL)) error = ENOBUFS; diff --git a/sys/contrib/ipfilter/netinet/ip_compat.h b/sys/contrib/ipfilter/netinet/ip_compat.h index d4b7bf6..bf67827 100644 --- a/sys/contrib/ipfilter/netinet/ip_compat.h +++ b/sys/contrib/ipfilter/netinet/ip_compat.h @@ -104,7 +104,6 @@ struct ether_addr { # include #endif - /* * This is a workaround for troubles on FreeBSD and OpenBSD. */ @@ -198,10 +197,6 @@ typedef int minor_t; #endif /* SOLARIS */ #define IPMINLEN(i, h) ((i)->ip_len >= ((i)->ip_hl * 4 + sizeof(struct h))) -#if defined(__FreeBSD__) && (__FreeBSD__ >= 5) && defined(_KERNEL) -# include -#endif - #ifndef IP_OFFMASK #define IP_OFFMASK 0x1fff #endif @@ -216,6 +211,30 @@ typedef int minor_t; #endif /* BSD > 199306 */ +#if defined(__FreeBSD__) && (defined(KERNEL) || defined(_KERNEL)) +# include +# ifndef __FreeBSD_version +# include +# endif +# ifdef IPFILTER_LKM +# define ACTUALLY_LKM_NOT_KERNEL +# endif +# if defined(__FreeBSD_version) && (__FreeBSD_version < 300000) +# include +# else +# if (__FreeBSD_version >= 300000) && (__FreeBSD_version < 400000) +# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) +# define ACTUALLY_LKM_NOT_KERNEL +# endif +# endif +# endif +#endif /* __FreeBSD__ && KERNEL */ + +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 500000) && \ + defined(_KERNEL) +# include +#endif + /* * These operating systems already take care of the problem for us. */ @@ -231,6 +250,13 @@ typedef u_int32_t u_32_t; # include "opt_inet6.h" # endif # ifdef INET6 +# define USE_INET6 +# endif +# endif +# if !defined(_KERNEL) && !defined(IPFILTER_LKM) +# if (defined(__FreeBSD_version) && (__FreeBSD_version >= 400000)) || \ + (defined(OpenBSD) && (OpenBSD >= 200111)) || \ + (defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 105000000)) # define USE_INET6 # endif # endif @@ -342,40 +368,9 @@ union i6addr { #define IPOPT_EIP 145 /* EIP */ #define IPOPT_FINN 205 /* FINN */ - -#if defined(__FreeBSD__) && (defined(KERNEL) || defined(_KERNEL)) -# ifdef IPFILTER_LKM -# ifndef __FreeBSD_cc_version -# include -# else -# if __FreeBSD_cc_version < 430000 -# include -# else -# include -# endif -# endif -# define ACTUALLY_LKM_NOT_KERNEL -# else -# ifndef __FreeBSD_cc_version -# include -# else -# if __FreeBSD_cc_version < 430000 -# include -# else -# include -# endif -# endif -# endif -# if __FreeBSD__ < 3 -# include -# else -# if __FreeBSD__ == 3 -# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) -# define ACTUALLY_LKM_NOT_KERNEL -# endif -# endif -# endif -#endif /* __FreeBSD__ && KERNEL */ +#ifndef TCPOPT_WSCALE +# define TCPOPT_WSCALE 3 +#endif /* * Build some macros and #defines to enable the same code to compile anywhere @@ -581,7 +576,8 @@ extern void m_copyback __P((struct mbuf *, int, int, caddr_t)); defined(__FreeBSD__) || defined(__OpenBSD__) || defined(_BSDI_VERSION) # include # endif -# if !defined(__FreeBSD__) || (defined (__FreeBSD__) && __FreeBSD__>=3) +# if !defined(__FreeBSD__) || (defined (__FreeBSD_version) && \ + (__FreeBSD_version >= 300000)) # if (defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 105180000)) || \ (defined(OpenBSD) && (OpenBSD >= 200111)) # include @@ -590,9 +586,9 @@ extern void m_copyback __P((struct mbuf *, int, int, caddr_t)); extern vm_map_t kmem_map; # endif # include -# else /* !__FreeBSD__ || (__FreeBSD__ && __FreeBSD__>=3) */ +# else /* !__FreeBSD__ || (__FreeBSD__ && __FreeBSD_version >= 300000) */ # include -# endif /* !__FreeBSD__ || (__FreeBSD__ && __FreeBSD__>=3) */ +# endif /* !__FreeBSD__ || (__FreeBSD__ && __FreeBSD_version >= 300000) */ # ifdef M_PFIL # define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_PFIL, M_NOWAIT) # define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_PFIL, M_NOWAIT) diff --git a/sys/contrib/ipfilter/netinet/ip_fil.c b/sys/contrib/ipfilter/netinet/ip_fil.c index 643592c..3a41c5c 100644 --- a/sys/contrib/ipfilter/netinet/ip_fil.c +++ b/sys/contrib/ipfilter/netinet/ip_fil.c @@ -26,6 +26,7 @@ # endif #endif #ifdef __sgi +# define _KMEMUSER # include #endif #ifndef _KERNEL @@ -2142,8 +2143,8 @@ struct uio *uio; num = io->iov_len; if (num > left) num = left; - start = io->iov_base + offset; - if (start > io->iov_base + io->iov_len) { + start = (char *)io->iov_base + offset; + if (start > (char *)io->iov_base + io->iov_len) { offset -= io->iov_len; ioc++; continue; diff --git a/sys/contrib/ipfilter/netinet/ip_fil.h b/sys/contrib/ipfilter/netinet/ip_fil.h index b63769a..5088fb3 100644 --- a/sys/contrib/ipfilter/netinet/ip_fil.h +++ b/sys/contrib/ipfilter/netinet/ip_fil.h @@ -56,8 +56,8 @@ # define SIOCFRSYN _IOW('r', 73, u_int) # define SIOCFRZST _IOWR('r', 74, struct friostat *) # define SIOCZRLST _IOWR('r', 75, struct frentry *) -# define SIOCAUTHW _IOWR('r', 76, struct frauth_t *) -# define SIOCAUTHR _IOWR('r', 77, struct frauth_t *) +# define SIOCAUTHW _IOWR('r', 76, struct frauth *) +# define SIOCAUTHR _IOWR('r', 77, struct frauth *) # define SIOCATHST _IOWR('r', 78, struct fr_authstat *) # define SIOCSTLCK _IOWR('r', 79, u_int) # define SIOCSTPUT _IOWR('r', 80, struct ipstate_save *) @@ -81,8 +81,8 @@ # define SIOCFRSYN _IOW(r, 73, u_int) # define SIOCFRZST _IOWR(r, 74, struct friostat *) # define SIOCZRLST _IOWR(r, 75, struct frentry *) -# define SIOCAUTHW _IOWR(r, 76, struct frauth_t *) -# define SIOCAUTHR _IOWR(r, 77, struct frauth_t *) +# define SIOCAUTHW _IOWR(r, 76, struct frauth *) +# define SIOCAUTHR _IOWR(r, 77, struct frauth *) # define SIOCATHST _IOWR(r, 78, struct fr_authstat *) # define SIOCSTLCK _IOWR(r, 79, u_int) # define SIOCSTPUT _IOWR(r, 80, struct ipstate_save *) @@ -136,12 +136,11 @@ typedef struct fr_info { void *fin_ifp; /* interface packet is `on' */ struct fr_ip fin_fi; /* IP Packet summary */ u_short fin_data[2]; /* TCP/UDP ports, ICMP code/type */ - u_char fin_out; /* in or out ? 1 == out, 0 == in */ - u_char fin_rev; /* state only: 1 = reverse */ + u_int fin_out; /* in or out ? 1 == out, 0 == in */ u_short fin_hlen; /* length of IP header in bytes */ + u_char fin_rev; /* state only: 1 = reverse */ u_char fin_tcpf; /* TCP header flags (SYN, ACK, etc) */ - /* From here on is packet specific */ - u_char fin_icode; /* ICMP error to return */ + u_int fin_icode; /* ICMP error to return */ u_32_t fin_rule; /* rule # last matched */ u_32_t fin_group; /* group number, -1 for none */ struct frentry *fin_fr; /* last matching rule */ @@ -150,6 +149,7 @@ typedef struct fr_info { u_short fin_off; u_short fin_dlen; /* length of data portion of packet */ u_short fin_id; /* IP packet id field */ + u_int fin_misc; void *fin_mp; /* pointer to pointer to mbuf */ #if SOLARIS void *fin_qfm; /* pointer to mblk where pkt starts */ @@ -172,6 +172,11 @@ typedef struct fr_info { #define FI_LCSIZE offsetof(fr_info_t, fin_dp) /* + * For fin_misc + */ +#define FM_BADSTATE 0x00000001 + +/* * Size for copying cache fr_info structure */ #define FI_COPYSIZE offsetof(fr_info_t, fin_dp) @@ -422,10 +427,10 @@ typedef struct iplog { typedef struct ipflog { #if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ (defined(OpenBSD) && (OpenBSD >= 199603)) - u_char fl_ifname[LIFNAMSIZ]; + char fl_ifname[LIFNAMSIZ]; #else u_int fl_unit; - u_char fl_ifname[LIFNAMSIZ]; + char fl_ifname[LIFNAMSIZ]; #endif u_char fl_plen; /* extra data after hlen */ u_char fl_hlen; /* length of IP headers saved */ diff --git a/sys/contrib/ipfilter/netinet/ip_frag.c b/sys/contrib/ipfilter/netinet/ip_frag.c index 7b7c7e5..6ebbdce 100644 --- a/sys/contrib/ipfilter/netinet/ip_frag.c +++ b/sys/contrib/ipfilter/netinet/ip_frag.c @@ -584,9 +584,9 @@ void ipfr_slowtimer() if (fr_running <= 0) return; + READ_ENTER(&ipf_solaris); #endif - READ_ENTER(&ipf_solaris); #if defined(__sgi) && defined(_KERNEL) ipfilter_sgi_intfsync(); #endif diff --git a/sys/contrib/ipfilter/netinet/ip_log.c b/sys/contrib/ipfilter/netinet/ip_log.c index 189e682..7126b20 100644 --- a/sys/contrib/ipfilter/netinet/ip_log.c +++ b/sys/contrib/ipfilter/netinet/ip_log.c @@ -90,6 +90,7 @@ # include # include # ifdef __sgi +# define _KMEMUSER # include # ifdef IFF_DRVRLOCK /* IRIX6 */ # include diff --git a/sys/contrib/ipfilter/netinet/ip_nat.c b/sys/contrib/ipfilter/netinet/ip_nat.c index 60dd7ed..2e10f83 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.c +++ b/sys/contrib/ipfilter/netinet/ip_nat.c @@ -427,7 +427,7 @@ caddr_t data; int mode; { register ipnat_t *nat, *nt, *n = NULL, **np = NULL; - int error = 0, ret, arg; + int error = 0, ret, arg, getlock; ipnat_t natd; u_32_t i, j; @@ -438,9 +438,15 @@ int mode; nat = NULL; /* XXX gcc -Wuninitialized */ KMALLOC(nt, ipnat_t *); - if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) - error = IRCOPYPTR(data, (char *)&natd, sizeof(natd)); - else if (cmd == SIOCIPFFL) { /* SIOCFLNAT & SIOCCNATL */ + getlock = (mode & NAT_LOCKHELD) ? 0 : 1; + if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { + if (mode & NAT_SYSSPACE) { + bcopy(data, (char *)&natd, sizeof(natd)); + error = 0; + } else { + error = IRCOPYPTR(data, (char *)&natd, sizeof(natd)); + } + } else if (cmd == SIOCIPFFL) { /* SIOCFLNAT & SIOCCNATL */ error = IRCOPY(data, (char *)&arg, sizeof(arg)); if (error) error = EFAULT; @@ -452,7 +458,8 @@ int mode; /* * For add/delete, look to see if the NAT entry is already present */ - WRITE_ENTER(&ipf_nat); + if (getlock == 1) + WRITE_ENTER(&ipf_nat); if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { nat = &natd; nat->in_flags &= IPN_USERFLAGS; @@ -717,7 +724,8 @@ int mode; error = EINVAL; break; } - RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ + if (getlock == 1) + RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ done: if (nt) KFREE(nt); @@ -833,7 +841,7 @@ caddr_t data; return ENOMEM; bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); - bcopy((char *)aps, ipnn->ipn_data, sizeof(*aps)); + bcopy((char *)aps, (char *)ipnn->ipn_data, sizeof(*aps)); if (aps->aps_data) { bcopy(aps->aps_data, ipnn->ipn_data + sizeof(*aps), aps->aps_psiz); @@ -1652,11 +1660,12 @@ int dir; { u_32_t sum1, sum2, sumd, sumd2 = 0; struct in_addr in; + int flags, dlen; icmphdr_t *icmp; udphdr_t *udp; + tcphdr_t *tcp; nat_t *nat; ip_t *oip; - int flags; if ((fin->fin_fl & FI_SHORT) || (fin->fin_off != 0)) return NULL; @@ -1675,6 +1684,13 @@ int dir; else if (oip->ip_p == IPPROTO_UDP) flags = IPN_UDP; udp = (udphdr_t *)((((char *)oip) + (oip->ip_hl << 2))); + dlen = ip->ip_len - ((char *)udp - (char *)ip); + /* + * XXX - what if this is bogus hl and we go off the end ? + * In this case, nat_icmplookup() will have returned NULL. + */ + tcp = (tcphdr_t *)udp; + /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the @@ -1697,8 +1713,6 @@ int dir; * change in the UDP and TCP checksums require yet another * adjustment of the ICMP checksum of the ICMP error message. * - * For the moment we forget about TCP, because that checksum is not - * in the first 8 bytes, so it will not be available in most cases. */ if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) { @@ -1756,15 +1770,25 @@ int dir; sumd2 = sumd; } -#if 0 +#if 1 /* * Fix TCP pseudo header checksum to compensate for the * IP address change. Before we can do the change, we * must make sure that oip is sufficient large to hold * the TCP checksum (normally it does not!). */ - if (oip->ip_p == IPPROTO_TCP) { + if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { + sum1 = ntohs(tcp->th_sum); + fix_datacksum(&tcp->th_sum, sumd); + sum2 = ntohs(tcp->th_sum); + + /* + * Fix ICMP checksum to compensate the TCP + * checksum adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 = sumd; } #endif } else { @@ -1815,15 +1839,25 @@ int dir; sumd2 = sumd; } -#if 0 +#if 1 /* * Fix TCP pseudo header checksum to compensate for the * IP address change. Before we can do the change, we * must make sure that oip is sufficient large to hold * the TCP checksum (normally it does not!). */ - if (oip->ip_p == IPPROTO_TCP) { + if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { + sum1 = ntohs(tcp->th_sum); + fix_datacksum(&tcp->th_sum, sumd); + sum2 = ntohs(tcp->th_sum); + + /* + * Fix ICMP checksum to compensate the TCP + * checksum adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 = sumd; }; #endif @@ -1831,14 +1865,6 @@ int dir; } if ((flags & IPN_TCPUDP) != 0) { - tcphdr_t *tcp; - - /* - * XXX - what if this is bogus hl and we go off the end ? - * In this case, nat_icmpinlookup() will have returned NULL. - */ - tcp = (tcphdr_t *)udp; - /* * Step 2 : * For offending TCP/UDP IP packets, translate the ports as @@ -1854,8 +1880,9 @@ int dir; * * To further complicate: the TCP checksum is not in the first * 8 bytes of the offending ip packet, so it most likely is not - * available (we might have to fix that if the encounter a - * device that returns more than 8 data bytes on icmp error) + * available. Some OSses like Solaris return enough bytes to + * include the TCP checksum. So we have to check if the + * ip->ip_len actually holds the TCP checksum of the oip! */ if (nat->nat_oport == tcp->th_dport) { @@ -1893,6 +1920,27 @@ int dir; CALC_SUMD(sum1, sum2, sumd); sumd2 += sumd; } + + /* + * Fix tcp checksum (if present) to compensate + * port adjustment. NOTE : the offending IP + * packet flows the other direction compared to + * the ICMP message. + */ + if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { + + sum1 = ntohs(tcp->th_sum); + fix_datacksum(&tcp->th_sum, sumd); + sum2 = ntohs(tcp->th_sum); + + /* + * Fix ICMP checksum to + * compensate TCP checksum + * adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; + } } } else { if (tcp->th_dport != nat->nat_outport) { @@ -1928,6 +1976,26 @@ int dir; CALC_SUMD(sum1, sum2, sumd); sumd2 += sumd; } + + /* + * Fix tcp checksum (if present) to compensate + * port adjustment. NOTE : the offending IP + * packet flows the other direction compared to + * the ICMP message. + */ + if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { + + sum1 = ntohs(tcp->th_sum); + fix_datacksum(&tcp->th_sum, sumd); + sum2 = ntohs(tcp->th_sum); + + /* + * Fix ICMP checksum to compensate + * UDP checksum adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; + } } } if (sumd2) { @@ -2437,7 +2505,7 @@ maskloop: csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, - nat->nat_tcpstate, fin, 1); + nat->nat_tcpstate, fin, 1, 0); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT @@ -2645,7 +2713,7 @@ maskloop: csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, - nat->nat_tcpstate, fin, 0); + nat->nat_tcpstate, fin, 0, 0); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT diff --git a/sys/contrib/ipfilter/netinet/ip_nat.h b/sys/contrib/ipfilter/netinet/ip_nat.h index be0bb83..f1258be 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.h +++ b/sys/contrib/ipfilter/netinet/ip_nat.h @@ -277,6 +277,8 @@ typedef struct natlog { (sd) = (s2) - (s1); \ (sd) = ((sd) & 0xffff) + ((sd) >> 16); } +#define NAT_SYSSPACE 0x80000000 +#define NAT_LOCKHELD 0x40000000 extern u_int ipf_nattable_sz; extern u_int ipf_natrules_sz; diff --git a/sys/contrib/ipfilter/netinet/ip_state.c b/sys/contrib/ipfilter/netinet/ip_state.c index 322e59f..350cd1f1 100644 --- a/sys/contrib/ipfilter/netinet/ip_state.c +++ b/sys/contrib/ipfilter/netinet/ip_state.c @@ -125,6 +125,7 @@ static ips_stat_t *fr_statetstats __P((void)); static void fr_delstate __P((ipstate_t *)); static int fr_state_remove __P((caddr_t)); static void fr_ipsmove __P((ipstate_t **, ipstate_t *, u_int)); +static int fr_tcpoptions __P((tcphdr_t *)); int fr_stputent __P((caddr_t)); int fr_stgetent __P((caddr_t)); void fr_stinsert __P((ipstate_t *)); @@ -300,7 +301,7 @@ caddr_t data; if ((sp->is_p == st.is_p) && (sp->is_v == st.is_v) && !bcmp((char *)&sp->is_src, (char *)&st.is_src, sizeof(st.is_src)) && - !bcmp((char *)&sp->is_dst, (char *)&st.is_src, + !bcmp((char *)&sp->is_dst, (char *)&st.is_dst, sizeof(st.is_dst)) && !bcmp((char *)&sp->is_ps, (char *)&st.is_ps, sizeof(st.is_ps))) { @@ -580,7 +581,8 @@ u_int flags; void *ifp; int out; - if (fr_state_lock || (fin->fin_off != 0) || (fin->fin_fl & FI_SHORT)) + if (fr_state_lock || (fin->fin_off != 0) || (fin->fin_fl & FI_SHORT) || + (fin->fin_misc & FM_BADSTATE)) return NULL; if (ips_num == fr_statemax) { ips_stats.iss_max++; @@ -621,6 +623,8 @@ u_int flags; switch (is->is_p) { + int off; + #ifdef USE_INET6 case IPPROTO_ICMPV6 : ic = (struct icmp *)fin->fin_dp; @@ -682,15 +686,22 @@ u_int flags; hv += is->is_dport; } is->is_send = ntohl(tcp->th_seq) + fin->fin_dlen - - (tcp->th_off << 2) + + (off = (tcp->th_off << 2)) + ((tcp->th_flags & TH_SYN) ? 1 : 0) + ((tcp->th_flags & TH_FIN) ? 1 : 0); is->is_maxsend = is->is_send; - is->is_dend = 0; is->is_maxdwin = 1; is->is_maxswin = ntohs(tcp->th_win); if (is->is_maxswin == 0) is->is_maxswin = 1; + + if ((tcp->th_flags & TH_OPENING) == TH_SYN) + is->is_fsm = 1; + + if ((tcp->th_flags & TH_SYN) && + ((tcp->th_off << 2) >= (sizeof(*tcp) + 4))) + is->is_swscale = fr_tcpoptions(tcp); + /* * If we're creating state for a starting connection, start the * timer on it as we'll never see an error if it fails to @@ -787,7 +798,7 @@ u_int flags; is->is_me = stsave; if (is->is_p == IPPROTO_TCP) { fr_tcp_age(&is->is_age, is->is_state, fin, - 0); /* 0 = packet from the source */ + 0, is->is_fsm); /* 0 = packet from the source */ } #ifdef IPFILTER_LOG ipstate_log(is, ISL_NEW); @@ -800,6 +811,46 @@ u_int flags; } +static int fr_tcpoptions(tcp) +tcphdr_t *tcp; +{ + u_char *opt, *last; + int wscale; + + opt = (u_char *) (tcp + 1); + last = ((u_char *)tcp) + (tcp->th_off << 2); + + /* If we don't find wscale here, we need to clear it */ + wscale = -2; + + /* Termination condition picked such that opt[0 .. 2] exist */ + while ((opt < last - 2) && (*opt != TCPOPT_EOL)) { + switch (*opt) { + case TCPOPT_NOP: + opt++; + continue; + case TCPOPT_WSCALE: + /* Proper length ? */ + if (opt[1] == 3) { + if (opt[2] > 14) + wscale = 14; + else + wscale = opt[2]; + } + break; + default: + /* Unknown options must be two bytes+ */ + if (opt[1] < 2) + break; + opt += opt[1]; + continue; + } + break; + } + return wscale; +} + + /* * check to see if a packet with TCP headers fits within the TCP window. @@ -815,9 +866,10 @@ tcphdr_t *tcp; register tcp_seq seq, ack, end; register int ackskew; tcpdata_t *fdata, *tdata; - u_short win, maxwin; - int ret = 0; + u_32_t win, maxwin; + int ret = 0, off; int source; + int wscale; /* * Find difference between last checked packet and this packet. @@ -827,15 +879,29 @@ tcphdr_t *tcp; source = 0; fdata = &is->is_tcp.ts_data[!source]; tdata = &is->is_tcp.ts_data[source]; + off = tcp->th_off << 2; seq = ntohl(tcp->th_seq); ack = ntohl(tcp->th_ack); win = ntohs(tcp->th_win); - end = seq + fin->fin_dlen - (tcp->th_off << 2) + + end = seq + fin->fin_dlen - off + ((tcp->th_flags & TH_SYN) ? 1 : 0) + ((tcp->th_flags & TH_FIN) ? 1 : 0); + + if ((tcp->th_flags & TH_SYN) && (off >= sizeof(*tcp) + 4)) + wscale = fr_tcpoptions(tcp); + else + wscale = -1; + MUTEX_ENTER(&is->is_lock); - if (fdata->td_end == 0) { + + if (wscale >= 0) + fdata->td_wscale = wscale; + else if (wscale == -2) + fdata->td_wscale = tdata->td_wscale = 0; + + if ((fdata->td_end == 0) && + (!is->is_fsm || ((tcp->th_flags & TH_OPENING) == TH_OPENING))) { /* * Must be a (outgoing) SYN-ACK in reply to a SYN. */ @@ -855,6 +921,7 @@ tcphdr_t *tcp; if (seq == end) seq = end = fdata->td_end; + win <<= fdata->td_wscale; maxwin = tdata->td_maxwin; ackskew = tdata->td_end - ack; @@ -880,29 +947,33 @@ tcphdr_t *tcp; * Thus, when ackskew is negative but still seems to belong * to this session, we bump up the destinations end value. */ - if (ackskew < 0) - tdata->td_end = ack; - - /* update max window seen */ - if (fdata->td_maxwin < win) - fdata->td_maxwin = win; - if (SEQ_GT(end, fdata->td_end)) - fdata->td_end = end; - if (SEQ_GE(ack + win, tdata->td_maxend)) { - tdata->td_maxend = ack + win; - if (win == 0) - tdata->td_maxend++; - } - - ATOMIC_INCL(ips_stats.iss_hits); /* * Nearing end of connection, start timeout. */ /* source ? 0 : 1 -> !source */ - fr_tcp_age(&is->is_age, is->is_state, fin, !source); - ret = 1; + if (fr_tcp_age(&is->is_age, is->is_state, fin, !source, + (int)is->is_fsm) == 0) { + if (ackskew < 0) + tdata->td_end = ack; + + /* update max window seen */ + if (fdata->td_maxwin < win) + fdata->td_maxwin = win; + if (SEQ_GT(end, fdata->td_end)) + fdata->td_end = end; + if (SEQ_GE(ack + win, tdata->td_maxend)) { + tdata->td_maxend = ack + win; + if (win == 0) + tdata->td_maxend++; + } + + ATOMIC_INCL(ips_stats.iss_hits); + ret = 1; + } } MUTEX_EXIT(&is->is_lock); + if ((ret == 0) && (tcp->th_flags != TH_SYN)) + fin->fin_misc |= FM_BADSTATE; return ret; } @@ -1081,9 +1152,9 @@ fr_info_t *fin; register ipstate_t *is, **isp; register u_short sport, dport; register u_char pr; + u_short savelen, ohlen; union i6addr dst, src; struct icmp *ic; - u_short savelen; icmphdr_t *icmp; fr_info_t ofin; int type, len; @@ -1112,14 +1183,15 @@ fr_info_t *fin; return NULL; oip = (ip_t *)((char *)ic + ICMPERR_ICMPHLEN); - if (fin->fin_plen < ICMPERR_MAXPKTLEN + ((oip->ip_hl - 5) << 2)) + ohlen = oip->ip_hl << 2; + if (fin->fin_plen < ICMPERR_MAXPKTLEN + ohlen - sizeof(*oip)) return NULL; /* * Sanity checks. */ len = fin->fin_dlen - ICMPERR_ICMPHLEN; - if ((len <= 0) || ((oip->ip_hl << 2) > len)) + if ((len <= 0) || (ohlen > len)) return NULL; /* @@ -1159,7 +1231,7 @@ fr_info_t *fin; switch (oip->ip_p) { case IPPROTO_ICMP : - icmp = (icmphdr_t *)((char *)oip + (oip->ip_hl << 2)); + icmp = (icmphdr_t *)((char *)oip + ohlen); /* * a ICMP error can only be generated as a result of an @@ -1189,7 +1261,7 @@ fr_info_t *fin; savelen = oip->ip_len; oip->ip_len = len; ofin.fin_v = 4; - fr_makefrip(oip->ip_hl << 2, oip, &ofin); + fr_makefrip(ohlen, oip, &ofin); oip->ip_len = savelen; ofin.fin_ifp = fin->fin_ifp; ofin.fin_out = !fin->fin_out; @@ -1211,12 +1283,14 @@ fr_info_t *fin; case IPPROTO_TCP : case IPPROTO_UDP : + if (fin->fin_plen < ICMPERR_MAXPKTLEN) + return NULL; break; default : return NULL; } - tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); + tcp = (tcphdr_t *)((char *)oip + ohlen); dport = tcp->th_dport; sport = tcp->th_sport; @@ -1241,7 +1315,7 @@ fr_info_t *fin; savelen = oip->ip_len; oip->ip_len = len; ofin.fin_v = 4; - fr_makefrip(oip->ip_hl << 2, oip, &ofin); + fr_makefrip(ohlen, oip, &ofin); oip->ip_len = savelen; ofin.fin_ifp = fin->fin_ifp; ofin.fin_out = !fin->fin_out; @@ -1483,9 +1557,8 @@ retry_tcpudp: fr_matchsrcdst(is, src, dst, fin, tcp)) { rev = fin->fin_rev; if ((pr == IPPROTO_TCP)) { - if (!fr_tcpstate(is, fin, ip, tcp)) { - continue; - } + if (!fr_tcpstate(is, fin, ip, tcp)) + is = NULL; } else if ((pr == IPPROTO_UDP)) { if (is->is_frage[rev] != 0) is->is_age = is->is_frage[rev]; @@ -1506,6 +1579,7 @@ retry_tcpudp: } break; } + RWLOCK_EXIT(&ipf_state); if (!tryagain && ips_wild) { hv -= dport; @@ -1705,15 +1779,16 @@ void fr_timeoutstate() * dir == 1 : a packet from dest to source * */ -void fr_tcp_age(age, state, fin, dir) +int fr_tcp_age(age, state, fin, dir, fsm) u_long *age; u_char *state; fr_info_t *fin; -int dir; +int dir, fsm; { tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; u_char flags = tcp->th_flags; int dlen, ostate; + u_long newage; ostate = state[1 - dir]; @@ -1727,10 +1802,10 @@ int dir; *age = fr_tcpclosewait; state[dir] = TCPS_CLOSE_WAIT; } - return; + return 0; } - *age = fr_tcptimeout; /* default 4 mins */ + newage = 0; switch(state[dir]) { @@ -1741,11 +1816,11 @@ int dir; * CLOSED -> SYN_RECEIVED */ state[dir] = TCPS_SYN_RECEIVED; - *age = fr_tcptimeout; - } else if ((flags & (TH_SYN|TH_ACK)) == TH_SYN) { + newage = fr_tcptimeout; + } else if ((flags & TH_OPENING) == TH_SYN) { /* 'dir' sent S, CLOSED -> SYN_SENT */ state[dir] = TCPS_SYN_SENT; - *age = fr_tcptimeout; + newage = fr_tcptimeout; } /* * The next piece of code makes it possible to get @@ -1754,12 +1829,12 @@ int dir; * does not work when a strict 'flags S keep state' is * used for tcp connections of course */ - if ((flags & (TH_FIN|TH_SYN|TH_RST|TH_ACK)) == TH_ACK) { + if (!fsm && (flags & (TH_FIN|TH_SYN|TH_RST|TH_ACK)) == TH_ACK) { /* we saw an A, guess 'dir' is in ESTABLISHED mode */ if (state[1 - dir] == TCPS_CLOSED || state[1 - dir] == TCPS_ESTABLISHED) { state[dir] = TCPS_ESTABLISHED; - *age = fr_tcpidletimeout; + newage = fr_tcpidletimeout; } } /* @@ -1774,14 +1849,24 @@ int dir; break; case TCPS_SYN_SENT: /* 2 */ - if ((flags & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK) { + if (flags == TH_SYN) { + /* + * A retransmitted SYN packet. We do not reset the + * timeout here to fr_tcptimeout because a connection + * connect timeout does not renew after every packet + * that is sent. We need to set newage to something + * to indicate the packet has passed the check for its + * flags being valid in the TCP FSM. + */ + newage = *age; + } else if ((flags & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK) { /* * We see an A from 'dir' which is in SYN_SENT * state: 'dir' sent an A in response to an SA * which it received, SYN_SENT -> ESTABLISHED */ state[dir] = TCPS_ESTABLISHED; - *age = fr_tcpidletimeout; + newage = fr_tcpidletimeout; } else if (flags & TH_FIN) { /* * We see an F from 'dir' which is in SYN_SENT @@ -1789,7 +1874,7 @@ int dir; * connection; SYN_SENT -> FIN_WAIT_1 */ state[dir] = TCPS_FIN_WAIT_1; - *age = fr_tcpidletimeout; /* or fr_tcptimeout? */ + newage = fr_tcpidletimeout; /* or fr_tcptimeout? */ } else if ((flags & TH_OPENING) == TH_OPENING) { /* * We see an SA from 'dir' which is already in @@ -1797,7 +1882,7 @@ int dir; * simultaneous open; SYN_SENT -> SYN_RECEIVED */ state[dir] = TCPS_SYN_RECEIVED; - *age = fr_tcptimeout; + newage = fr_tcptimeout; } break; @@ -1809,7 +1894,7 @@ int dir; * SYN_RECEIVED -> ESTABLISHED */ state[dir] = TCPS_ESTABLISHED; - *age = fr_tcpidletimeout; + newage = fr_tcpidletimeout; } else if (flags & TH_FIN) { /* * We see an F from 'dir' which is in SYN_RECEIVED @@ -1817,7 +1902,7 @@ int dir; * SYN_RECEIVED -> FIN_WAIT_1 */ state[dir] = TCPS_FIN_WAIT_1; - *age = fr_tcpidletimeout; + newage = fr_tcpidletimeout; } break; @@ -1829,7 +1914,7 @@ int dir; * ESTABLISHED -> FIN_WAIT_1 */ state[dir] = TCPS_FIN_WAIT_1; - *age = fr_tcphalfclosed; + newage = fr_tcphalfclosed; } else if (flags & TH_ACK) { /* an ACK, should we exclude other flags here? */ if (ostate == TCPS_FIN_WAIT_1) { @@ -1841,13 +1926,13 @@ int dir; * a half-closed connection */ state[dir] = TCPS_CLOSE_WAIT; - *age = fr_tcphalfclosed; + newage = fr_tcphalfclosed; } else if (ostate < TCPS_CLOSE_WAIT) /* * Still a fully established connection, * reset timeout */ - *age = fr_tcpidletimeout; + newage = fr_tcpidletimeout; } break; @@ -1857,7 +1942,7 @@ int dir; * Application closed and 'dir' sent a FIN, we're now * going into LAST_ACK state */ - *age = fr_tcplastack; + newage = fr_tcplastack; state[dir] = TCPS_LAST_ACK; } else { /* @@ -1865,7 +1950,7 @@ int dir; * closed already and we did not close our side yet; * reset timeout */ - *age = fr_tcphalfclosed; + newage = fr_tcphalfclosed; } break; @@ -1882,14 +1967,14 @@ int dir; * packet here? does the window code guarantee that? */ state[dir] = TCPS_TIME_WAIT; - *age = fr_tcptimeout; + newage = fr_tcptimeout; } else /* * We closed our side of the connection already but the * other side is still active (ESTABLISHED/CLOSE_WAIT); * continue with this half-closed connection */ - *age = fr_tcphalfclosed; + newage = fr_tcphalfclosed; break; case TCPS_CLOSING: /* 7 */ @@ -1903,7 +1988,7 @@ int dir; * There is still data to be delivered, reset * timeout */ - *age = fr_tcplastack; + newage = fr_tcplastack; } /* * We cannot detect when we go out of LAST_ACK state to CLOSED @@ -1918,9 +2003,16 @@ int dir; break; case TCPS_TIME_WAIT: /* 10 */ + newage = fr_tcptimeout; /* default 4 mins */ /* we're in 2MSL timeout now */ break; } + + if (newage != 0) { + *age = newage; + return 0; + } + return -1; } @@ -2070,8 +2162,14 @@ fr_info_t *fin; hv = (pr = oip->ip6_nxt); src.in6 = oip->ip6_src; hv += src.in4.s_addr; + hv += src.i6[1]; + hv += src.i6[2]; + hv += src.i6[3]; dst.in6 = oip->ip6_dst; hv += dst.in4.s_addr; + hv += dst.i6[1]; + hv += dst.i6[2]; + hv += dst.i6[3]; hv += dport; hv += sport; hv %= fr_statesize; diff --git a/sys/contrib/ipfilter/netinet/ip_state.h b/sys/contrib/ipfilter/netinet/ip_state.h index a645868..780e30b 100644 --- a/sys/contrib/ipfilter/netinet/ip_state.h +++ b/sys/contrib/ipfilter/netinet/ip_state.h @@ -43,7 +43,8 @@ typedef struct icmpstate { typedef struct tcpdata { u_32_t td_end; u_32_t td_maxend; - u_short td_maxwin; + u_32_t td_maxwin; + u_char td_wscale; } tcpdata_t; typedef struct tcpstate { @@ -59,20 +60,22 @@ typedef struct ipstate { struct ipstate *is_hnext; struct ipstate **is_phnext; struct ipstate **is_me; - u_long is_age; - u_int is_frage[2]; /* age from filter rule, forward & reverse */ - u_int is_pass; + frentry_t *is_rule; U_QUAD_T is_pkts; U_QUAD_T is_bytes; - void *is_ifp[4]; - frentry_t *is_rule; union i6addr is_src; union i6addr is_dst; + void *is_ifp[4]; + u_long is_age; + u_int is_frage[2]; /* age from filter rule, forward & reverse */ + u_int is_pass; u_char is_p; /* Protocol */ - u_char is_v; - u_int is_hv; + u_char is_v; /* IP version */ + u_char is_fsm; /* 1 = following FSM, 0 = not */ + u_char is_xxx; /* pad */ + u_int is_hv; /* hash value for this in the table */ u_32_t is_rulen; /* rule number */ - u_32_t is_flags; + u_32_t is_flags; /* flags for this structure */ u_32_t is_opt; /* packet options set */ u_32_t is_optmsk; /* " " mask */ u_short is_sec; /* security options set */ @@ -101,6 +104,8 @@ typedef struct ipstate { #define is_dend is_tcp.ts_data[1].td_end #define is_maxswin is_tcp.ts_data[0].td_maxwin #define is_maxdwin is_tcp.ts_data[1].td_maxwin +#define is_swscale is_tcp.ts_data[0].td_wscale +#define is_dwscale is_tcp.ts_data[1].td_wscale #define is_maxsend is_tcp.ts_data[0].td_maxend #define is_maxdend is_tcp.ts_data[1].td_maxend #define is_sport is_tcp.ts_sport @@ -192,7 +197,7 @@ extern ipstate_t *fr_addstate __P((ip_t *, fr_info_t *, ipstate_t **, u_int)); extern frentry_t *fr_checkstate __P((ip_t *, fr_info_t *)); extern void ip_statesync __P((void *)); extern void fr_timeoutstate __P((void)); -extern void fr_tcp_age __P((u_long *, u_char *, fr_info_t *, int)); +extern int fr_tcp_age __P((u_long *, u_char *, fr_info_t *, int, int)); extern void fr_stateunload __P((void)); extern void ipstate_log __P((struct ipstate *, u_int)); #if defined(__NetBSD__) || defined(__OpenBSD__) diff --git a/sys/contrib/ipfilter/netinet/ipl.h b/sys/contrib/ipfilter/netinet/ipl.h index fdb289a..96a2a20 100644 --- a/sys/contrib/ipfilter/netinet/ipl.h +++ b/sys/contrib/ipfilter/netinet/ipl.h @@ -10,6 +10,6 @@ #ifndef __IPL_H__ #define __IPL_H__ -#define IPL_VERSION "IP Filter: v3.4.25" +#define IPL_VERSION "IP Filter: v3.4.26" #endif -- cgit v1.1