diff options
-rw-r--r-- | sys/net/bridgestp.c | 4 | ||||
-rw-r--r-- | sys/net/if.c | 6 | ||||
-rw-r--r-- | sys/net/if_bridge.c | 4 | ||||
-rw-r--r-- | sys/netinet/fil.c | 844 | ||||
-rw-r--r-- | sys/netinet/ip_auth.c | 68 | ||||
-rw-r--r-- | sys/netinet/ip_auth.h | 9 | ||||
-rw-r--r-- | sys/netinet/ip_compat.h (renamed from sys/netinet/ip_fil_compat.h) | 251 | ||||
-rw-r--r-- | sys/netinet/ip_fil.c | 763 | ||||
-rw-r--r-- | sys/netinet/ip_fil.h | 225 | ||||
-rw-r--r-- | sys/netinet/ip_frag.c | 162 | ||||
-rw-r--r-- | sys/netinet/ip_frag.h | 11 | ||||
-rw-r--r-- | sys/netinet/ip_ftp_pxy.c | 678 | ||||
-rw-r--r-- | sys/netinet/ip_log.c | 43 | ||||
-rw-r--r-- | sys/netinet/ip_nat.c | 1529 | ||||
-rw-r--r-- | sys/netinet/ip_nat.h | 138 | ||||
-rw-r--r-- | sys/netinet/ip_proxy.c | 98 | ||||
-rw-r--r-- | sys/netinet/ip_proxy.h | 28 | ||||
-rw-r--r-- | sys/netinet/ip_raudio_pxy.c | 19 | ||||
-rw-r--r-- | sys/netinet/ip_rcmd_pxy.c | 29 | ||||
-rw-r--r-- | sys/netinet/ip_state.c | 1286 | ||||
-rw-r--r-- | sys/netinet/ip_state.h | 75 | ||||
-rw-r--r-- | sys/netinet/ipl.h | 7 |
22 files changed, 4566 insertions, 1711 deletions
diff --git a/sys/net/bridgestp.c b/sys/net/bridgestp.c index 234be26bd34..c1757150cd3 100644 --- a/sys/net/bridgestp.c +++ b/sys/net/bridgestp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bridgestp.c,v 1.2 2000/12/18 03:38:56 mickey Exp $ */ +/* $OpenBSD: bridgestp.c,v 1.3 2001/01/17 04:47:18 fgsch Exp $ */ /* * Copyright (c) 2000 Jason L. Wright (jason@thought.net) @@ -65,7 +65,7 @@ #include <netinet/if_ether.h> #ifdef IPFILTER -#include <netinet/ip_fil_compat.h> +#include <netinet/ip_compat.h> #include <netinet/ip_fil.h> #endif #endif diff --git a/sys/net/if.c b/sys/net/if.c index 907ec88b903..7e9182f3b7d 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if.c,v 1.38 2000/10/07 03:43:16 itojun Exp $ */ +/* $OpenBSD: if.c,v 1.39 2001/01/17 04:47:18 fgsch Exp $ */ /* $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $ */ /* @@ -104,7 +104,7 @@ #endif #ifdef IPFILTER -#include <netinet/ip_fil_compat.h> +#include <netinet/ip_compat.h> #include <netinet/ip_fil.h> #include <netinet/ip_nat.h> #endif @@ -374,7 +374,7 @@ if_detach(ifp) #ifdef IPFILTER /* XXX More ipf & ipnat cleanup needed. */ - nat_ifdetach(ifp); + nat_clearlist(); #endif /* Deallocate private resources. */ diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 5b24f22dc96..21dcd2769ac 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bridge.c,v 1.44 2000/12/30 22:56:23 angelos Exp $ */ +/* $OpenBSD: if_bridge.c,v 1.45 2001/01/17 04:47:18 fgsch Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -64,7 +64,7 @@ #include <net/if_enc.h> #ifdef IPFILTER -#include <netinet/ip_fil_compat.h> +#include <netinet/ip_compat.h> #include <netinet/ip_fil.h> #endif #endif diff --git a/sys/netinet/fil.c b/sys/netinet/fil.c index 828ad128d60..f3f70f32c21 100644 --- a/sys/netinet/fil.c +++ b/sys/netinet/fil.c @@ -1,15 +1,15 @@ -/* $OpenBSD: fil.c,v 1.26 2000/08/10 05:50:25 kjell Exp $ */ +/* $OpenBSD: fil.c,v 1.27 2001/01/17 04:47:11 fgsch Exp $ */ /* - * Copyright (C) 1993-1998 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[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-1996 Darren Reed"; -static const char rcsid[] = "@(#)$IPFilter: fil.c,v 2.3.2.22 2000/07/08 02:13:28 darrenr Exp $"; +static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$IPFilter: fil.c,v 2.35.2.30 2000/12/17 05:49:22 darrenr Exp $"; #endif #include <sys/errno.h> @@ -23,8 +23,13 @@ static const char rcsid[] = "@(#)$IPFilter: fil.c,v 2.3.2.22 2000/07/08 02:13:28 #endif #if (defined(KERNEL) || defined(_KERNEL)) && defined(__FreeBSD_version) && \ (__FreeBSD_version >= 220000) -# if (__FreeBSD_version >= 400019) -# define CSUM_DELAY_DATA +# if (__FreeBSD_version >= 400000) +# ifndef KLD_MODULE +# include "opt_inet6.h" +# endif +# if (__FreeBSD_version == 400019) +# define CSUM_DELAY_DATA +# endif # endif # include <sys/filio.h> # include <sys/fcntl.h> @@ -72,14 +77,20 @@ static const char rcsid[] = "@(#)$IPFilter: fil.c,v 2.3.2.22 2000/07/08 02:13:28 #include <netinet/tcp.h> #include <netinet/udp.h> #include <netinet/ip_icmp.h> -#include <netinet/ip_fil_compat.h> +#include "netinet/ip_compat.h" +#ifdef USE_INET6 +# include <netinet/icmp6.h> +# if !SOLARIS && defined(_KERNEL) +# include <netinet6/in6_var.h> +# endif +#endif #include <netinet/tcpip.h> -#include <netinet/ip_fil.h> -#include <netinet/ip_proxy.h> -#include <netinet/ip_nat.h> -#include <netinet/ip_frag.h> -#include <netinet/ip_state.h> -#include <netinet/ip_auth.h> +#include "netinet/ip_fil.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_state.h" +#include "netinet/ip_auth.h" # if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include <sys/malloc.h> # if defined(_KERNEL) && !defined(IPFILTER_LKM) @@ -89,38 +100,46 @@ static const char rcsid[] = "@(#)$IPFilter: fil.c,v 2.3.2.22 2000/07/08 02:13:28 #ifndef MIN # define MIN(a,b) (((a)<(b))?(a):(b)) #endif -#include <netinet/ipl.h> +#include "netinet/ipl.h" #ifndef _KERNEL # include "ipf.h" # include "ipt.h" extern int opts; -# define FR_IFVERBOSE(ex,second,verb_pr) if (ex) { verbose verb_pr; \ - second; } -# define FR_IFDEBUG(ex,second,verb_pr) if (ex) { debug verb_pr; \ - second; } # define FR_VERBOSE(verb_pr) verbose verb_pr # define FR_DEBUG(verb_pr) debug verb_pr # define IPLLOG(a, c, d, e) ipllog() #else /* #ifndef _KERNEL */ -# define FR_IFVERBOSE(ex,second,verb_pr) ; -# define FR_IFDEBUG(ex,second,verb_pr) ; # define FR_VERBOSE(verb_pr) # define FR_DEBUG(verb_pr) # define IPLLOG(a, c, d, e) ipflog(a, c, d, e) # if SOLARIS || defined(__sgi) extern KRWLOCK_T ipf_mutex, ipf_auth, ipf_nat; extern kmutex_t ipf_rw; +# endif +# if SOLARIS +# define FR_NEWAUTH(m, fi, ip, qif) fr_newauth((mb_t *)m, fi, \ + ip, qif) +# else /* SOLARIS */ +# define FR_NEWAUTH(m, fi, ip, qif) fr_newauth((mb_t *)m, fi, ip) # endif /* SOLARIS || __sgi */ #endif /* _KERNEL */ struct filterstats frstats[2] = {{0,0,0,0,0},{0,0,0,0,0}}; struct frentry *ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } }, +#ifdef USE_INET6 + *ipfilter6[2][2] = { { NULL, NULL }, { NULL, NULL } }, + *ipacct6[2][2] = { { NULL, NULL }, { NULL, NULL } }, +#endif *ipacct[2][2] = { { NULL, NULL }, { NULL, NULL } }; struct frgroup *ipfgroups[3][2]; -int fr_flags = IPF_LOGGING, fr_active = 0; +int fr_flags = IPF_LOGGING; +int fr_active = 0; +int fr_chksrc = 0; +int fr_minttl = 3; +int fr_minttllog = 1; #if defined(IPFILTER_DEFAULT_BLOCK) int fr_pass = FR_NOMATCH|FR_BLOCK; #else @@ -130,7 +149,6 @@ char ipfilter_version[] = IPL_VERSION; fr_info_t frcache[2]; -static int fr_tcpudpchk __P((frentry_t *, fr_info_t *)); static int frflushlist __P((int, minor_t, int *, frentry_t **)); #ifdef _KERNEL static void frsynclist __P((frentry_t *)); @@ -187,12 +205,12 @@ int hlen; ip_t *ip; fr_info_t *fin; { - struct optlist *op; - tcphdr_t *tcp; - fr_ip_t *fi = &fin->fin_fi; u_short optmsk = 0, secmsk = 0, auth = 0; - int i, mv, ol, off; + int i, mv, ol, off, p, plen, v; + fr_ip_t *fi = &fin->fin_fi; + struct optlist *op; u_char *s, opt; + tcphdr_t *tcp; fin->fin_rev = 0; fin->fin_fr = NULL; @@ -201,67 +219,160 @@ fr_info_t *fin; fin->fin_data[1] = 0; fin->fin_rule = -1; fin->fin_group = -1; - fin->fin_id = ip->ip_id; #ifdef _KERNEL fin->fin_icode = ipl_unreach; #endif - fi->fi_v = ip->ip_v; - fi->fi_tos = ip->ip_tos; + v = fin->fin_v; + fi->fi_v = v; fin->fin_hlen = hlen; - fin->fin_dlen = ip->ip_len - hlen; - tcp = (tcphdr_t *)((char *)ip + hlen); + if (v == 4) { + fin->fin_id = ip->ip_id; + fi->fi_tos = ip->ip_tos; + off = (ip->ip_off & IP_OFFMASK) << 3; + tcp = (tcphdr_t *)((char *)ip + hlen); + (*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4)); + fi->fi_src.i6[1] = 0; + fi->fi_src.i6[2] = 0; + fi->fi_src.i6[3] = 0; + fi->fi_dst.i6[1] = 0; + fi->fi_dst.i6[2] = 0; + fi->fi_dst.i6[3] = 0; + fi->fi_saddr = ip->ip_src.s_addr; + fi->fi_daddr = ip->ip_dst.s_addr; + p = ip->ip_p; + fi->fi_fl = (hlen > sizeof(ip_t)) ? FI_OPTIONS : 0; + if (ip->ip_off & 0x3fff) + fi->fi_fl |= FI_FRAG; + plen = ip->ip_len; + fin->fin_dlen = plen - hlen; + } +#ifdef USE_INET6 + else if (v == 6) { + ip6_t *ip6 = (ip6_t *)ip; + + off = 0; + p = ip6->ip6_nxt; + fi->fi_p = p; + fi->fi_ttl = ip6->ip6_hlim; + tcp = (tcphdr_t *)(ip6 + 1); + fi->fi_src.in6 = ip6->ip6_src; + fi->fi_dst.in6 = ip6->ip6_dst; + fin->fin_id = (u_short)(ip6->ip6_flow & 0xffff); + fi->fi_tos = 0; + fi->fi_fl = 0; + plen = ntohs(ip6->ip6_plen); + fin->fin_dlen = plen; + } +#endif + else + return; + + fin->fin_off = off; + fin->fin_plen = plen; fin->fin_dp = (void *)tcp; - (*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4)); - fi->fi_src.s_addr = ip->ip_src.s_addr; - fi->fi_dst.s_addr = ip->ip_dst.s_addr; - - fi->fi_fl = (hlen > sizeof(ip_t)) ? FI_OPTIONS : 0; - off = (ip->ip_off & IP_OFFMASK) << 3; - if (ip->ip_off & 0x3fff) - fi->fi_fl |= FI_FRAG; - switch (ip->ip_p) + + switch (p) { +#ifdef USE_INET6 + case IPPROTO_ICMPV6 : + { + int minicmpsz = sizeof(struct icmp6_hdr); + struct icmp6_hdr *icmp6; + + if (fin->fin_dlen > 1) { + fin->fin_data[0] = *(u_short *)tcp; + + icmp6 = (struct icmp6_hdr *)tcp; + + switch (icmp6->icmp6_type) + { + case ICMP6_ECHO_REPLY : + case ICMP6_ECHO_REQUEST : + minicmpsz = ICMP6ERR_MINPKTLEN; + break; + case ICMP6_DST_UNREACH : + case ICMP6_PACKET_TOO_BIG : + case ICMP6_TIME_EXCEEDED : + case ICMP6_PARAM_PROB : + minicmpsz = ICMP6ERR_IPICMPHLEN; + break; + default : + break; + } + } + + if (!(plen >= hlen + minicmpsz)) + fi->fi_fl |= FI_SHORT; + + break; + } +#endif case IPPROTO_ICMP : { int minicmpsz = sizeof(struct icmp); icmphdr_t *icmp; - icmp = (icmphdr_t *)tcp; - - if (!off && (icmp->icmp_type == ICMP_ECHOREPLY || - icmp->icmp_type == ICMP_ECHO)) - minicmpsz = ICMP_MINLEN; - - if (!off && (icmp->icmp_type == ICMP_TSTAMP || - icmp->icmp_type == ICMP_TSTAMPREPLY)) - minicmpsz = 20; -/* type(1) + code(1) + cksum(2) + id(2) + seq(2) + 3*timestamp(3*4) */ + if (!off && (fin->fin_dlen > 1)) { + fin->fin_data[0] = *(u_short *)tcp; - if (!off && (icmp->icmp_type == ICMP_MASKREQ || - icmp->icmp_type == ICMP_MASKREPLY)) - minicmpsz = 12; -/* type(1) + code(1) + cksum(2) + id(2) + seq(2) + mask(4) */ + icmp = (icmphdr_t *)tcp; + + if (icmp->icmp_type == ICMP_ECHOREPLY || + icmp->icmp_type == ICMP_ECHO) + minicmpsz = ICMP_MINLEN; + + /* + * type(1) + code(1) + cksum(2) + id(2) seq(2) + + * 3*timestamp(3*4) + */ + else if (icmp->icmp_type == ICMP_TSTAMP || + icmp->icmp_type == ICMP_TSTAMPREPLY) + minicmpsz = 20; + + /* + * type(1) + code(1) + cksum(2) + id(2) seq(2) + + * mask(4) + */ + else if (icmp->icmp_type == ICMP_MASKREQ || + icmp->icmp_type == ICMP_MASKREPLY) + minicmpsz = 12; + } - if ((!(ip->ip_len >= hlen + minicmpsz) && !off) || + if ((!(plen >= hlen + minicmpsz) && !off) || (off && off < sizeof(struct icmp))) fi->fi_fl |= FI_SHORT; - if (fin->fin_dlen > 1) - fin->fin_data[0] = *(u_short *)tcp; + break; } case IPPROTO_TCP : fi->fi_fl |= FI_TCPUDP; - if ((!IPMINLEN(ip, tcphdr) && !off) || - (off && off < sizeof(struct tcphdr))) - fi->fi_fl |= FI_SHORT; +#ifdef USE_INET6 + if (v == 6) { + if (plen < sizeof(struct tcphdr)) + fi->fi_fl |= FI_SHORT; + } else +#endif + if (v == 4) { + if ((!IPMINLEN(ip, tcphdr) && !off) || + (off && off < sizeof(struct tcphdr))) + fi->fi_fl |= FI_SHORT; + } if (!(fi->fi_fl & FI_SHORT) && !off) fin->fin_tcpf = tcp->th_flags; goto getports; case IPPROTO_UDP : fi->fi_fl |= FI_TCPUDP; - if ((!IPMINLEN(ip, udphdr) && !off) || - (off && off < sizeof(struct udphdr))) - fi->fi_fl |= FI_SHORT; +#ifdef USE_INET6 + if (v == 6) { + if (plen < sizeof(struct udphdr)) + fi->fi_fl |= FI_SHORT; + } else +#endif + if (v == 4) { + if ((!IPMINLEN(ip, udphdr) && !off) || + (off && off < sizeof(struct udphdr))) + fi->fi_fl |= FI_SHORT; + } getports: if (!off && (fin->fin_dlen > 3)) { fin->fin_data[0] = ntohs(tcp->th_sport); @@ -272,6 +383,14 @@ getports: break; } +#ifdef USE_INET6 + if (v == 6) { + fi->fi_optmsk = 0; + fi->fi_secmsk = 0; + fi->fi_auth = 0; + return; + } +#endif for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) { opt = *s; @@ -332,8 +451,8 @@ getports: /* * check an IP packet for TCP/UDP characteristics such as ports and flags. */ -static int fr_tcpudpchk(fr, fin) -frentry_t *fr; +int fr_tcpudpchk(ft, fin) +frtuc_t *ft; fr_info_t *fin; { register u_short po, tup; @@ -346,8 +465,8 @@ fr_info_t *fin; * * compare destination ports */ - if ((i = (int)fr->fr_dcmp)) { - po = fr->fr_dport; + if ((i = (int)ft->ftu_dcmp)) { + po = ft->ftu_dport; tup = fin->fin_data[1]; /* * Do opposite test to that required and @@ -366,17 +485,17 @@ fr_info_t *fin; else if (!--i && tup < po) /* GT or EQ */ err = 0; else if (!--i && /* Out of range */ - (tup >= po && tup <= fr->fr_dtop)) + (tup >= po && tup <= ft->ftu_dtop)) err = 0; else if (!--i && /* In range */ - (tup <= po || tup >= fr->fr_dtop)) + (tup <= po || tup >= ft->ftu_dtop)) err = 0; } /* * compare source ports */ - if (err && (i = (int)fr->fr_scmp)) { - po = fr->fr_sport; + if (err && (i = (int)ft->ftu_scmp)) { + po = ft->ftu_sport; tup = fin->fin_data[0]; if (!--i && tup != po) err = 0; @@ -391,10 +510,10 @@ fr_info_t *fin; else if (!--i && tup < po) err = 0; else if (!--i && /* Out of range */ - (tup >= po && tup <= fr->fr_stop)) + (tup >= po && tup <= ft->ftu_stop)) err = 0; else if (!--i && /* In range */ - (tup <= po || tup >= fr->fr_stop)) + (tup <= po || tup >= ft->ftu_stop)) err = 0; } @@ -406,14 +525,14 @@ fr_info_t *fin; */ if (err && (fin->fin_fi.fi_p == IPPROTO_TCP)) { if (fin->fin_fi.fi_fl & FI_SHORT) - return !(fr->fr_tcpf | fr->fr_tcpfm); + return !(ft->ftu_tcpf | ft->ftu_tcpfm); /* * Match the flags ? If not, abort this match. */ - if (fr->fr_tcpfm && - fr->fr_tcpf != (fin->fin_tcpf & fr->fr_tcpfm)) { + if (ft->ftu_tcpfm && + ft->ftu_tcpf != (fin->fin_tcpf & ft->ftu_tcpfm)) { FR_DEBUG(("f. %#x & %#x != %#x\n", fin->fin_tcpf, - fr->fr_tcpfm, fr->fr_tcpf)); + ft->ftu_tcpfm, ft->ftu_tcpf)); err = 0; } } @@ -440,7 +559,10 @@ void *m; fin->fin_fr = NULL; fin->fin_rule = 0; fin->fin_group = 0; - off = ip->ip_off & IP_OFFMASK; + if (fin->fin_v == 4) + off = ip->ip_off & IP_OFFMASK; + else + off = 0; pass |= (fi->fi_fl << 24); if ((fi->fi_fl & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) @@ -484,23 +606,77 @@ void *m; lip = (u_32_t *)fi; lm = (u_32_t *)&fr->fr_mip; ld = (u_32_t *)&fr->fr_ip; - i = ((lip[0] & lm[0]) != ld[0]); - FR_IFDEBUG(i,continue,("0. %#08x & %#08x != %#08x\n", - lip[0], lm[0], ld[0])); - i |= ((lip[1] & lm[1]) != ld[1]) << 19; + i = ((*lip & *lm) != *ld); + FR_DEBUG(("0. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); + if (i) + continue; + /* + * We now know whether the packet version and the + * rule version match, along with protocol, ttl and + * tos. + */ + lip++, lm++, ld++; + /* + * Unrolled loops (4 each, for 32 bits). + */ + i |= ((*lip & *lm) != *ld) << 19; + FR_DEBUG(("1a. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); + if (fi->fi_v == 6) { + lip++, lm++, ld++; + i |= ((*lip & *lm) != *ld) << 19; + FR_DEBUG(("1b. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); + lip++, lm++, ld++; + i |= ((*lip & *lm) != *ld) << 19; + FR_DEBUG(("1c. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); + lip++, lm++, ld++; + i |= ((*lip & *lm) != *ld) << 19; + FR_DEBUG(("1d. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); + } else { + lip += 3; + lm += 3; + ld += 3; + } i ^= (fr->fr_flags & FR_NOTSRCIP); - FR_IFDEBUG(i,continue,("1. %#08x & %#08x != %#08x\n", - lip[1], lm[1], ld[1])); - i |= ((lip[2] & lm[2]) != ld[2]) << 20; + if (i) + continue; + lip++, lm++, ld++; + i |= ((*lip & *lm) != *ld) << 20; + FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); + if (fi->fi_v == 6) { + lip++, lm++, ld++; + i |= ((*lip & *lm) != *ld) << 20; + FR_DEBUG(("2b. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); + lip++, lm++, ld++; + i |= ((*lip & *lm) != *ld) << 20; + FR_DEBUG(("2c. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); + lip++, lm++, ld++; + i |= ((*lip & *lm) != *ld) << 20; + FR_DEBUG(("2d. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); + } else { + lip += 3; + lm += 3; + ld += 3; + } i ^= (fr->fr_flags & FR_NOTDSTIP); - FR_IFDEBUG(i,continue,("2. %#08x & %#08x != %#08x\n", - lip[2], lm[2], ld[2])); - i |= ((lip[3] & lm[3]) != ld[3]); - FR_IFDEBUG(i,continue,("3. %#08x & %#08x != %#08x\n", - lip[3], lm[3], ld[3])); - i |= ((lip[4] & lm[4]) != ld[4]); - FR_IFDEBUG(i,continue,("4. %#08x & %#08x != %#08x\n", - lip[4], lm[4], ld[4])); + if (i) + continue; + lip++, lm++, ld++; + i |= ((*lip & *lm) != *ld); + FR_DEBUG(("3. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); + lip++, lm++, ld++; + i |= ((*lip & *lm) != *ld); + FR_DEBUG(("4. %#08x & %#08x != %#08x\n", + *lip, *lm, *ld)); if (i) continue; } @@ -513,7 +689,7 @@ void *m; fr->fr_tcpfm)) continue; if (fi->fi_fl & FI_TCPUDP) { - if (!fr_tcpudpchk(fr, fin)) + if (!fr_tcpudpchk(&fr->fr_tuc, fin)) continue; } else if (fr->fr_icmpm || fr->fr_icmp) { if ((fi->fi_p != IPPROTO_ICMP) || off || @@ -531,24 +707,27 @@ void *m; * Just log this packet... */ passt = fr->fr_flags; - if ((passt & FR_CALLNOW) && fr->fr_func) - passt = (*fr->fr_func)(passt, ip, fin); +#if (BSD >= 199306) && (defined(_KERNEL) || defined(KERNEL)) + if (securelevel <= 0) +#endif + if ((passt & FR_CALLNOW) && fr->fr_func) + passt = (*fr->fr_func)(passt, ip, fin); fin->fin_fr = fr; #ifdef IPFILTER_LOG if ((passt & FR_LOGMASK) == FR_LOG) { if (!IPLLOG(passt, ip, fin, m)) { if (passt & FR_LOGORBLOCK) passt |= FR_BLOCK|FR_QUICK; - ATOMIC_INC(frstats[fin->fin_out].fr_skip); + ATOMIC_INCL(frstats[fin->fin_out].fr_skip); } - ATOMIC_INC(frstats[fin->fin_out].fr_pkl); + ATOMIC_INCL(frstats[fin->fin_out].fr_pkl); logged = 1; } #endif /* IPFILTER_LOG */ if (!(skip = fr->fr_skip) && (passt & FR_LOGMASK) != FR_LOG) pass = passt; FR_DEBUG(("pass %#x\n", pass)); - ATOMIC_INC(fr->fr_hits); + ATOMIC_INCL(fr->fr_hits); if (pass & FR_ACCOUNT) fr->fr_bytes += (U_QUAD_T)ip->ip_len; else @@ -598,14 +777,15 @@ int out; */ fr_info_t frinfo, *fc; register fr_info_t *fin = &frinfo; - frentry_t *fr = NULL; - int changed, error = EHOSTUNREACH; + int changed, error = EHOSTUNREACH, v = ip->ip_v; + frentry_t *fr = NULL, *list; u_32_t pass, apass; #if !SOLARIS || !defined(_KERNEL) register mb_t *m = *mp; #endif #ifdef _KERNEL + int p, len, drop = 0, logit = 0; mb_t *mc = NULL; # if !defined(__SVR4) && !defined(__svr4__) # ifdef __sgi @@ -631,13 +811,26 @@ int out; } # endif /* CSUM_DELAY_DATA */ +# ifdef USE_INET6 + if (v == 6) { + len = ntohs(((ip6_t*)ip)->ip6_plen); + p = ((ip6_t *)ip)->ip6_nxt; + } else +# endif + { + p = ip->ip_p; + len = ip->ip_len; + } - if ((ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP || - ip->ip_p == IPPROTO_ICMP)) { + if ((p == IPPROTO_TCP || p == IPPROTO_UDP || p == IPPROTO_ICMP +# ifdef USE_INET6 + || (v == 6 && p == IPPROTO_ICMPV6) +# endif + )) { int plen = 0; - if ((ip->ip_off & IP_OFFMASK) == 0) - switch(ip->ip_p) + if ((v == 6) || (ip->ip_off & IP_OFFMASK) == 0) + switch(p) { case IPPROTO_TCP: plen = sizeof(tcphdr_t); @@ -649,26 +842,35 @@ int out; case IPPROTO_ICMP: plen = ICMPERR_MAXPKTLEN - sizeof(ip_t); break; +# ifdef USE_INET6 + case IPPROTO_ICMPV6 : + /* + * XXX does not take intermediate header + * into account + */ + plen = ICMP6ERR_MINPKTLEN + 8 - sizeof(ip6_t); + break; +# endif } - up = MIN(hlen + plen, ip->ip_len); + up = MIN(hlen + plen, len); if (up > m->m_len) { # ifdef __sgi /* Under IRIX, avoid m_pullup as it makes ping <hostname> panic */ if ((up > sizeof(hbuf)) || (m_length(m) < up)) { - ATOMIC_INC(frstats[out].fr_pull[1]); + ATOMIC_INCL(frstats[out].fr_pull[1]); return -1; } m_copydata(m, 0, up, hbuf); - ATOMIC_INC(frstats[out].fr_pull[0]); + ATOMIC_INCL(frstats[out].fr_pull[0]); ip = (ip_t *)hbuf; # else /* __ sgi */ # ifndef linux if ((*mp = m_pullup(m, up)) == 0) { - ATOMIC_INC(frstats[out].fr_pull[1]); + ATOMIC_INCL(frstats[out].fr_pull[1]); return -1; } else { - ATOMIC_INC(frstats[out].fr_pull[0]); + ATOMIC_INCL(frstats[out].fr_pull[0]); m = *mp; ip = mtod(m, ip_t *); } @@ -688,23 +890,66 @@ int out; fin->fin_qif = qif; # endif #endif /* _KERNEL */ - + /* * Be careful here: ip_id is in network byte order when called * from ip_output() */ - if (out) + if ((out) && (v == 4)) ip->ip_id = ntohs(ip->ip_id); - fr_makefrip(hlen, ip, fin); + + changed = 0; fin->fin_ifp = ifp; + fin->fin_v = v; fin->fin_out = out; fin->fin_mp = mp; + fr_makefrip(hlen, ip, fin); + +#ifdef _KERNEL +# ifdef USE_INET6 + if (v == 6) { + ATOMIC_INCL(frstats[0].fr_ipv6[out]); + if (((ip6_t *)ip)->ip6_hlim < fr_minttl) { + ATOMIC_INCL(frstats[0].fr_badttl); + if (fr_minttllog) + logit = -2; + } + } else +# endif + if (!out) { + if (fr_chksrc && !fr_verifysrc(ip->ip_src, ifp)) { + ATOMIC_INCL(frstats[0].fr_badsrc); + if (fr_chksrc == 2) + logit = -2; + } else if (ip->ip_ttl < fr_minttl) { + ATOMIC_INCL(frstats[0].fr_badttl); + if (fr_minttllog) + logit = -3; + } + } + if (drop) { +# ifdef IPFILTER_LOG + if (logit) { + fin->fin_group = logit; + pass = FR_INQUE|FR_NOMATCH|FR_LOGB; + (void) IPLLOG(pass, ip, fin, m); + } +# endif +# if !SOLARIS + m_freem(m); +# endif + return error; + } +#endif pass = fr_pass; + if (fin->fin_fi.fi_fl & FI_SHORT) { + ATOMIC_INCL(frstats[out].fr_short); + } READ_ENTER(&ipf_mutex); if (fin->fin_fi.fi_fl & FI_SHORT) - ATOMIC_INC(frstats[out].fr_short); + ATOMIC_INCL(frstats[out].fr_short); /* * Check auth now. This, combined with the check below to see if apass @@ -716,10 +961,16 @@ int out; apass = fr_checkauth(ip, fin); if (!out) { +#ifdef USE_INET6 + if (v == 6) + list = ipacct6[0][fr_active]; + else +#endif + list = ipacct[0][fr_active]; changed = ip_natin(ip, fin); - if (!apass && (fin->fin_fr = ipacct[0][fr_active]) && + if (!apass && (fin->fin_fr = list) && (fr_scanlist(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT)) { - ATOMIC_INC(frstats[0].fr_acct); + ATOMIC_INCL(frstats[0].fr_acct); } } @@ -738,19 +989,25 @@ int out; * earlier. */ bcopy((char *)fc, (char *)fin, FI_COPYSIZE); - ATOMIC_INC(frstats[out].fr_chit); + ATOMIC_INCL(frstats[out].fr_chit); if ((fr = fin->fin_fr)) { - ATOMIC_INC(fr->fr_hits); + ATOMIC_INCL(fr->fr_hits); pass = fr->fr_flags; } } else { - if ((fin->fin_fr = ipfilter[out][fr_active])) +#ifdef USE_INET6 + if (v == 6) + list = ipfilter6[out][fr_active]; + else +#endif + list = ipfilter[out][fr_active]; + if ((fin->fin_fr = list)) pass = fr_scanlist(fr_pass, ip, fin, m); if (!(pass & (FR_KEEPSTATE|FR_DONTCACHE))) bcopy((char *)fin, (char *)fc, FI_COPYSIZE); if (pass & FR_NOMATCH) { - ATOMIC_INC(frstats[out].fr_nom); + ATOMIC_INCL(frstats[out].fr_nom); } } fr = fin->fin_fr; @@ -774,9 +1031,9 @@ int out; READ_ENTER(&ipf_auth); if ((fin->fin_fr = ipauth) && (pass = fr_scanlist(0, ip, fin, m))) { - ATOMIC_INC(fr_authstats.fas_hits); + ATOMIC_INCL(fr_authstats.fas_hits); } else { - ATOMIC_INC(fr_authstats.fas_miss); + ATOMIC_INCL(fr_authstats.fas_miss); } RWLOCK_EXIT(&ipf_auth); } @@ -785,19 +1042,19 @@ int out; if ((pass & (FR_KEEPFRAG|FR_KEEPSTATE)) == FR_KEEPFRAG) { if (fin->fin_fi.fi_fl & FI_FRAG) { if (ipfr_newfrag(ip, fin, pass) == -1) { - ATOMIC_INC(frstats[out].fr_bnfr); + ATOMIC_INCL(frstats[out].fr_bnfr); } else { - ATOMIC_INC(frstats[out].fr_nfr); + ATOMIC_INCL(frstats[out].fr_nfr); } } else { - ATOMIC_INC(frstats[out].fr_cfr); + ATOMIC_INCL(frstats[out].fr_cfr); } } if (pass & FR_KEEPSTATE) { if (fr_addstate(ip, fin, 0) == NULL) { - ATOMIC_INC(frstats[out].fr_bads); + ATOMIC_INCL(frstats[out].fr_bads); } else { - ATOMIC_INC(frstats[out].fr_ads); + ATOMIC_INCL(frstats[out].fr_ads); } } } else if (fr != NULL) { @@ -806,17 +1063,26 @@ int out; pass &= ~(FR_LOGFIRST|FR_LOG); } - if (fr && fr->fr_func && !(pass & FR_CALLNOW)) - pass = (*fr->fr_func)(pass, ip, fin); +#if (BSD >= 199306) && (defined(_KERNEL) || defined(KERNEL)) + if (securelevel <= 0) +#endif + if (fr && fr->fr_func && !(pass & FR_CALLNOW)) + pass = (*fr->fr_func)(pass, ip, fin); /* * Only count/translate packets which will be passed on, out the * interface. */ if (out && (pass & FR_PASS)) { - if ((fin->fin_fr = ipacct[1][fr_active]) && +#ifdef USE_INET6 + if (v == 6) + list = ipacct6[1][fr_active]; + else +#endif + list = ipacct[1][fr_active]; + if ((fin->fin_fr = list) && (fr_scanlist(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT)) { - ATOMIC_INC(frstats[1].fr_acct); + ATOMIC_INCL(frstats[1].fr_acct); } fin->fin_fr = fr; changed = ip_natout(ip, fin); @@ -828,22 +1094,22 @@ int out; if ((fr_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { if ((fr_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { pass |= FF_LOGNOMATCH; - ATOMIC_INC(frstats[out].fr_npkl); + ATOMIC_INCL(frstats[out].fr_npkl); goto logit; } else if (((pass & FR_LOGMASK) == FR_LOGP) || ((pass & FR_PASS) && (fr_flags & FF_LOGPASS))) { if ((pass & FR_LOGMASK) != FR_LOGP) pass |= FF_LOGPASS; - ATOMIC_INC(frstats[out].fr_ppkl); + ATOMIC_INCL(frstats[out].fr_ppkl); goto logit; } else if (((pass & FR_LOGMASK) == FR_LOGB) || ((pass & FR_BLOCK) && (fr_flags & FF_LOGBLOCK))) { if ((pass & FR_LOGMASK) != FR_LOGB) pass |= FF_LOGBLOCK; - ATOMIC_INC(frstats[out].fr_bpkl); + ATOMIC_INCL(frstats[out].fr_bpkl); logit: if (!IPLLOG(pass, ip, fin, m)) { - ATOMIC_INC(frstats[out].fr_skip); + ATOMIC_INCL(frstats[out].fr_skip); if ((pass & (FR_PASS|FR_LOGORBLOCK)) == (FR_PASS|FR_LOGORBLOCK)) pass ^= FR_PASS|FR_BLOCK; @@ -852,7 +1118,7 @@ logit: } #endif /* IPFILTER_LOG */ - if (out) + if ((out) && (v == 4)) ip->ip_id = htons(ip->ip_id); #ifdef _KERNEL @@ -873,9 +1139,9 @@ logit: # endif #endif if (pass & FR_PASS) { - ATOMIC_INC(frstats[out].fr_pass); + ATOMIC_INCL(frstats[out].fr_pass); } else if (pass & FR_BLOCK) { - ATOMIC_INC(frstats[out].fr_block); + ATOMIC_INCL(frstats[out].fr_block); /* * Should we return an ICMP packet to indicate error * status passing through the packet filter ? @@ -887,31 +1153,31 @@ logit: if (!out) { #ifdef _KERNEL if (pass & FR_RETICMP) { - struct in_addr dst; + int dst; if ((pass & FR_RETMASK) == FR_FAKEICMP) - dst = ip->ip_dst; + dst = 1; else - dst.s_addr = 0; + dst = 0; send_icmp_err(ip, ICMP_UNREACH, fin, dst); - ATOMIC_INC(frstats[0].fr_ret); + ATOMIC_INCL(frstats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if (send_reset(ip, fin) == 0) { - ATOMIC_INC(frstats[1].fr_ret); + ATOMIC_INCL(frstats[1].fr_ret); } } #else if ((pass & FR_RETMASK) == FR_RETICMP) { verbose("- ICMP unreachable sent\n"); - ATOMIC_INC(frstats[0].fr_ret); + ATOMIC_INCL(frstats[0].fr_ret); } else if ((pass & FR_RETMASK) == FR_FAKEICMP) { verbose("- forged ICMP unreachable sent\n"); - ATOMIC_INC(frstats[0].fr_ret); + ATOMIC_INCL(frstats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_fi.fi_fl & FI_SHORT)) { verbose("- TCP RST sent\n"); - ATOMIC_INC(frstats[1].fr_ret); + ATOMIC_INCL(frstats[1].fr_ret); } #endif } else { @@ -927,6 +1193,10 @@ logit: * Once we're finished return to our caller, freeing the packet if * we are dropping it (* BSD ONLY *). */ + if ((changed == -1) && (pass & FR_PASS)) { + pass &= ~FR_PASS; + pass |= FR_BLOCK; + } #if defined(_KERNEL) # if !SOLARIS # if !defined(linux) @@ -954,11 +1224,11 @@ logit: if (((pass & FR_FASTROUTE) && !out) || (fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) { - if (ipfr_fastroute(qif, ip, m, mp, fin, fdp) == 0) + if (ipfr_fastroute(ip, m, mp, fin, fdp) == 0) m = *mp = NULL; } if (mc) - ipfr_fastroute(qif, ip, mc, mp, fin, &fr->fr_dif); + ipfr_fastroute(ip, mc, mp, fin, &fr->fr_dif); } # endif /* !SOLARIS */ return (pass & FR_PASS) ? 0 : error; @@ -1190,7 +1460,7 @@ nodata: * SUCH DAMAGE. * * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 - * $IPFilter: fil.c,v 2.3.2.22 2000/07/08 02:13:28 darrenr Exp $ + * $IPFilter: fil.c,v 2.35.2.30 2000/12/17 05:49:22 darrenr Exp $ */ /* * Copy data from an mbuf chain starting "off" bytes from the beginning, @@ -1290,8 +1560,7 @@ out: frgroup_t *fr_findgroup(num, flags, which, set, fgpp) -u_int num; -u_32_t flags; +u_32_t num, flags; minor_t which; int set; frgroup_t ***fgpp; @@ -1320,7 +1589,7 @@ frgroup_t ***fgpp; frgroup_t *fr_addgroup(num, fp, which, set) -u_int num; +u_32_t num; frentry_t *fp; minor_t which; int set; @@ -1332,7 +1601,7 @@ int set; KMALLOC(fg, frgroup_t *); if (fg) { - fg->fg_num = num & 0xffff; + fg->fg_num = num; fg->fg_next = *fgp; fg->fg_head = fp; fg->fg_start = &fp->fr_grp; @@ -1343,8 +1612,7 @@ int set; void fr_delgroup(num, flags, which, set) -u_int num; -u_32_t flags; +u_32_t num, flags; minor_t which; int set; { @@ -1382,11 +1650,11 @@ frentry_t **listp; MUTEX_EXIT(&ipf_rw); } - ATOMIC_DEC(fp->fr_ref); + ATOMIC_DEC32(fp->fr_ref); if (fp->fr_grhead) { - fr_delgroup((u_int)fp->fr_grhead, fp->fr_flags, + fr_delgroup(fp->fr_grhead, fp->fr_flags, unit, set); - fp->fr_grhead = NULL; + fp->fr_grhead = 0; } if (fp->fr_ref == 0) { KFREE(fp); @@ -1415,10 +1683,18 @@ int flags; set = 1 - set; if (flags & FR_OUTQUE) { +#ifdef USE_INET6 + (void) frflushlist(set, unit, &flushed, &ipfilter6[1][set]); + (void) frflushlist(set, unit, &flushed, &ipacct6[1][set]); +#endif (void) frflushlist(set, unit, &flushed, &ipfilter[1][set]); (void) frflushlist(set, unit, &flushed, &ipacct[1][set]); } if (flags & FR_INQUE) { +#ifdef USE_INET6 + (void) frflushlist(set, unit, &flushed, &ipfilter6[0][set]); + (void) frflushlist(set, unit, &flushed, &ipacct6[0][set]); +#endif (void) frflushlist(set, unit, &flushed, &ipfilter[0][set]); (void) frflushlist(set, unit, &flushed, &ipacct[0][set]); } @@ -1497,10 +1773,14 @@ u_32_t ip; /* * return the first IP Address associated with an interface */ -int fr_ifpaddr(ifptr, inp) +int fr_ifpaddr(v, ifptr, inp) +int v; void *ifptr; struct in_addr *inp; { +# ifdef USE_INET6 + struct in6_addr *inp6 = NULL; +# endif # if SOLARIS ill_t *ill = ifptr; # else @@ -1509,13 +1789,30 @@ struct in_addr *inp; struct in_addr in; # if SOLARIS - in.s_addr = ill->ill_ipif->ipif_local_addr; +# ifdef USE_INET6 + if (v == 6) { + struct in6_addr in6; + + /* + * First is always link local. + */ + if (ill->ill_ipif->ipif_next) + in6 = ill->ill_ipif->ipif_next->ipif_v6lcl_addr; + else + bzero((char *)&in6, sizeof(in6)); + bcopy((char *)&in6, (char *)inp, sizeof(in6)); + } else +# endif + { + in.s_addr = ill->ill_ipif->ipif_local_addr; + *inp = in; + } # else /* SOLARIS */ # if linux ; # else /* linux */ - struct ifaddr *ifa; struct sockaddr_in *sin; + struct ifaddr *ifa; # if (__FreeBSD_version >= 300000) ifa = TAILQ_FIRST(&ifp->if_addrhead); @@ -1534,8 +1831,17 @@ struct in_addr *inp; sin = (struct sockaddr_in *)&ifa->ifa_addr; # else sin = (struct sockaddr_in *)ifa->ifa_addr; - while (sin && ifa && - sin->sin_family != AF_INET) { + while (sin && ifa) { + if ((v == 4) && (sin->sin_family == AF_INET)) + break; +# ifdef USE_INET6 + if ((v == 6) && (sin->sin_family == AF_INET6)) { + inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr; + if (!IN6_IS_ADDR_LINKLOCAL(inp6) && + !IN6_IS_ADDR_LOOPBACK(inp6)) + break; + } +# endif # if (__FreeBSD_version >= 300000) ifa = TAILQ_NEXT(ifa, ifa_link); # else @@ -1553,10 +1859,17 @@ struct in_addr *inp; if (sin == NULL) return -1; # endif /* (BSD < 199306) && (!__sgi && IFF_DRVLOCK) */ - in = sin->sin_addr; +# ifdef USE_INET6 + if (v == 6) + bcopy((char *)inp6, (char *)inp, sizeof(*inp6)); + else +# endif + { + in = sin->sin_addr; + *inp = in; + } # endif /* linux */ # endif /* SOLARIS */ - *inp = in; return 0; } @@ -1566,7 +1879,7 @@ register frentry_t *fr; { for (; fr; fr = fr->fr_next) { if (fr->fr_ifa != NULL) { - fr->fr_ifa = GETUNIT(fr->fr_ifname); + fr->fr_ifa = GETUNIT(fr->fr_ifname, fr->fr_ip.fi_v); if (fr->fr_ifa == NULL) fr->fr_ifa = (void *)-1; } @@ -1578,9 +1891,9 @@ register frentry_t *fr; void frsync() { +# if !SOLARIS register struct ifnet *ifp; -# if !SOLARIS # if defined(__OpenBSD__) || ((NetBSD >= 199511) && (NetBSD < 1991011)) || \ (defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)) # if (NetBSD >= 199905) || defined(__OpenBSD__) @@ -1603,19 +1916,212 @@ void frsync() frsynclist(ipacct[1][fr_active]); frsynclist(ipfilter[0][fr_active]); frsynclist(ipfilter[1][fr_active]); +#ifdef USE_INET6 + frsynclist(ipacct6[0][fr_active]); + frsynclist(ipacct6[1][fr_active]); + frsynclist(ipfilter6[0][fr_active]); + frsynclist(ipfilter6[1][fr_active]); +#endif RWLOCK_EXIT(&ipf_mutex); } + +/* + * In the functions below, bcopy() is called because the pointer being + * copied _from_ in this instance is a pointer to a char buf (which could + * end up being unaligned) and on the kernel's local stack. + */ +int ircopyptr(a, b, c) +void *a, *b; +size_t c; +{ + caddr_t ca; + int err; + +#if SOLARIS + if (copyin(a, (char *)&ca, sizeof(ca))) + return EFAULT; +#else + bcopy(a, &ca, sizeof(ca)); +#endif + err = copyin(ca, b, c); + if (err) + err = EFAULT; + return err; +} + + +int iwcopyptr(a, b, c) +void *a, *b; +size_t c; +{ + caddr_t ca; + int err; + +#if SOLARIS + if (copyin(b, (char *)&ca, sizeof(ca))) + return EFAULT; #else + bcopy(b, &ca, sizeof(ca)); +#endif + err = copyout(a, ca, c); + if (err) + err = EFAULT; + return err; +} + +#else /* _KERNEL */ /* * return the first IP Address associated with an interface */ -int fr_ifpaddr(ifptr, inp) +int fr_ifpaddr(v, ifptr, inp) +int v; void *ifptr; struct in_addr *inp; { return 0; } -#endif + + +int ircopyptr(a, b, c) +void *a, *b; +size_t c; +{ + caddr_t ca; + + bcopy(a, &ca, sizeof(ca)); + bcopy(ca, b, c); + return 0; +} + + +int iwcopyptr(a, b, c) +void *a, *b; +size_t c; +{ + caddr_t ca; + + bcopy(b, &ca, sizeof(ca)); + bcopy(a, ca, c); + return 0; +} + + +#endif + + +int fr_lock(data, lockp) +caddr_t data; +int *lockp; +{ + int arg, error; + + error = IRCOPY(data, (caddr_t)&arg, sizeof(arg)); + if (!error) { + error = IWCOPY((caddr_t)lockp, data, sizeof(*lockp)); + if (!error) + *lockp = arg; + } + return error; +} + + +void fr_getstat(fiop) +friostat_t *fiop; +{ + bcopy((char *)frstats, (char *)fiop->f_st, sizeof(filterstats_t) * 2); + fiop->f_locks[0] = fr_state_lock; + fiop->f_locks[1] = fr_nat_lock; + fiop->f_locks[2] = fr_frag_lock; + fiop->f_locks[3] = fr_auth_lock; + fiop->f_fin[0] = ipfilter[0][0]; + fiop->f_fin[1] = ipfilter[0][1]; + fiop->f_fout[0] = ipfilter[1][0]; + fiop->f_fout[1] = ipfilter[1][1]; + fiop->f_acctin[0] = ipacct[0][0]; + fiop->f_acctin[1] = ipacct[0][1]; + fiop->f_acctout[0] = ipacct[1][0]; + fiop->f_acctout[1] = ipacct[1][1]; +#ifdef USE_INET6 + fiop->f_fin6[0] = ipfilter6[0][0]; + fiop->f_fin6[1] = ipfilter6[0][1]; + fiop->f_fout6[0] = ipfilter6[1][0]; + fiop->f_fout6[1] = ipfilter6[1][1]; + fiop->f_acctin6[0] = ipacct6[0][0]; + fiop->f_acctin6[1] = ipacct6[0][1]; + fiop->f_acctout6[0] = ipacct6[1][0]; + fiop->f_acctout6[1] = ipacct6[1][1]; +#else + fiop->f_fin6[0] = NULL; + fiop->f_fin6[1] = NULL; + fiop->f_fout6[0] = NULL; + fiop->f_fout6[1] = NULL; + fiop->f_acctin6[0] = NULL; + fiop->f_acctin6[1] = NULL; + fiop->f_acctout6[0] = NULL; + fiop->f_acctout6[1] = NULL; +#endif + fiop->f_active = fr_active; + fiop->f_froute[0] = ipl_frouteok[0]; + fiop->f_froute[1] = ipl_frouteok[1]; + + fiop->f_running = fr_running; + fiop->f_groups[0][0] = ipfgroups[0][0]; + fiop->f_groups[0][1] = ipfgroups[0][1]; + fiop->f_groups[1][0] = ipfgroups[1][0]; + fiop->f_groups[1][1] = ipfgroups[1][1]; + fiop->f_groups[2][0] = ipfgroups[2][0]; + fiop->f_groups[2][1] = ipfgroups[2][1]; +#ifdef IPFILTER_LOG + fiop->f_logging = 1; +#else + fiop->f_logging = 0; +#endif + fiop->f_defpass = fr_pass; + strncpy(fiop->f_version, ipfilter_version, sizeof(fiop->f_version)); +} + + +#ifdef USE_INET6 +int icmptoicmp6types[ICMP_MAXTYPE+1] = { + ICMP6_ECHO_REPLY, /* 0: ICMP_ECHOREPLY */ + -1, /* 1: UNUSED */ + -1, /* 2: UNUSED */ + ICMP6_DST_UNREACH, /* 3: ICMP_UNREACH */ + -1, /* 4: ICMP_SOURCEQUENCH */ + ND_REDIRECT, /* 5: ICMP_REDIRECT */ + -1, /* 6: UNUSED */ + -1, /* 7: UNUSED */ + ICMP6_ECHO_REQUEST, /* 8: ICMP_ECHO */ + -1, /* 9: UNUSED */ + -1, /* 10: UNUSED */ + ICMP6_TIME_EXCEEDED, /* 11: ICMP_TIMXCEED */ + ICMP6_PARAM_PROB, /* 12: ICMP_PARAMPROB */ + -1, /* 13: ICMP_TSTAMP */ + -1, /* 14: ICMP_TSTAMPREPLY */ + -1, /* 15: ICMP_IREQ */ + -1, /* 16: ICMP_IREQREPLY */ + -1, /* 17: ICMP_MASKREQ */ + -1, /* 18: ICMP_MASKREPLY */ +}; + + +int icmptoicmp6unreach[ICMP_MAX_UNREACH] = { + ICMP6_DST_UNREACH_ADDR, /* 0: ICMP_UNREACH_NET */ + ICMP6_DST_UNREACH_ADDR, /* 1: ICMP_UNREACH_HOST */ + -1, /* 2: ICMP_UNREACH_PROTOCOL */ + ICMP6_DST_UNREACH_NOPORT, /* 3: ICMP_UNREACH_PORT */ + -1, /* 4: ICMP_UNREACH_NEEDFRAG */ + ICMP6_DST_UNREACH_NOTNEIGHBOR, /* 5: ICMP_UNREACH_SRCFAIL */ + ICMP6_DST_UNREACH_ADDR, /* 6: ICMP_UNREACH_NET_UNKNOWN */ + ICMP6_DST_UNREACH_ADDR, /* 7: ICMP_UNREACH_HOST_UNKNOWN */ + -1, /* 8: ICMP_UNREACH_ISOLATED */ + ICMP6_DST_UNREACH_ADMIN, /* 9: ICMP_UNREACH_NET_PROHIB */ + ICMP6_DST_UNREACH_ADMIN, /* 10: ICMP_UNREACH_HOST_PROHIB */ + -1, /* 11: ICMP_UNREACH_TOSNET */ + -1, /* 12: ICMP_UNREACH_TOSHOST */ + ICMP6_DST_UNREACH_ADMIN, /* 13: ICMP_UNREACH_ADMIN_PROHIBIT */ +}; +#endif diff --git a/sys/netinet/ip_auth.c b/sys/netinet/ip_auth.c index 67f134fcd9d..e196aca1417 100644 --- a/sys/netinet/ip_auth.c +++ b/sys/netinet/ip_auth.c @@ -1,14 +1,14 @@ -/* $OpenBSD: ip_auth.c,v 1.14 2000/05/24 21:59:10 kjell Exp $ */ +/* $OpenBSD: ip_auth.c,v 1.15 2001/01/17 04:47:11 fgsch Exp $ */ /* - * Copyright (C) 1998 by Darren Reed & Guido van Rooij. + * Copyright (C) 1998-2000 by Darren Reed & Guido van Rooij. * * 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 rcsid[] = "@(#)$IPFilter: ip_auth.c,v 2.1.2.4 2000/05/22 06:57:45 darrenr Exp $"; +static const char rcsid[] = "@(#)$IPFilter: ip_auth.c,v 2.11.2.4 2000/08/05 14:48:50 darrenr Exp $"; #endif #include <sys/errno.h> @@ -32,7 +32,7 @@ static const char rcsid[] = "@(#)$IPFilter: ip_auth.c,v 2.1.2.4 2000/05/22 06:57 # include <sys/protosw.h> #endif #include <sys/socket.h> -#if defined(_KERNEL) && !defined(linux) +#if (defined(_KERNEL) || defined(KERNEL)) && !defined(linux) # include <sys/systm.h> #endif #if !defined(__SVR4) && !defined(__svr4__) @@ -48,7 +48,7 @@ static const char rcsid[] = "@(#)$IPFilter: ip_auth.c,v 2.1.2.4 2000/05/22 06:57 # include <sys/stream.h> # include <sys/kmem.h> #endif -#if _BSDI_VERSION >= 199802 +#if (_BSDI_VERSION >= 199802) || (__FreeBSD_version >= 400000) # include <sys/queue.h> #endif #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi) @@ -91,10 +91,10 @@ extern struct ifqueue ipintrq; /* ip packet input queue */ #endif #include <netinet/udp.h> #include <netinet/ip_icmp.h> -#include <netinet/ip_fil_compat.h> +#include "netinet/ip_compat.h" #include <netinet/tcpip.h> -#include <netinet/ip_fil.h> -#include <netinet/ip_auth.h> +#include "netinet/ip_fil.h" +#include "netinet/ip_auth.h" #if !SOLARIS && !defined(linux) # include <net/netisr.h> # ifdef __FreeBSD__ @@ -125,11 +125,12 @@ static struct wait_queue *ipfauthwait = NULL; int fr_authsize = FR_NUMAUTH; int fr_authused = 0; int fr_defaultauthage = 600; +int fr_auth_lock = 0; fr_authstat_t fr_authstats; -frauth_t fr_auth[FR_NUMAUTH]; +static frauth_t fr_auth[FR_NUMAUTH]; mb_t *fr_authpkts[FR_NUMAUTH]; -int fr_authstart = 0, fr_authend = 0, fr_authnext = 0; -frauthent_t *fae_list = NULL; +static int fr_authstart = 0, fr_authend = 0, fr_authnext = 0; +static frauthent_t *fae_list = NULL; frentry_t *ipauth = NULL; @@ -146,6 +147,9 @@ fr_info_t *fin; u_32_t pass; int i; + if (fr_auth_lock) + return 0; + READ_ENTER(&ipf_auth); for (i = fr_authstart; i != fr_authend; ) { /* @@ -208,6 +212,9 @@ ip_t *ip; #endif int i; + if (fr_auth_lock) + return 0; + WRITE_ENTER(&ipf_auth); if (fr_authstart > fr_authend) { fr_authstats.fas_nospace++; @@ -237,14 +244,15 @@ ip_t *ip; * them. */ # if SOLARIS && defined(_KERNEL) - if (ip == (ip_t *)m->b_rptr) + if ((ip == (ip_t *)m->b_rptr) && (ip->ip_v == 4)) # endif { register u_short bo; bo = ip->ip_len; ip->ip_len = htons(bo); -# if !SOLARIS /* 4.4BSD converts this ip_input.c, but I don't in solaris.c */ +# if !SOLARIS && !defined(__NetBSD__) + /* 4.4BSD converts this ip_input.c, but I don't in solaris.c */ bo = ip->ip_id; ip->ip_id = htons(bo); # endif @@ -271,7 +279,7 @@ ip_t *ip; int fr_auth_ioctl(data, cmd, fr, frptr) caddr_t data; -#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) || (FreeBSD_version >= 300003) u_long cmd; #else int cmd; @@ -279,11 +287,8 @@ int cmd; frentry_t *fr, **frptr; { mb_t *m; -#if defined(_KERNEL) -# if !SOLARIS +#if defined(_KERNEL) && !SOLARIS struct ifqueue *ifq; - int s; -# endif #endif frauth_t auth, *au = &auth; frauthent_t *fae, **faep; @@ -291,12 +296,17 @@ frentry_t *fr, **frptr; switch (cmd) { + case SIOCSTLCK : + error = fr_lock(data, &fr_auth_lock); + break; case SIOCINIFR : case SIOCRMIFR : case SIOCADIFR : error = EINVAL; break; case SIOCINAFR : + error = EINVAL; + break; case SIOCRMAFR : case SIOCADAFR : for (faep = &fae_list; (fae = *faep); ) @@ -317,8 +327,8 @@ frentry_t *fr, **frptr; } else { KMALLOC(fae, frauthent_t *); if (fae != NULL) { - IRCOPY((char *)data, (char *)&fae->fae_fr, - sizeof(fae->fae_fr)); + bcopy((char *)fr, (char *)&fae->fae_fr, + sizeof(*fr)); WRITE_ENTER(&ipf_auth); fae->fae_age = fr_defaultauthage; fae->fae_fr.fr_hits = 0; @@ -336,15 +346,18 @@ frentry_t *fr, **frptr; READ_ENTER(&ipf_auth); fr_authstats.fas_faelist = fae_list; RWLOCK_EXIT(&ipf_auth); - IWCOPY((char *)&fr_authstats, data, sizeof(fr_authstats)); + error = IWCOPYPTR((char *)&fr_authstats, data, + sizeof(fr_authstats)); break; case SIOCAUTHW: fr_authioctlloop: READ_ENTER(&ipf_auth); if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) { - IWCOPY((char *)&fr_auth[fr_authnext], data, - sizeof(fr_info_t)); + error = IWCOPYPTR((char *)&fr_auth[fr_authnext], data, + sizeof(fr_info_t)); RWLOCK_EXIT(&ipf_auth); + if (error) + break; WRITE_ENTER(&ipf_auth); fr_authnext++; if (fr_authnext == FR_NUMAUTH) @@ -375,7 +388,9 @@ fr_authioctlloop: goto fr_authioctlloop; break; case SIOCAUTHR: - IRCOPY(data, (caddr_t)&auth, sizeof(auth)); + error = IRCOPYPTR(data, (caddr_t)&auth, sizeof(auth)); + if (error) + return error; WRITE_ENTER(&ipf_auth); i = au->fra_index; if ((i < 0) || (i > FR_NUMAUTH) || @@ -389,7 +404,6 @@ fr_authioctlloop: fr_authpkts[i] = NULL; #ifdef _KERNEL RWLOCK_EXIT(&ipf_auth); - SPL_NET(s); # ifndef linux if (m && au->fra_info.fin_out) { # if SOLARIS @@ -455,7 +469,6 @@ fr_authioctlloop: } } # endif - SPL_X(s); #endif /* _KERNEL */ break; default : @@ -509,6 +522,9 @@ void fr_authexpire() int s; #endif + if (fr_auth_lock) + return; + SPL_NET(s); WRITE_ENTER(&ipf_auth); for (i = 0, fra = fr_auth; i < FR_NUMAUTH; i++, fra++) { diff --git a/sys/netinet/ip_auth.h b/sys/netinet/ip_auth.h index adbed16d23f..2c2a6f7e5f3 100644 --- a/sys/netinet/ip_auth.h +++ b/sys/netinet/ip_auth.h @@ -1,13 +1,13 @@ -/* $OpenBSD: ip_auth.h,v 1.8 2000/05/24 21:59:11 kjell Exp $ */ +/* $OpenBSD: ip_auth.h,v 1.9 2001/01/17 04:47:12 fgsch Exp $ */ /* - * Copyright (C) 1997-1998 by Darren Reed & Guido Van Rooij. + * Copyright (C) 1997-2000 by Darren Reed & Guido Van Rooij. * * 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. * - * $IPFilter: ip_auth.h,v 2.1.2.1 2000/05/22 06:57:47 darrenr Exp $ + * $IPFilter: ip_auth.h,v 2.3.2.2 2000/10/19 15:38:44 darrenr Exp $ * */ #ifndef __IP_AUTH_H__ @@ -48,10 +48,9 @@ typedef struct fr_authstat { extern frentry_t *ipauth; extern struct fr_authstat fr_authstats; extern int fr_defaultauthage; -extern int fr_authstart; -extern int fr_authend; extern int fr_authsize; extern int fr_authused; +extern int fr_auth_lock; extern u_32_t fr_checkauth __P((ip_t *, fr_info_t *)); extern void fr_authexpire __P((void)); extern void fr_authunload __P((void)); diff --git a/sys/netinet/ip_fil_compat.h b/sys/netinet/ip_compat.h index 97f420e12ae..03547a9b376 100644 --- a/sys/netinet/ip_fil_compat.h +++ b/sys/netinet/ip_compat.h @@ -1,14 +1,14 @@ -/* $OpenBSD: ip_fil_compat.h,v 1.18 2000/08/10 05:50:26 kjell Exp $ */ +/* $OpenBSD: ip_compat.h,v 1.1 2001/01/17 04:47:12 fgsch Exp $ */ /* - * Copyright (C) 1993-1998 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. * * @(#)ip_compat.h 1.8 1/14/96 - * $IPFilter: ip_compat.h,v 2.1.2.6 2000/04/25 16:21:11 darrenr Exp $ + * $IPFilter: ip_compat.h,v 2.26.2.7 2000/11/12 11:53:45 darrenr Exp $ */ #ifndef __IP_COMPAT_H__ @@ -29,6 +29,11 @@ #ifndef SOLARIS #define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) #endif +#if SOLARIS2 >= 8 +# ifndef USE_INET6 +# define USE_INET6 +# endif +#endif #if defined(_KERNEL) || defined(KERNEL) || defined(__KERNEL__) # undef KERNEL @@ -108,9 +113,13 @@ struct ether_addr { # include <inet/ip_ire.h> # endif /* _KERNEL */ # if SOLARIS2 >= 8 -# define ipif_local_addr ipif_lcl_addr +# include <inet/ip_if.h> +# include <netinet/ip6.h> +# define ipif_local_addr ipif_lcl_addr /* Only defined in private include file */ -# define V4_PART_OF_V6(v6) v6.s6_addr32[3] +# ifndef V4_PART_OF_V6 +# define V4_PART_OF_V6(v6) v6.s6_addr32[3] +# endif # endif #else # if !defined(__sgi) @@ -119,6 +128,10 @@ 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 <machine/in_cksum.h> +#endif + #ifndef IP_OFFMASK #define IP_OFFMASK 0x1fff #endif @@ -132,12 +145,25 @@ typedef int minor_t; # define QUAD_T long #endif /* BSD > 199306 */ + /* * These operating systems already take care of the problem for us. */ #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \ defined(__sgi) typedef u_int32_t u_32_t; +# if defined(_KERNEL) && !defined(IPFILTER_LKM) +# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 104110000) +# include "opt_inet.h" +# endif +# if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) && \ + !defined(KLD_MODULE) +# include "opt_inet6.h" +# endif +# ifdef INET6 +# define USE_INET6 +# endif +# endif #else /* * Really, any arch where sizeof(long) != sizeof(int). @@ -145,10 +171,38 @@ typedef u_int32_t u_32_t; # if defined(__alpha__) || defined(__alpha) || defined(_LP64) typedef unsigned int u_32_t; # else -typedef unsigned long u_32_t; +# if SOLARIS2 >= 6 +typedef uint32_t u_32_t; +# else +typedef unsigned int u_32_t; +# endif # endif #endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */ +#ifdef USE_INET6 +# if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) +# include <netinet/ip6.h> +# ifdef _KERNEL +# include <netinet6/ip6_var.h> +# endif +typedef struct ip6_hdr ip6_t; +# endif +union i6addr { + u_32_t i6[4]; + struct in_addr in4; + struct in6_addr in6; +}; +#else +union i6addr { + u_32_t i6[4]; + struct in_addr in4; +}; +#endif + +#define IP6CMP(a,b) bcmp((char *)&(a), (char *)&(b), sizeof(a)) +#define IP6EQ(a,b) (bcmp((char *)&(a), (char *)&(b), sizeof(a)) == 0) +#define IP6NEQ(a,b) (bcmp((char *)&(a), (char *)&(b), sizeof(a)) != 0) + #ifndef MAX #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif @@ -211,11 +265,20 @@ typedef unsigned long u_32_t; #if defined(__FreeBSD__) && (defined(KERNEL) || defined(_KERNEL)) +# ifdef IPFILTER_LKM +# include <osreldate.h> +# define ACTUALLY_LKM_NOT_KERNEL +# else +# include <sys/osreldate.h> +# endif # if __FreeBSD__ < 3 # include <machine/spl.h> -# endif -# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) -# define ACTUALLY_LKM_NOT_KERNEL +# else +# if __FreeBSD__ == 3 +# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) +# define ACTUALLY_LKM_NOT_KERNEL +# endif +# endif # endif #endif /* __FreeBSD__ && KERNEL */ @@ -223,12 +286,52 @@ typedef unsigned long u_32_t; * Build some macros and #defines to enable the same code to compile anywhere * Well, that's the idea, anyway :-) */ +#if !SOLARIS || (SOLARIS2 < 6) || !defined(KERNEL) +# define ATOMIC_INCL ATOMIC_INC +# define ATOMIC_INC64 ATOMIC_INC +# define ATOMIC_INC32 ATOMIC_INC +# define ATOMIC_INC16 ATOMIC_INC +# define ATOMIC_DECL ATOMIC_DEC +# define ATOMIC_DEC64 ATOMIC_DEC +# define ATOMIC_DEC32 ATOMIC_DEC +# define ATOMIC_DEC16 ATOMIC_DEC +#endif +#ifdef __sgi +# define hz HZ +# include <sys/ksynch.h> +# define IPF_LOCK_PL plhi +# include <sys/sema.h> +#undef kmutex_t +typedef struct { + lock_t *l; + int pl; +} kmutex_t; +# undef MUTEX_INIT +# undef MUTEX_DESTROY +#endif #ifdef KERNEL # if SOLARIS -# define ATOMIC_INC(x) { mutex_enter(&ipf_rw); (x)++; \ +# if SOLARIS2 >= 6 +# include <sys/atomic.h> +# if SOLARIS2 == 6 +# define ATOMIC_INCL(x) atomic_add_long((uint32_t*)&(x), 1) +# define ATOMIC_DECL(x) atomic_add_long((uint32_t*)&(x), -1) +# else +# define ATOMIC_INCL(x) atomic_add_long(&(x), 1) +# define ATOMIC_DECL(x) atomic_add_long(&(x), -1) +# endif +# define ATOMIC_INC64(x) atomic_add_64((uint64_t*)&(x), 1) +# define ATOMIC_INC32(x) atomic_add_32((uint32_t*)&(x), 1) +# define ATOMIC_INC16(x) atomic_add_16((uint16_t*)&(x), 1) +# define ATOMIC_DEC64(x) atomic_add_64((uint64_t*)&(x), -1) +# define ATOMIC_DEC32(x) atomic_add_32((uint32_t*)&(x), -1) +# define ATOMIC_DEC16(x) atomic_add_16((uint16_t*)&(x), -1) +# else +# define ATOMIC_INC(x) { mutex_enter(&ipf_rw); (x)++; \ mutex_exit(&ipf_rw); } -# define ATOMIC_DEC(x) { mutex_enter(&ipf_rw); (x)--; \ +# define ATOMIC_DEC(x) { mutex_enter(&ipf_rw); (x)--; \ mutex_exit(&ipf_rw); } +# endif # define MUTEX_ENTER(x) mutex_enter(x) # if 1 # define KRWLOCK_T krwlock_t @@ -251,10 +354,14 @@ typedef unsigned long u_32_t; # define RWLOCK_EXIT(x) mutex_exit(x) # define RW_DESTROY(x) mutex_destroy(x) # endif +# define MUTEX_INIT(x, y, z) mutex_init((x), (y), MUTEX_DRIVER, (z)) +# define MUTEX_DESTROY(x) mutex_destroy(x) # define MUTEX_EXIT(x) mutex_exit(x) # define MTOD(m,t) (t)((m)->b_rptr) -# define IRCOPY(a,b,c) copyin((a), (b), (c)) -# define IWCOPY(a,b,c) copyout((a), (b), (c)) +# define IRCOPY(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) +# define IWCOPY(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) +# define IRCOPYPTR ircopyptr +# define IWCOPYPTR iwcopyptr # define FREE_MB_T(m) freemsg(m) # define SPL_NET(x) ; # define SPL_IMP(x) ; @@ -290,21 +397,13 @@ typedef struct qif { * in case the ILL has disappeared... */ size_t qf_hl; /* header length */ + int qf_sap; } qif_t; -extern ill_t *get_unit __P((char *)); -# define GETUNIT(n) get_unit((n)) +extern ill_t *get_unit __P((char *, int)); +# define GETUNIT(n, v) get_unit(n, v) # define IFNAME(x) ((ill_t *)x)->ill_name # else /* SOLARIS */ # if defined(__sgi) -# define hz HZ -# include <sys/ksynch.h> -# define IPF_LOCK_PL plhi -# include <sys/sema.h> -#undef kmutex_t -typedef struct { - lock_t *l; - int pl; -} kmutex_t; # define ATOMIC_INC(x) { MUTEX_ENTER(&ipf_rw); \ (x)++; MUTEX_EXIT(&ipf_rw); } # define ATOMIC_DEC(x) { MUTEX_ENTER(&ipf_rw); \ @@ -315,38 +414,44 @@ typedef struct { # define WRITE_ENTER(x) MUTEX_ENTER(x) # define RW_UPGRADE(x) ; # define MUTEX_DOWNGRADE(x) ; -# define RWLOCK_EXIT(x) MUTEX_EXIT(x) -# define MUTEX_EXIT(x) UNLOCK((x)->l, (x)->pl); +# define RWLOCK_EXIT(x) MUTEX_EXIT(x) +# define MUTEX_EXIT(x) UNLOCK((x)->l, (x)->pl); +# define MUTEX_INIT(x,y,z) (x)->l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP) +# define MUTEX_DESTROY(x) LOCK_DEALLOC((x)->l) # else /* __sgi */ # define ATOMIC_INC(x) (x)++ # define ATOMIC_DEC(x) (x)-- # define MUTEX_ENTER(x) ; -# define READ_ENTER(x) ; -# define WRITE_ENTER(x) ; -# define RW_UPGRADE(x) ; +# define READ_ENTER(x) ; +# define WRITE_ENTER(x) ; +# define RW_UPGRADE(x) ; # define MUTEX_DOWNGRADE(x) ; -# define RWLOCK_EXIT(x) ; -# define MUTEX_EXIT(x) ; +# define RWLOCK_EXIT(x) ; +# define MUTEX_EXIT(x) ; +# define MUTEX_INIT(x,y,z) ; +# define MUTEX_DESTROY(x) ; # endif /* __sgi */ # ifndef linux # define FREE_MB_T(m) m_freem(m) # define MTOD(m,t) mtod(m,t) -# define IRCOPY(a,b,c) bcopy((a), (b), (c)) -# define IWCOPY(a,b,c) bcopy((a), (b), (c)) +# define IRCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IWCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IRCOPYPTR ircopyptr +# define IWCOPYPTR iwcopyptr # endif /* !linux */ # endif /* SOLARIS */ # ifdef sun # if !SOLARIS # include <sys/kmem_alloc.h> -# define GETUNIT(n) ifunit((n), IFNAMSIZ) +# define GETUNIT(n, v) ifunit(n, IFNAMSIZ) # define IFNAME(x) ((struct ifnet *)x)->if_name # endif # else # ifndef linux -# define GETUNIT(n) ifunit((n)) +# define GETUNIT(n, v) ifunit(n) # if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ - (defined(OpenBSD) && (OpenBSD >= 199603)) + (defined(OpenBSD) && (OpenBSD >= 199603)) # define IFNAME(x) ((struct ifnet *)x)->if_xname # else # define IFNAME(x) ((struct ifnet *)x)->if_name @@ -424,6 +529,8 @@ extern vm_map_t kmem_map; # define ATOMIC_DEC(x) (x)-- # define MUTEX_ENTER(x) ; # define READ_ENTER(x) ; +# define MUTEX_INIT(x,y,z) ; +# define MUTEX_DESTROY(x) ; # define WRITE_ENTER(x) ; # define RW_UPGRADE(x) ; # define MUTEX_DOWNGRADE(x) ; @@ -437,9 +544,11 @@ extern vm_map_t kmem_map; # define KMALLOCS(a,b,c) (a) = (b)malloc(c) # define KFREE(x) free(x) # define KFREES(x,s) free(x) -# define GETUNIT(x) get_unit(x) -# define IRCOPY(a,b,c) bcopy((a), (b), (c)) -# define IWCOPY(a,b,c) bcopy((a), (b), (c)) +# define GETUNIT(x, v) get_unit(x,v) +# define IRCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IWCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IRCOPYPTR ircopyptr +# define IWCOPYPTR iwcopyptr #endif /* KERNEL */ #if SOLARIS @@ -761,7 +870,7 @@ typedef struct uio { # define if_name name # ifdef KERNEL -# define GETUNIT(x) dev_get(x) +# define GETUNIT(x, v) dev_get(x) # define FREE_MB_T(m) kfree_skb(m, FREE_WRITE) # define uniqtime do_gettimeofday # undef INT_MAX @@ -783,16 +892,50 @@ typedef struct uio { # define KMALLOCS(a,b,c) (a) = (b)kmalloc((c), GFP_ATOMIC) # define KFREE(x) kfree_s((x), sizeof(*(x))) # define KFREES(x,s) kfree_s((x), (s)) -# define IRCOPY(a,b,c) { \ - error = verify_area(VERIFY_READ, (a) ,(c)); \ - if (!error) \ - memcpy_fromfs((b), (a), (c)); \ - } -# define IWCOPY(a,b,c) { \ - error = verify_area(VERIFY_WRITE, (b), (c)); \ - if (!error) \ - memcpy_tofs((b), (a), (c)); \ - } +#define IRCOPY(const void *a, void *b, size_t c) { \ + int error; \ + + error = verify_area(VERIFY_READ, a ,c); \ + if (!error) \ + memcpy_fromfs(b, a, c); \ + return error; \ +} +static inline int IWCOPY(const void *a, void *b, size_t c) +{ + int error; + + error = verify_area(VERIFY_WRITE, b, c); + if (!error) + memcpy_tofs(b, a, c); + return error; +} +static inline int IRCOPYPTR(const void *a, void *b, size_t c) { + caddr_t ca; + int error; + + error = verify_area(VERIFY_READ, a ,sizeof(ca)); + if (!error) { + memcpy_fromfs(ca, a, sizeof(ca)); + error = verify_area(VERIFY_READ, ca , c); + if (!error) + memcpy_fromfs(b, ca, c); + } + return error; +} +static inline int IWCOPYPTR(const void *a, void *b, size_t c) { + caddr_t ca; + int error; + + + error = verify_area(VERIFY_READ, b ,sizeof(ca)); + if (!error) { + memcpy_fromfs(ca, b, sizeof(ca)); + error = verify_area(VERIFY_WRITE, ca, c); + if (!error) + memcpy_tofs(ca, a, c); + } + return error; +} # else # define __KERNEL__ # undef INT_MAX @@ -833,12 +976,18 @@ struct ether_addr { #define A_A & #endif +#define TCPF_ALL (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + #ifndef ICMP_ROUTERADVERT # define ICMP_ROUTERADVERT 9 #endif #ifndef ICMP_ROUTERSOLICIT # define ICMP_ROUTERSOLICIT 10 #endif +#undef ICMP_MAX_UNREACH +#define ICMP_MAX_UNREACH 14 +#undef ICMP_MAXTYPE +#define ICMP_MAXTYPE 18 /* * ICMP error replies have an IP header (20 bytes), 8 bytes of ICMP data, * another IP header and then 64 bits of data, totalling 56. Of course, @@ -848,5 +997,7 @@ struct ether_addr { #define ICMPERR_IPICMPHLEN (20 + 8) #define ICMPERR_MINPKTLEN (20 + 8 + 20) #define ICMPERR_MAXPKTLEN (20 + 8 + 20 + 8) +#define ICMP6ERR_MINPKTLEN (40 + 8) +#define ICMP6ERR_IPICMPHLEN (40 + 8 + 40) #endif /* __IP_COMPAT_H__ */ diff --git a/sys/netinet/ip_fil.c b/sys/netinet/ip_fil.c index ce308dacb5c..da31e69f30d 100644 --- a/sys/netinet/ip_fil.c +++ b/sys/netinet/ip_fil.c @@ -1,15 +1,15 @@ -/* $OpenBSD: ip_fil.c,v 1.37 2000/09/07 19:45:04 art Exp $ */ +/* $OpenBSD: ip_fil.c,v 1.38 2001/01/17 04:47:12 fgsch Exp $ */ /* - * Copyright (C) 1993-1998 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_fil.c 2.41 6/5/96 (C) 1993-1995 Darren Reed"; -static const char rcsid[] = "@(#)$IPFilter: ip_fil.c,v 2.4.2.22 2000/07/08 02:43:47 darrenr Exp $"; +static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$IPFilter: ip_fil.c,v 2.42.2.17 2000/10/19 15:39:42 darrenr Exp $"; #endif #ifndef SOLARIS @@ -19,15 +19,17 @@ static const char rcsid[] = "@(#)$IPFilter: ip_fil.c,v 2.4.2.22 2000/07/08 02:43 #if defined(KERNEL) && !defined(_KERNEL) # define _KERNEL #endif +#if defined(_KERNEL) && defined(__FreeBSD_version) && \ + (__FreeBSD_version >= 400000) && !defined(KLD_MODULE) +#include "opt_inet6.h" +#endif #include <sys/param.h> #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ defined(_KERNEL) # include "opt_ipfilter_log.h" #endif -#ifdef __FreeBSD__ -# if defined(_KERNEL) && !defined(IPFILTER_LKM) -# include <sys/osreldate.h> -# else +#if defined(__FreeBSD__) && !defined(__FreeBSD_version) +# if !defined(_KERNEL) || defined(IPFILTER_LKM) # include <osreldate.h> # endif #endif @@ -97,13 +99,16 @@ static const char rcsid[] = "@(#)$IPFilter: ip_fil.c,v 2.4.2.22 2000/07/08 02:43 # include <unistd.h> # include <syslog.h> #endif -#include <netinet/ip_fil_compat.h> -#include <netinet/ip_fil.h> -#include <netinet/ip_proxy.h> -#include <netinet/ip_nat.h> -#include <netinet/ip_frag.h> -#include <netinet/ip_state.h> -#include <netinet/ip_auth.h> +#include "netinet/ip_compat.h" +#ifdef USE_INET6 +# include <netinet/icmp6.h> +#endif +#include "netinet/ip_fil.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_state.h" +#include "netinet/ip_auth.h" #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include <sys/malloc.h> #endif @@ -128,16 +133,11 @@ extern int tcp_ttl; # endif #endif -# if defined (__OpenBSD__) -int ipl_unreach = ICMP_UNREACH_FILTER_PROHIB; -# else int ipl_unreach = ICMP_UNREACH_FILTER; -# endif - u_long ipl_frouteok[2] = {0, 0}; -static void frzerostats __P((caddr_t)); -#if defined(__NetBSD__) || defined(__OpenBSD__) +static int frzerostats __P((caddr_t)); +#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) static int frrequest __P((int, u_long, caddr_t, int)); #else static int frrequest __P((int, int, caddr_t, int)); @@ -169,10 +169,15 @@ int fr_running = 0; #if (__FreeBSD_version >= 300000) && defined(_KERNEL) struct callout_handle ipfr_slowtimer_ch; #endif - -#ifdef __OpenBSD__ -#include <sys/timeout.h> -struct timeout ipfr_slowtimer_to; +#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) +# include <sys/callout.h> +struct callout ipfr_slowtimer_ch; +#endif +#if defined(__OpenBSD__) +struct timeout ipfr_slowtimer_ch; +#endif +#if defined(__sgi) && defined(_KERNEL) +toid_t ipfr_slowtimer_ch; #endif #if (_BSDI_VERSION >= 199510) && defined(_KERNEL) @@ -258,14 +263,31 @@ int iplattach() error = pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT, &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); if (error) { +# ifdef USE_INET6 + goto pfil_error; +# else appr_unload(); ip_natunload(); fr_stateunload(); return error; +# endif } # else pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT); # endif +# ifdef USE_INET6 + error = pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inetsw[ip_protox[IPPROTO_IPV6]].pr_pfh); + if (error) { + pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); +pfil_error: + appr_unload(); + ip_natunload(); + fr_stateunload(); + return error; + } +# endif # endif # ifdef __sgi @@ -293,24 +315,30 @@ int iplattach() defpass = "no-match -> block"; #if !defined(__OpenBSD__) - printf("IP Filter: initialized. Default = %s all, Logging = %s\n", - defpass, + printf("%s initialized. Default = %s all, Logging = %s\n", + ipfilter_version, defpass, # ifdef IPFILTER_LOG "enabled"); # else "disabled"); # endif - printf("%s\n", ipfilter_version); #endif -#ifdef _KERNEL -# if (__FreeBSD_version >= 300000) && defined(_KERNEL) - ipfr_slowtimer_ch = timeout(ipfr_slowtimer, NULL, hz/2); -# elif defined(__OpenBSD__) - timeout_set(&ipfr_slowtimer_to, ipfr_slowtimer, NULL); - timeout_add(&ipfr_slowtimer_to, hz/2); -#else +#ifdef _KERNEL +# if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) + callout_init(&ipfr_slowtimer_ch); + callout_reset(&ipfr_slowtimer_ch, hz / 2, ipfr_slowtimer, NULL); +# else +# if defined(__OpenBSD__) + timeout_set(&ipfr_slowtimer_ch, ipfr_slowtimer, NULL); + timeout_add(&ipfr_slowtimer_ch, hz / 2); +# else +# if (__FreeBSD_version >= 300000) || defined(__sgi) + ipfr_slowtimer_ch = timeout(ipfr_slowtimer, NULL, hz/2); +# else timeout(ipfr_slowtimer, NULL, hz/2); +# endif +# endif # endif #endif return 0; @@ -328,18 +356,24 @@ int ipldetach() int error = 0; #endif -#ifdef _KERNEL -# if (__FreeBSD_version >= 300000) - untimeout(ipfr_slowtimer, NULL, ipfr_slowtimer_ch); -# elif defined(__OpenBSD__) - timeout_del(&ipfr_slowtimer_to); +#ifdef _KERNEL +# if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) + callout_stop(&ipfr_slowtimer_ch); # else -# ifdef __sgi - untimeout(ipfr_slowtimer); +# if defined(__OpenBSD__) + timeout_del(&ipfr_slowtimer_ch); # else +# if (__FreeBSD_version >= 300000) + untimeout(ipfr_slowtimer, NULL, ipfr_slowtimer_ch); +# else +# ifdef __sgi + untimeout(ipfr_slowtimer_ch); +# else untimeout(ipfr_slowtimer, NULL); -# endif -# endif +# endif +# endif /* FreeBSD */ +# endif /* OpenBSD */ +# endif /* NetBSD */ #endif SPL_NET(s); if (!fr_running) @@ -349,6 +383,10 @@ int ipldetach() return 0; } +#if !defined(__OpenBSD__) + printf("%s unloaded\n", ipfilter_version); +#endif + fr_checkp = fr_savep; i = frflush(IPL_LOGIPF, i); fr_running = 0; @@ -356,12 +394,18 @@ int ipldetach() # ifdef NETBSD_PF # if __NetBSD_Version__ >= 104200000 error = pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT, - &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); + &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); if (error) return error; # else pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT); # endif +# ifdef USE_INET6 + error = pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inetsw[ip_protox[IPPROTO_IPV6]].pr_pfh); + if (error) + return error; +# endif # endif # ifdef __sgi @@ -380,26 +424,20 @@ int ipldetach() #endif /* _KERNEL */ -static void frzerostats(data) +static int frzerostats(data) caddr_t data; { friostat_t fio; + int error; + + fr_getstat(&fio); + error = IWCOPYPTR((caddr_t)&fio, data, sizeof(fio)); + if (error) + return EFAULT; - bcopy((char *)frstats, (char *)fio.f_st, - sizeof(struct filterstats) * 2); - fio.f_fin[0] = ipfilter[0][0]; - fio.f_fin[1] = ipfilter[0][1]; - fio.f_fout[0] = ipfilter[1][0]; - fio.f_fout[1] = ipfilter[1][1]; - fio.f_acctin[0] = ipacct[0][0]; - fio.f_acctin[1] = ipacct[0][1]; - fio.f_acctout[0] = ipacct[1][0]; - fio.f_acctout[1] = ipacct[1][1]; - fio.f_active = fr_active; - fio.f_froute[0] = ipl_frouteok[0]; - fio.f_froute[1] = ipl_frouteok[1]; - IWCOPY((caddr_t)&fio, data, sizeof(fio)); bzero((char *)frstats, sizeof(*frstats) * 2); + + return 0; } @@ -414,20 +452,21 @@ int IPL_EXTERN(ioctl)(dev_t dev, int cmd, caddr_t data, int mode ) #else int IPL_EXTERN(ioctl)(dev, cmd, data, mode -#if ((_BSDI_VERSION >= 199510) || (BSD >= 199506) || (NetBSD >= 199511) || \ - (__FreeBSD_version >= 220000) || defined(__OpenBSD__)) && defined(_KERNEL) +# if (defined(_KERNEL) && ((_BSDI_VERSION >= 199510) || (BSD >= 199506) || \ + (NetBSD >= 199511) || (__FreeBSD_version >= 220000) || \ + defined(__OpenBSD__))) , p) struct proc *p; -#else +# else ) -#endif +# endif dev_t dev; -#if defined(__NetBSD__) || defined(__OpenBSD__) || \ - (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) +# if defined(__NetBSD__) || defined(__OpenBSD__) || \ + (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) u_long cmd; -#else +# else int cmd; -#endif +# endif caddr_t data; int mode; #endif /* __sgi */ @@ -445,37 +484,7 @@ int mode; unit = GET_MINOR(dev); if ((IPL_LOGMAX < unit) || (unit < 0)) return ENXIO; - -# if defined(__OpenBSD__) - /* Prevent IPF and NAT changes when securelevel > 1 */ - if (securelevel > 1) { - switch (cmd) { -# ifndef IPFILTER_LKM - case SIOCFRENB: -# endif - case SIOCSETFF: - case SIOCADAFR: - case SIOCADIFR: - case SIOCINAFR: - case SIOCINIFR: - case SIOCRMAFR: - case SIOCRMIFR: - case SIOCZRLST: - case SIOCSWAPA: - case SIOCFRZST: - case SIOCIPFFL: -# ifdef IPFILTER_LOG - case SIOCIPFFB: -# endif - case SIOCADNAT: - case SIOCRMNAT: - case SIOCFLNAT: - case SIOCCNATL: - return EPERM; - } - } -# endif /* OpenBSD */ -#else /* _KERNEL */ +#else unit = dev; #endif @@ -489,7 +498,6 @@ int mode; SPL_X(s); return error; } - if (unit == IPL_LOGSTATE) { if (fr_running) error = fr_state_ioctl(data, cmd, mode); @@ -498,12 +506,19 @@ int mode; SPL_X(s); return error; } + if (unit == IPL_LOGAUTH) { + if (!fr_running) + return EIO; + error = fr_auth_ioctl(data, cmd, NULL, NULL); + SPL_X(s); + return error; + } switch (cmd) { case FIONREAD : #ifdef IPFILTER_LOG - IWCOPY((caddr_t)&iplused[IPL_LOGIPF], (caddr_t)data, - sizeof(iplused[IPL_LOGIPF])); + error = IWCOPY((caddr_t)&iplused[IPL_LOGIPF], (caddr_t)data, + sizeof(iplused[IPL_LOGIPF])); #endif break; #if !defined(IPFILTER_LKM) && defined(_KERNEL) @@ -514,7 +529,9 @@ int mode; if (!(mode & FWRITE)) error = EPERM; else { - IRCOPY(data, (caddr_t)&enable, sizeof(enable)); + error = IRCOPY(data, (caddr_t)&enable, sizeof(enable)); + if (error) + break; if (enable) error = iplattach(); else @@ -527,10 +544,11 @@ int mode; if (!(mode & FWRITE)) error = EPERM; else - IRCOPY(data, (caddr_t)&fr_flags, sizeof(fr_flags)); + error = IRCOPY(data, (caddr_t)&fr_flags, + sizeof(fr_flags)); break; case SIOCGETFF : - IWCOPY((caddr_t)&fr_flags, data, sizeof(fr_flags)); + error = IWCOPY((caddr_t)&fr_flags, data, sizeof(fr_flags)); break; case SIOCINAFR : case SIOCRMAFR : @@ -560,55 +578,42 @@ int mode; break; case SIOCGETFS : { - struct friostat fio; - - bcopy((char *)frstats, (char *)fio.f_st, - sizeof(struct filterstats) * 2); - fio.f_fin[0] = ipfilter[0][0]; - fio.f_fin[1] = ipfilter[0][1]; - fio.f_fout[0] = ipfilter[1][0]; - fio.f_fout[1] = ipfilter[1][1]; - fio.f_acctin[0] = ipacct[0][0]; - fio.f_acctin[1] = ipacct[0][1]; - fio.f_acctout[0] = ipacct[1][0]; - fio.f_acctout[1] = ipacct[1][1]; - fio.f_auth = ipauth; - fio.f_active = fr_active; - fio.f_froute[0] = ipl_frouteok[0]; - fio.f_froute[1] = ipl_frouteok[1]; - fio.f_running = fr_running; - fio.f_groups[0][0] = ipfgroups[0][0]; - fio.f_groups[0][1] = ipfgroups[0][1]; - fio.f_groups[1][0] = ipfgroups[1][0]; - fio.f_groups[1][1] = ipfgroups[1][1]; - fio.f_groups[2][0] = ipfgroups[2][0]; - fio.f_groups[2][1] = ipfgroups[2][1]; -#ifdef IPFILTER_LOG - fio.f_logging = 1; -#else - fio.f_logging = 0; -#endif - fio.f_defpass = fr_pass; - strncpy(fio.f_version, ipfilter_version, - sizeof(fio.f_version)); - IWCOPY((caddr_t)&fio, data, sizeof(fio)); + friostat_t fio; + + fr_getstat(&fio); + error = IWCOPYPTR((caddr_t)&fio, data, sizeof(fio)); + if (error) + return EFAULT; break; } case SIOCFRZST : if (!(mode & FWRITE)) error = EPERM; else - frzerostats(data); + error = frzerostats(data); break; case SIOCIPFFL : if (!(mode & FWRITE)) error = EPERM; else { - IRCOPY(data, (caddr_t)&tmp, sizeof(tmp)); - tmp = frflush(unit, tmp); - IWCOPY((caddr_t)&tmp, data, sizeof(tmp)); + error = IRCOPY(data, (caddr_t)&tmp, sizeof(tmp)); + if (!error) { + tmp = frflush(unit, tmp); + error = IWCOPY((caddr_t)&tmp, data, + sizeof(tmp)); + } } break; + case SIOCSTLCK : + error = IRCOPY(data, (caddr_t)&tmp, sizeof(tmp)); + if (!error) { + fr_state_lock = tmp; + fr_nat_lock = tmp; + fr_frag_lock = tmp; + fr_auth_lock = tmp; + } else + error = EFAULT; + break; #ifdef IPFILTER_LOG case SIOCIPFFB : if (!(mode & FWRITE)) @@ -618,7 +623,10 @@ int mode; break; #endif /* IPFILTER_LOG */ case SIOCGFRST : - IWCOPY((caddr_t)ipfr_fragstats(), data, sizeof(ipfrstat_t)); + error = IWCOPYPTR((caddr_t)ipfr_fragstats(), data, + sizeof(ipfrstat_t)); + if (error) + return EFAULT; break; case SIOCAUTHW : case SIOCAUTHR : @@ -626,9 +634,6 @@ int mode; error = EPERM; break; } - case SIOCATHST : - error = fr_auth_ioctl(data, cmd, NULL, NULL); - break; case SIOCFRSYN : if (!(mode & FWRITE)) error = EPERM; @@ -666,6 +671,20 @@ void *ifp; for (f = ipfilter[1][fr_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; +#ifdef USE_INET6 + for (f = ipacct6[0][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipacct6[1][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipfilter6[0][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipfilter6[1][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; +#endif RWLOCK_EXIT(&ipf_mutex); ip_natsync(ifp); } @@ -673,7 +692,7 @@ void *ifp; static int frrequest(unit, req, data, set) int unit; -#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) u_long req; #else int req; @@ -686,12 +705,19 @@ caddr_t data; frentry_t frd; frdest_t *fdp; frgroup_t *fg = NULL; + u_int *p, *pp; int error = 0, in; u_int group; fp = &frd; - IRCOPY(data, (caddr_t)fp, sizeof(*fp)); + error = IRCOPYPTR(data, (caddr_t)fp, sizeof(*fp)); + if (error) + return EFAULT; fp->fr_ref = 0; +#if (BSD >= 199306) && defined(_KERNEL) + if ((securelevel > 0) && (fp->fr_func != NULL)) + return EPERM; +#endif /* * Check that the group number does exist and that if a head group @@ -708,10 +734,16 @@ caddr_t data; if (unit == IPL_LOGAUTH) ftail = fprev = &ipauth; - else if (fp->fr_flags & FR_ACCOUNT) + else if ((fp->fr_flags & FR_ACCOUNT) && (fp->fr_v == 4)) ftail = fprev = &ipacct[in][set]; - else if (fp->fr_flags & (FR_OUTQUE|FR_INQUE)) + else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) && (fp->fr_v == 4)) ftail = fprev = &ipfilter[in][set]; +#ifdef USE_INET6 + else if ((fp->fr_flags & FR_ACCOUNT) && (fp->fr_v == 6)) + ftail = fprev = &ipacct6[in][set]; + else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) && (fp->fr_v == 6)) + ftail = fprev = &ipfilter6[in][set]; +#endif else return ESRCH; @@ -724,13 +756,13 @@ caddr_t data; bzero((char *)frcache, sizeof(frcache[0]) * 2); if (*fp->fr_ifname) { - fp->fr_ifa = GETUNIT(fp->fr_ifname); + fp->fr_ifa = GETUNIT(fp->fr_ifname, fp->fr_v); if (!fp->fr_ifa) fp->fr_ifa = (void *)-1; } #if BSD >= 199306 if (*fp->fr_oifname) { - fp->fr_oifa = GETUNIT(fp->fr_oifname); + fp->fr_oifa = GETUNIT(fp->fr_oifname, fp->fr_v); if (!fp->fr_oifa) fp->fr_oifa = (void *)-1; } @@ -739,7 +771,7 @@ caddr_t data; fdp = &fp->fr_dif; fp->fr_flags &= ~FR_DUP; if (*fdp->fd_ifname) { - fdp->fd_ifp = GETUNIT(fdp->fd_ifname); + fdp->fd_ifp = GETUNIT(fdp->fd_ifname, fp->fr_v); if (!fdp->fd_ifp) fdp->fd_ifp = (struct ifnet *)-1; else @@ -748,7 +780,7 @@ caddr_t data; fdp = &fp->fr_tif; if (*fdp->fd_ifname) { - fdp->fd_ifp = GETUNIT(fdp->fd_ifname); + fdp->fd_ifp = GETUNIT(fdp->fd_ifname, fp->fr_v); if (!fdp->fd_ifp) fdp->fd_ifp = (struct ifnet *)-1; } @@ -757,9 +789,13 @@ caddr_t data; * Look for a matching filter rule, but don't include the next or * interface pointer in the comparison (fr_next, fr_ifa). */ + for (fp->fr_cksum = 0, p = (u_int *)&fp->fr_ip, pp = &fp->fr_cksum; + p < pp; p++) + fp->fr_cksum += *p; + for (; (f = *ftail); ftail = &f->fr_next) - if (bcmp((char *)&f->fr_ip, (char *)&fp->fr_ip, - FR_CMPSIZ) == 0) + if ((fp->fr_cksum == f->fr_cksum) && + !bcmp((char *)&f->fr_ip, (char *)&fp->fr_ip, FR_CMPSIZ)) break; /* @@ -768,16 +804,21 @@ caddr_t data; if (req == SIOCZRLST) { if (!f) return ESRCH; - IWCOPY((caddr_t)f, data, sizeof(*f)); + error = IWCOPYPTR((caddr_t)f, data, sizeof(*f)); + if (error) + return EFAULT; f->fr_hits = 0; f->fr_bytes = 0; return 0; } if (!f) { - if (req == SIOCINAFR || req == SIOCINIFR) { - ftail = fprev; + if (req != SIOCINAFR && req != SIOCINIFR) + while ((f = *ftail)) + ftail = &f->fr_next; + else { if (fp->fr_hits) { + ftail = fprev; while (--fp->fr_hits && (f = *ftail)) ftail = &f->fr_next; } @@ -785,11 +826,16 @@ caddr_t data; } } - if (req == SIOCDELFR || req == SIOCRMIFR) { + if (req == SIOCRMAFR || req == SIOCRMIFR) { if (!f) error = ESRCH; else { - if (f->fr_ref > 1) + /* + * Only return EBUSY if there is a group list, else + * it's probably just state information referencing + * the rule. + */ + if ((f->fr_ref > 1) && f->fr_grp) return EBUSY; if (fg && fg->fg_head) fg->fg_head->fr_ref--; @@ -800,7 +846,9 @@ caddr_t data; unit, set); fixskip(fprev, f, -1); *ftail = f->fr_next; - KFREE(f); + f->fr_next = NULL; + if (f->fr_ref == 0) + KFREE(f); } } else { if (f) @@ -929,8 +977,11 @@ struct ip *oip; fr_info_t *fin; { struct tcphdr *tcp, *tcp2; + int tlen = 0, hlen; struct mbuf *m; - int tlen = 0; +#ifdef USE_INET6 + ip6_t *ip6, *oip6 = (ip6_t *)oip; +#endif ip_t *ip; tcp = (struct tcphdr *)fin->fin_dp; @@ -946,52 +997,93 @@ fr_info_t *fin; if (m == NULL) return -1; - if (tcp->th_flags & TH_SYN) - tlen = 1; - m->m_len = sizeof(*tcp2) + sizeof(*ip); + tlen = oip->ip_len - fin->fin_hlen - (tcp->th_off << 2) + + ((tcp->th_flags & TH_SYN) ? 1 : 0) + + ((tcp->th_flags & TH_FIN) ? 1 : 0); + +#ifdef USE_INET6 + hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t); +#else + hlen = sizeof(ip_t); +#endif + m->m_len = sizeof(*tcp2) + hlen; # if BSD >= 199306 m->m_data += max_linkhdr; - m->m_pkthdr.len = sizeof(*tcp2) + sizeof(*ip); + m->m_pkthdr.len = m->m_len; m->m_pkthdr.rcvif = (struct ifnet *)0; # endif ip = mtod(m, struct ip *); - bzero((char *)ip, sizeof(*tcp2) + sizeof(*ip)); - tcp2 = (struct tcphdr *)(ip + 1); +# ifdef USE_INET6 + ip6 = (ip6_t *)ip; +# endif + bzero((char *)ip, sizeof(*tcp2) + hlen); + tcp2 = (struct tcphdr *)((char *)ip + hlen); - ip->ip_src.s_addr = oip->ip_dst.s_addr; - ip->ip_dst.s_addr = oip->ip_src.s_addr; - tcp2->th_dport = tcp->th_sport; tcp2->th_sport = tcp->th_dport; - tcp2->th_ack = ntohl(tcp->th_seq); - tcp2->th_ack += tlen; - tcp2->th_ack = htonl(tcp2->th_ack); + tcp2->th_dport = tcp->th_sport; + if (tcp->th_flags & TH_ACK) { + tcp2->th_seq = tcp->th_ack; + tcp2->th_flags = TH_RST; + } else { + tcp2->th_ack = ntohl(tcp->th_seq); + tcp2->th_ack += tlen; + tcp2->th_ack = htonl(tcp2->th_ack); + tcp2->th_flags = TH_RST|TH_ACK; + } tcp2->th_off = sizeof(*tcp2) >> 2; - tcp2->th_flags = TH_RST|TH_ACK; +# ifdef USE_INET6 + if (fin->fin_v == 6) { + ip6->ip6_plen = htons(sizeof(struct tcphdr)); + ip6->ip6_nxt = IPPROTO_TCP; + ip6->ip6_src = oip6->ip6_dst; + ip6->ip6_dst = oip6->ip6_src; + tcp2->th_sum = in6_cksum(m, IPPROTO_TCP, + sizeof(*ip6), sizeof(*tcp2)); + return send_ip(oip, fin, m); + } +# endif ip->ip_p = IPPROTO_TCP; ip->ip_len = htons(sizeof(struct tcphdr)); - tcp2->th_sum = in_cksum(m, sizeof(*ip) + sizeof(*tcp2)); - - ip->ip_id = oip->ip_id; - ip->ip_tos = oip->ip_tos; - ip->ip_len = sizeof(*ip) + sizeof(*tcp2); - - return send_ip(ip, fin, m); + ip->ip_src.s_addr = oip->ip_dst.s_addr; + ip->ip_dst.s_addr = oip->ip_src.s_addr; + tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2)); + ip->ip_len = hlen + sizeof(*tcp2); + return send_ip(oip, fin, m); } -static int send_ip(ip, fin, m) +static int send_ip(oip, fin, m) +ip_t *oip; fr_info_t *fin; struct mbuf *m; -ip_t *ip; { - ip->ip_v = IPVERSION; - ip->ip_hl = (sizeof(*ip) >> 2); + ip_t *ip; + + ip = mtod(m, ip_t *); + + ip->ip_v = fin->fin_v; + if (ip->ip_v == 4) { + ip->ip_hl = (sizeof(*oip) >> 2); + ip->ip_v = IPVERSION; + ip->ip_tos = oip->ip_tos; + ip->ip_id = oip->ip_id; + ip->ip_off = 0; # if (BSD < 199306) || defined(__sgi) - ip->ip_ttl = tcp_ttl; + ip->ip_ttl = tcp_ttl; # else - ip->ip_ttl = ip_defttl; + ip->ip_ttl = ip_defttl; # endif + ip->ip_sum = 0; + } +# ifdef USE_INET6 + else if (ip->ip_v == 6) { + ip6_t *ip6 = (ip6_t *)ip; + + ip6->ip6_hlim = 127; + return ip6_output(m, NULL, NULL, 0, NULL, NULL); + } +# endif # ifdef IPSEC m->m_pkthdr.rcvif = NULL; # endif @@ -1003,76 +1095,165 @@ int send_icmp_err(oip, type, fin, dst) ip_t *oip; int type; fr_info_t *fin; -struct in_addr dst; +int dst; { + int err, hlen = 0, xtra = 0, iclen, ohlen = 0, avail, code; + struct in_addr dst4; struct icmp *icmp; struct mbuf *m; - ip_t *nip; - int code; - - if ((oip->ip_p == IPPROTO_ICMP) && !(fin->fin_fi.fi_fl & FI_SHORT)) - switch (ntohs(fin->fin_data[0]) >> 8) - { - case ICMP_ECHO : - case ICMP_TSTAMP : - case ICMP_IREQ : - case ICMP_MASKREQ : - break; - default : - return 0; - } + void *ifp; +#ifdef USE_INET6 + ip6_t *ip6, *oip6 = (ip6_t *)oip; + struct in6_addr dst6; +#endif + ip_t *ip; + + if ((type < 0) || (type > ICMP_MAXTYPE)) + return -1; code = fin->fin_icode; +#ifdef USE_INET6 + if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int))) + return -1; +#endif + + avail = 0; + m = NULL; + ifp = fin->fin_ifp; + if (fin->fin_v == 4) { + if ((oip->ip_p == IPPROTO_ICMP) && + !(fin->fin_fi.fi_fl & FI_SHORT)) + switch (ntohs(fin->fin_data[0]) >> 8) + { + case ICMP_ECHO : + case ICMP_TSTAMP : + case ICMP_IREQ : + case ICMP_MASKREQ : + break; + default : + return 0; + } + # if (BSD < 199306) || defined(__sgi) - m = m_get(M_DONTWAIT, MT_HEADER); + avail = MLEN; + m = m_get(M_DONTWAIT, MT_HEADER); # else - m = m_gethdr(M_DONTWAIT, MT_HEADER); + avail = MHLEN; + m = m_gethdr(M_DONTWAIT, MT_HEADER); # endif - if (m == NULL) - return ENOBUFS; - m->m_len = sizeof(*nip) + sizeof(*icmp) + 8; + if (m == NULL) + return ENOBUFS; + + if (dst == 0) { + if (fr_ifpaddr(4, ifp, &dst4) == -1) + return -1; + } else + dst4.s_addr = oip->ip_dst.s_addr; + + hlen = sizeof(ip_t); + ohlen = oip->ip_hl << 2; + xtra = 8; + } + +#ifdef USE_INET6 + else if (fin->fin_v == 6) { + hlen = sizeof(ip6_t); + ohlen = sizeof(ip6_t); + type = icmptoicmp6types[type]; + if (type == ICMP6_DST_UNREACH) + code = icmptoicmp6unreach[code]; + + MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (!m) + return ENOBUFS; + + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return ENOBUFS; + } +# ifdef M_TRAILINGSPACE + m->m_len = 0; + avail = M_TRAILINGSPACE(m); +# else + avail = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN; +# endif + xtra = MIN(ntohs(oip6->ip6_plen) + sizeof(ip6_t), + avail - hlen - sizeof(*icmp) - max_linkhdr); + if (dst == 0) { + if (fr_ifpaddr(6, ifp, (struct in_addr *)&dst6) == -1) + return -1; + } else + dst6 = oip6->ip6_dst; + } +#endif + + iclen = hlen + sizeof(*icmp); # if BSD >= 199306 + avail -= (max_linkhdr + iclen); m->m_data += max_linkhdr; - m->m_pkthdr.len = sizeof(*nip) + sizeof(*icmp) + 8; m->m_pkthdr.rcvif = (struct ifnet *)0; -# endif - - bzero(mtod(m, char *), (size_t)sizeof(*nip) + sizeof(*icmp) + 8); - nip = mtod(m, ip_t *); - icmp = (struct icmp *)(nip + 1); - - nip->ip_p = IPPROTO_ICMP; - nip->ip_id = oip->ip_id; - nip->ip_sum = 0; - nip->ip_ttl = 60; - nip->ip_tos = oip->ip_tos; - nip->ip_len = sizeof(*nip) + sizeof(*icmp) + 8; - if (dst.s_addr == 0) { - if (fr_ifpaddr(fin->fin_ifp, &dst) == -1) - return -1; - } - nip->ip_src = dst; - nip->ip_dst = oip->ip_src; + if (xtra > avail) + xtra = avail; + iclen += xtra; + m->m_pkthdr.len = iclen; +#else + avail -= (m->m_off + iclen); + if (xtra > avail) + xtra = avail; + iclen += xtra; +#endif + m->m_len = iclen; + ip = mtod(m, ip_t *); + icmp = (struct icmp *)((char *)ip + hlen); + bzero((char *)ip, iclen); icmp->icmp_type = type; - icmp->icmp_code = code; + icmp->icmp_code = fin->fin_icode; icmp->icmp_cksum = 0; - bcopy((char *)oip, (char *)&icmp->icmp_ip, sizeof(*oip)); - bcopy((char *)oip + (oip->ip_hl << 2), - (char *)&icmp->icmp_ip + sizeof(*oip), 8); /* 64 bits */ -# ifndef sparc - { - register u_short __iplen, __ipoff; - ip_t *ip = &icmp->icmp_ip; +#ifdef icmp_nextmtu + if (type == ICMP_UNREACH && + fin->fin_icode == ICMP_UNREACH_NEEDFRAG && ifp) + icmp->icmp_nextmtu = htons(((struct ifnet *) ifp)->if_mtu); +#endif - __iplen = ip->ip_len; - __ipoff = ip->ip_off; - ip->ip_len = htons(__iplen); - ip->ip_off = htons(__ipoff); + if (avail) { + bcopy((char *)oip, (char *)&icmp->icmp_ip, MIN(ohlen, avail)); + avail -= MIN(ohlen, avail); } -# endif - icmp->icmp_cksum = ipf_cksum((u_short *)icmp, sizeof(*icmp) + 8); - return send_ip(nip, fin, m); + +#ifdef USE_INET6 + ip6 = (ip6_t *)ip; + if (fin->fin_v == 6) { + ip6->ip6_flow = 0; + ip6->ip6_plen = htons(iclen - hlen); + ip6->ip6_nxt = IPPROTO_ICMPV6; + ip6->ip6_hlim = 0; + ip6->ip6_src = dst6; + ip6->ip6_dst = oip6->ip6_src; + if (avail) + bcopy((char *)oip + ohlen, + (char *)&icmp->icmp_ip + ohlen, avail); + icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6, + sizeof(*ip6), iclen - hlen); + } else +#endif + { + ip->ip_src.s_addr = dst4.s_addr; + ip->ip_dst.s_addr = oip->ip_src.s_addr; + + if (avail > 8) + avail = 8; + if (avail) + bcopy((char *)oip + ohlen, + (char *)&icmp->icmp_ip + ohlen, avail); + icmp->icmp_cksum = ipf_cksum((u_short *)icmp, + sizeof(*icmp) + 8); + ip->ip_len = iclen; + ip->ip_p = IPPROTO_ICMP; + } + err = send_ip(oip, fin, m); + return err; } @@ -1122,6 +1303,16 @@ frdest_t *fdp; hlen = fin->fin_hlen; ip = mtod(m0, struct ip *); + +#ifdef USE_INET6 + if (ip->ip_v == 6) { + /* + * currently "to <if>" and "to <if>:ip#" are not supported + * for IPv6 + */ + return ip6_output(m0, NULL, NULL, 0, NULL, NULL); + } +#endif /* * Route packet. */ @@ -1190,27 +1381,43 @@ frdest_t *fdp; fin->fin_out = 1; if ((fin->fin_fr = ipacct[1][fr_active]) && (fr_scanlist(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT)) { - ATOMIC_INC(frstats[1].fr_acct); + ATOMIC_INCL(frstats[1].fr_acct); } fin->fin_fr = NULL; - if (!fr || !(fr->fr_flags & FR_RETMASK)) { + if (!fr || !(fr->fr_flags & FR_RETMASK)) (void) fr_checkstate(ip, fin); - (void) ip_natout(ip, fin); - } + (void) ip_natout(ip, fin); } else ip->ip_sum = 0; /* * If small enough for interface, can just send directly. */ if (ip->ip_len <= ifp->if_mtu) { +# if BSD >= 199306 + int i = 0; + +# ifdef MCLISREFERENCED + if ((m->m_flags & M_EXT) && MCLISREFERENCED(m)) +# else + if (m->m_flags & M_EXT) +# endif + i = 1; +# endif +# ifndef sparc ip->ip_id = htons(ip->ip_id); ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); +# endif if (!ip->ip_sum) ip->ip_sum = in_cksum(m, hlen); # if BSD >= 199306 error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro->ro_rt); + if (i) { + ip->ip_id = ntohs(ip->ip_id); + ip->ip_len = ntohs(ip->ip_len); + ip->ip_off = ntohs(ip->ip_off); + } # else error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst); # endif @@ -1279,7 +1486,9 @@ frdest_t *fdp; m->m_pkthdr.len = mhlen + len; m->m_pkthdr.rcvif = NULL; # endif +# ifndef sparc mhip->ip_off = htons((u_short)mhip->ip_off); +# endif mhip->ip_sum = 0; mhip->ip_sum = in_cksum(m, mhlen); *mnext = m; @@ -1322,16 +1531,44 @@ done: bad: if (error == EMSGSIZE) { sifp = fin->fin_ifp; - fin->fin_ifp = ifp; code = fin->fin_icode; fin->fin_icode = ICMP_UNREACH_NEEDFRAG; - (void) send_icmp_err(ip, ICMP_UNREACH, fin, ip->ip_dst); + fin->fin_ifp = ifp; + (void) send_icmp_err(ip, ICMP_UNREACH, fin, 1); fin->fin_ifp = sifp; fin->fin_icode = code; } m_freem(m); goto done; } + + +int fr_verifysrc(ipa, ifp) +struct in_addr ipa; +void *ifp; +{ + struct sockaddr_in *dst; + struct route iproute; + + bzero((char *)&iproute, sizeof(iproute)); + dst = (struct sockaddr_in *)&iproute.ro_dst; + dst->sin_family = AF_INET; + dst->sin_addr = ipa; +# if (BSD >= 199306) && !defined(__NetBSD__) && !defined(__bsdi__) && \ + !defined(__OpenBSD__) +# ifdef RTF_CLONING + rtalloc_ign(&iproute, RTF_CLONING); +# else + rtalloc_ign(&iproute, RTF_PRCLONING); +# endif +# else + rtalloc(&iproute); +# endif + if (iproute.ro_rt == NULL) + return 0; + return (ifp == iproute.ro_rt->rt_ifp); +} + #else /* #ifdef _KERNEL */ @@ -1346,7 +1583,6 @@ static int no_output __P((struct ifnet *ifp, struct mbuf *m, return 0; } - # ifdef __STDC__ # ifdef __sgi static int write_output __P((struct ifnet *ifp, struct mbuf *m, @@ -1364,33 +1600,28 @@ ip_t *ip; { # endif char fname[32]; - FILE *fp; + int fd; # if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ (defined(OpenBSD) && (OpenBSD >= 199603)) -# if defined __OpenBSD__ - sprintf(fname, "/var/run/%s", ifp->if_xname); -# else sprintf(fname, "/tmp/%s", ifp->if_xname); -# endif # else sprintf(fname, "/tmp/%s%d", ifp->if_name, ifp->if_unit); # endif - /* - * XXX - * This is still raceable, if the attacker gains the ability to - * erase the existing file in /tmp - */ - if ((fp = fopen(fname, "a"))) { - fwrite((char *)ip, ntohs(ip->ip_len), 1, fp); - fclose(fp); + fd = open(fname, O_WRONLY|O_APPEND); + if (fd == -1) { + perror("open"); + return -1; } + write(fd, (char *)ip, ntohs(ip->ip_len)); + close(fd); return 0; } -struct ifnet *get_unit(name) +struct ifnet *get_unit(name, v) char *name; +int v; { struct ifnet *ifp, **ifa; # if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ @@ -1469,11 +1700,7 @@ void init_ifp() (defined(OpenBSD) && (OpenBSD >= 199603)) for (ifa = ifneta; ifa && (ifp = *ifa); ifa++) { ifp->if_output = write_output; -# if defined(__OpenBSD__) - sprintf(fname, "/var/run/%s", ifp->if_xname); -# else sprintf(fname, "/tmp/%s", ifp->if_xname); -# endif fd = open(fname, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600); if (fd == -1) perror("open"); diff --git a/sys/netinet/ip_fil.h b/sys/netinet/ip_fil.h index 4ca811561b3..72b5025c19e 100644 --- a/sys/netinet/ip_fil.h +++ b/sys/netinet/ip_fil.h @@ -1,14 +1,14 @@ -/* $OpenBSD: ip_fil.h,v 1.20 2000/05/24 21:59:11 kjell Exp $ */ +/* $OpenBSD: ip_fil.h,v 1.21 2001/01/17 04:47:13 fgsch Exp $ */ /* - * Copyright (C) 1993-1998 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. * * @(#)ip_fil.h 1.35 6/5/96 - * $IPFilter: ip_fil.h,v 2.3.2.11 2000/05/22 06:57:50 darrenr Exp $ + * $IPFilter: ip_fil.h,v 2.29.2.4 2000/11/12 11:54:53 darrenr Exp $ */ #ifndef __IP_FIL_H__ @@ -39,58 +39,69 @@ #endif #if defined(__STDC__) || defined(__GNUC__) -# define SIOCADAFR _IOW('r', 60, struct frentry) -# define SIOCRMAFR _IOW('r', 61, struct frentry) +# define SIOCADAFR _IOW('r', 60, struct frentry *) +# define SIOCRMAFR _IOW('r', 61, struct frentry *) # define SIOCSETFF _IOW('r', 62, u_int) # define SIOCGETFF _IOR('r', 63, u_int) -# define SIOCGETFS _IOR('r', 64, struct friostat) +# define SIOCGETFS _IOWR('r', 64, struct friostat *) # define SIOCIPFFL _IOWR('r', 65, int) # define SIOCIPFFB _IOR('r', 66, int) -# define SIOCADIFR _IOW('r', 67, struct frentry) -# define SIOCRMIFR _IOW('r', 68, struct frentry) +# define SIOCADIFR _IOW('r', 67, struct frentry *) +# define SIOCRMIFR _IOW('r', 68, struct frentry *) # define SIOCSWAPA _IOR('r', 69, u_int) -# define SIOCINAFR _IOW('r', 70, struct frentry) -# define SIOCINIFR _IOW('r', 71, struct frentry) +# define SIOCINAFR _IOW('r', 70, struct frentry *) +# define SIOCINIFR _IOW('r', 71, struct frentry *) # define SIOCFRENB _IOW('r', 72, u_int) # 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 fr_info) -# define SIOCAUTHR _IOWR('r', 77, struct fr_info) -# define SIOCATHST _IOWR('r', 78, struct fr_authstat) +# define SIOCFRZST _IOWR('r', 74, struct friostat *) +# define SIOCZRLST _IOWR('r', 75, struct frentry *) +# define SIOCAUTHW _IOWR('r', 76, struct fr_info *) +# define SIOCAUTHR _IOWR('r', 77, struct fr_info *) +# define SIOCATHST _IOWR('r', 78, struct fr_authstat *) +# define SIOCSTLCK _IOWR('r', 79, u_int) +# define SIOCSTPUT _IOWR('r', 80, struct ipstate_save *) +# define SIOCSTGET _IOWR('r', 81, struct ipstate_save *) +# define SIOCSTGSZ _IOWR('r', 82, struct natget *) +# define SIOCGFRST _IOWR('r', 83, struct ipfrstat *) #else -# define SIOCADAFR _IOW(r, 60, struct frentry) -# define SIOCRMAFR _IOW(r, 61, struct frentry) +# define SIOCADAFR _IOW(r, 60, struct frentry *) +# define SIOCRMAFR _IOW(r, 61, struct frentry *) # define SIOCSETFF _IOW(r, 62, u_int) # define SIOCGETFF _IOR(r, 63, u_int) -# define SIOCGETFS _IOR(r, 64, struct friostat) +# define SIOCGETFS _IOWR(r, 64, struct friostat *) # define SIOCIPFFL _IOWR(r, 65, int) # define SIOCIPFFB _IOR(r, 66, int) -# define SIOCADIFR _IOW(r, 67, struct frentry) -# define SIOCRMIFR _IOW(r, 68, struct frentry) +# define SIOCADIFR _IOW(r, 67, struct frentry *) +# define SIOCRMIFR _IOW(r, 68, struct frentry *) # define SIOCSWAPA _IOR(r, 69, u_int) -# define SIOCINAFR _IOW(r, 70, struct frentry) -# define SIOCINIFR _IOW(r, 71, struct frentry) +# define SIOCINAFR _IOW(r, 70, struct frentry *) +# define SIOCINIFR _IOW(r, 71, struct frentry *) # define SIOCFRENB _IOW(r, 72, u_int) # 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 fr_info) -# define SIOCAUTHR _IOWR(r, 77, struct fr_info) -# define SIOCATHST _IOWR(r, 78, struct fr_authstat) +# define SIOCFRZST _IOWR(r, 74, struct friostat *) +# define SIOCZRLST _IOWR(r, 75, struct frentry *) +# define SIOCAUTHW _IOWR(r, 76, struct fr_info *) +# define SIOCAUTHR _IOWR(r, 77, struct fr_info *) +# define SIOCATHST _IOWR(r, 78, struct fr_authstat *) +# define SIOCSTLCK _IOWR(r, 79, u_int) +# define SIOCSTPUT _IOWR(r, 80, struct ipstate_save *) +# define SIOCSTGET _IOWR(r, 81, struct ipstate_save *) +# define SIOCSTGSZ _IOWR(r, 82, struct natget *) +# define SIOCGFRST _IOWR(r, 83, struct ipfrstat *) #endif #define SIOCADDFR SIOCADAFR #define SIOCDELFR SIOCRMAFR #define SIOCINSFR SIOCINAFR + typedef struct fr_ip { - u_char fi_v:4; /* IP version */ - u_char fi_fl:4; /* packet flags */ - u_char fi_tos; /* IP packet TOS */ - u_char fi_ttl; /* IP packet TTL */ - u_char fi_p; /* IP packet protocol */ - struct in_addr fi_src; /* source address from packet */ - struct in_addr fi_dst; /* destination address from packet */ + u_32_t fi_v:4; /* IP version */ + u_32_t fi_fl:4; /* packet flags */ + u_32_t fi_tos:8; /* IP packet TOS */ + u_32_t fi_ttl:8; /* IP packet TTL */ + u_32_t fi_p:8; /* IP packet protocol */ + union i6addr fi_src; /* source address from packet */ + union i6addr fi_dst; /* destination address from packet */ u_32_t fi_optmsk; /* bitmask composed from IP options */ u_short fi_secmsk; /* bitmask composed from IP security options */ u_short fi_auth; /* authentication code from IP sec. options */ @@ -102,13 +113,21 @@ typedef struct fr_ip { #define FI_SHORT (FF_SHORT >> 24) #define FI_CMP (FI_OPTIONS|FI_TCPUDP|FI_SHORT) +#define fi_saddr fi_src.in4.s_addr +#define fi_daddr fi_dst.in4.s_addr + + /* * These are both used by the state and NAT code to indicate that one port or * the other should be treated as a wildcard. */ #define FI_W_SPORT 0x00000100 #define FI_W_DPORT 0x00000200 -#define FI_WILD (FI_W_SPORT|FI_W_DPORT) +#define FI_WILDP (FI_W_SPORT|FI_W_DPORT) +#define FI_W_SADDR 0x00000400 +#define FI_W_DADDR 0x00000800 +#define FI_WILDA (FI_W_SADDR|FI_W_DADDR) +#define FI_NEWFR 0x00001000 typedef struct fr_info { void *fin_ifp; /* interface packet is `on' */ @@ -121,18 +140,22 @@ typedef struct fr_info { /* From here on is packet specific */ u_char fin_icode; /* ICMP error to return */ u_short fin_rule; /* rule # last matched */ - u_short fin_group; /* group number, -1 for none */ + u_32_t fin_group; /* group number, -1 for none */ struct frentry *fin_fr; /* last matching rule */ char *fin_dp; /* start of data past IP header */ u_short fin_dlen; /* length of data portion of packet */ u_short fin_id; /* IP packet id field */ void *fin_mp; /* pointer to pointer to mbuf */ -#if SOLARIS && defined(_KERNEL) +#if SOLARIS void *fin_qfm; /* pointer to mblk where pkt starts */ void *fin_qif; #endif + u_short fin_plen; + u_short fin_off; } fr_info_t; +#define fin_v fin_fi.fi_v + /* * Size for compares on fr_info structures */ @@ -149,10 +172,30 @@ typedef struct frdest { char fd_ifname[IFNAMSIZ]; } frdest_t; +typedef struct frpcmp { + int frp_cmp; /* data for port comparisons */ + u_short frp_port; /* top port for <> and >< */ + u_short frp_top; /* top port for <> and >< */ +} frpcmp_t; + +typedef struct frtuc { + u_char ftu_tcpfm; /* tcp flags mask */ + u_char ftu_tcpf; /* tcp flags */ + frpcmp_t ftu_src; + frpcmp_t ftu_dst; +} frtuc_t; + +#define ftu_scmp ftu_src.frp_cmp +#define ftu_dcmp ftu_dst.frp_cmp +#define ftu_sport ftu_src.frp_port +#define ftu_dport ftu_dst.frp_port +#define ftu_stop ftu_src.frp_top +#define ftu_dtop ftu_dst.frp_top + typedef struct frentry { struct frentry *fr_next; - u_short fr_group; /* group to which this rule belongs */ - u_short fr_grhead; /* group # which this rule starts */ + u_32_t fr_group; /* group to which this rule belongs */ + u_32_t fr_grhead; /* group # which this rule starts */ struct frentry *fr_grp; int fr_ref; /* reference count - for grouping */ void *fr_ifa; @@ -171,22 +214,16 @@ typedef struct frentry { struct fr_ip fr_ip; struct fr_ip fr_mip; /* mask structure */ - u_char fr_tcpfm; /* tcp flags mask */ - u_char fr_tcpf; /* tcp flags */ u_short fr_icmpm; /* data for ICMP packets (mask) */ u_short fr_icmp; - u_char fr_scmp; /* data for port comparisons */ - u_char fr_dcmp; - u_short fr_dport; - u_short fr_sport; - u_short fr_stop; /* top port for <> and >< */ - u_short fr_dtop; /* top port for <> and >< */ + frtuc_t fr_tuc; u_32_t fr_flags; /* per-rule flags && options (see below) */ - u_short fr_skip; /* # of rules to skip */ - u_short fr_loglevel; /* syslog log facility + priority */ + u_int fr_skip; /* # of rules to skip */ + u_int fr_loglevel; /* syslog log facility + priority */ int (*fr_func) __P((int, ip_t *, fr_info_t *)); /* call this function */ + int fr_sap; /* For solaris only */ u_char fr_icode; /* return ICMP code */ char fr_ifname[IFNAMSIZ]; #if BSD >= 199306 @@ -194,15 +231,25 @@ typedef struct frentry { #endif struct frdest fr_tif; /* "to" interface */ struct frdest fr_dif; /* duplicate packet interfaces */ + u_int fr_cksum; /* checksum on filter rules for performance */ } frentry_t; +#define fr_v fr_ip.fi_v #define fr_proto fr_ip.fi_p #define fr_ttl fr_ip.fi_ttl #define fr_tos fr_ip.fi_tos -#define fr_dst fr_ip.fi_dst -#define fr_src fr_ip.fi_src -#define fr_dmsk fr_mip.fi_dst -#define fr_smsk fr_mip.fi_src +#define fr_tcpfm fr_tuc.ftu_tcpfm +#define fr_tcpf fr_tuc.ftu_tcpf +#define fr_scmp fr_tuc.ftu_scmp +#define fr_dcmp fr_tuc.ftu_dcmp +#define fr_dport fr_tuc.ftu_dport +#define fr_sport fr_tuc.ftu_sport +#define fr_stop fr_tuc.ftu_stop +#define fr_dtop fr_tuc.ftu_dtop +#define fr_dst fr_ip.fi_dst.in4 +#define fr_src fr_ip.fi_src.in4 +#define fr_dmsk fr_mip.fi_dst.in4 +#define fr_smsk fr_mip.fi_src.in4 #ifndef offsetof #define offsetof(t,m) (int)((&((t *)0L)->m)) @@ -289,6 +336,8 @@ typedef struct filterstats { u_long fr_chit; /* cached hit */ u_long fr_tcpbad; /* TCP checksum check failures */ u_long fr_pull[2]; /* good and bad pullup attempts */ + u_long fr_badsrc; /* source received doesn't match route */ + u_long fr_badttl; /* TTL in packet doesn't reach minimum */ #if SOLARIS u_long fr_notdata; /* PROTO/PCPROTO that have no data */ u_long fr_nodata; /* mblks that have no data */ @@ -297,6 +346,7 @@ typedef struct filterstats { u_long fr_drop; /* packets dropped - no info for them! */ u_long fr_copy; /* messages copied due to db_ref > 1 */ #endif + u_long fr_ipv6[2]; /* IPv6 packets in/out */ } filterstats_t; /* @@ -308,6 +358,10 @@ typedef struct friostat { struct frentry *f_fout[2]; struct frentry *f_acctin[2]; struct frentry *f_acctout[2]; + struct frentry *f_fin6[2]; + struct frentry *f_fout6[2]; + struct frentry *f_acctin6[2]; + struct frentry *f_acctout6[2]; struct frentry *f_auth; struct frgroup *f_groups[3][2]; u_long f_froute[2]; @@ -315,11 +369,8 @@ typedef struct friostat { char f_active; /* 1 or 0 - active rule set */ char f_running; /* 1 if running, else 0 */ char f_logging; /* 1 if enabled, else 0 */ -#if !SOLARIS && defined(sun) - char f_version[17]; /* version string */ -#else char f_version[32]; /* version string */ -#endif + int f_locks[4]; } friostat_t; typedef struct optlist { @@ -332,7 +383,7 @@ typedef struct optlist { * Group list structure. */ typedef struct frgroup { - u_short fg_num; + u_32_t fg_num; struct frgroup *fg_next; struct frentry *fg_head; struct frentry **fg_start; @@ -365,18 +416,16 @@ typedef struct ipflog { #endif u_char fl_plen; /* extra data after hlen */ u_char fl_hlen; /* length of IP headers saved */ - u_short fl_rule; /* assume never more than 64k rules, total */ - u_short fl_group; u_short fl_loglevel; /* syslog log level */ + u_32_t fl_rule; + u_32_t fl_group; u_32_t fl_flags; u_32_t fl_lflags; } ipflog_t; -#if !defined(__OpenBSD__) -# ifndef ICMP_UNREACH_FILTER -# define ICMP_UNREACH_FILTER 13 -# endif +#ifndef ICMP_UNREACH_FILTER +# define ICMP_UNREACH_FILTER 13 #endif #ifndef IPF_LOGGING @@ -438,7 +487,7 @@ extern int send_reset __P((ip_t *, struct ifnet *)); extern int icmp_error __P((ip_t *, struct ifnet *)); extern int ipf_log __P((void)); extern int ipfr_fastroute __P((ip_t *, fr_info_t *, frdest_t *)); -extern struct ifnet *get_unit __P((char *)); +extern struct ifnet *get_unit __P((char *, int)); # if defined(__NetBSD__) || defined(__OpenBSD__) || \ (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) extern int iplioctl __P((dev_t, u_long, caddr_t, int)); @@ -459,7 +508,7 @@ extern int ipflog_clear __P((minor_t)); extern int ipflog_read __P((minor_t, struct uio *)); extern int ipflog __P((u_int, ip_t *, fr_info_t *, mb_t *)); extern int ipllog __P((int, fr_info_t *, void **, size_t *, int *, int)); -extern int send_icmp_err __P((ip_t *, int, fr_info_t *, struct in_addr)); +extern int send_icmp_err __P((ip_t *, int, fr_info_t *, int)); extern int send_reset __P((ip_t *, fr_info_t *)); # if SOLARIS extern int fr_check __P((ip_t *, int, void *, int, qif_t *, mb_t **)); @@ -473,15 +522,13 @@ extern int iplioctl __P((dev_t, int, int *, int, cred_t *, int *)); extern int iplopen __P((dev_t *, int, int, cred_t *)); extern int iplclose __P((dev_t, int, int, cred_t *)); extern int ipfsync __P((void)); -extern int ipfr_fastroute __P((qif_t *, ip_t *, mblk_t *, mblk_t **, - fr_info_t *, frdest_t *)); +extern int ipfr_fastroute __P((ip_t *, mblk_t *, mblk_t **, + fr_info_t *, frdest_t *)); extern void copyin_mblk __P((mblk_t *, size_t, size_t, char *)); extern void copyout_mblk __P((mblk_t *, size_t, size_t, char *)); extern int fr_qin __P((queue_t *, mblk_t *)); extern int fr_qout __P((queue_t *, mblk_t *)); -# ifdef IPFILTER_LOG extern int iplread __P((dev_t, struct uio *, cred_t *)); -# endif # else /* SOLARIS */ extern int fr_check __P((ip_t *, int, void *, int, mb_t **)); extern int (*fr_checkp) __P((ip_t *, int, void *, int, mb_t **)); @@ -535,35 +582,51 @@ extern int iplread(struct inode *, struct file *, char *, int); # endif /* SOLARIS */ #endif /* #ifndef _KERNEL */ +extern char *memstr __P((char *, char *, int, int)); extern void fixskip __P((frentry_t **, frentry_t *, int)); extern int countbits __P((u_32_t)); extern int ipldetach __P((void)); -extern u_short fr_tcpsum __P((mb_t *, ip_t *, tcphdr_t *)); -extern int fr_scanlist __P((u_32_t, ip_t *, fr_info_t *, void *)); extern u_short ipf_cksum __P((u_short *, int)); -extern int fr_copytolog __P((int, char *, int)); -extern void fr_forgetifp __P((void *)); +extern int ircopyptr __P((void *, void *, size_t)); +extern int iwcopyptr __P((void *, void *, size_t)); + extern int frflush __P((minor_t, int)); extern void frsync __P((void)); -extern frgroup_t *fr_addgroup __P((u_int, frentry_t *, minor_t, int)); -extern frgroup_t *fr_findgroup __P((u_int, u_32_t, minor_t, int, frgroup_t ***)); -extern void fr_delgroup __P((u_int, u_32_t, minor_t, int)); +extern frgroup_t *fr_addgroup __P((u_32_t, frentry_t *, minor_t, int)); +extern void fr_delgroup __P((u_32_t, u_32_t, minor_t, int)); +extern frgroup_t *fr_findgroup __P((u_32_t, u_32_t, minor_t, int, + frgroup_t ***)); + +extern int fr_copytolog __P((int, char *, int)); +extern void fr_forgetifp __P((void *)); +extern void fr_getstat __P((struct friostat *)); +extern int fr_ifpaddr __P((int, void *, struct in_addr *)); +extern int fr_lock __P((caddr_t, int *)); extern void fr_makefrip __P((int, ip_t *, fr_info_t *)); -extern int fr_ifpaddr __P((void *, struct in_addr *)); -extern char *memstr __P((char *, char *, int, int)); +extern u_short fr_tcpsum __P((mb_t *, ip_t *, tcphdr_t *)); +extern int fr_scanlist __P((u_32_t, ip_t *, fr_info_t *, void *)); +extern int fr_tcpudpchk __P((frtuc_t *, fr_info_t *)); +extern int fr_verifysrc __P((struct in_addr, void *)); + extern int ipl_unreach; extern int fr_running; extern u_long ipl_frouteok[2]; extern int fr_pass; extern int fr_flags; extern int fr_active; +extern int fr_chksrc; +extern int fr_minttl; +extern int fr_minttllog; extern fr_info_t frcache[2]; extern char ipfilter_version[]; -#ifdef IPFILTER_LOG extern iplog_t **iplh[IPL_LOGMAX+1], *iplt[IPL_LOGMAX+1]; extern size_t iplused[IPL_LOGMAX + 1]; -#endif extern struct frentry *ipfilter[2][2], *ipacct[2][2]; +#ifdef USE_INET6 +extern struct frentry *ipfilter6[2][2], *ipacct6[2][2]; +extern int icmptoicmp6types[ICMP_MAXTYPE+1]; +extern int icmptoicmp6unreach[ICMP_MAX_UNREACH]; +#endif extern struct frgroup *ipfgroups[3][2]; extern struct filterstats frstats[]; diff --git a/sys/netinet/ip_frag.c b/sys/netinet/ip_frag.c index 6152e243353..4f5bd7822ad 100644 --- a/sys/netinet/ip_frag.c +++ b/sys/netinet/ip_frag.c @@ -1,15 +1,15 @@ -/* $OpenBSD: ip_frag.c,v 1.19 2000/09/07 19:45:04 art Exp $ */ +/* $OpenBSD: ip_frag.c,v 1.20 2001/01/17 04:47:13 fgsch Exp $ */ /* - * Copyright (C) 1993-1998 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[] = "@(#)$IPFilter: ip_frag.c,v 2.4.2.5 2000/06/06 15:50:48 darrenr Exp $"; +static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$IPFilter: ip_frag.c,v 2.10.2.7 2000/11/27 10:26:56 darrenr Exp $"; #endif #if defined(KERNEL) && !defined(_KERNEL) @@ -26,7 +26,7 @@ static const char rcsid[] = "@(#)$IPFilter: ip_frag.c,v 2.4.2.5 2000/06/06 15:50 # include <string.h> # include <stdlib.h> #endif -#if defined(KERNEL) && (__FreeBSD_version >= 220000) +#if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000) # include <sys/filio.h> # include <sys/fcntl.h> #else @@ -69,35 +69,40 @@ static const char rcsid[] = "@(#)$IPFilter: ip_frag.c,v 2.4.2.5 2000/06/06 15:50 #include <netinet/tcp.h> #include <netinet/udp.h> #include <netinet/ip_icmp.h> -#include <netinet/ip_fil_compat.h> +#include "netinet/ip_compat.h" #include <netinet/tcpip.h> -#include <netinet/ip_fil.h> -#include <netinet/ip_proxy.h> -#include <netinet/ip_nat.h> -#include <netinet/ip_frag.h> -#include <netinet/ip_state.h> -#include <netinet/ip_auth.h> +#include "netinet/ip_fil.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_nat.h" +#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 +# endif extern struct callout_handle ipfr_slowtimer_ch; # endif #endif -#ifdef __OpenBSD__ -#include <sys/timeout.h> -extern struct timeout ipfr_slowtimer_to; +#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) +# include <sys/callout.h> +extern struct callout ipfr_slowtimer_ch; +#elif defined(__OpenBSD__) +extern struct timeout ipfr_slowtimer_ch; #endif -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 */ +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; + #ifdef _KERNEL # if SOLARIS2 >= 7 extern timeout_id_t ipfr_timer_id; @@ -155,6 +160,7 @@ ipfr_t *table[]; idx += ip->ip_src.s_addr; frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; idx += ip->ip_dst.s_addr; + frag.ipfr_ifp = fin->fin_ifp; idx *= 127; idx %= IPFT_SIZE; @@ -164,7 +170,7 @@ ipfr_t *table[]; for (fp = &table[idx]; (fra = *fp); fp = &fra->ipfr_next) if (!bcmp((char *)&frag.ipfr_src, (char *)&fra->ipfr_src, IPFR_CMPSZ)) { - ATOMIC_INC(ipfr_stats.ifs_exists); + ATOMIC_INCL(ipfr_stats.ifs_exists); return NULL; } @@ -174,12 +180,12 @@ ipfr_t *table[]; */ KMALLOC(fra, ipfr_t *); if (fra == NULL) { - ATOMIC_INC(ipfr_stats.ifs_nomem); + ATOMIC_INCL(ipfr_stats.ifs_nomem); return NULL; } if ((fra->ipfr_rule = fin->fin_fr) != NULL) { - ATOMIC_INC(fin->fin_fr->fr_ref); + ATOMIC_INC32(fin->fin_fr->fr_ref); } @@ -199,8 +205,8 @@ ipfr_t *table[]; * Compute the offset of the expected start of the next packet. */ fra->ipfr_off = (ip->ip_off & IP_OFFMASK) + (fin->fin_dlen >> 3); - ATOMIC_INC(ipfr_stats.ifs_new); - ATOMIC_INC(ipfr_inuse); + ATOMIC_INCL(ipfr_stats.ifs_new); + ATOMIC_INC32(ipfr_inuse); return fra; } @@ -212,6 +218,8 @@ u_int pass; { ipfr_t *ipf; + if ((ip->ip_v != 4) || (fr_frag_lock)) + return -1; WRITE_ENTER(&ipf_frag); ipf = ipfr_new(ip, fin, pass, ipfr_heads); RWLOCK_EXIT(&ipf_frag); @@ -227,6 +235,8 @@ nat_t *nat; { ipfr_t *ipf; + if ((ip->ip_v != 4) || (fr_frag_lock)) + return -1; WRITE_ENTER(&ipf_natfrag); ipf = ipfr_new(ip, fin, pass, ipfr_nattab); if (ipf != NULL) { @@ -265,6 +275,7 @@ ipfr_t *table[]; idx += ip->ip_src.s_addr; frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; idx += ip->ip_dst.s_addr; + frag.ipfr_ifp = fin->fin_ifp; idx *= 127; idx %= IPFT_SIZE; @@ -300,7 +311,7 @@ ipfr_t *table[]; else f->ipfr_off = atoff; } - ATOMIC_INC(ipfr_stats.ifs_hits); + ATOMIC_INCL(ipfr_stats.ifs_hits); return f; } return NULL; @@ -317,6 +328,8 @@ fr_info_t *fin; nat_t *nat; ipfr_t *ipf; + if ((ip->ip_v != 4) || (fr_frag_lock)) + return NULL; READ_ENTER(&ipf_natfrag); ipf = ipfr_lookup(ip, fin, ipfr_nattab); if (ipf != NULL) { @@ -345,6 +358,8 @@ fr_info_t *fin; frentry_t *fr = NULL; ipfr_t *fra; + if ((ip->ip_v != 4) || (fr_frag_lock)) + return NULL; READ_ENTER(&ipf_frag); fra = ipfr_lookup(ip, fin, ipfr_heads); if (fra != NULL) @@ -380,7 +395,7 @@ ipfr_t *fra; fr = fra->ipfr_rule; if (fr != NULL) { - ATOMIC_DEC(fr->fr_ref); + ATOMIC_DEC32(fr->fr_ref); if (fr->fr_ref == 0) KFREE(fr); } @@ -427,19 +442,7 @@ void ipfr_unload() #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) -# if defined(SOLARIS2) && (SOLARIS2 < 7) -void ipfr_slowtimer() -# else -void ipfr_slowtimer __P((void *ptr)) -# endif -# else -int ipfr_slowtimer() -# endif +void ipfr_fragexpire() { ipfr_t **fp, *fra; nat_t *nat; @@ -447,18 +450,11 @@ int ipfr_slowtimer() #if defined(_KERNEL) # if !SOLARIS int s; -# else - extern int fr_running; - - if (fr_running <= 0) - return; # endif #endif - READ_ENTER(&ipf_solaris); -#ifdef __sgi - ipfilter_sgi_intfsync(); -#endif + if (fr_frag_lock) + return; SPL_NET(s); WRITE_ENTER(&ipf_frag); @@ -474,8 +470,8 @@ int ipfr_slowtimer() if (fra->ipfr_ttl == 0) { *fp = fra->ipfr_next; ipfr_delete(fra); - ATOMIC_INC(ipfr_stats.ifs_expire); - ATOMIC_DEC(ipfr_inuse); + ATOMIC_INCL(ipfr_stats.ifs_expire); + ATOMIC_DEC32(ipfr_inuse); } else fp = &fra->ipfr_next; } @@ -494,8 +490,8 @@ int ipfr_slowtimer() for (fp = &ipfr_nattab[idx]; (fra = *fp); ) { --fra->ipfr_ttl; if (fra->ipfr_ttl == 0) { - ATOMIC_INC(ipfr_stats.ifs_expire); - ATOMIC_DEC(ipfr_inuse); + ATOMIC_INCL(ipfr_stats.ifs_expire); + ATOMIC_DEC32(ipfr_inuse); nat = fra->ipfr_data; if (nat != NULL) { if (nat->nat_data == fra) @@ -509,25 +505,59 @@ int ipfr_slowtimer() 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 -# if (__FreeBSD_version >= 300000) - ipfr_slowtimer_ch = timeout(ipfr_slowtimer, NULL, hz/2); -# elif defined(__OpenBSD__) - timeout_add(&ipfr_slowtimer_to, hz/2); +# if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000) + callout_reset(&ipfr_slowtimer_ch, hz / 2, ipfr_slowtimer, NULL); +# else +# if defined(__OpenBSD__) + timeout_add(&ipfr_slowtimer_ch, hz/2); # else +# if (__FreeBSD_version >= 300000) + ipfr_slowtimer_ch = timeout(ipfr_slowtimer, NULL, hz/2); +# else timeout(ipfr_slowtimer, NULL, hz/2); -# endif -# endif -# if (BSD < 199306) && !defined(__sgi) +# endif +# if (BSD < 199306) && !defined(__sgi) return 0; -# endif -# endif - RWLOCK_EXIT(&ipf_solaris); +# endif /* FreeBSD */ +# endif /* OpenBSD */ +# endif /* NetBSD */ +# endif /* SOLARIS */ } #endif /* defined(_KERNEL) */ diff --git a/sys/netinet/ip_frag.h b/sys/netinet/ip_frag.h index a2efb5908eb..352a4601063 100644 --- a/sys/netinet/ip_frag.h +++ b/sys/netinet/ip_frag.h @@ -1,14 +1,14 @@ -/* $OpenBSD: ip_frag.h,v 1.12 2000/03/13 23:40:18 kjell Exp $ */ +/* $OpenBSD: ip_frag.h,v 1.13 2001/01/17 04:47:14 fgsch Exp $ */ /* - * Copyright (C) 1993-1998 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. * * @(#)ip_frag.h 1.5 3/24/96 - * $IPFilter: ip_frag.h,v 2.2 1999/08/06 06:26:38 darrenr Exp $ + * $IPFilter: ip_frag.h,v 2.4.2.2 2000/11/10 13:10:54 darrenr Exp $ */ #ifndef __IP_FRAG_H__ @@ -21,6 +21,7 @@ typedef struct ipfr { void *ipfr_data; struct in_addr ipfr_src; struct in_addr ipfr_dst; + void *ipfr_ifp; u_short ipfr_id; u_char ipfr_p; u_char ipfr_tos; @@ -44,6 +45,7 @@ typedef struct ipfrstat { #define IPFR_CMPSZ (4 + 4 + 2 + 1 + 1) extern int fr_ipfrttl; +extern int fr_frag_lock; extern ipfrstat_t *ipfr_fragstats __P((void)); extern int ipfr_newfrag __P((ip_t *, fr_info_t *, u_int)); extern int ipfr_nat_newfrag __P((ip_t *, fr_info_t *, u_int, struct nat *)); @@ -51,6 +53,7 @@ extern nat_t *ipfr_nat_knownfrag __P((ip_t *, fr_info_t *)); extern frentry_t *ipfr_knownfrag __P((ip_t *, fr_info_t *)); extern void ipfr_forget __P((void *)); extern void ipfr_unload __P((void)); +extern void ipfr_fragexpire __P((void)); #if (BSD >= 199306) || SOLARIS || defined(__sgi) # if defined(SOLARIS2) && (SOLARIS2 < 7) @@ -60,6 +63,6 @@ extern void ipfr_slowtimer __P((void *)); # endif #else extern int ipfr_slowtimer __P((void)); -#endif +#endif /* (BSD >= 199306) || SOLARIS */ #endif /* __IP_FIL_H__ */ diff --git a/sys/netinet/ip_ftp_pxy.c b/sys/netinet/ip_ftp_pxy.c index 16ec4c3edf5..c450ddb7c0d 100644 --- a/sys/netinet/ip_ftp_pxy.c +++ b/sys/netinet/ip_ftp_pxy.c @@ -1,8 +1,10 @@ -/* $OpenBSD: ip_ftp_pxy.c,v 1.11 2000/08/10 05:50:26 kjell Exp $ */ +/* $OpenBSD: ip_ftp_pxy.c,v 1.12 2001/01/17 04:47:14 fgsch Exp $ */ /* * Simple FTP transparent proxy for in-kernel use. For use with the NAT * code. + * + * $IPFilter: ip_ftp_pxy.c,v 2.7.2.20 2000/12/02 00:15:06 darrenr Exp $ */ #if SOLARIS && defined(_KERNEL) extern kmutex_t ipf_rw; @@ -20,21 +22,25 @@ extern kmutex_t ipf_rw; #define IPF_MAXPORTLEN 30 #define IPF_MIN227LEN 39 #define IPF_MAX227LEN 51 -#define IPF_FTPBUFSZ MAX(96,IPF_MAX227LEN) /* This *MUST* be >= 51! */ +#define IPF_FTPBUFSZ 96 /* This *MUST* be >= 53! */ +int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); +int ippr_ftp_complete __P((char *, size_t)); +int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_ftp_init __P((void)); int ippr_ftp_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); -int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); -int ippr_ftp_portmsg __P((fr_info_t *, ip_t *, nat_t *)); -int ippr_ftp_pasvmsg __P((fr_info_t *, ip_t *, nat_t *)); -int ippr_ftp_complete __P((char *, size_t, char *)); - -u_short ipf_ftp_atoi __P((char **)); +int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); +int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); +int ippr_ftp_process __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); +int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); +int ippr_ftp_valid __P((char *, size_t)); +u_short ippr_ftp_atoi __P((char **)); static frentry_t natfr; int ippr_ftp_pasvonly = 0; +int ippr_ftp_insecure = 0; /* @@ -49,76 +55,6 @@ int ippr_ftp_init() } -int ippr_ftp_complete(buf, len, cbuf) -char *buf, *cbuf; -size_t len; -{ - register char *s, c; - register size_t i; - - if (len < 5) - return -1; - s = buf; - c = *s++; - i = len - 1; - - if (isdigit(c)) { - c = *s++; - i--; - if (isdigit(c)) { - c = *s++; - i--; - if (isdigit(c)) { - c = *s++; - i--; - if (c != '-' && c != ' ') - return -1; - } else - return -1; - } else - return -1; - } else if (isalpha(c)) { - cbuf[0] = toupper(c); - c = *s++; - i--; - if (isalpha(c)) { - cbuf[1] = toupper(c); - c = *s++; - i--; - if (isalpha(c)) { - cbuf[2] = toupper(c); - c = *s++; - i--; - if (isalpha(c)) { - cbuf[3] = toupper(c); - c = *s++; - cbuf[4] = c; - i--; - if (c != ' ') - return -1; - } else - return -1; - } else - return -1; - } else - return -1; - } else - return -1; - - for (; i && (c = *s); i--, s++) { - if ((c == '\r') && (i != 2)) - return -1; - if ((c == '\n') && (i != 1)) - return -1; - else if ((i == 2) && (c != '\r')) - return -1; - else if ((i == 1) && (c != '\n')) - return -1; - } - return i; -} - - int ippr_ftp_new(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; @@ -126,6 +62,7 @@ ap_session_t *aps; nat_t *nat; { ftpinfo_t *ftp; + ftpside_t *f; KMALLOC(ftp, ftpinfo_t *); if (ftp == NULL) @@ -133,54 +70,32 @@ nat_t *nat; aps->aps_data = ftp; aps->aps_psiz = sizeof(ftpinfo_t); - ftp->ftp_passok = 0; + bzero((char *)ftp, sizeof(*ftp)); + f = &ftp->ftp_side[0]; + f->ftps_rptr = f->ftps_buf; + f->ftps_wptr = f->ftps_buf; + f = &ftp->ftp_side[1]; + f->ftps_rptr = f->ftps_buf; + f->ftps_wptr = f->ftps_buf; return 0; } -/* - * ipf_ftp_atoi - implement a version of atoi which processes numbers in - * pairs separated by commas (which are expected to be in the range 0 - 255), - * returning a 16 bit number combining either side of the , as the MSB and - * LSB. - */ -u_short ipf_ftp_atoi(ptr) -char **ptr; -{ - register char *s = *ptr, c; - register u_char i = 0, j = 0; - - while ((c = *s++) && isdigit(c)) { - i *= 10; - i += c - '0'; - } - if (c != ',') { - *ptr = NULL; - return 0; - } - while ((c = *s++) && isdigit(c)) { - j *= 10; - j += c - '0'; - } - *ptr = s; - return (i << 8) | j; -} - - -int ippr_ftp_portmsg(fin, ip, nat) +int ippr_ftp_port(fin, ip, nat, f, dlen) fr_info_t *fin; ip_t *ip; nat_t *nat; +ftpside_t *f; +int dlen; { - char portbuf[IPF_FTPBUFSZ], newbuf[IPF_FTPBUFSZ], *s, cmd[6]; tcphdr_t *tcp, tcph, *tcp2 = &tcph; - size_t nlen = 0, dlen, olen; + char newbuf[IPF_FTPBUFSZ], *s; u_short a5, a6, sp, dp; u_int a1, a2, a3, a4; struct in_addr swip; - int off, inc = 0; - ftpinfo_t *ftp; + size_t nlen, olen; fr_info_t fi; + int inc, off; nat_t *ipn; mb_t *m; #if SOLARIS @@ -188,71 +103,25 @@ nat_t *nat; #endif tcp = (tcphdr_t *)fin->fin_dp; - bzero(portbuf, sizeof(portbuf)); - off = (ip->ip_hl << 2) + (tcp->th_off << 2); - -#if SOLARIS - m = fin->fin_qfm; - - dlen = msgdsize(m) - off; - if (dlen > 0) - copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#else - m = *(mb_t **)fin->fin_mp; - - dlen = mbufchainlen(m) - off; - if (dlen > 0) - m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#endif - if (dlen <= 0) - return 0; - bzero(cmd, sizeof(cmd)); - portbuf[sizeof(portbuf) - 1] = '\0'; - *newbuf = '\0'; - - /* - * Check that a user is progressing through the login ok. - */ - if (ippr_ftp_complete(portbuf, dlen, cmd)) - return 0; - - ftp = nat->nat_aps->aps_data; - switch (ftp->ftp_passok) - { - case 0 : - if (!strncmp(cmd, "USER ", 5)) - ftp->ftp_passok = 1; - break; - case 2 : - if (!strncmp(cmd, "PASS ", 5)) - ftp->ftp_passok = 3; - break; - } - if (ftp->ftp_passok != 4) - return 0; /* * Check for client sending out PORT message. */ - if (!ippr_ftp_pasvonly && !strncmp(cmd, "PORT ", 5)) { - if (dlen < IPF_MINPORTLEN) - return 0; - } else + if (dlen < IPF_MINPORTLEN) return 0; - + off = fin->fin_hlen + (tcp->th_off << 2); /* * Skip the PORT command + space */ - s = portbuf + 5; + s = f->ftps_rptr + 5; /* * Pick out the address components, two at a time. */ - a1 = ipf_ftp_atoi(&s); + a1 = ippr_ftp_atoi(&s); if (!s) return 0; - a2 = ipf_ftp_atoi(&s); + a2 = ippr_ftp_atoi(&s); if (!s) return 0; - /* * check that IP address in the PORT/PASV reply is the same as the * sender of the command - prevents using PORT for port scanning. @@ -262,7 +131,7 @@ nat_t *nat; if (a1 != ntohl(nat->nat_inip.s_addr)) return 0; - a5 = ipf_ftp_atoi(&s); + a5 = ippr_ftp_atoi(&s); if (!s) return 0; if (*s == ')') @@ -279,6 +148,7 @@ nat_t *nat; } else return 0; a5 >>= 8; + a5 &= 0xff; /* * Calculate new address parts for PORT command */ @@ -287,14 +157,18 @@ nat_t *nat; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; - olen = s - portbuf; + olen = s - f->ftps_rptr; /* DO NOT change this to sprintf! */ (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", "PORT", a1, a2, a3, a4, a5, a6); nlen = strlen(newbuf); inc = nlen - olen; + if ((inc + ip->ip_len) > 65535) + return 0; + #if SOLARIS + m = fin->fin_qfm; for (m1 = m; m1->b_cont; m1 = m1->b_cont) ; if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { @@ -320,10 +194,15 @@ nat_t *nat; } copyin_mblk(m, off, nlen, newbuf); #else + m = *((mb_t **)fin->fin_mp); if (inc < 0) m_adj(m, inc); /* the mbuf chain will be extended if necessary by m_copyback() */ m_copyback(m, off, nlen, newbuf); +# ifdef M_PKTHDR + if (!(m->m_flags & M_PKTHDR)) + m->m_pkthdr.len += inc; +# endif #endif if (inc != 0) { #if SOLARIS || defined(__sgi) @@ -338,7 +217,7 @@ nat_t *nat; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); - fix_outcksum(&ip->ip_sum, sum2, 0); + fix_outcksum(&ip->ip_sum, sum2); #endif ip->ip_len += inc; } @@ -361,17 +240,25 @@ nat_t *nat; */ dp = htons(fin->fin_data[1] - 1); ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip, - ip->ip_dst, (dp << 16) | sp); + ip->ip_dst, (dp << 16) | sp, 0); if (ipn == NULL) { + int slen; + + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp2); bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = sp; + tcp2->th_off = 5; tcp2->th_dport = 0; /* XXX - don't specify remote port */ fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; + fi.fin_dlen = sizeof(*tcp2); fi.fin_dp = (char *)tcp2; + fi.fin_fr = &natfr; swip = ip->ip_src; + fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; ip->ip_src = nat->nat_inip; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_DPORT, NAT_OUTBOUND); @@ -379,113 +266,97 @@ nat_t *nat; ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, FI_W_DPORT); } + ip->ip_len = slen; ip->ip_src = swip; } - return inc; + return APR_INC(inc); } -int ippr_ftp_out(fin, ip, aps, nat) +int ippr_ftp_client(fin, ip, nat, ftp, dlen) fr_info_t *fin; -ip_t *ip; -ap_session_t *aps; nat_t *nat; +ftpinfo_t *ftp; +ip_t *ip; +int dlen; { - return ippr_ftp_portmsg(fin, ip, nat); + char *rptr, *wptr, cmd[6], c; + ftpside_t *f; + int inc, i; + + inc = 0; + f = &ftp->ftp_side[0]; + rptr = f->ftps_rptr; + wptr = f->ftps_wptr; + + for (i = 0; (i < 5) && (i < dlen); i++) { + c = rptr[i]; + if (isalpha(c)) { + cmd[i] = toupper(c); + } else { + cmd[i] = c; + } + } + cmd[i] = '\0'; + + if ((ftp->ftp_passok == 0) && !strncmp(cmd, "USER ", 5)) + ftp->ftp_passok = 1; + else if ((ftp->ftp_passok == 2) && !strncmp(cmd, "PASS ", 5)) + ftp->ftp_passok = 3; + else if ((ftp->ftp_passok == 4) && !ippr_ftp_pasvonly && + !strncmp(cmd, "PORT ", 5)) { + inc = ippr_ftp_port(fin, ip, nat, f, dlen); + } else if (ippr_ftp_insecure && !ippr_ftp_pasvonly && + !strncmp(cmd, "PORT ", 5)) { + inc = ippr_ftp_port(fin, ip, nat, f, dlen); + } + + while ((*rptr++ != '\n') && (rptr < wptr)) + ; + f->ftps_rptr = rptr; + return inc; } -int ippr_ftp_pasvmsg(fin, ip, nat) +int ippr_ftp_pasv(fin, ip, nat, f, dlen) fr_info_t *fin; ip_t *ip; nat_t *nat; +ftpside_t *f; +int dlen; { - char portbuf[IPF_FTPBUFSZ], newbuf[IPF_FTPBUFSZ], *s, cmd[6]; - int off, olen, dlen, nlen = 0, inc = 0; - tcphdr_t tcph, *tcp2 = &tcph; + tcphdr_t *tcp, tcph, *tcp2 = &tcph; struct in_addr swip, swip2; - u_short a5, a6, dp, sp; + u_short a5, a6, sp, dp; u_int a1, a2, a3, a4; - ftpinfo_t *ftp; - tcphdr_t *tcp; fr_info_t fi; nat_t *ipn; - mb_t *m; -#if SOLARIS - mb_t *m1; -#endif - - tcp = (tcphdr_t *)fin->fin_dp; - off = (ip->ip_hl << 2) + (tcp->th_off << 2); - m = *(mb_t **)fin->fin_mp; - bzero(portbuf, sizeof(portbuf)); - -#if SOLARIS - m = fin->fin_qfm; - - dlen = msgdsize(m) - off; - if (dlen > 0) - copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#else - dlen = mbufchainlen(m) - off; - if (dlen > 0) - m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#endif - if (dlen <= 0) - return 0; - bzero(cmd, sizeof(cmd)); - portbuf[sizeof(portbuf) - 1] = '\0'; - *newbuf = '\0'; - - /* - * Check that a user is progressing through the login ok. - * Don't put the switch in one common function because one side - * should only see numeric responses and the other commands. - */ - - ftp = nat->nat_aps->aps_data; - switch (ftp->ftp_passok) - { - case 1 : - if (!strncmp(portbuf, "331", 3)) - ftp->ftp_passok = 2; - else if (!strncmp(portbuf, "530", 3)) - ftp->ftp_passok = 0; - break; - case 3 : - if (!strncmp(portbuf, "230", 3)) - ftp->ftp_passok = 4; - break; - default : - break; - } - - if (ippr_ftp_complete(portbuf, dlen, cmd) || (ftp->ftp_passok != 4)) - return 0; + int inc; + char *s; /* * Check for PASV reply message. */ - if (!strncmp(portbuf, "227 ", 4)) { - if (dlen < IPF_MIN227LEN) - return 0; - else if (strncmp(portbuf, "227 Entering Passive Mode", 25)) - return 0; - } else + if (dlen < IPF_MIN227LEN) + return 0; + else if (strncmp(f->ftps_rptr, "227 Entering Passive Mode", 25)) return 0; + + tcp = (tcphdr_t *)fin->fin_dp; + /* * Skip the PORT command + space */ - s = portbuf + 25; + s = f->ftps_rptr + 25; while (*s && !isdigit(*s)) s++; /* * Pick out the address components, two at a time. */ - a1 = ipf_ftp_atoi(&s); + a1 = ippr_ftp_atoi(&s); if (!s) return 0; - a2 = ipf_ftp_atoi(&s); + a2 = ippr_ftp_atoi(&s); if (!s) return 0; @@ -498,7 +369,7 @@ nat_t *nat; if (a1 != ntohl(nat->nat_oip.s_addr)) return 0; - a5 = ipf_ftp_atoi(&s); + a5 = ippr_ftp_atoi(&s); if (!s) return 0; @@ -523,13 +394,18 @@ nat_t *nat; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; - olen = s - portbuf; + inc = 0; +#if 0 + olen = s - f->ftps_rptr; (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6); - nlen = strlen(newbuf); inc = nlen - olen; + if ((inc + ip->ip_len) > 65535) + return 0; + #if SOLARIS + m = fin->fin_qfm; for (m1 = m; m1->b_cont; m1 = m1->b_cont) ; if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { @@ -550,13 +426,14 @@ nat_t *nat; } else { m1->b_wptr += inc; } - copyin_mblk(m, off, nlen, newbuf); -#else + /*copyin_mblk(m, off, nlen, newbuf);*/ +#else /* SOLARIS */ + m = *((mb_t **)fin->fin_mp); if (inc < 0) m_adj(m, inc); /* the mbuf chain will be extended if necessary by m_copyback() */ - m_copyback(m, off, nlen, newbuf); -#endif + /*m_copyback(m, off, nlen, newbuf);*/ +#endif /* SOLARIS */ if (inc != 0) { #if SOLARIS || defined(__sgi) register u_32_t sum1, sum2; @@ -570,10 +447,11 @@ nat_t *nat; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); - fix_outcksum(&ip->ip_sum, sum2, 0); -#endif + fix_outcksum(&ip->ip_sum, sum2); +#endif /* SOLARIS || defined(__sgi) */ ip->ip_len += inc; } +#endif /* 0 */ /* * Add skeleton NAT entry for connection which will come back the @@ -582,18 +460,27 @@ nat_t *nat; sp = 0; dp = htons(fin->fin_data[1] - 1); ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip, - ip->ip_dst, (dp << 16) | sp); + ip->ip_dst, (dp << 16) | sp, 0); if (ipn == NULL) { + int slen; + + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp2); bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = 0; /* XXX - fake it for nat_new */ - fi.fin_data[0] = a5 << 8 | a6; - tcp2->th_dport = htons(fi.fin_data[0]); - fi.fin_data[1] = 0; + tcp2->th_off = 5; + fi.fin_data[1] = a5 << 8 | a6; + fi.fin_dlen = sizeof(*tcp2); + tcp2->th_dport = htons(fi.fin_data[1]); + fi.fin_data[0] = 0; fi.fin_dp = (char *)tcp2; + fi.fin_fr = &natfr; swip = ip->ip_src; swip2 = ip->ip_dst; + fi.fin_fi.fi_daddr = ip->ip_src.s_addr; + fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; ip->ip_dst = ip->ip_src; ip->ip_src = nat->nat_inip; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_SPORT, @@ -602,6 +489,7 @@ nat_t *nat; ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, FI_W_SPORT); } + ip->ip_len = slen; ip->ip_src = swip; ip->ip_dst = swip2; } @@ -609,12 +497,292 @@ nat_t *nat; } +int ippr_ftp_server(fin, ip, nat, ftp, dlen) +fr_info_t *fin; +ip_t *ip; +nat_t *nat; +ftpinfo_t *ftp; +int dlen; +{ + char *rptr, *wptr; + ftpside_t *f; + int inc; + + inc = 0; + f = &ftp->ftp_side[1]; + rptr = f->ftps_rptr; + wptr = f->ftps_wptr; + + if ((ftp->ftp_passok == 1) && !strncmp(rptr, "331", 3)) + ftp->ftp_passok = 2; + else if ((ftp->ftp_passok == 3) && !strncmp(rptr, "230", 3)) + ftp->ftp_passok = 4; + else if ((ftp->ftp_passok == 3) && !strncmp(rptr, "530", 3)) + ftp->ftp_passok = 0; + else if ((ftp->ftp_passok == 4) && !strncmp(rptr, "227 ", 4)) { + inc = ippr_ftp_pasv(fin, ip, nat, f, dlen); + } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) { + inc = ippr_ftp_pasv(fin, ip, nat, f, dlen); + } + while ((*rptr++ != '\n') && (rptr < wptr)) + ; + f->ftps_rptr = rptr; + return inc; +} + + +/* + * Look to see if the buffer starts with something which we recognise as + * being the correct syntax for the FTP protocol. + */ +int ippr_ftp_valid(buf, len) +char *buf; +size_t len; +{ + register char *s, c; + register size_t i = len; + + if (i < 5) + return 2; + s = buf; + c = *s++; + i--; + + if (isdigit(c)) { + c = *s++; + i--; + if (isdigit(c)) { + c = *s++; + i--; + if (isdigit(c)) { + c = *s++; + i--; + if ((c != '-') && (c != ' ')) + return 1; + } else + return 1; + } else + return 1; + } else if (isalpha(c)) { + c = *s++; + i--; + if (isalpha(c)) { + c = *s++; + i--; + if (isalpha(c)) { + c = *s++; + i--; + if (isalpha(c)) { + c = *s++; + i--; + if ((c != ' ') && (c != '\r')) + return 1; + } else if ((c != ' ') && (c != '\r')) + return 1; + } else + return 1; + } else + return 1; + } else + return 1; + for (; i; i--) { + c = *s++; + if (c == '\n') + return 0; + } + return 2; +} + + +int ippr_ftp_process(fin, ip, nat, ftp, rv) +fr_info_t *fin; +ip_t *ip; +nat_t *nat; +ftpinfo_t *ftp; +int rv; +{ + int mlen, len, off, inc, i, sel; + char *rptr, *wptr; + ftpside_t *f, *t; + tcphdr_t *tcp; + mb_t *m; + + tcp = (tcphdr_t *)fin->fin_dp; + off = fin->fin_hlen + (tcp->th_off << 2); + +#if SOLARIS + m = fin->fin_qfm; +#else + m = *((mb_t **)fin->fin_mp); +#endif + +#if SOLARIS + mlen = msgdsize(m) - off; +#else + mlen = mbufchainlen(m) - off; +#endif + + t = &ftp->ftp_side[1 - rv]; + f = &ftp->ftp_side[rv]; + if (!mlen) { + if (!t->ftps_seq || + (int)ntohl(tcp->th_ack) - (int)t->ftps_seq > 0) + t->ftps_seq = ntohl(tcp->th_ack); + f->ftps_len = 0; + return 0; + } + + inc = 0; + rptr = f->ftps_rptr; + wptr = f->ftps_wptr; + + sel = nat->nat_aps->aps_sel[1 - rv]; + if (rv) + i = nat->nat_aps->aps_ackoff[sel]; + else + i = nat->nat_aps->aps_seqoff[sel]; + /* + * XXX - Ideally, this packet should get dropped because we now know + * that it is out of order (and there is no real danger in doing so + * apart from causing packets to go through here ordered). + */ + if (f->ftps_len + f->ftps_seq == ntohl(tcp->th_seq)) + f->ftps_seq = ntohl(tcp->th_seq); + else if (ntohl(tcp->th_seq) + i != f->ftps_seq) { + return APR_ERR(-1); + } + f->ftps_len = mlen; + + while (mlen > 0) { + len = MIN(mlen, FTP_BUFSZ / 2); + +#if SOLARIS + copyout_mblk(m, off, len, wptr); +#else + m_copydata(m, off, len, wptr); +#endif + mlen -= len; + off += len; + wptr += len; + f->ftps_wptr = wptr; + if (f->ftps_junk == 2) + f->ftps_junk = ippr_ftp_valid(rptr, wptr - rptr); + + while ((f->ftps_junk == 0) && (wptr > rptr)) { + f->ftps_junk = ippr_ftp_valid(rptr, wptr - rptr); + if (f->ftps_junk == 0) { + len = wptr - rptr; + f->ftps_rptr = rptr; + if (rv) + inc += ippr_ftp_server(fin, ip, nat, + ftp, len); + else + inc += ippr_ftp_client(fin, ip, nat, + ftp, len); + rptr = f->ftps_rptr; + } + } + + while ((f->ftps_junk == 1) && (rptr < wptr)) { + while ((rptr < wptr) && (*rptr != '\r')) + rptr++; + + if (*rptr == '\r') { + if (rptr + 1 < wptr) { + if (*(rptr + 1) == '\n') { + rptr += 2; + f->ftps_junk = 0; + } else + rptr++; + } else + break; + } + } + f->ftps_rptr = rptr; + + if (rptr == wptr) { + rptr = wptr = f->ftps_buf; + } else { + if ((wptr > f->ftps_buf + FTP_BUFSZ / 2)) { + i = wptr - rptr; + if ((rptr == f->ftps_buf) || + (wptr - rptr > FTP_BUFSZ / 2)) { + f->ftps_junk = 1; + rptr = wptr = f->ftps_buf; + } else { + bcopy(rptr, f->ftps_buf, i); + wptr = f->ftps_buf + i; + rptr = f->ftps_buf; + } + } + f->ftps_rptr = rptr; + f->ftps_wptr = wptr; + } + } + + t->ftps_seq = ntohl(tcp->th_ack); + f->ftps_rptr = rptr; + f->ftps_wptr = wptr; + return APR_INC(inc); +} + + +int ippr_ftp_out(fin, ip, aps, nat) +fr_info_t *fin; +ip_t *ip; +ap_session_t *aps; +nat_t *nat; +{ + ftpinfo_t *ftp; + + ftp = aps->aps_data; + if (ftp == NULL) + return 0; + return ippr_ftp_process(fin, ip, nat, ftp, 0); +} + + int ippr_ftp_in(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { + ftpinfo_t *ftp; + + ftp = aps->aps_data; + if (ftp == NULL) + return 0; + return ippr_ftp_process(fin, ip, nat, ftp, 1); +} + - return ippr_ftp_pasvmsg(fin, ip, nat); +/* + * ippr_ftp_atoi - implement a version of atoi which processes numbers in + * pairs separated by commas (which are expected to be in the range 0 - 255), + * returning a 16 bit number combining either side of the , as the MSB and + * LSB. + */ +u_short ippr_ftp_atoi(ptr) +char **ptr; +{ + register char *s = *ptr, c; + register u_char i = 0, j = 0; + + while ((c = *s++) && isdigit(c)) { + i *= 10; + i += c - '0'; + } + if (c != ',') { + *ptr = NULL; + return 0; + } + while ((c = *s++) && isdigit(c)) { + j *= 10; + j += c - '0'; + } + *ptr = s; + i &= 0xff; + j &= 0xff; + return (i << 8) | j; } diff --git a/sys/netinet/ip_log.c b/sys/netinet/ip_log.c index 554032e937d..1c798708e11 100644 --- a/sys/netinet/ip_log.c +++ b/sys/netinet/ip_log.c @@ -1,13 +1,13 @@ -/* $OpenBSD: ip_log.c,v 1.8 2000/03/13 23:40:18 kjell Exp $ */ +/* $OpenBSD: ip_log.c,v 1.9 2001/01/17 04:47:14 fgsch Exp $ */ /* - * Copyright (C) 1997-1998 by Darren Reed. + * Copyright (C) 1997-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. * - * $IPFilter: ip_log.c,v 2.1.2.2 1999/09/21 11:55:44 darrenr Exp $ + * $IPFilter: ip_log.c,v 2.5.2.2 2000/08/13 03:50:41 darrenr Exp $ */ #include <sys/param.h> #if defined(KERNEL) && !defined(_KERNEL) @@ -18,12 +18,13 @@ #endif #ifdef __FreeBSD__ # if defined(_KERNEL) && !defined(IPFILTER_LKM) -# include <sys/osreldate.h> # if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include "opt_ipfilter.h" # endif # else -# include <osreldate.h> +# ifdef KLD_MODULE +# include <osreldate.h> +# endif # endif #endif #ifdef IPFILTER_LOG @@ -104,14 +105,14 @@ # ifndef _KERNEL # include <syslog.h> # endif -# include <netinet/ip_fil_compat.h> +# include "netinet/ip_compat.h" # include <netinet/tcpip.h> -# include <netinet/ip_fil.h> -# include <netinet/ip_proxy.h> -# include <netinet/ip_nat.h> -# include <netinet/ip_frag.h> -# include <netinet/ip_state.h> -# include <netinet/ip_auth.h> +# include "netinet/ip_fil.h" +# include "netinet/ip_proxy.h" +# include "netinet/ip_nat.h" +# include "netinet/ip_frag.h" +# include "netinet/ip_state.h" +# include "netinet/ip_auth.h" # if (__FreeBSD_version >= 300000) # include <sys/malloc.h> # endif @@ -130,7 +131,7 @@ extern kcondvar_t iplwait; iplog_t **iplh[IPL_LOGMAX+1], *iplt[IPL_LOGMAX+1], *ipll[IPL_LOGMAX+1]; size_t iplused[IPL_LOGMAX+1]; -fr_info_t iplcrc[IPL_LOGMAX+1]; +static fr_info_t iplcrc[IPL_LOGMAX+1]; # ifdef linux static struct wait_queue *iplwait[IPL_LOGMAX+1]; # endif @@ -173,6 +174,7 @@ mb_t *m; size_t sizes[2]; void *ptrs[2]; int types[2]; + u_char p; # if SOLARIS ill_t *ifp = fin->fin_ifp; # else @@ -183,15 +185,16 @@ mb_t *m; * calculate header size. */ hlen = fin->fin_hlen; - if ((ip->ip_off & IP_OFFMASK) == 0) { - if (ip->ip_p == IPPROTO_TCP) + if (fin->fin_off == 0) { + p = fin->fin_fi.fi_p; + if (p == IPPROTO_TCP) hlen += MIN(sizeof(tcphdr_t), fin->fin_dlen); - else if (ip->ip_p == IPPROTO_UDP) + else if (p == IPPROTO_UDP) hlen += MIN(sizeof(udphdr_t), fin->fin_dlen); - else if (ip->ip_p == IPPROTO_ICMP) { - struct icmp *icmp; + else if (p == IPPROTO_ICMP) { + struct icmp *icmp; - icmp = (struct icmp *)((char *)ip + hlen); + icmp = (struct icmp *)fin->fin_dp; /* * For ICMP, if the packet is an error packet, also @@ -236,7 +239,7 @@ mb_t *m; if ((ipfl.fl_ifname[2] = ifp->if_name[2])) ipfl.fl_ifname[3] = ifp->if_name[3]; # endif - mlen = (flags & FR_LOGBODY) ? MIN(ip->ip_len - hlen, 128) : 0; + mlen = (flags & FR_LOGBODY) ? MIN(fin->fin_plen - hlen, 128) : 0; # endif ipfl.fl_plen = (u_char)mlen; ipfl.fl_hlen = (u_char)hlen; diff --git a/sys/netinet/ip_nat.c b/sys/netinet/ip_nat.c index c618c90ff7b..cc01ec039bb 100644 --- a/sys/netinet/ip_nat.c +++ b/sys/netinet/ip_nat.c @@ -1,7 +1,7 @@ -/* $OpenBSD: ip_nat.c,v 1.35 2000/08/10 15:43:20 kjell Exp $ */ +/* $OpenBSD: ip_nat.c,v 1.36 2001/01/17 04:47:14 fgsch Exp $ */ /* - * Copyright (C) 1995-1998 by Darren Reed. + * Copyright (C) 1995-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 @@ -11,7 +11,7 @@ */ #if !defined(lint) static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; -static const char rcsid[] = "@(#)$IPFilter: ip_nat.c,v 2.2.2.21 2000/08/08 16:00:33 darrenr Exp $"; +static const char rcsid[] = "@(#)$IPFilter: ip_nat.c,v 2.37.2.31 2000/12/16 21:06:40 darrenr Exp $"; #endif #if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL) @@ -97,13 +97,13 @@ extern struct ifnet vpnif; #include <netinet/tcp.h> #include <netinet/udp.h> #include <netinet/ip_icmp.h> -#include <netinet/ip_fil_compat.h> +#include "netinet/ip_compat.h" #include <netinet/tcpip.h> -#include <netinet/ip_fil.h> -#include <netinet/ip_proxy.h> -#include <netinet/ip_nat.h> -#include <netinet/ip_frag.h> -#include <netinet/ip_state.h> +#include "netinet/ip_fil.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_state.h" #if (__FreeBSD_version >= 300000) # include <sys/malloc.h> #endif @@ -119,24 +119,36 @@ ipnat_t *nat_list = NULL; u_int ipf_nattable_sz = NAT_TABLE_SZ; u_int ipf_natrules_sz = NAT_SIZE; u_int ipf_rdrrules_sz = RDR_SIZE; +u_int ipf_hostmap_sz = HOSTMAP_SIZE; u_32_t nat_masks = 0; u_32_t rdr_masks = 0; ipnat_t **nat_rules = NULL; ipnat_t **rdr_rules = NULL; +hostmap_t **maptable = NULL; u_long fr_defnatage = DEF_NAT_AGE, fr_defnaticmpage = 6; /* 3 seconds */ natstat_t nat_stats; +int fr_nat_lock = 0; #if (SOLARIS || defined(__sgi)) && defined(_KERNEL) extern kmutex_t ipf_rw; extern KRWLOCK_T ipf_nat; #endif static int nat_flushtable __P((void)); -static int nat_clearlist __P((void)); +static void nat_addnat __P((struct ipnat *)); +static void nat_addrdr __P((struct ipnat *)); static void nat_delete __P((struct nat *)); static void nat_delrdr __P((struct ipnat *)); static void nat_delnat __P((struct ipnat *)); +static int fr_natgetent __P((caddr_t)); +static int fr_natgetsz __P((caddr_t)); +static int fr_natputent __P((caddr_t)); +static void nat_tabmove __P((nat_t *, u_32_t)); +static int nat_match __P((fr_info_t *, ipnat_t *, ip_t *)); +static hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, + struct in_addr)); +static void nat_hostmapdel __P((struct hostmap *)); int nat_init() @@ -164,46 +176,135 @@ int nat_init() bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *)); else return -1; + + KMALLOCS(maptable, hostmap_t **, sizeof(hostmap_t *) * ipf_hostmap_sz); + if (maptable != NULL) + bzero((char *)maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); + else + return -1; return 0; } -void nat_delrdr(n) +static void nat_addrdr(n) ipnat_t *n; { - ipnat_t **n1; - u_32_t iph; + ipnat_t **np; + u_32_t j; u_int hv; + int k; + + k = countbits(n->in_outmsk); + if ((k >= 0) && (k != 32)) + rdr_masks |= 1 << k; + j = (n->in_outip & n->in_outmsk); + hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz); + np = rdr_rules + hv; + while (*np != NULL) + np = &(*np)->in_rnext; + n->in_rnext = NULL; + n->in_prnext = np; + *np = n; +} - iph = n->in_outip & n->in_outmsk; - hv = NAT_HASH_FN(iph, ipf_rdrrules_sz); - for (n1 = &rdr_rules[hv]; *n1 && (*n1 != n); n1 = &(*n1)->in_rnext) - ; - if (*n1) - *n1 = n->in_rnext; + +static void nat_addnat(n) +ipnat_t *n; +{ + ipnat_t **np; + u_32_t j; + u_int hv; + int k; + + k = countbits(n->in_inmsk); + if ((k >= 0) && (k != 32)) + nat_masks |= 1 << k; + j = (n->in_inip & n->in_inmsk); + hv = NAT_HASH_FN(j, 0, ipf_natrules_sz); + np = nat_rules + hv; + while (*np != NULL) + np = &(*np)->in_mnext; + n->in_mnext = NULL; + n->in_pmnext = np; + *np = n; +} + + +static void nat_delrdr(n) +ipnat_t *n; +{ + if (n->in_rnext) + n->in_rnext->in_prnext = n->in_prnext; + *n->in_prnext = n->in_rnext; } static void nat_delnat(n) ipnat_t *n; { - ipnat_t **n1; - u_32_t iph; + if (n->in_mnext) + n->in_mnext->in_pmnext = n->in_pmnext; + *n->in_pmnext = n->in_mnext; +} + + +/* + * check if an ip address has already been allocated for a given mapping that + * is not doing port based translation. + * + * Must be called with ipf_nat held as a write lock. + */ +static struct hostmap *nat_hostmap(np, real, map) +ipnat_t *np; +struct in_addr real; +struct in_addr map; +{ + hostmap_t *hm; u_int hv; - iph = n->in_inip & n->in_inmsk; - hv = NAT_HASH_FN(iph, ipf_natrules_sz); - for (n1 = &nat_rules[hv]; *n1 && (*n1 != n); n1 = &(*n1)->in_mnext) - ; - if (*n1) - *n1 = n->in_mnext; + hv = real.s_addr % HOSTMAP_SIZE; + for (hm = maptable[hv]; hm; hm = hm->hm_next) + if ((hm->hm_realip.s_addr == real.s_addr) && + (np == hm->hm_ipnat)) { + hm->hm_ref++; + return hm; + } + + KMALLOC(hm, hostmap_t *); + if (hm) { + hm->hm_next = maptable[hv]; + hm->hm_pnext = maptable + hv; + if (maptable[hv]) + maptable[hv]->hm_pnext = &hm->hm_next; + maptable[hv] = hm; + hm->hm_ipnat = np; + hm->hm_realip = real; + hm->hm_mapip = map; + hm->hm_ref = 1; + } + return hm; +} + + +/* + * Must be called with ipf_nat held as a write lock. + */ +static void nat_hostmapdel(hm) +struct hostmap *hm; +{ + ATOMIC_DEC32(hm->hm_ref); + if (hm->hm_ref == 0) { + if (hm->hm_next) + hm->hm_next->hm_pnext = hm->hm_pnext; + *hm->hm_pnext = hm->hm_next; + KFREE(hm); + } } -void fix_outcksum(sp, n , len) +void fix_outcksum(sp, n) u_short *sp; u_32_t n; -int len; { register u_short sumshort; register u_32_t sum1; @@ -226,10 +327,9 @@ int len; } -void fix_incksum(sp, n , len) +void fix_incksum(sp, n) u_short *sp; u_32_t n; -int len; { register u_short sumshort; register u_32_t sum1; @@ -242,7 +342,11 @@ int len; return; } #endif +#ifdef sparc + sum1 = (~(*sp)) & 0xffff; +#else sum1 = (~ntohs(*sp)) & 0xffff; +#endif sum1 += ~(n) & 0xffff; sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ @@ -253,6 +357,38 @@ int len; /* + * fix_datacksum is used *only* for the adjustments of checksums in the data + * section of an IP packet. + * + * The only situation in which you need to do this is when NAT'ing an + * ICMP error message. Such a message, contains in its body the IP header + * of the original IP packet, that causes the error. + * + * You can't use fix_incksum or fix_outcksum in that case, because for the + * kernel the data section of the ICMP error is just data, and no special + * processing like hardware cksum or ntohs processing have been done by the + * kernel on the data section. + */ +void fix_datacksum(sp, n) +u_short *sp; +u_32_t n; +{ + register u_short sumshort; + register u_32_t sum1; + + if (!n) + return; + + sum1 = (~ntohs(*sp)) & 0xffff; + sum1 += (n); + sum1 = (sum1 >> 16) + (sum1 & 0xffff); + /* Again */ + sum1 = (sum1 >> 16) + (sum1 & 0xffff); + sumshort = ~(u_short)sum1; + *(sp) = htons(sumshort); +} + +/* * How the NAT is organised and works. * * Inside (interface y) NAT Outside (interface x) @@ -277,7 +413,7 @@ int len; * Handle ioctls which manipulate the NAT. */ int nat_ioctl(data, cmd, mode) -#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) u_long cmd; #else int cmd; @@ -286,12 +422,9 @@ caddr_t data; int mode; { register ipnat_t *nat, *nt, *n = NULL, **np = NULL; - int error = 0, ret, k; + int error = 0, ret, arg; ipnat_t natd; u_32_t i, j; -#if defined(_KERNEL) && !SOLARIS - int s; -#endif #if (BSD >= 199306) && defined(_KERNEL) if ((securelevel >= 2) && (mode & FWRITE)) @@ -301,19 +434,27 @@ int mode; nat = NULL; /* XXX gcc -Wuninitialized */ KMALLOC(nt, ipnat_t *); if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) - IRCOPY(data, (char *)&natd, sizeof(natd)); + error = IRCOPYPTR(data, (char *)&natd, sizeof(natd)); + else if (cmd == SIOCIPFFL) { /* SIOCFLNAT & SIOCCNATL */ + error = IRCOPY(data, (char *)&arg, sizeof(arg)); + if (error) + error = EFAULT; + } + + if (error) + goto done; /* * For add/delete, look to see if the NAT entry is already present */ - SPL_NET(s); WRITE_ENTER(&ipf_nat); if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { nat = &natd; nat->in_flags &= IPN_USERFLAGS; if ((nat->in_redir & NAT_MAPBLK) == 0) { - nat->in_inip &= nat->in_inmsk; - if ((nat->in_flags & IPN_RANGE) == 0) + if ((nat->in_flags & IPN_SPLIT) == 0) + nat->in_inip &= nat->in_inmsk; + if ((nat->in_flags & IPN_IPRANGE) == 0) nat->in_outip &= nat->in_outmsk; } for (np = &nat_list; (n = *np); np = &n->in_next) @@ -354,7 +495,7 @@ int mode; n = nt; nt = NULL; bcopy((char *)nat, (char *)n, sizeof(*n)); - n->in_ifp = (void *)GETUNIT(n->in_ifname); + n->in_ifp = (void *)GETUNIT(n->in_ifname, 4); if (!n->in_ifp) n->in_ifp = (void *)-1; if (n->in_plabel[0] != '\0') { @@ -368,32 +509,12 @@ int mode; *np = n; if (n->in_redir & NAT_REDIRECT) { - u_int hv; - - k = countbits(n->in_outmsk); - if ((k >= 0) && (k != 32)) - rdr_masks |= 1 << k; - j = (n->in_outip & n->in_outmsk); - hv = NAT_HASH_FN(j, ipf_rdrrules_sz); - np = rdr_rules + hv; - while (*np != NULL) - np = &(*np)->in_rnext; - n->in_rnext = NULL; - *np = n; + n->in_flags &= ~IPN_NOTDST; + nat_addrdr(n); } if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { - u_int hv; - - k = countbits(n->in_inmsk); - if ((k >= 0) && (k != 32)) - nat_masks |= 1 << k; - j = (n->in_inip & n->in_inmsk); - hv = NAT_HASH_FN(j, ipf_natrules_sz); - np = nat_rules + hv; - while (*np != NULL) - np = &(*np)->in_mnext; - n->in_mnext = NULL; - *np = n; + n->in_flags &= ~IPN_NOTSRC; + nat_addnat(n); } n->in_use = 0; @@ -401,8 +522,10 @@ int mode; n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); else if (n->in_flags & IPN_AUTOPORTMAP) n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); - else if (n->in_flags & IPN_RANGE) + else if (n->in_flags & IPN_IPRANGE) n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); + else if (n->in_flags & IPN_SPLIT) + n->in_space = 2; else n->in_space = ~ntohl(n->in_outmsk); /* @@ -415,15 +538,18 @@ int mode; * If to a single IP address, set to 1. */ if (n->in_space) { - if ((n->in_flags & IPN_RANGE) != 0) + if ((n->in_flags & IPN_IPRANGE) != 0) n->in_space += 1; else n->in_space -= 1; } else n->in_space = 1; if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && - ((n->in_flags & IPN_RANGE) == 0)) + ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) n->in_nip = ntohl(n->in_outip) + 1; + else if ((n->in_flags & IPN_SPLIT) && + (n->in_redir & NAT_REDIRECT)) + n->in_nip = ntohl(n->in_inip); else n->in_nip = ntohl(n->in_outip); if (n->in_redir & NAT_MAP) { @@ -505,44 +631,79 @@ int mode; nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz; nat_stats.ns_instances = nat_instances; nat_stats.ns_apslist = ap_sess_list; - IWCOPY((char *)&nat_stats, (char *)data, sizeof(nat_stats)); + error = IWCOPYPTR((char *)&nat_stats, (char *)data, + sizeof(nat_stats)); break; case SIOCGNATL : { natlookup_t nl; MUTEX_DOWNGRADE(&ipf_nat); - IRCOPY((char *)data, (char *)&nl, sizeof(nl)); + error = IRCOPYPTR((char *)data, (char *)&nl, sizeof(nl)); + if (error) + break; if (nat_lookupredir(&nl)) { - IWCOPY((char *)&nl, (char *)data, sizeof(nl)); + error = IWCOPYPTR((char *)&nl, (char *)data, + sizeof(nl)); } else error = ESRCH; break; } - case SIOCFLNAT : + case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ if (!(mode & FWRITE)) { error = EPERM; break; } - ret = nat_flushtable(); + error = 0; + if (arg == 0) + ret = nat_flushtable(); + else if (arg == 1) + ret = nat_clearlist(); + else + error = EINVAL; MUTEX_DOWNGRADE(&ipf_nat); - IWCOPY((caddr_t)&ret, data, sizeof(ret)); - break; - case SIOCCNATL : - if (!(mode & FWRITE)) { - error = EPERM; - break; + if (!error) { + error = IWCOPY((caddr_t)&ret, data, sizeof(ret)); + if (error) + error = EFAULT; } - ret = nat_clearlist(); - MUTEX_DOWNGRADE(&ipf_nat); - IWCOPY((caddr_t)&ret, data, sizeof(ret)); + break; + case SIOCSTLCK : + error = IRCOPY(data, (caddr_t)&arg, sizeof(arg)); + if (!error) { + error = IWCOPY((caddr_t)&fr_nat_lock, data, + sizeof(fr_nat_lock)); + if (!error) + fr_nat_lock = arg; + } else + error = EFAULT; + break; + case SIOCSTPUT : + if (fr_nat_lock) + error = fr_natputent(data); + else + error = EACCES; + break; + case SIOCSTGSZ : + if (fr_nat_lock) + error = fr_natgetsz(data); + else + error = EACCES; + break; + case SIOCSTGET : + if (fr_nat_lock) + error = fr_natgetent(data); + else + error = EACCES; break; case FIONREAD : #ifdef IPFILTER_LOG + arg = (int)iplused[IPL_LOGNAT]; MUTEX_DOWNGRADE(&ipf_nat); - IWCOPY((caddr_t)&iplused[IPL_LOGNAT], (caddr_t)data, - sizeof(iplused[IPL_LOGNAT])); + error = IWCOPY((caddr_t)&arg, (caddr_t)data, sizeof(arg)); + if (error) + error = EFAULT; #endif break; default : @@ -550,39 +711,314 @@ int mode; break; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ - SPL_X(s); +done: if (nt) KFREE(nt); return error; } +static int fr_natgetsz(data) +caddr_t data; +{ + ap_session_t *aps; + nat_t *nat, *n; + int error = 0; + natget_t ng; + + error = IRCOPY(data, (caddr_t)&ng, sizeof(ng)); + if (error) + return EFAULT; + + nat = ng.ng_ptr; + if (!nat) { + nat = nat_instances; + ng.ng_sz = 0; + if (nat == NULL) { + error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); + if (error) + error = EFAULT; + return error; + } + } else { + /* + * Make sure the pointer we're copying from exists in the + * current list of entries. Security precaution to prevent + * copying of random kernel data. + */ + for (n = nat_instances; n; n = n->nat_next) + if (n == nat) + break; + if (!n) + return ESRCH; + } + + ng.ng_sz = sizeof(nat_save_t); + aps = nat->nat_aps; + if ((aps != NULL) && (aps->aps_data != 0)) { + ng.ng_sz += sizeof(ap_session_t); + ng.ng_sz += aps->aps_psiz; + } + + error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); + if (error) + error = EFAULT; + return error; +} + + +static int fr_natgetent(data) +caddr_t data; +{ + nat_save_t ipn, *ipnp, *ipnn = NULL; + register nat_t *n, *nat; + ap_session_t *aps; + int error; + + error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); + if (error) + return EFAULT; + error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn)); + if (error) + return EFAULT; + + nat = ipn.ipn_next; + if (!nat) { + nat = nat_instances; + if (nat == NULL) { + if (nat_instances == NULL) + return ENOENT; + return 0; + } + } else { + /* + * Make sure the pointer we're copying from exists in the + * current list of entries. Security precaution to prevent + * copying of random kernel data. + */ + for (n = nat_instances; n; n = n->nat_next) + if (n == nat) + break; + if (!n) + return ESRCH; + } + + ipn.ipn_next = nat->nat_next; + ipn.ipn_dsize = 0; + bcopy((char *)nat, (char *)&ipn.ipn_nat, sizeof(ipn.ipn_nat)); + ipn.ipn_nat.nat_data = NULL; + + if (nat->nat_ptr) { + bcopy((char *)nat->nat_ptr, (char *)&ipn.ipn_ipnat, + sizeof(ipn.ipn_ipnat)); + } + + if (nat->nat_fr) + bcopy((char *)nat->nat_fr, (char *)&ipn.ipn_rule, + sizeof(ipn.ipn_rule)); + + if ((aps = nat->nat_aps)) { + ipn.ipn_dsize = sizeof(*aps); + if (aps->aps_data) + ipn.ipn_dsize += aps->aps_psiz; + KMALLOCS(ipnn, nat_save_t *, sizeof(*ipnn) + ipn.ipn_dsize); + if (ipnn == NULL) + return ENOMEM; + bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); + + bcopy((char *)aps, ipnn->ipn_data, sizeof(*aps)); + if (aps->aps_data) { + bcopy(aps->aps_data, ipnn->ipn_data + sizeof(*aps), + aps->aps_psiz); + ipnn->ipn_dsize += aps->aps_psiz; + } + error = IWCOPY((caddr_t)ipnn, ipnp, + sizeof(ipn) + ipn.ipn_dsize); + if (error) + error = EFAULT; + KFREES(ipnn, sizeof(*ipnn) + ipn.ipn_dsize); + } else { + error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); + if (error) + error = EFAULT; + } + return error; +} + + +static int fr_natputent(data) +caddr_t data; +{ + nat_save_t ipn, *ipnp, *ipnn = NULL; + register nat_t *n, *nat; + ap_session_t *aps; + frentry_t *fr; + ipnat_t *in; + + int error; + + error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); + if (error) + return EFAULT; + error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn)); + if (error) + return EFAULT; + nat = NULL; + if (ipn.ipn_dsize) { + KMALLOCS(ipnn, nat_save_t *, sizeof(ipn) + ipn.ipn_dsize); + if (ipnn == NULL) + return ENOMEM; + bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); + error = IRCOPY((caddr_t)ipnp, (caddr_t)ipn.ipn_data, + ipn.ipn_dsize); + if (error) { + error = EFAULT; + goto junkput; + } + } else + ipnn = NULL; + + KMALLOC(nat, nat_t *); + if (nat == NULL) { + error = EFAULT; + goto junkput; + } + + bcopy((char *)&ipn.ipn_nat, (char *)nat, sizeof(*nat)); + /* + * Initialize all these so that nat_delete() doesn't cause a crash. + */ + nat->nat_phnext[0] = NULL; + nat->nat_phnext[1] = NULL; + fr = nat->nat_fr; + nat->nat_fr = NULL; + aps = nat->nat_aps; + nat->nat_aps = NULL; + in = nat->nat_ptr; + nat->nat_ptr = NULL; + nat->nat_data = NULL; + + /* + * Restore the rule associated with this nat session + */ + if (in) { + KMALLOC(in, ipnat_t *); + if (in == NULL) { + error = ENOMEM; + goto junkput; + } + nat->nat_ptr = in; + bcopy((char *)&ipn.ipn_ipnat, (char *)in, sizeof(*in)); + in->in_use = 1; + in->in_flags |= IPN_DELETE; + in->in_next = NULL; + in->in_rnext = NULL; + in->in_prnext = NULL; + in->in_mnext = NULL; + in->in_pmnext = NULL; + in->in_ifp = GETUNIT(in->in_ifname, 4); + if (in->in_plabel[0] != '\0') { + in->in_apr = appr_match(in->in_p, in->in_plabel); + } + } + + /* + * Restore ap_session_t structure. Include the private data allocated + * if it was there. + */ + if (aps) { + KMALLOC(aps, ap_session_t *); + if (aps == NULL) { + error = ENOMEM; + goto junkput; + } + nat->nat_aps = aps; + aps->aps_next = ap_sess_list; + ap_sess_list = aps; + bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); + if (in) + aps->aps_apr = in->in_apr; + if (aps->aps_psiz) { + KMALLOCS(aps->aps_data, void *, aps->aps_psiz); + if (aps->aps_data == NULL) { + error = ENOMEM; + goto junkput; + } + bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, + aps->aps_psiz); + } else { + aps->aps_psiz = 0; + aps->aps_data = NULL; + } + } + + /* + * If there was a filtering rule associated with this entry then + * build up a new one. + */ + if (fr != NULL) { + if (nat->nat_flags & FI_NEWFR) { + KMALLOC(fr, frentry_t *); + nat->nat_fr = fr; + if (fr == NULL) { + error = ENOMEM; + goto junkput; + } + bcopy((char *)&ipn.ipn_fr, (char *)fr, sizeof(*fr)); + ipn.ipn_nat.nat_fr = fr; + error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); + if (error) { + error = EFAULT; + goto junkput; + } + } else { + for (n = nat_instances; n; n = n->nat_next) + if (n->nat_fr == fr) + break; + if (!n) { + error = ESRCH; + goto junkput; + } + } + } + + if (ipnn) + KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize); + nat_insert(nat); + return 0; +junkput: + if (ipnn) + KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize); + if (nat) + nat_delete(nat); + return error; +} + + /* * Delete a nat entry from the various lists and table. */ static void nat_delete(natd) struct nat *natd; { - register struct nat **natp, *nat; struct ipnat *ipn; - for (natp = natd->nat_hstart[0]; (nat = *natp); - natp = &nat->nat_hnext[0]) - if (nat == natd) { - *natp = nat->nat_hnext[0]; - break; - } - - for (natp = natd->nat_hstart[1]; (nat = *natp); - natp = &nat->nat_hnext[1]) - if (nat == natd) { - *natp = nat->nat_hnext[1]; - break; - } + if (natd->nat_flags & FI_WILDP) + nat_stats.ns_wilds--; + if (natd->nat_hnext[0]) + natd->nat_hnext[0]->nat_phnext[0] = natd->nat_phnext[0]; + *natd->nat_phnext[0] = natd->nat_hnext[0]; + if (natd->nat_hnext[1]) + natd->nat_hnext[1]->nat_phnext[1] = natd->nat_phnext[1]; + *natd->nat_phnext[1] = natd->nat_hnext[1]; if (natd->nat_fr != NULL) { - ATOMIC_DEC(natd->nat_fr->fr_ref); + ATOMIC_DEC32(natd->nat_fr->fr_ref); } + + if (natd->nat_hm != NULL) + nat_hostmapdel(natd->nat_hm); + /* * If there is an active reference from the nat entry to its parent * rule, decrement the rule's reference count and free it too if no @@ -600,6 +1036,7 @@ struct nat *natd; } } + MUTEX_DESTROY(&natd->nat_lock); /* * If there's a fragment table entry too for this nat entry, then * dereference that as well. @@ -610,26 +1047,6 @@ struct nat *natd; KFREE(natd); } -void -nat_ifdetach(ifp) - struct ifnet *ifp; -{ - ipnat_t *n, **np = &nat_list; - - while ((n = *np)) { - *np = n->in_next; - if (!n->in_use) { - if (n->in_apr) - appr_free(n->in_apr); - KFREE(n); - nat_stats.ns_rules--; - } else { - n->in_flags |= IPN_DELETE; - n->in_next = NULL; - } - } -} - /* * nat_flushtable - clear the NAT table of all mapping entries. @@ -638,7 +1055,7 @@ static int nat_flushtable() { register nat_t *nat, **natp; register int j = 0; - + /* * ALL NAT mappings deleted, so lets just make the deletions * quicker. @@ -652,6 +1069,9 @@ static int nat_flushtable() for (natp = &nat_instances; (nat = *natp); ) { *natp = nat->nat_next; +#ifdef IPFILTER_LOG + nat_log(nat, NL_FLUSH); +#endif nat_delete(nat); j++; } @@ -663,7 +1083,7 @@ static int nat_flushtable() /* * nat_clearlist - delete all rules in the active NAT mapping list. */ -static int nat_clearlist() +int nat_clearlist() { register ipnat_t *n, **np = &nat_list; int i = 0; @@ -705,11 +1125,11 @@ int direction; { register u_32_t sum1, sum2, sumd, l; u_short port = 0, sport = 0, dport = 0, nport = 0; - nat_t *nat, **natp, *natl = NULL; struct in_addr in, inb; tcphdr_t *tcp = NULL; + hostmap_t *hm = NULL; + nat_t *nat, *natl; u_short nflags; - u_int hv; #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) qif_t *qf = fin->fin_qif; #endif @@ -723,11 +1143,15 @@ int direction; /* Give me a new nat */ KMALLOC(nat, nat_t *); - if (nat == NULL) + if (nat == NULL) { + nat_stats.ns_memfail++; return NULL; + } bzero((char *)nat, sizeof(*nat)); nat->nat_flags = flags; + if (flags & FI_WILDP) + nat_stats.ns_wilds++; /* * Search the current table for a match. */ @@ -748,42 +1172,33 @@ int direction; do { port = 0; - in.s_addr = np->in_nip; + in.s_addr = htonl(np->in_nip); if (l == 0) { /* * Check to see if there is an existing NAT * setup for this IP address pair. */ - natl = nat_maplookup(fin->fin_ifp, flags, - ip->ip_src, ip->ip_dst); - if (natl != NULL) { - in = natl->nat_outip; - if ((in.s_addr & np->in_outmsk) != - np->in_outip) - in.s_addr = 0; - else -#ifndef sparc - in.s_addr = ntohl(in.s_addr); -#else - ; -#endif - } + hm = nat_hostmap(np, ip->ip_src, in); + if (hm != NULL) + in.s_addr = hm->hm_mapip.s_addr; + } else if ((l == 1) && (hm != NULL)) { + nat_hostmapdel(hm); + hm = NULL; } + in.s_addr = ntohl(in.s_addr); + + nat->nat_hm = hm; if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { - if (l > 0) { - KFREE(nat); - return NULL; - } + if (l > 0) + goto badnat; } if (np->in_redir & NAT_MAPBLK) { if ((l >= np->in_ppip) || ((l > 0) && - !(flags & IPN_TCPUDP))) { - KFREE(nat); - return NULL; - } + !(flags & IPN_TCPUDP))) + goto badnat; /* * map-block - Calculate destination address. */ @@ -811,23 +1226,19 @@ int direction; * 0/32 - use the interface's IP address. */ if ((l > 0) || - fr_ifpaddr(fin->fin_ifp, &in) == -1) { - KFREE(nat); - return NULL; - } + fr_ifpaddr(4, fin->fin_ifp, &in) == -1) + goto badnat; in.s_addr = ntohl(in.s_addr); } else if (!np->in_outip && !np->in_outmsk) { /* * 0/0 - use the original source address/port. */ - if (l > 0) { - KFREE(nat); - return NULL; - } + if (l > 0) + goto badnat; in.s_addr = ntohl(ip->ip_src.s_addr); } else if ((np->in_outmsk != 0xffffffff) && (np->in_pnext == 0) && - ((l > 0) || (natl == NULL))) + ((l > 0) || (hm == NULL))) np->in_nip++; natl = NULL; @@ -836,8 +1247,7 @@ int direction; (np->in_flags & IPN_AUTOPORTMAP)) { if ((l > 0) && (l % np->in_ppip == 0)) { if (l > np->in_space) { - KFREE(nat); - return NULL; + goto badnat; } else if ((l > np->in_ppip) && np->in_outmsk != 0xffffffff) np->in_nip++; @@ -863,7 +1273,7 @@ int direction; } } - if (np->in_flags & IPN_RANGE) { + if (np->in_flags & IPN_IPRANGE) { if (np->in_nip > ntohl(np->in_outmsk)) np->in_nip = ntohl(np->in_outip); } else { @@ -887,9 +1297,9 @@ int direction; * this is appropriate. */ inb.s_addr = htonl(in.s_addr); - natl = nat_inlookup(fin->fin_ifp, flags & ~FI_WILD, + natl = nat_inlookup(fin->fin_ifp, flags & ~FI_WILDP, (u_int)ip->ip_p, ip->ip_dst, inb, - (port << 16) | dport); + (port << 16) | dport, 1); /* * Has the search wrapped around and come back to the @@ -897,10 +1307,8 @@ int direction; */ if ((natl != NULL) && (np->in_pnext != 0) && (st_port == np->in_pnext) && - (np->in_nip != 0) && (st_ip == np->in_nip)) { - KFREE(nat); - return NULL; - } + (np->in_nip != 0) && (st_ip == np->in_nip)) + goto badnat; l++; } while (natl != NULL); @@ -911,6 +1319,9 @@ int direction; nat->nat_inip = ip->ip_src; nat->nat_outip.s_addr = htonl(in.s_addr); nat->nat_oip = ip->ip_dst; + if (nat->nat_hm == NULL) + nat->nat_hm = nat_hostmap(np, ip->ip_src, + nat->nat_outip); sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)) + ntohs(sport); sum2 = LONG_SUM(in.s_addr) + ntohs(port); @@ -927,18 +1338,45 @@ int direction; * we want to rewrite to a fixed internal address and fixed * internal port. */ - in.s_addr = ntohl(np->in_inip); - if (!(nport = np->in_pnext)) + if (np->in_flags & IPN_SPLIT) { + in.s_addr = np->in_nip; + if (np->in_inip == htonl(in.s_addr)) + np->in_nip = ntohl(np->in_inmsk); + else { + np->in_nip = ntohl(np->in_inip); + if (np->in_flags & IPN_ROUNDR) { + nat_delrdr(np); + nat_addrdr(np); + } + } + } else { + in.s_addr = ntohl(np->in_inip); + if (np->in_flags & IPN_ROUNDR) { + nat_delrdr(np); + nat_addrdr(np); + } + } + if (!np->in_pnext) nport = dport; + else { + /* + * Whilst not optimized for the case where + * pmin == pmax, the gain is not significant. + */ + nport = ntohs(dport) - ntohs(np->in_pmin) + + ntohs(np->in_pnext); + nport = htons(nport); + } /* * When the redirect-to address is set to 0.0.0.0, just * assume a blank `forwarding' of the packet. We don't * setup any translation for this either. */ - if ((in.s_addr == 0) && (nport == dport)) { - KFREE(nat); - return NULL; + if (in.s_addr == 0) { + if (nport == dport) + goto badnat; + in.s_addr = ntohl(ip->ip_dst.s_addr); } nat->nat_inip.s_addr = htonl(in.s_addr); @@ -986,28 +1424,21 @@ int direction; nat->nat_ipsumd = nat->nat_sumd[0]; in.s_addr = htonl(in.s_addr); - nat->nat_next = nat_instances; - nat_instances = nat; - hv = NAT_HASH_FN(nat->nat_inip.s_addr, ipf_nattable_sz); - natp = &nat_table[0][hv]; - nat->nat_hstart[0] = natp; - nat->nat_hnext[0] = *natp; - *natp = nat; - hv = NAT_HASH_FN(nat->nat_outip.s_addr, ipf_nattable_sz); - natp = &nat_table[1][hv]; - nat->nat_hstart[1] = natp; - nat->nat_hnext[1] = *natp; - *natp = nat; + +#ifdef _KERNEL + strncpy(nat->nat_ifname, IFNAME(fin->fin_ifp), IFNAMSIZ); +#endif + nat_insert(nat); + nat->nat_dir = direction; nat->nat_ifp = fin->fin_ifp; nat->nat_ptr = np; nat->nat_p = ip->ip_p; nat->nat_bytes = 0; nat->nat_pkts = 0; - nat->nat_age = fr_defnatage; nat->nat_fr = fin->fin_fr; if (nat->nat_fr != NULL) { - ATOMIC_INC(nat->nat_fr->fr_ref); + ATOMIC_INC32(nat->nat_fr->fr_ref); } if (direction == NAT_OUTBOUND) { if (flags & IPN_TCPUDP) @@ -1016,24 +1447,70 @@ int direction; if (flags & IPN_TCPUDP) tcp->th_dport = nport; } - nat_stats.ns_added++; - nat_stats.ns_inuse++; np->in_use++; +#ifdef IPFILTER_LOG + nat_log(nat, (u_int)np->in_redir); +#endif return nat; +badnat: + nat_stats.ns_badnat++; + if ((hm = nat->nat_hm) != NULL) + nat_hostmapdel(hm); + KFREE(nat); + return NULL; } -nat_t *nat_icmpinlookup(ip, fin) +void nat_insert(nat) +nat_t *nat; +{ + nat_t **natp; + u_int hv; + + MUTEX_INIT(&nat->nat_lock, "nat entry lock", NULL); + + nat->nat_age = fr_defnatage; + nat->nat_ifname[sizeof(nat->nat_ifname) - 1] = '\0'; + if (nat->nat_ifname[0] !='\0') { + nat->nat_ifp = GETUNIT(nat->nat_ifname, 4); + } + + nat->nat_next = nat_instances; + nat_instances = nat; + + hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, + ipf_nattable_sz); + natp = &nat_table[0][hv]; + if (*natp) + (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; + nat->nat_phnext[0] = natp; + nat->nat_hnext[0] = *natp; + *natp = nat; + + hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, + ipf_nattable_sz); + natp = &nat_table[1][hv]; + if (*natp) + (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; + nat->nat_phnext[1] = natp; + nat->nat_hnext[1] = *natp; + *natp = nat; + + nat_stats.ns_added++; + nat_stats.ns_inuse++; +} + + +nat_t *nat_icmplookup(ip, fin, dir) ip_t *ip; fr_info_t *fin; +int dir; { - tcphdr_t *tcp = NULL; icmphdr_t *icmp; + tcphdr_t *tcp = NULL; ip_t *oip; int flags = 0, type, minlen; - if ((fin->fin_fi.fi_fl & FI_SHORT) || (ip->ip_off & IP_OFFMASK)) - return NULL; icmp = (icmphdr_t *)fin->fin_dp; /* * Does it at least have the return (basic) IP header ? @@ -1053,7 +1530,9 @@ fr_info_t *fin; oip = (ip_t *)((char *)fin->fin_dp + 8); minlen = (oip->ip_hl << 2); - if (ip->ip_len < ICMPERR_MINPKTLEN + minlen) + if (minlen < sizeof(ip_t)) + return NULL; + if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen) return NULL; /* * Is the buffer big enough for all of it ? It's the size of the IP @@ -1087,15 +1566,24 @@ fr_info_t *fin; flags = IPN_UDP; if (flags & IPN_TCPUDP) { minlen += 8; /* + 64bits of data to get ports */ - if (ip->ip_len < ICMPERR_MINPKTLEN + minlen) + if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen) return NULL; tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); - return nat_inlookup(fin->fin_ifp, flags, (u_int)oip->ip_p, - oip->ip_dst, oip->ip_src, - (tcp->th_sport << 16) | tcp->th_dport); + if (dir == NAT_INBOUND) + return nat_inlookup(fin->fin_ifp, flags, + (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, + (tcp->th_sport << 16) | tcp->th_dport, 0); + else + return nat_outlookup(fin->fin_ifp, flags, + (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, + (tcp->th_sport << 16) | tcp->th_dport, 0); } - return nat_inlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, oip->ip_dst, - oip->ip_src, 0); + if (dir == NAT_INBOUND) + return nat_inlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, + oip->ip_dst, oip->ip_src, 0, 0); + else + return nat_outlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, + oip->ip_dst, oip->ip_src, 0, 0); } @@ -1103,19 +1591,26 @@ fr_info_t *fin; * This should *ONLY* be used for incoming packets to make sure a NAT'd ICMP * packet gets correctly recognised. */ -nat_t *nat_icmpin(ip, fin, nflags) +nat_t *nat_icmp(ip, fin, nflags, dir) ip_t *ip; fr_info_t *fin; u_int *nflags; +int dir; { - u_32_t sum1, sum2, sumd; + u_32_t sum1, sum2, sumd, sumd2 = 0; struct in_addr in; icmphdr_t *icmp; + udphdr_t *udp; nat_t *nat; ip_t *oip; int flags = 0; - if (!(nat = nat_icmpinlookup(ip, fin))) + if ((fin->fin_fi.fi_fl & FI_SHORT) || (ip->ip_off & IP_OFFMASK)) + return NULL; + /* + * nat_icmplookup() will return NULL for `defective' packets. + */ + if ((ip->ip_v != 4) || !(nat = nat_icmplookup(ip, fin, dir))) return NULL; *nflags = IPN_ICMPERR; icmp = (icmphdr_t *)fin->fin_dp; @@ -1124,16 +1619,34 @@ u_int *nflags; flags = IPN_TCP; else if (oip->ip_p == IPPROTO_UDP) flags = IPN_UDP; + udp = (udphdr_t *)((((char *)oip) + (oip->ip_hl << 2))); /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the - * IP address change is it will be modified again in ip_natout + * IP address change as it will be modified again in ip_natout * for both address and port. Two checksum changes are * necessary for the two header address changes. Be careful * to only modify the checksum once for the port # and twice * for the IP#. */ - if (nat->nat_dir == NAT_OUTBOUND) { + + /* + * Step 1 + * Fix the IP addresses in the offending IP packet. You also need + * to adjust the IP header checksum of that offending IP packet + * and the ICMP checksum of the ICMP error message itself. + * + * Unfortunately, for UDP and TCP, the IP addresses are also contained + * in the pseudo header that is used to compute the UDP resp. TCP + * checksum. So, we must compensate that as well. Even worse, the + * 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) { sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); in = nat->nat_inip; oip->ip_src = in; @@ -1148,21 +1661,119 @@ u_int *nflags; CALC_SUMD(sum1, sum2, sumd); if (nat->nat_dir == NAT_OUTBOUND) { - fix_incksum(&oip->ip_sum, sumd, 0); + /* + * Fix IP checksum of the offending IP packet to adjust for + * the change in the IP address. + * + * Normally, you would expect that the ICMP checksum of the + * ICMP error message needs to be adjusted as well for the + * IP address change in oip. + * However, this is a NOP, because the ICMP checksum is + * calculated over the complete ICMP packet, which includes the + * changed oip IP addresses and oip->ip_sum. However, these + * two changes cancel each other out (if the delta for + * the IP address is x, then the delta for ip_sum is minus x), + * so no change in the icmp_cksum is necessary. + * + * Be careful that nat_dir refers to the direction of the + * offending IP packet (oip), not to its ICMP response (icmp) + */ + fix_datacksum(&oip->ip_sum, sumd); - sumd += (sumd & 0xffff); - while (sumd > 0xffff) - sumd = (sumd & 0xffff) + (sumd >> 16); - fix_outcksum(&icmp->icmp_cksum, sumd, 0); + /* + * Fix UDP pseudo header checksum to compensate for the + * IP address change. + */ + if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { + /* + * The UDP checksum is optional, only adjust it + * if it has been set. + */ + sum1 = ntohs(udp->uh_sum); + fix_datacksum(&udp->uh_sum, sumd); + sum2 = ntohs(udp->uh_sum); + + /* + * Fix ICMP checksum to compensate the UDP + * checksum adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 = sumd; + } + +#if 0 + /* + * 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) { + + } +#endif } else { - fix_outcksum(&oip->ip_sum, sumd, 0); - sumd += (sumd & 0xffff); - while (sumd > 0xffff) - sumd = (sumd & 0xffff) + (sumd >> 16); - fix_incksum(&icmp->icmp_cksum, sumd, 0); - } + /* + * Fix IP checksum of the offending IP packet to adjust for + * the change in the IP address. + * + * Normally, you would expect that the ICMP checksum of the + * ICMP error message needs to be adjusted as well for the + * IP address change in oip. + * However, this is a NOP, because the ICMP checksum is + * calculated over the complete ICMP packet, which includes the + * changed oip IP addresses and oip->ip_sum. However, these + * two changes cancel each other out (if the delta for + * the IP address is x, then the delta for ip_sum is minus x), + * so no change in the icmp_cksum is necessary. + * + * Be careful that nat_dir refers to the direction of the + * offending IP packet (oip), not to its ICMP response (icmp) + */ + fix_datacksum(&oip->ip_sum, sumd); + +/* XXX FV : without having looked at Solaris source code, it seems unlikely + * that SOLARIS would compensate this in the kernel (a body of an IP packet + * in the data section of an ICMP packet). I have the feeling that this should + * be unconditional, but I'm not in a position to check. + */ +#if !SOLARIS && !defined(__sgi) + /* + * Fix UDP pseudo header checksum to compensate for the + * IP address change. + */ + if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { + /* + * The UDP checksum is optional, only adjust it + * if it has been set + */ + sum1 = ntohs(udp->uh_sum); + fix_datacksum(&udp->uh_sum, sumd); + sum2 = ntohs(udp->uh_sum); + /* + * Fix ICMP checksum to compensate the UDP + * checksum adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 = sumd; + } + +#if 0 + /* + * 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) { + + }; +#endif + +#endif + } if ((flags & IPN_TCPUDP) != 0) { tcphdr_t *tcp; @@ -1171,23 +1782,106 @@ u_int *nflags; * 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 *)((((char *)oip) + (oip->ip_hl << 2))); + tcp = (tcphdr_t *)udp; - if (nat->nat_dir == NAT_OUTBOUND) { + /* + * Step 2 : + * For offending TCP/UDP IP packets, translate the ports as + * well, based on the NAT specification. Of course such + * a change must be reflected in the ICMP checksum as well. + * + * Advance notice : Now it becomes complicated :-) + * + * Since the port fields are part of the TCP/UDP checksum + * of the offending IP packet, you need to adjust that checksum + * as well... but, if you change, you must change the icmp + * checksum *again*, to reflect that change. + * + * 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) + */ + + if (nat->nat_oport == tcp->th_dport) { if (tcp->th_sport != nat->nat_inport) { + /* + * Fix ICMP checksum to compensate port + * adjustment. + */ sum1 = ntohs(tcp->th_sport); sum2 = ntohs(nat->nat_inport); CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; tcp->th_sport = nat->nat_inport; - fix_outcksum(&icmp->icmp_cksum, sumd, 0); + + /* + * Fix udp checksum to compensate port + * adjustment. NOTE : the offending IP packet + * flows the other direction compared to the + * ICMP message. + * + * The UDP checksum is optional, only adjust + * it if it has been set. + */ + if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { + + sum1 = ntohs(udp->uh_sum); + fix_datacksum(&udp->uh_sum, sumd); + sum2 = ntohs(udp->uh_sum); + + /* + * Fix ICMP checksum to + * compensate UDP checksum + * adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; + } } } else { if (tcp->th_dport != nat->nat_outport) { + /* + * Fix ICMP checksum to compensate port + * adjustment. + */ sum1 = ntohs(tcp->th_dport); sum2 = ntohs(nat->nat_outport); CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; tcp->th_dport = nat->nat_outport; - fix_incksum(&icmp->icmp_cksum, sumd, 0); + + /* + * Fix udp checksum to compensate port + * adjustment. NOTE : the offending IP + * packet flows the other direction compared + * to the ICMP message. + * + * The UDP checksum is optional, only adjust + * it if it has been set. + */ + if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { + + sum1 = ntohs(udp->uh_sum); + fix_datacksum(&udp->uh_sum, sumd); + sum2 = ntohs(udp->uh_sum); + + /* + * Fix ICMP checksum to compensate + * UDP checksum adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; + } + } + } + if (sumd2) { + sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); + sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); + if (nat->nat_dir == NAT_OUTBOUND) { + fix_outcksum(&icmp->icmp_cksum, sumd2); + } else { + fix_incksum(&icmp->icmp_cksum, sumd2); } } } @@ -1206,36 +1900,121 @@ u_int *nflags; * we're looking for a table entry, based on the destination address. * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ -nat_t *nat_inlookup(ifp, flags, p, src, mapdst, ports) +nat_t *nat_inlookup(ifp, flags, p, src, mapdst, ports, rw) void *ifp; register u_int flags, p; struct in_addr src , mapdst; u_32_t ports; +int rw; { - register u_short sport, mapdport; + register u_short sport, dport; register nat_t *nat; register int nflags; + register u_32_t dst; u_int hv; - mapdport = ports >> 16; + dst = mapdst.s_addr; + dport = ports >> 16; sport = ports & 0xffff; flags &= IPN_TCPUDP; - hv = NAT_HASH_FN(mapdst.s_addr, ipf_nattable_sz); + hv = NAT_HASH_FN(dst, dport, ipf_nattable_sz); nat = nat_table[1][hv]; for (; nat; nat = nat->nat_hnext[1]) { nflags = nat->nat_flags; if ((!ifp || ifp == nat->nat_ifp) && nat->nat_oip.s_addr == src.s_addr && - nat->nat_outip.s_addr == mapdst.s_addr && + nat->nat_outip.s_addr == dst && (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP))) || (p == nat->nat_p)) && (!flags || (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && - ((nat->nat_outport == mapdport) || - (nflags & FI_W_SPORT))))) + ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))))) return nat; } - return NULL; + if (!nat_stats.ns_wilds || !(flags & IPN_TCPUDP)) + return NULL; + if (!rw) { + RWLOCK_EXIT(&ipf_nat); + } + hv = NAT_HASH_FN(dst, 0, ipf_nattable_sz); + if (!rw) { + WRITE_ENTER(&ipf_nat); + } + nat = nat_table[1][hv]; + for (; nat; nat = nat->nat_hnext[1]) { + nflags = nat->nat_flags; + if (ifp && ifp != nat->nat_ifp) + continue; + if (!(nflags & IPN_TCPUDP)) + continue; + if (!(nflags & FI_WILDP)) + continue; + if (nat->nat_oip.s_addr != src.s_addr || + nat->nat_outip.s_addr != dst) + continue; + if (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && + ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))) { + nat_tabmove(nat, ports); + break; + } + } + if (!rw) { + MUTEX_DOWNGRADE(&ipf_nat); + } + return nat; +} + + +/* + * This function is only called for TCP/UDP NAT table entries where the + * original was placed in the table without hashing on the ports and we now + * want to include hashing on port numbers. + */ +static void nat_tabmove(nat, ports) +nat_t *nat; +u_32_t ports; +{ + register u_short sport, dport; + nat_t **natp; + u_int hv; + + dport = ports >> 16; + sport = ports & 0xffff; + + if (nat->nat_oport == dport) { + nat->nat_inport = sport; + nat->nat_outport = sport; + } + + /* + * Remove the NAT entry from the old location + */ + if (nat->nat_hnext[0]) + nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; + *nat->nat_phnext[0] = nat->nat_hnext[0]; + + if (nat->nat_hnext[1]) + nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; + *nat->nat_phnext[1] = nat->nat_hnext[1]; + + /* + * Add into the NAT table in the new position + */ + hv = NAT_HASH_FN(nat->nat_inip.s_addr, sport, ipf_nattable_sz); + natp = &nat_table[0][hv]; + if (*natp) + (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; + nat->nat_phnext[0] = natp; + nat->nat_hnext[0] = *natp; + *natp = nat; + + hv = NAT_HASH_FN(nat->nat_outip.s_addr, sport, ipf_nattable_sz); + natp = &nat_table[1][hv]; + if (*natp) + (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; + nat->nat_phnext[1] = natp; + nat->nat_hnext[1] = *natp; + *natp = nat; } @@ -1245,28 +2024,31 @@ u_32_t ports; * we're looking for a table entry, based on the source address. * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ -nat_t *nat_outlookup(ifp, flags, p, src, dst, ports) +nat_t *nat_outlookup(ifp, flags, p, src, dst, ports, rw) void *ifp; register u_int flags, p; struct in_addr src , dst; u_32_t ports; +int rw; { register u_short sport, dport; register nat_t *nat; register int nflags; + u_32_t srcip; u_int hv; sport = ports & 0xffff; dport = ports >> 16; flags &= IPN_TCPUDP; + srcip = src.s_addr; - hv = NAT_HASH_FN(src.s_addr, ipf_nattable_sz); + hv = NAT_HASH_FN(srcip, sport, ipf_nattable_sz); nat = nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { nflags = nat->nat_flags; if ((!ifp || ifp == nat->nat_ifp) && - nat->nat_inip.s_addr == src.s_addr && + nat->nat_inip.s_addr == srcip && nat->nat_oip.s_addr == dst.s_addr && (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP))) || (p == nat->nat_p)) && (!flags || @@ -1274,36 +2056,37 @@ u_32_t ports; (nat->nat_oport == dport || nflags & FI_W_DPORT)))) return nat; } - return NULL; -} - - -/* - * check if an ip address has already been allocated for a given mapping that - * is not doing port based translation. - */ -nat_t *nat_maplookup(ifp, flags, src, dst) -void *ifp; -register u_int flags; -struct in_addr src , dst; -{ - register nat_t *nat; - register int oflags; - u_int hv; - - hv = NAT_HASH_FN(src.s_addr, ipf_nattable_sz); + if (!nat_stats.ns_wilds || !(flags & IPN_TCPUDP)) + return NULL; + if (!rw) { + RWLOCK_EXIT(&ipf_nat); + } + hv = NAT_HASH_FN(srcip, 0, ipf_nattable_sz); + if (!rw) { + WRITE_ENTER(&ipf_nat); + } nat = nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { - oflags = (flags & IPN_TCPUDP) & nat->nat_ptr->in_flags; - if (oflags != 0) + nflags = nat->nat_flags; + if (ifp && ifp != nat->nat_ifp) continue; - - if ((!ifp || ifp == nat->nat_ifp) && - nat->nat_inip.s_addr == src.s_addr && - nat->nat_oip.s_addr == dst.s_addr) - return nat; + if (!(nflags & IPN_TCPUDP)) + continue; + if (!(nflags & FI_WILDP)) + continue; + if ((nat->nat_inip.s_addr != srcip) || + (nat->nat_oip.s_addr != dst.s_addr)) + continue; + if (((nat->nat_inport == sport) || (nflags & FI_W_SPORT)) && + ((nat->nat_oport == dport) || (nflags & FI_W_DPORT))) { + nat_tabmove(nat, ports); + break; + } } - return NULL; + if (!rw) { + MUTEX_DOWNGRADE(&ipf_nat); + } + return nat; } @@ -1322,7 +2105,7 @@ register natlookup_t *np; * ip address. Else, we use the fake. */ if ((nat = nat_outlookup(NULL, np->nl_flags, 0, np->nl_inip, - np->nl_outip, ports))) { + np->nl_outip, ports, 0))) { np->nl_realip = nat->nat_outip; np->nl_realport = nat->nat_outport; } @@ -1330,6 +2113,50 @@ register natlookup_t *np; } +static int nat_match(fin, np, ip) +fr_info_t *fin; +ipnat_t *np; +ip_t *ip; +{ + frtuc_t *ft; + + if (ip->ip_v != 4) + return 0; + + if (np->in_p && ip->ip_p != np->in_p) + return 0; + if (fin->fin_out) { + if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) + return 0; + if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) + ^ ((np->in_flags & IPN_NOTSRC) != 0)) + return 0; + if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) + ^ ((np->in_flags & IPN_NOTDST) != 0)) + return 0; + } else { + if (!(np->in_redir & NAT_REDIRECT)) + return 0; + if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) + ^ ((np->in_flags & IPN_NOTSRC) != 0)) + return 0; + if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) + ^ ((np->in_flags & IPN_NOTDST) != 0)) + return 0; + } + + ft = &np->in_tuc; + if (!(fin->fin_fi.fi_fl & FI_TCPUDP) || + (fin->fin_fi.fi_fl & FI_SHORT) || (ip->ip_off & IP_OFFMASK)) { + if (ft->ftu_scmp || ft->ftu_dcmp) + return 0; + return 1; + } + + return fr_tcpudpchk(ft, fin); +} + + /* * Packets going out on the external interface go through this. * Here, the source address requires alteration, if anything. @@ -1341,16 +2168,16 @@ fr_info_t *fin; register ipnat_t *np = NULL; register u_32_t ipa; tcphdr_t *tcp = NULL; - u_short nflags = 0, sport = 0, dport = 0, *csump = NULL; + u_short sport = 0, dport = 0, *csump = NULL; struct ifnet *ifp; int natadd = 1; frentry_t *fr; - u_int hv, msk; + u_int nflags = 0, hv, msk; u_32_t iph; nat_t *nat; int i; - if (nat_list == NULL) + if (nat_list == NULL || (fr_nat_lock)) return 0; if ((fr = fin->fin_fr) && !(fr->fr_flags & FR_DUP) && @@ -1374,11 +2201,16 @@ fr_info_t *fin; ipa = ip->ip_src.s_addr; READ_ENTER(&ipf_nat); - if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && + + if ((ip->ip_p == IPPROTO_ICMP) && + (nat = nat_icmp(ip, fin, &nflags, NAT_OUTBOUND))) + ; + else if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && (nat = ipfr_nat_knownfrag(ip, fin))) natadd = 0; - else if ((nat = nat_outlookup(ifp, nflags, (u_int)ip->ip_p, ip->ip_src, - ip->ip_dst, (dport << 16) | sport))) { + else if ((nat = nat_outlookup(ifp, nflags, (u_int)ip->ip_p, + ip->ip_src, ip->ip_dst, + (dport << 16) | sport, 0))) { nflags = nat->nat_flags; if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { if ((nflags & FI_W_SPORT) && @@ -1391,6 +2223,7 @@ fr_info_t *fin; nat->nat_outport = sport; nat->nat_flags &= ~(FI_W_DPORT|FI_W_SPORT); nflags = nat->nat_flags; + nat_stats.ns_wilds--; } } else { RWLOCK_EXIT(&ipf_nat); @@ -1403,15 +2236,21 @@ fr_info_t *fin; i = 32; maskloop: iph = ipa & htonl(msk); - hv = NAT_HASH_FN(iph, ipf_natrules_sz); + hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); for (np = nat_rules[hv]; np; np = np->in_mnext) { - if ((np->in_ifp == ifp) && np->in_space && - (!(np->in_flags & IPN_RF) || - (np->in_flags & nflags)) && - ((ipa & np->in_inmsk) == np->in_inip) && - ((np->in_redir & (NAT_MAP|NAT_MAPBLK)) || - (np->in_pnext == sport))) { + if ((np->in_ifp && (np->in_ifp != ifp)) || + !np->in_space) + continue; + if ((np->in_flags & IPN_RF) && + !(np->in_flags & nflags)) + continue; + if (np->in_flags & IPN_FILTER) { + if (!nat_match(fin, np, ip)) + continue; + } else if ((ipa & np->in_inmsk) != np->in_inip) + continue; + if (np->in_redir & (NAT_MAP|NAT_MAPBLK)) { if (*np->in_plabel && !appr_ok(ip, tcp, np)) continue; /* @@ -1425,9 +2264,6 @@ maskloop: if ((nat = nat_new(np, ip, fin, (u_int)nflags, NAT_OUTBOUND))) { np->in_hits++; -#ifdef IPFILTER_LOG - nat_log(nat, (u_int)np->in_redir); -#endif break; } } @@ -1443,27 +2279,44 @@ maskloop: MUTEX_DOWNGRADE(&ipf_nat); } + /* + * NOTE: ipf_nat must now only be held as a read lock + */ if (nat) { np = nat->nat_ptr; if (natadd && fin->fin_fi.fi_fl & FI_FRAG) ipfr_nat_newfrag(ip, fin, 0, nat); - ip->ip_src = nat->nat_outip; - MUTEX_ENTER(&ipf_rw); + MUTEX_ENTER(&nat->nat_lock); nat->nat_age = fr_defnatage; nat->nat_bytes += ip->ip_len; nat->nat_pkts++; - MUTEX_EXIT(&ipf_rw); + MUTEX_EXIT(&nat->nat_lock); /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ + if (nflags == IPN_ICMPERR) { + u_32_t s1, s2, sumd; + + s1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); + s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); + CALC_SUMD(s1, s2, sumd); + + if (nat->nat_dir == NAT_OUTBOUND) + fix_incksum(&ip->ip_sum, sumd); + else + fix_outcksum(&ip->ip_sum, sumd); + } #if SOLARIS || defined(__sgi) - if (nat->nat_dir == NAT_OUTBOUND) - fix_outcksum(&ip->ip_sum, nat->nat_ipsumd, 0); - else - fix_incksum(&ip->ip_sum, nat->nat_ipsumd, 0); + else { + if (nat->nat_dir == NAT_OUTBOUND) + fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); + else + fix_incksum(&ip->ip_sum, nat->nat_ipsumd); + } #endif + ip->ip_src = nat->nat_outip; if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { @@ -1475,14 +2328,14 @@ maskloop: if (ip->ip_p == IPPROTO_TCP) { csump = &tcp->th_sum; - MUTEX_ENTER(&ipf_rw); + MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, - nat->nat_tcpstate, ip, fin, 1); + nat->nat_tcpstate, fin, 1); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT - else if (nat->nat_age > DEF_NAT_AGE) - nat->nat_age = DEF_NAT_AGE; + else if (nat->nat_age > fr_defnatage) + nat->nat_age = fr_defnatage; #endif /* * Increase this because we may have @@ -1492,7 +2345,7 @@ maskloop: */ if (nat->nat_age == fr_tcpclosed) nat->nat_age = fr_tcplastack; - MUTEX_EXIT(&ipf_rw); + MUTEX_EXIT(&nat->nat_lock); } else if (ip->ip_p == IPPROTO_UDP) { udphdr_t *udp = (udphdr_t *)tcp; @@ -1504,19 +2357,22 @@ maskloop: if (csump) { if (nat->nat_dir == NAT_OUTBOUND) - fix_outcksum(csump, nat->nat_sumd[1], - ip->ip_len); + fix_outcksum(csump, nat->nat_sumd[1]); else - fix_incksum(csump, nat->nat_sumd[1], - ip->ip_len); + fix_incksum(csump, nat->nat_sumd[1]); } } + if ((np->in_apr != NULL) && (np->in_dport == 0 || - (tcp != NULL && dport == np->in_dport))) - (void) appr_check(ip, fin, nat); - ATOMIC_INC(nat_stats.ns_mapped[1]); + (tcp != NULL && dport == np->in_dport))) { + i = appr_check(ip, fin, nat); + if (i == 0) + i = 1; + } else + i = 1; + ATOMIC_INCL(nat_stats.ns_mapped[1]); RWLOCK_EXIT(&ipf_nat); /* READ */ - return 1; + return i; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ return 0; @@ -1542,7 +2398,7 @@ fr_info_t *fin; u_32_t iph; int i; - if (nat_list == NULL) + if ((nat_list == NULL) || (ip->ip_v != 4) || (fr_nat_lock)) return 0; if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { @@ -1563,13 +2419,15 @@ fr_info_t *fin; READ_ENTER(&ipf_nat); - if ((ip->ip_p == IPPROTO_ICMP) && (nat = nat_icmpin(ip, fin, &nflags))) + if ((ip->ip_p == IPPROTO_ICMP) && + (nat = nat_icmp(ip, fin, &nflags, NAT_INBOUND))) ; - else if ((ip->ip_off & IP_OFFMASK) && + else if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && (nat = ipfr_nat_knownfrag(ip, fin))) natadd = 0; else if ((nat = nat_inlookup(fin->fin_ifp, nflags, (u_int)ip->ip_p, - ip->ip_src, in, (dport << 16) | sport))) { + ip->ip_src, in, (dport << 16) | sport, + 0))) { nflags = nat->nat_flags; if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { if ((nat->nat_oport != sport) && (nflags & FI_W_DPORT)) @@ -1579,6 +2437,7 @@ fr_info_t *fin; nat->nat_outport = dport; nat->nat_flags &= ~(FI_W_SPORT|FI_W_DPORT); nflags = nat->nat_flags; + nat_stats.ns_wilds--; } } else { RWLOCK_EXIT(&ipf_nat); @@ -1591,24 +2450,28 @@ fr_info_t *fin; i = 32; maskloop: iph = in.s_addr & htonl(msk); - hv = NAT_HASH_FN(iph, ipf_rdrrules_sz); - for (np = rdr_rules[hv]; np; np = np->in_rnext) - if ((np->in_ifp == ifp) && - (!np->in_flags || (nflags & np->in_flags)) && - ((in.s_addr & np->in_outmsk) == np->in_outip) && - ((src.s_addr & np->in_srcmsk) == np->in_srcip) && - (np->in_redir & NAT_REDIRECT) && - (!np->in_pmin || np->in_pmin == dport) && - (!np->in_p || np->in_p == ip->ip_p)) { + hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); + for (np = rdr_rules[hv]; np; np = np->in_rnext) { + if ((np->in_ifp && (np->in_ifp != ifp)) || + (np->in_p && (np->in_p != ip->ip_p)) || + (np->in_flags && !(nflags & np->in_flags))) + continue; + if (np->in_flags & IPN_FILTER) { + if (!nat_match(fin, np, ip)) + continue; + } else if ((in.s_addr & np->in_outmsk) != np->in_outip) + continue; + if ((np->in_redir & NAT_REDIRECT) && + (!np->in_pmin || (np->in_flags & IPN_FILTER) || + ((ntohs(np->in_pmax) >= ntohs(dport)) && + (ntohs(dport) >= ntohs(np->in_pmin))))) if ((nat = nat_new(np, ip, fin, nflags, NAT_INBOUND))) { np->in_hits++; -#ifdef IPFILTER_LOG - nat_log(nat, (u_int)np->in_redir); -#endif break; } - } + } + if ((np == NULL) && (i > 0)) { do { i--; @@ -1619,24 +2482,33 @@ maskloop: } MUTEX_DOWNGRADE(&ipf_nat); } + + /* + * NOTE: ipf_nat must now only be held as a read lock + */ if (nat) { np = nat->nat_ptr; fin->fin_fr = nat->nat_fr; if (natadd && fin->fin_fi.fi_fl & FI_FRAG) ipfr_nat_newfrag(ip, fin, 0, nat); if ((np->in_apr != NULL) && (np->in_dport == 0 || - (tcp != NULL && sport == np->in_dport))) - (void) appr_check(ip, fin, nat); + (tcp != NULL && sport == np->in_dport))) { + i = appr_check(ip, fin, nat); + if (i == -1) { + RWLOCK_EXIT(&ipf_nat); + return i; + } + } - MUTEX_ENTER(&ipf_rw); + MUTEX_ENTER(&nat->nat_lock); if (nflags != IPN_ICMPERR) nat->nat_age = fr_defnatage; nat->nat_bytes += ip->ip_len; nat->nat_pkts++; - MUTEX_EXIT(&ipf_rw); + MUTEX_EXIT(&nat->nat_lock); ip->ip_dst = nat->nat_inip; - fin->fin_fi.fi_dst = nat->nat_inip; + fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; /* * Fix up checksums, not by recalculating them, but @@ -1644,9 +2516,9 @@ maskloop: */ #if SOLARIS || defined(__sgi) if (nat->nat_dir == NAT_OUTBOUND) - fix_incksum(&ip->ip_sum, nat->nat_ipsumd, 0); + fix_incksum(&ip->ip_sum, nat->nat_ipsumd); else - fix_outcksum(&ip->ip_sum, nat->nat_ipsumd, 0); + fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); #endif if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { @@ -1658,14 +2530,14 @@ maskloop: if (ip->ip_p == IPPROTO_TCP) { csump = &tcp->th_sum; - MUTEX_ENTER(&ipf_rw); + MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, - nat->nat_tcpstate, ip, fin, 0); + nat->nat_tcpstate, fin, 0); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT - else if (nat->nat_age > DEF_NAT_AGE) - nat->nat_age = DEF_NAT_AGE; + else if (nat->nat_age > fr_defnatage) + nat->nat_age = fr_defnatage; #endif /* * Increase this because we may have @@ -1675,7 +2547,7 @@ maskloop: */ if (nat->nat_age == fr_tcpclosed) nat->nat_age = fr_tcplastack; - MUTEX_EXIT(&ipf_rw); + MUTEX_EXIT(&nat->nat_lock); } else if (ip->ip_p == IPPROTO_UDP) { udphdr_t *udp = (udphdr_t *)tcp; @@ -1684,14 +2556,15 @@ maskloop: } else if (ip->ip_p == IPPROTO_ICMP) { nat->nat_age = fr_defnaticmpage; } + if (csump) { if (nat->nat_dir == NAT_OUTBOUND) - fix_incksum(csump, nat->nat_sumd[0], 0); + fix_incksum(csump, nat->nat_sumd[0]); else - fix_outcksum(csump, nat->nat_sumd[0], 0); + fix_outcksum(csump, nat->nat_sumd[0]); } } - ATOMIC_INC(nat_stats.ns_mapped[0]); + ATOMIC_INCL(nat_stats.ns_mapped[0]); RWLOCK_EXIT(&ipf_nat); /* READ */ return 1; } @@ -1726,6 +2599,10 @@ void ip_natunload() KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); rdr_rules = NULL; } + if (maptable != NULL) { + KFREES(maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); + maptable = NULL; + } } @@ -1791,7 +2668,7 @@ void *ifp; * new one. */ sum1 = nat->nat_outip.s_addr; - if (fr_ifpaddr(ifp2, &in) != -1) + if (fr_ifpaddr(4, ifp2, &in) != -1) nat->nat_outip = in; sum2 = nat->nat_outip.s_addr; @@ -1812,7 +2689,7 @@ void *ifp; for (n = nat_list; (n != NULL); n = n->in_next) if (n->in_ifp == ifp) { - n->in_ifp = (void *)GETUNIT(n->in_ifname); + n->in_ifp = (void *)GETUNIT(n->in_ifname, 4); if (!n->in_ifp) n->in_ifp = (void *)-1; } diff --git a/sys/netinet/ip_nat.h b/sys/netinet/ip_nat.h index 9d126294ba9..8b218a28148 100644 --- a/sys/netinet/ip_nat.h +++ b/sys/netinet/ip_nat.h @@ -1,14 +1,14 @@ -/* $OpenBSD: ip_nat.h,v 1.17 2000/04/05 05:35:27 kjell Exp $ */ +/* $OpenBSD: ip_nat.h,v 1.18 2001/01/17 04:47:15 fgsch Exp $ */ /* - * Copyright (C) 1995-1998 by Darren Reed. + * Copyright (C) 1995-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. * * @(#)ip_nat.h 1.5 2/4/96 - * $IPFilter: ip_nat.h,v 2.1.2.4 2000/03/15 13:57:03 darrenr Exp $ + * $IPFilter: ip_nat.h,v 2.17.2.14 2000/11/18 03:58:04 darrenr Exp $ */ #ifndef __IP_NAT_H__ @@ -19,23 +19,15 @@ #endif #if defined(__STDC__) || defined(__GNUC__) -#define SIOCADNAT _IOW('r', 80, struct ipnat) -#define SIOCRMNAT _IOW('r', 81, struct ipnat) -#define SIOCGNATS _IOR('r', 82, struct natstat) -#define SIOCGNATL _IOWR('r', 83, struct natlookup) -#define SIOCGFRST _IOR('r', 84, struct ipfrstat) -#define SIOCGIPST _IOR('r', 85, struct ips_stat) -#define SIOCFLNAT _IOWR('r', 86, int) -#define SIOCCNATL _IOWR('r', 87, int) +#define SIOCADNAT _IOW('r', 60, struct ipnat *) +#define SIOCRMNAT _IOW('r', 61, struct ipnat *) +#define SIOCGNATS _IOWR('r', 62, struct natstat *) +#define SIOCGNATL _IOWR('r', 63, struct natlookup *) #else -#define SIOCADNAT _IOW(r, 80, struct ipnat) -#define SIOCRMNAT _IOW(r, 81, struct ipnat) -#define SIOCGNATS _IOR(r, 82, struct natstat) -#define SIOCGNATL _IOWR(r, 83, struct natlookup) -#define SIOCGFRST _IOR(r, 84, struct ipfrstat) -#define SIOCGIPST _IOR(r, 85, struct ips_stat) -#define SIOCFLNAT _IOWR(r, 86, int) -#define SIOCCNATL _IOWR(r, 87, int) +#define SIOCADNAT _IOW(r, 60, struct ipnat *) +#define SIOCRMNAT _IOW(r, 61, struct ipnat *) +#define SIOCGNATS _IOWR(r, 62, struct natstat *) +#define SIOCGNATL _IOWR(r, 63, struct natlookup *) #endif #undef LARGE_NAT /* define this if you're setting up a system to NAT @@ -47,14 +39,17 @@ */ #define NAT_SIZE 127 #define RDR_SIZE 127 +#define HOSTMAP_SIZE 127 #define NAT_TABLE_SZ 127 #ifdef LARGE_NAT #undef NAT_SIZE #undef RDR_SIZE #undef NAT_TABLE_SZ +#undef HOSTMAP_SIZE 127 #define NAT_SIZE 2047 #define RDR_SIZE 2047 #define NAT_TABLE_SZ 16383 +#define HOSTMAP_SIZE 8191 #endif #ifndef APR_LABELLEN #define APR_LABELLEN 16 @@ -85,17 +80,24 @@ typedef struct nat { u_char nat_tcpstate[2]; u_char nat_p; /* protocol for NAT */ struct ipnat *nat_ptr; /* pointer back to the rule */ + struct hostmap *nat_hm; struct nat *nat_next; struct nat *nat_hnext[2]; - struct nat **nat_hstart[2]; + struct nat **nat_phnext[2]; void *nat_ifp; int nat_dir; + char nat_ifname[IFNAMSIZ]; +#if SOLARIS || defined(__sgi) + kmutex_t nat_lock; +#endif } nat_t; typedef struct ipnat { struct ipnat *in_next; struct ipnat *in_rnext; + struct ipnat **in_prnext; struct ipnat *in_mnext; + struct ipnat **in_pmnext; void *in_ifp; void *in_apr; u_long in_space; @@ -103,18 +105,19 @@ typedef struct ipnat { u_int in_hits; struct in_addr in_nextip; u_short in_pnext; - u_short in_ppip; /* ports per IP */ u_short in_ippip; /* IP #'s per IP# */ - u_short in_flags; /* From here to in_dport must be reflected */ + u_32_t in_flags; /* From here to in_dport must be reflected */ + u_short in_spare; + u_short in_ppip; /* ports per IP */ u_short in_port[2]; /* correctly in IPN_CMPSIZ */ struct in_addr in_in[2]; struct in_addr in_out[2]; struct in_addr in_src[2]; + struct frtuc in_tuc; int in_redir; /* 0 if it's a mapping, 1 if it's a hard redir */ char in_ifname[IFNAMSIZ]; char in_plabel[APR_LABELLEN]; /* proxy label */ char in_p; /* protocol */ - u_short in_dport; } ipnat_t; #define in_pmin in_port[0] /* Also holds static redir port */ @@ -126,6 +129,12 @@ typedef struct ipnat { #define in_outmsk in_out[1].s_addr #define in_srcip in_src[0].s_addr #define in_srcmsk in_src[1].s_addr +#define in_scmp in_tuc.ftu_scmp +#define in_dcmp in_tuc.ftu_dcmp +#define in_stop in_tuc.ftu_stop +#define in_dtop in_tuc.ftu_dtop +#define in_sport in_tuc.ftu_sport +#define in_dport in_tuc.ftu_dport #define NAT_OUTBOUND 0 #define NAT_INBOUND 1 @@ -134,6 +143,11 @@ typedef struct ipnat { #define NAT_REDIRECT 0x02 #define NAT_BIMAP (NAT_MAP|NAT_REDIRECT) #define NAT_MAPBLK 0x04 +/* 0x100 reserved for FI_W_SPORT */ +/* 0x200 reserved for FI_W_DPORT */ +/* 0x400 reserved for FI_W_SADDR */ +/* 0x800 reserved for FI_W_DADDR */ +/* 0x1000 reserved for FI_W_NEWFR */ #define MAPBLK_MINPORT 1024 /* don't use reserved ports for src port */ #define USABLE_PORTS (65536 - MAPBLK_MINPORT) @@ -150,6 +164,34 @@ typedef struct natlookup { u_short nl_realport; } natlookup_t; + +typedef struct nat_save { + void *ipn_next; + struct nat ipn_nat; + struct ipnat ipn_ipnat; + struct frentry ipn_fr; + int ipn_dsize; + char ipn_data[4]; +} nat_save_t; + +#define ipn_rule ipn_nat.nat_fr + +typedef struct natget { + void *ng_ptr; + int ng_sz; +} natget_t; + + +typedef struct hostmap { + struct hostmap *hm_next; + struct hostmap **hm_pnext; + struct ipnat *hm_ipnat; + struct in_addr hm_realip; + struct in_addr hm_mapip; + int hm_ref; +} hostmap_t; + + typedef struct natstat { u_long ns_mapped[2]; u_long ns_rules; @@ -158,6 +200,8 @@ typedef struct natstat { u_long ns_inuse; u_long ns_logged; u_long ns_logfail; + u_long ns_memfail; + u_long ns_badnat; nat_t **ns_table[2]; ipnat_t *ns_list; void *ns_apslist; @@ -165,18 +209,25 @@ typedef struct natstat { u_int ns_rultab_sz; u_int ns_rdrtab_sz; nat_t *ns_instances; + u_int ns_wilds; } natstat_t; -#define IPN_ANY 0x00 -#define IPN_TCP 0x01 -#define IPN_UDP 0x02 +#define IPN_ANY 0x000 +#define IPN_TCP 0x001 +#define IPN_UDP 0x002 #define IPN_TCPUDP (IPN_TCP|IPN_UDP) -#define IPN_DELETE 0x04 -#define IPN_ICMPERR 0x08 +#define IPN_DELETE 0x004 +#define IPN_ICMPERR 0x008 #define IPN_RF (IPN_TCPUDP|IPN_DELETE|IPN_ICMPERR) -#define IPN_AUTOPORTMAP 0x10 -#define IPN_RANGE 0x20 -#define IPN_USERFLAGS (IPN_TCPUDP|IPN_AUTOPORTMAP|IPN_RANGE) +#define IPN_AUTOPORTMAP 0x010 +#define IPN_IPRANGE 0x020 +#define IPN_USERFLAGS (IPN_TCPUDP|IPN_AUTOPORTMAP|IPN_IPRANGE|IPN_SPLIT|\ + IPN_ROUNDR|IPN_FILTER|IPN_NOTSRC|IPN_NOTDST) +#define IPN_FILTER 0x040 +#define IPN_SPLIT 0x080 +#define IPN_ROUNDR 0x100 +#define IPN_NOTSRC 0x080000 +#define IPN_NOTDST 0x100000 typedef struct natlog { @@ -196,9 +247,12 @@ typedef struct natlog { #define NL_NEWMAP NAT_MAP #define NL_NEWRDR NAT_REDIRECT +#define NL_NEWBIMAP NAT_BIMAP +#define NL_NEWBLOCK NAT_MAPBLK +#define NL_FLUSH 0xfffe #define NL_EXPIRE 0xffff -#define NAT_HASH_FN(k,m) (((k) + ((k) >> 12)) % (m)) +#define NAT_HASH_FN(k,l,m) (((k) + ((k) >> 12) + l) % (m)) #define LONG_SUM(in) (((in) & 0xffff) + ((in) >> 16)) @@ -217,6 +271,7 @@ typedef struct natlog { extern u_int ipf_nattable_sz; extern u_int ipf_natrules_sz; extern u_int ipf_rdrrules_sz; +extern int fr_nat_lock; extern void ip_natsync __P((void *)); extern u_long fr_defnatage; extern u_long fr_defnaticmpage; @@ -225,29 +280,32 @@ extern nat_t *nat_instances; extern ipnat_t **nat_rules; extern ipnat_t **rdr_rules; extern natstat_t nat_stats; -#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) extern int nat_ioctl __P((caddr_t, u_long, int)); #else extern int nat_ioctl __P((caddr_t, int, int)); #endif -extern void nat_ifdetach __P((struct ifnet *)); extern int nat_init __P((void)); extern nat_t *nat_new __P((ipnat_t *, ip_t *, fr_info_t *, u_int, int)); extern nat_t *nat_outlookup __P((void *, u_int, u_int, struct in_addr, - struct in_addr, u_32_t)); + struct in_addr, u_32_t, int)); extern nat_t *nat_inlookup __P((void *, u_int, u_int, struct in_addr, - struct in_addr, u_32_t)); + struct in_addr, u_32_t, int)); extern nat_t *nat_maplookup __P((void *, u_int, struct in_addr, struct in_addr)); extern nat_t *nat_lookupredir __P((natlookup_t *)); -extern nat_t *nat_icmpinlookup __P((ip_t *, fr_info_t *)); -extern nat_t *nat_icmpin __P((ip_t *, fr_info_t *, u_int *)); +extern nat_t *nat_icmplookup __P((ip_t *, fr_info_t *, int)); +extern nat_t *nat_icmp __P((ip_t *, fr_info_t *, u_int *, int)); +extern void nat_insert __P((nat_t *)); + +extern int nat_clearlist __P((void)); extern int ip_natout __P((ip_t *, fr_info_t *)); extern int ip_natin __P((ip_t *, fr_info_t *)); extern void ip_natunload __P((void)), ip_natexpire __P((void)); extern void nat_log __P((struct nat *, u_int)); -extern void fix_incksum __P((u_short *, u_32_t, int)); -extern void fix_outcksum __P((u_short *, u_32_t, int)); +extern void fix_incksum __P((u_short *, u_32_t)); +extern void fix_outcksum __P((u_short *, u_32_t)); +extern void fix_datacksum __P((u_short *, u_32_t)); #endif /* __IP_NAT_H__ */ diff --git a/sys/netinet/ip_proxy.c b/sys/netinet/ip_proxy.c index d6803d78744..24de449b3d6 100644 --- a/sys/netinet/ip_proxy.c +++ b/sys/netinet/ip_proxy.c @@ -1,14 +1,14 @@ -/* $OpenBSD: ip_proxy.c,v 1.11 2000/08/10 05:50:26 kjell Exp $ */ +/* $OpenBSD: ip_proxy.c,v 1.12 2001/01/17 04:47:15 fgsch Exp $ */ /* - * Copyright (C) 1997-1998 by Darren Reed. + * Copyright (C) 1997-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 rcsid[] = "@(#)$IPFilter: ip_proxy.c,v 2.2.2.4 2000/03/15 13:57:53 darrenr Exp $"; +static const char rcsid[] = "@(#)$IPFilter: ip_proxy.c,v 2.9.2.1 2000/05/06 12:30:50 darrenr Exp $"; #endif #if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL) @@ -70,12 +70,12 @@ static const char rcsid[] = "@(#)$IPFilter: ip_proxy.c,v 2.2.2.4 2000/03/15 13:5 #include <netinet/tcp.h> #include <netinet/udp.h> #include <netinet/ip_icmp.h> -#include <netinet/ip_fil_compat.h> +#include "netinet/ip_compat.h" #include <netinet/tcpip.h> -#include <netinet/ip_fil.h> -#include <netinet/ip_proxy.h> -#include <netinet/ip_nat.h> -#include <netinet/ip_state.h> +#include "netinet/ip_fil.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_state.h" #if (__FreeBSD_version >= 300000) # include <sys/malloc.h> #endif @@ -93,30 +93,69 @@ static int appr_fixseqack __P((fr_info_t *, ip_t *, ap_session_t *, int )); #define AP_SESS_SIZE 53 #if defined(_KERNEL) && !defined(linux) -#include <netinet/ip_ftp_pxy.c> -#include <netinet/ip_rcmd_pxy.c> -#include <netinet/ip_raudio_pxy.c> +#include "netinet/ip_ftp_pxy.c" +#include "netinet/ip_rcmd_pxy.c" +#include "netinet/ip_raudio_pxy.c" #endif ap_session_t *ap_sess_tab[AP_SESS_SIZE]; ap_session_t *ap_sess_list = NULL; +aproxy_t *ap_proxylist = NULL; aproxy_t ap_proxies[] = { #ifdef IPF_FTP_PROXY - { "ftp", (char)IPPROTO_TCP, 0, 0, ippr_ftp_init, NULL, + { NULL, "ftp", (char)IPPROTO_TCP, 0, 0, ippr_ftp_init, NULL, ippr_ftp_new, ippr_ftp_in, ippr_ftp_out }, #endif #ifdef IPF_RCMD_PROXY - { "rcmd", (char)IPPROTO_TCP, 0, 0, ippr_rcmd_init, NULL, + { NULL, "rcmd", (char)IPPROTO_TCP, 0, 0, ippr_rcmd_init, NULL, ippr_rcmd_new, NULL, ippr_rcmd_out }, #endif #ifdef IPF_RAUDIO_PROXY - { "raudio", (char)IPPROTO_TCP, 0, 0, ippr_raudio_init, NULL, + { NULL, "raudio", (char)IPPROTO_TCP, 0, 0, ippr_raudio_init, NULL, ippr_raudio_new, ippr_raudio_in, ippr_raudio_out }, #endif - { "", '\0', 0, 0, NULL, NULL } + { NULL, "", '\0', 0, 0, NULL, NULL } }; +int appr_add(ap) +aproxy_t *ap; +{ + aproxy_t *a; + + for (a = ap_proxies; a->apr_p; a++) + if ((a->apr_p == ap->apr_p) && + !strncmp(a->apr_label, ap->apr_label, + sizeof(ap->apr_label))) + return -1; + + for (a = ap_proxylist; a->apr_p; a = a->apr_next) + if ((a->apr_p == ap->apr_p) && + !strncmp(a->apr_label, ap->apr_label, + sizeof(ap->apr_label))) + return -1; + ap->apr_next = ap_proxylist; + ap_proxylist = ap; + return (*ap->apr_init)(); +} + + +int appr_del(ap) +aproxy_t *ap; +{ + aproxy_t *a, **app; + + for (app = &ap_proxylist; (a = *app); app = &a->apr_next) + if (a == ap) { + if (ap->apr_ref != 0) + return 1; + *app = a->apr_next; + return 0; + } + return -1; +} + + int appr_ok(ip, tcp, nat) ip_t *ip; tcphdr_t *tcp; @@ -183,6 +222,7 @@ nat_t *nat; aproxy_t *apr; tcphdr_t *tcp = NULL; u_32_t sum; + short rv; int err; if (nat->nat_aps == NULL) @@ -217,8 +257,12 @@ nat_t *nat; err = (*apr->apr_inpkt)(fin, ip, aps, nat); } + rv = APR_EXIT(err); + if (rv == -1) + return rv; + if (tcp != NULL) { - err = appr_fixseqack(fin, ip, aps, err); + err = appr_fixseqack(fin, ip, aps, APR_INC(err)); #if SOLARIS && defined(_KERNEL) tcp->th_sum = fr_tcpsum(fin->fin_qfm, ip, tcp); #else @@ -227,9 +271,9 @@ nat_t *nat; } aps->aps_bytes += ip->ip_len; aps->aps_pkts++; - return 2; + return 1; } - return -1; + return 0; } @@ -245,6 +289,13 @@ char *name; ap->apr_ref++; return ap; } + + for (ap = ap_proxylist; ap; ap = ap->apr_next) + if ((ap->apr_p == pr) && + !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) { + ap->apr_ref++; + return ap; + } return NULL; } @@ -270,11 +321,9 @@ ap_session_t *aps; break; } - if (a) { - if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) - KFREES(aps->aps_data, aps->aps_psiz); - KFREE(aps); - } + if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) + KFREES(aps->aps_data, aps->aps_psiz); + KFREE(aps); } @@ -398,4 +447,7 @@ void appr_unload() for (ap = ap_proxies; ap->apr_p; ap++) if (ap->apr_fini) (*ap->apr_fini)(); + for (ap = ap_proxylist; ap; ap = ap->apr_next) + if (ap->apr_fini) + (*ap->apr_fini)(); } diff --git a/sys/netinet/ip_proxy.h b/sys/netinet/ip_proxy.h index f20e8b9d566..59a94412063 100644 --- a/sys/netinet/ip_proxy.h +++ b/sys/netinet/ip_proxy.h @@ -1,13 +1,13 @@ -/* $OpenBSD: ip_proxy.h,v 1.8 2000/04/05 05:35:27 kjell Exp $ */ +/* $OpenBSD: ip_proxy.h,v 1.9 2001/01/17 04:47:15 fgsch Exp $ */ /* - * Copyright (C) 1997-1998 by Darren Reed. + * Copyright (C) 1997-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. * - * $IPFilter: ip_proxy.h,v 2.1.2.3 2000/03/15 13:58:15 darrenr Exp $ + * $IPFilter: ip_proxy.h,v 2.8.2.4 2000/12/02 00:15:03 darrenr Exp $ */ #ifndef __IP_PROXY_H__ @@ -56,7 +56,7 @@ typedef struct ap_session { int aps_psiz; /* size of private data */ struct ap_session *aps_hnext; struct ap_session *aps_next; -} ap_session_t ; +} ap_session_t; #define aps_sport aps_un.apu_tcp.apt_sport #define aps_dport aps_un.apu_tcp.apt_dport @@ -69,6 +69,7 @@ typedef struct ap_session { typedef struct aproxy { + struct aproxy *apr_next; char apr_label[APR_LABELLEN]; /* Proxy label # */ u_char apr_p; /* protocol */ int apr_ref; /* +1 per rule referencing it */ @@ -85,12 +86,26 @@ typedef struct aproxy { #define APR_DELETE 1 +#define APR_ERR(x) (((x) & 0xffff) << 16) +#define APR_EXIT(x) (((x) >> 16) & 0xffff) +#define APR_INC(x) ((x) & 0xffff) +#define FTP_BUFSZ 160 /* * For the ftp proxy. */ +typedef struct ftpside { + char *ftps_rptr; + char *ftps_wptr; + u_32_t ftps_seq; + u_32_t ftps_len; + int ftps_junk; + char ftps_buf[FTP_BUFSZ]; +} ftpside_t; + typedef struct ftpinfo { - u_int ftp_passok; + u_int ftp_passok; + ftpside_t ftp_side[2]; } ftpinfo_t; /* @@ -128,7 +143,10 @@ typedef struct { extern ap_session_t *ap_sess_tab[AP_SESS_SIZE]; extern ap_session_t *ap_sess_list; extern aproxy_t ap_proxies[]; +extern int ippr_ftp_pasvonly; +extern int appr_add __P((aproxy_t *)); +extern int appr_del __P((aproxy_t *)); extern int appr_init __P((void)); extern void appr_unload __P((void)); extern int appr_ok __P((ip_t *, tcphdr_t *, struct ipnat *)); diff --git a/sys/netinet/ip_raudio_pxy.c b/sys/netinet/ip_raudio_pxy.c index 4f1eb26ff43..171ae21ad7e 100644 --- a/sys/netinet/ip_raudio_pxy.c +++ b/sys/netinet/ip_raudio_pxy.c @@ -1,5 +1,8 @@ -/* $OpenBSD: ip_raudio_pxy.c,v 1.7 2000/04/05 05:35:27 kjell Exp $ */ +/* $OpenBSD: ip_raudio_pxy.c,v 1.8 2001/01/17 04:47:16 fgsch Exp $ */ +/* + * $IPFilter: ip_raudio_pxy.c,v 1.7.2.3 2000/10/27 22:54:04 darrenr Exp $ + */ #if SOLARIS && defined(_KERNEL) extern kmutex_t ipf_rw; #endif @@ -170,9 +173,9 @@ nat_t *nat; tcphdr_t *tcp, tcph, *tcp2 = &tcph; raudio_t *rap = aps->aps_data; struct in_addr swa, swb; - u_int a1, a2, a3, a4; + int off, dlen, slen; + int a1, a2, a3, a4; u_short sp, dp; - int off, dlen; fr_info_t fi; tcp_seq seq; nat_t *ipn; @@ -261,9 +264,13 @@ nat_t *nat; bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); + tcp2->th_off = 5; fi.fin_dp = (char *)tcp2; fi.fin_fr = &raudiofr; + fi.fin_dlen = sizeof(*tcp2); tcp2->th_win = htons(8192); + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp); if (((rap->rap_mode & RAP_M_UDP_ROBUST) == RAP_M_UDP_ROBUST) && (rap->rap_srport != 0)) { @@ -274,8 +281,7 @@ nat_t *nat; fi.fin_data[0] = dp; fi.fin_data[1] = sp; ipn = nat_new(nat->nat_ptr, ip, &fi, - IPN_UDP | (sp ? 0 : FI_W_SPORT), - NAT_OUTBOUND); + IPN_UDP | (sp ? 0 : FI_W_SPORT), NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, sp ? 0 : FI_W_SPORT); @@ -295,8 +301,9 @@ nat_t *nat; (void) fr_addstate(ip, &fi, FI_W_DPORT); } } - + ip->ip_p = swp; + ip->ip_len = slen; ip->ip_src = swa; ip->ip_dst = swb; return 0; diff --git a/sys/netinet/ip_rcmd_pxy.c b/sys/netinet/ip_rcmd_pxy.c index 0edfba96e3d..b3113f563a1 100644 --- a/sys/netinet/ip_rcmd_pxy.c +++ b/sys/netinet/ip_rcmd_pxy.c @@ -1,6 +1,9 @@ -/* $OpenBSD: ip_rcmd_pxy.c,v 1.4 2000/03/13 23:40:18 kjell Exp $ */ +/* $OpenBSD: ip_rcmd_pxy.c,v 1.5 2001/01/17 04:47:16 fgsch Exp $ */ /* + * $IPFilter: ip_rcmd_pxy.c,v 1.4.2.4 2000/11/01 14:34:20 darrenr Exp $ + */ +/* * Simple RCMD transparent proxy for in-kernel use. For use with the NAT * code. */ @@ -92,8 +95,17 @@ nat_t *nat; #endif tcp = (tcphdr_t *)fin->fin_dp; + + if (tcp->th_flags & TH_SYN) { + *(u_32_t *)aps->aps_data = htonl(ntohl(tcp->th_seq) + 1); + return 0; + } + + if ((*(u_32_t *)aps->aps_data != 0) && + (tcp->th_seq != *(u_32_t *)aps->aps_data)) + return 0; + off = (ip->ip_hl << 2) + (tcp->th_off << 2); - m = *(mb_t **)fin->fin_mp; #if SOLARIS m = fin->fin_qfm; @@ -102,13 +114,11 @@ nat_t *nat; bzero(portbuf, sizeof(portbuf)); copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); #else + m = *(mb_t **)fin->fin_mp; dlen = mbufchainlen(m) - off; bzero(portbuf, sizeof(portbuf)); m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); #endif - if ((*(u_32_t *)aps->aps_data != 0) && - (tcp->th_seq != *(u_32_t *)aps->aps_data)) - return 0; portbuf[sizeof(portbuf) - 1] = '\0'; s = portbuf; @@ -123,16 +133,22 @@ nat_t *nat; sp = htons(sp); dp = htons(fin->fin_data[1]); ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip, - ip->ip_dst, (dp << 16) | sp); + ip->ip_dst, (dp << 16) | sp, 0); if (ipn == NULL) { + int slen; + + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp); bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = sp; tcp2->th_dport = 0; /* XXX - don't specify remote port */ + tcp2->th_off = 5; fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; + fi.fin_dlen = sizeof(*tcp2); swip = ip->ip_src; ip->ip_src = nat->nat_inip; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_DPORT, @@ -142,6 +158,7 @@ nat_t *nat; fi.fin_fr = &rcmdfr; (void) fr_addstate(ip, &fi, FI_W_DPORT); } + ip->ip_len = slen; ip->ip_src = swip; } return 0; diff --git a/sys/netinet/ip_state.c b/sys/netinet/ip_state.c index 51cda10dee3..28f99920c36 100644 --- a/sys/netinet/ip_state.c +++ b/sys/netinet/ip_state.c @@ -1,15 +1,15 @@ -/* $OpenBSD: ip_state.c,v 1.23 2000/08/10 05:50:26 kjell Exp $ */ +/* $OpenBSD: ip_state.c,v 1.24 2001/01/17 04:47:16 fgsch Exp $ */ /* - * Copyright (C) 1995-1998 by Darren Reed. + * Copyright (C) 1995-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_state.c 1.8 6/5/96 (C) 1993-1995 Darren Reed"; -static const char rcsid[] = "@(#)$IPFilter: ip_state.c,v 2.3.2.28 2000/08/08 16:00:35 darrenr Exp $"; +static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$IPFilter: ip_state.c,v 2.30.2.27 2000/12/02 00:15:25 darrenr Exp $"; #endif #include <sys/errno.h> @@ -20,6 +20,10 @@ static const char rcsid[] = "@(#)$IPFilter: ip_state.c,v 2.3.2.28 2000/08/08 16: defined(_KERNEL) # include "opt_ipfilter_log.h" #endif +#if defined(_KERNEL) && defined(__FreeBSD_version) && \ + (__FreeBSD_version >= 400000) && !defined(KLD_MODULE) +#include "opt_inet6.h" +#endif #if !defined(_KERNEL) && !defined(KERNEL) && !defined(__KERNEL__) # include <stdio.h> # include <stdlib.h> @@ -45,7 +49,7 @@ static const char rcsid[] = "@(#)$IPFilter: ip_state.c,v 2.3.2.28 2000/08/08 16: # include <sys/protosw.h> #endif #include <sys/socket.h> -#if defined(_KERNEL) && !defined(linux) +#if (defined(_KERNEL) || defined(KERNEL)) && !defined(linux) # include <sys/systm.h> #endif #if !defined(__SVR4) && !defined(__svr4__) @@ -77,13 +81,16 @@ static const char rcsid[] = "@(#)$IPFilter: ip_state.c,v 2.3.2.28 2000/08/08 16: #endif #include <netinet/udp.h> #include <netinet/ip_icmp.h> -#include <netinet/ip_fil_compat.h> +#include "netinet/ip_compat.h" #include <netinet/tcpip.h> -#include <netinet/ip_fil.h> -#include <netinet/ip_nat.h> -#include <netinet/ip_frag.h> -#include <netinet/ip_proxy.h> -#include <netinet/ip_state.h> +#include "netinet/ip_fil.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_state.h" +#ifdef USE_INET6 +#include <netinet/icmp6.h> +#endif #if (__FreeBSD_version >= 300000) # include <sys/malloc.h> # if (defined(_KERNEL) || defined(KERNEL)) && !defined(IPFILTER_LKM) @@ -98,20 +105,31 @@ static const char rcsid[] = "@(#)$IPFilter: ip_state.c,v 2.3.2.28 2000/08/08 16: #define TCP_CLOSE (TH_FIN|TH_RST) -ipstate_t **ips_table = NULL; -int ips_num = 0; -ips_stat_t ips_stats; +static ipstate_t **ips_table = NULL; +static ipstate_t *ips_list = NULL; +static int ips_num = 0; +static int ips_wild = 0; +static ips_stat_t ips_stats; #if (SOLARIS || defined(__sgi)) && defined(_KERNEL) extern KRWLOCK_T ipf_state, ipf_mutex; extern kmutex_t ipf_rw; #endif -static int fr_matchsrcdst __P((ipstate_t *, struct in_addr, struct in_addr, +#ifdef USE_INET6 +static frentry_t *fr_checkicmp6matchingstate __P((ip6_t *, fr_info_t *)); +#endif +static int fr_matchsrcdst __P((ipstate_t *, union i6addr, union i6addr, fr_info_t *, tcphdr_t *)); static frentry_t *fr_checkicmpmatchingstate __P((ip_t *, fr_info_t *)); +static int fr_matchicmpqueryreply __P((int, ipstate_t *, icmphdr_t *)); static int fr_state_flush __P((int)); 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)); +int fr_stputent __P((caddr_t)); +int fr_stgetent __P((caddr_t)); +void fr_stinsert __P((ipstate_t *)); #define FIVE_DAYS (2 * 5 * 86400) /* 5 days: half closed session */ @@ -121,21 +139,35 @@ u_long fr_tcpidletimeout = FIVE_DAYS, fr_tcpclosewait = 2 * TCP_MSL, fr_tcplastack = 2 * TCP_MSL, fr_tcptimeout = 2 * TCP_MSL, - fr_tcpclosed = 1, + fr_tcpclosed = 120, + fr_tcphalfclosed = 2 * 2 * 3600, /* 2 hours */ fr_udptimeout = 240, fr_icmptimeout = 120; int fr_statemax = IPSTATE_MAX, fr_statesize = IPSTATE_SIZE; -int fr_state_doflush = 0; +int fr_state_doflush = 0, + fr_state_lock = 0; +static int icmpreplytype4[ICMP_MAXTYPE + 1]; int fr_stateinit() { + int i; + KMALLOCS(ips_table, ipstate_t **, fr_statesize * sizeof(ipstate_t *)); if (ips_table != NULL) bzero((char *)ips_table, fr_statesize * sizeof(ipstate_t *)); else return -1; + + /* fill icmp reply type table */ + for (i = 0; i <= ICMP_MAXTYPE; i++) + icmpreplytype4[i] = -1; + icmpreplytype4[ICMP_ECHO] = ICMP_ECHOREPLY; + icmpreplytype4[ICMP_TSTAMP] = ICMP_TSTAMPREPLY; + icmpreplytype4[ICMP_IREQ] = ICMP_IREQREPLY; + icmpreplytype4[ICMP_MASKREQ] = ICMP_MASKREPLY; + return 0; } @@ -144,6 +176,7 @@ static ips_stat_t *fr_statetstats() { ips_stats.iss_active = ips_num; ips_stats.iss_table = ips_table; + ips_stats.iss_list = ips_list; return &ips_stats; } @@ -157,7 +190,6 @@ static ips_stat_t *fr_statetstats() static int fr_state_flush(which) int which; { - register int i; register ipstate_t *is, **isp; #if defined(_KERNEL) && !SOLARIS int s; @@ -165,48 +197,72 @@ int which; int delete, removed = 0; SPL_NET(s); - WRITE_ENTER(&ipf_state); - for (i = fr_statesize - 1; i >= 0; i--) - for (isp = &ips_table[i]; (is = *isp); ) { - delete = 0; + for (isp = &ips_list; (is = *isp); ) { + delete = 0; - switch (which) - { - case 0 : - delete = 1; - break; - case 1 : - if (is->is_p != IPPROTO_TCP) - break; - if ((is->is_state[0] != TCPS_ESTABLISHED) || - (is->is_state[1] != TCPS_ESTABLISHED)) - delete = 1; + switch (which) + { + case 0 : + delete = 1; + break; + case 1 : + if (is->is_p != IPPROTO_TCP) break; - } + if ((is->is_state[0] != TCPS_ESTABLISHED) || + (is->is_state[1] != TCPS_ESTABLISHED)) + delete = 1; + break; + } - if (delete) { - *isp = is->is_next; - if (is->is_p == IPPROTO_TCP) - ips_stats.iss_fin++; - else - ips_stats.iss_expire++; - if (ips_table[i] == NULL) - ips_stats.iss_inuse--; + if (delete) { + if (is->is_p == IPPROTO_TCP) + ips_stats.iss_fin++; + else + ips_stats.iss_expire++; #ifdef IPFILTER_LOG - ipstate_log(is, ISL_FLUSH); + ipstate_log(is, ISL_FLUSH); #endif - fr_delstate(is); - ips_num--; - removed++; - } else - isp = &is->is_next; - } - RWLOCK_EXIT(&ipf_state); + fr_delstate(is); + removed++; + } else + isp = &is->is_next; + } SPL_X(s); return removed; } +static int fr_state_remove(data) +caddr_t data; +{ + ipstate_t *sp, st; + int error; + + sp = &st; + error = IRCOPYPTR(data, (caddr_t)&st, sizeof(st)); + if (error) + return EFAULT; + + for (sp = ips_list; sp; sp = sp->is_next) + 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, + sizeof(st.is_dst)) && + !bcmp((char *)&sp->is_ps, (char *)&st.is_ps, + sizeof(st.is_ps))) { + WRITE_ENTER(&ipf_state); +#ifdef IPFILTER_LOG + ipstate_log(sp, ISL_REMOVE); +#endif + fr_delstate(sp); + RWLOCK_EXIT(&ipf_state); + return 0; + } + return ESRCH; +} + + int fr_state_ioctl(data, cmd, mode) caddr_t data; #if defined(__NetBSD__) || defined(__OpenBSD__) @@ -216,15 +272,22 @@ int cmd; #endif int mode; { - int arg, ret, error = 0; + int arg, ret, error = 0; switch (cmd) { + case SIOCDELST : + error = fr_state_remove(data); + break; case SIOCIPFFL : - IRCOPY(data, (caddr_t)&arg, sizeof(arg)); + error = IRCOPY(data, (caddr_t)&arg, sizeof(arg)); + if (error) + break; if (arg == 0 || arg == 1) { + WRITE_ENTER(&ipf_state); ret = fr_state_flush(arg); - IWCOPY((caddr_t)&ret, data, sizeof(ret)); + RWLOCK_EXIT(&ipf_state); + error = IWCOPY((caddr_t)&ret, data, sizeof(ret)); } else error = EINVAL; break; @@ -240,15 +303,33 @@ int mode; } break; #endif - case SIOCGIPST : - IWCOPY((caddr_t)fr_statetstats(), data, sizeof(ips_stat_t)); + case SIOCGETFS : + error = IWCOPYPTR((caddr_t)fr_statetstats(), data, + sizeof(ips_stat_t)); break; case FIONREAD : #ifdef IPFILTER_LOG - IWCOPY((caddr_t)&iplused[IPL_LOGSTATE], (caddr_t)data, - sizeof(iplused[IPL_LOGSTATE])); + arg = (int)iplused[IPL_LOGSTATE]; + error = IWCOPY((caddr_t)&arg, (caddr_t)data, sizeof(arg)); #endif break; + case SIOCSTLCK : + error = fr_lock(data, &fr_state_lock); + break; + case SIOCSTPUT : + if (!fr_state_lock) { + error = EACCES; + break; + } + error = fr_stputent(data); + break; + case SIOCSTGET : + if (!fr_state_lock) { + error = EACCES; + break; + } + error = fr_stgetent(data); + break; default : error = EINVAL; break; @@ -257,6 +338,157 @@ int mode; } +int fr_stgetent(data) +caddr_t data; +{ + register ipstate_t *is, *isn; + ipstate_save_t ips, *ipsp; + int error; + + error = IRCOPY(data, (caddr_t)&ipsp, sizeof(ipsp)); + if (error) + return EFAULT; + error = IRCOPY((caddr_t)ipsp, (caddr_t)&ips, sizeof(ips)); + if (error) + return EFAULT; + + isn = ips.ips_next; + if (!isn) { + isn = ips_list; + if (isn == NULL) { + if (ips.ips_next == NULL) + return ENOENT; + return 0; + } + } else { + /* + * Make sure the pointer we're copying from exists in the + * current list of entries. Security precaution to prevent + * copying of random kernel data. + */ + for (is = ips_list; is; is = is->is_next) + if (is == isn) + break; + if (!is) + return ESRCH; + } + ips.ips_next = isn->is_next; + bcopy((char *)isn, (char *)&ips.ips_is, sizeof(ips.ips_is)); + if (isn->is_rule) + bcopy((char *)isn->is_rule, (char *)&ips.ips_fr, + sizeof(ips.ips_fr)); + error = IWCOPY((caddr_t)&ips, ipsp, sizeof(ips)); + if (error) + error = EFAULT; + return error; +} + + +int fr_stputent(data) +caddr_t data; +{ + register ipstate_t *is, *isn; + ipstate_save_t ips, *ipsp; + int error, out; + frentry_t *fr; + + error = IRCOPY(data, (caddr_t)&ipsp, sizeof(ipsp)); + if (error) + return EFAULT; + error = IRCOPY((caddr_t)ipsp, (caddr_t)&ips, sizeof(ips)); + if (error) + return EFAULT; + + KMALLOC(isn, ipstate_t *); + if (isn == NULL) + return ENOMEM; + + bcopy((char *)&ips.ips_is, (char *)isn, sizeof(*isn)); + fr = isn->is_rule; + if (fr != NULL) { + if (isn->is_flags & FI_NEWFR) { + KMALLOC(fr, frentry_t *); + if (fr == NULL) { + KFREE(isn); + return ENOMEM; + } + bcopy((char *)&ips.ips_fr, (char *)fr, sizeof(*fr)); + out = fr->fr_flags & FR_OUTQUE ? 1 : 0; + isn->is_rule = fr; + ips.ips_is.is_rule = fr; + if (*fr->fr_ifname) { + fr->fr_ifa = GETUNIT(fr->fr_ifname, fr->fr_v); + if (fr->fr_ifa == NULL) + fr->fr_ifa = (void *)-1; +#ifdef _KERNEL + else { + strncpy(isn->is_ifname[out], + IFNAME(fr->fr_ifa), IFNAMSIZ); + isn->is_ifp[out] = fr->fr_ifa; + } +#endif + } else + fr->fr_ifa = NULL; + /* + * send a copy back to userland of what we ended up + * to allow for verification. + */ + error = IWCOPY((caddr_t)&ips, ipsp, sizeof(ips)); + if (error) { + KFREE(isn); + KFREE(fr); + return EFAULT; + } + } else { + for (is = ips_list; is; is = is->is_next) + if (is->is_rule == fr) + break; + if (!is) { + KFREE(isn); + return ESRCH; + } + } + } + fr_stinsert(isn); + return 0; +} + + +void fr_stinsert(is) +register ipstate_t *is; +{ + register u_int hv = is->is_hv; + + MUTEX_INIT(&is->is_lock, "ipf state entry", NULL); + + is->is_ifname[0][sizeof(is->is_ifname[0]) - 1] = '\0'; + if (is->is_ifname[0][0] != '\0') { + is->is_ifp[0] = GETUNIT(is->is_ifname[0], is->is_v); + } + is->is_ifname[1][sizeof(is->is_ifname[0]) - 1] = '\0'; + if (is->is_ifname[1][0] != '\0') { + is->is_ifp[1] = GETUNIT(is->is_ifname[1], is->is_v); + } + + /* + * add into list table. + */ + if (ips_list) + ips_list->is_pnext = &is->is_next; + is->is_pnext = &ips_list; + is->is_next = ips_list; + ips_list = is; + if (ips_table[hv]) + ips_table[hv]->is_phnext = &is->is_hnext; + else + ips_stats.iss_inuse++; + is->is_phnext = ips_table + hv; + is->is_hnext = ips_table[hv]; + ips_table[hv] = is; + ips_num++; +} + + /* * Create a new ipstate structure and hang it off the hash table. */ @@ -265,18 +497,22 @@ ip_t *ip; fr_info_t *fin; u_int flags; { + register tcphdr_t *tcp = NULL; register ipstate_t *is; register u_int hv; ipstate_t ips; u_int pass; + int out; - if ((ip->ip_off & IP_OFFMASK) || (fin->fin_fi.fi_fl & FI_SHORT)) + if (fr_state_lock || (fin->fin_off & IP_OFFMASK) || + (fin->fin_fi.fi_fl & FI_SHORT)) return NULL; if (ips_num == fr_statemax) { ips_stats.iss_max++; fr_state_doflush = 1; return NULL; } + out = fin->fin_out; is = &ips; bzero((char *)is, sizeof(*is)); ips.is_age = 1; @@ -285,38 +521,70 @@ u_int flags; /* * Copy and calculate... */ - hv = (is->is_p = ip->ip_p); - hv += (is->is_src.s_addr = ip->ip_src.s_addr); - hv += (is->is_dst.s_addr = ip->ip_dst.s_addr); + hv = (is->is_p = fin->fin_fi.fi_p); + is->is_src = fin->fin_fi.fi_src; + hv += is->is_saddr; + is->is_dst = fin->fin_fi.fi_dst; + hv += is->is_daddr; +#ifdef USE_INET6 + if (fin->fin_v == 6) { + if (is->is_p == IPPROTO_ICMPV6) { + if (IN6_IS_ADDR_MULTICAST(&is->is_dst.in6)) + flags |= FI_W_DADDR; + if (out) + hv -= is->is_daddr; + else + hv -= is->is_saddr; + } + } +#endif - switch (ip->ip_p) + switch (is->is_p) { +#ifdef USE_INET6 + case IPPROTO_ICMPV6 : +#endif case IPPROTO_ICMP : { struct icmp *ic = (struct icmp *)fin->fin_dp; +#ifdef USE_INET6 + if ((is->is_p == IPPROTO_ICMPV6) && + ((ic->icmp_type & ICMP6_INFOMSG_MASK) == 0)) + return NULL; +#endif switch (ic->icmp_type) { - case ICMP_ECHO : - is->is_icmp.ics_type = ICMP_ECHOREPLY; /* XXX */ +#ifdef USE_INET6 + case ICMP6_ECHO_REQUEST : + is->is_icmp.ics_type = ICMP6_ECHO_REPLY; hv += (is->is_icmp.ics_id = ic->icmp_id); hv += (is->is_icmp.ics_seq = ic->icmp_seq); break; + case ICMP6_MEMBERSHIP_QUERY : + case ND_ROUTER_SOLICIT : + case ND_NEIGHBOR_SOLICIT : + is->is_icmp.ics_type = ic->icmp_type + 1; + break; +#endif + case ICMP_ECHO : case ICMP_TSTAMP : case ICMP_IREQ : case ICMP_MASKREQ : - is->is_icmp.ics_type = ic->icmp_type + 1; + is->is_icmp.ics_type = ic->icmp_type; + hv += (is->is_icmp.ics_id = ic->icmp_id); + hv += (is->is_icmp.ics_seq = ic->icmp_seq); break; default : return NULL; } - ATOMIC_INC(ips_stats.iss_icmp); + ATOMIC_INCL(ips_stats.iss_icmp); is->is_age = fr_icmptimeout; break; } case IPPROTO_TCP : { - register tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; + tcp = (tcphdr_t *)fin->fin_dp; if (tcp->th_flags & TH_RST) return NULL; @@ -330,14 +598,13 @@ u_int flags; hv += tcp->th_dport; hv += tcp->th_sport; } - if (tcp->th_seq != 0) { - is->is_send = ntohl(tcp->th_seq) + ip->ip_len - - fin->fin_hlen - (tcp->th_off << 2) + - ((tcp->th_flags & TH_SYN) ? 1 : 0) + - ((tcp->th_flags & TH_FIN) ? 1 : 0); - is->is_maxsend = is->is_send + 1; - } + is->is_send = ntohl(tcp->th_seq) + fin->fin_dlen - + (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; @@ -346,16 +613,12 @@ u_int flags; * timer on it as we'll never see an error if it fails to * connect. */ - MUTEX_ENTER(&ipf_rw); - ips_stats.iss_tcp++; - fr_tcp_age(&is->is_age, is->is_state, ip, fin, - tcp->th_sport == is->is_sport); - MUTEX_EXIT(&ipf_rw); + ATOMIC_INCL(ips_stats.iss_tcp); break; } case IPPROTO_UDP : { - register tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; + tcp = (tcphdr_t *)fin->fin_dp; is->is_dport = tcp->th_dport; is->is_sport = tcp->th_sport; @@ -363,7 +626,7 @@ u_int flags; hv += tcp->th_dport; hv += tcp->th_sport; } - ATOMIC_INC(ips_stats.iss_udp); + ATOMIC_INCL(ips_stats.iss_udp); is->is_age = fr_udptimeout; break; } @@ -373,14 +636,15 @@ u_int flags; KMALLOC(is, ipstate_t *); if (is == NULL) { - ATOMIC_INC(ips_stats.iss_nomem); + ATOMIC_INCL(ips_stats.iss_nomem); return NULL; } bcopy((char *)&ips, (char *)is, sizeof(*is)); hv %= fr_statesize; + is->is_hv = hv; is->is_rule = fin->fin_fr; if (is->is_rule != NULL) { - ATOMIC_INC(is->is_rule->fr_ref); + ATOMIC_INC32(is->is_rule->fr_ref); pass = is->is_rule->fr_flags; } else pass = fr_flags; @@ -388,12 +652,13 @@ u_int flags; is->is_pass = pass; is->is_pkts = 1; - is->is_bytes = ip->ip_len; + is->is_bytes = fin->fin_dlen + fin->fin_hlen; /* * We want to check everything that is a property of this packet, * but we don't (automatically) care about it's fragment status as * this may change. */ + is->is_v = fin->fin_fi.fi_v; is->is_opt = fin->fin_fi.fi_optmsk; is->is_optmsk = 0xffffffff; is->is_sec = fin->fin_fi.fi_secmsk; @@ -402,33 +667,29 @@ u_int flags; is->is_authmsk = 0xffff; is->is_flags = fin->fin_fi.fi_fl & FI_CMP; is->is_flags |= FI_CMP << 4; - is->is_flags |= flags & (FI_W_DPORT|FI_W_SPORT); -#ifdef _KERNEL - strncpy(is->is_ifname[fin->fin_out], IFNAME(fin->fin_ifp), IFNAMSIZ); + is->is_flags |= flags & (FI_WILDP|FI_WILDA); + if (flags & (FI_WILDP|FI_WILDA)) + ips_wild++; + is->is_ifp[1 - out] = NULL; + is->is_ifp[out] = fin->fin_ifp; +#ifdef _KERNEL + strncpy(is->is_ifname[out], IFNAME(fin->fin_ifp), IFNAMSIZ); #endif - is->is_ifname[1 - fin->fin_out][0] = '\0'; - /* - * add into table. - */ - is->is_next = ips_table[hv]; - ips_table[hv] = is; - if (is->is_next == NULL) - ips_stats.iss_inuse++; - if (fin->fin_out) { - is->is_ifpin = NULL; - is->is_ifpout = fin->fin_ifp; - } else { - is->is_ifpin = fin->fin_ifp; - is->is_ifpout = NULL; - } + is->is_ifname[1 - out][0] = '\0'; if (pass & FR_LOGFIRST) is->is_pass &= ~(FR_LOGFIRST|FR_LOG); - ATOMIC_INC(ips_num); + fr_stinsert(is); + if (is->is_p == IPPROTO_TCP) { + MUTEX_ENTER(&is->is_lock); + fr_tcp_age(&is->is_age, is->is_state, fin, + 0); /* 0 = packet from the source */ + MUTEX_EXIT(&is->is_lock); + } #ifdef IPFILTER_LOG ipstate_log(is, ISL_NEW); #endif RWLOCK_EXIT(&ipf_state); - fin->fin_rev = (is->is_dst.s_addr != ip->ip_dst.s_addr); + fin->fin_rev = IP6NEQ(is->is_dst, fin->fin_fi.fi_dst); if (fin->fin_fi.fi_fl & FI_FRAG) ipfr_newfrag(ip, fin, pass ^ FR_KEEPSTATE); return is; @@ -457,16 +718,17 @@ tcphdr_t *tcp; /* * Find difference between last checked packet and this packet. */ - source = (ip->ip_src.s_addr == is->is_src.s_addr); + source = IP6EQ(fin->fin_fi.fi_src, is->is_src); fdata = &is->is_tcp.ts_data[!source]; tdata = &is->is_tcp.ts_data[source]; seq = ntohl(tcp->th_seq); ack = ntohl(tcp->th_ack); win = ntohs(tcp->th_win); - end = seq + ip->ip_len - fin->fin_hlen - (tcp->th_off << 2) + + end = seq + fin->fin_dlen - (tcp->th_off << 2) + ((tcp->th_flags & TH_SYN) ? 1 : 0) + ((tcp->th_flags & TH_FIN) ? 1 : 0); + MUTEX_ENTER(&is->is_lock); if (fdata->td_end == 0) { /* * Must be a (outgoing) SYN-ACK in reply to a SYN. @@ -478,9 +740,6 @@ tcphdr_t *tcp; if (!(tcp->th_flags & TH_ACK)) { /* Pretend an ack was sent */ ack = tdata->td_end; - win = 1; - if ((tcp->th_flags == TH_SYN) && (tdata->td_maxwin == 0)) - tdata->td_maxwin = 1; } else if (((tcp->th_flags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) && (ack == 0)) { /* gross hack to get around certain broken tcp stacks */ @@ -529,24 +788,22 @@ tcphdr_t *tcp; tdata->td_maxend++; } - ATOMIC_INC(ips_stats.iss_hits); - is->is_pkts++; - is->is_bytes += ip->ip_len; + ATOMIC_INCL(ips_stats.iss_hits); /* * Nearing end of connection, start timeout. */ - MUTEX_ENTER(&ipf_rw); - fr_tcp_age(&is->is_age, is->is_state, ip, fin, source); - MUTEX_EXIT(&ipf_rw); + /* source ? 0 : 1 -> !source */ + fr_tcp_age(&is->is_age, is->is_state, fin, !source); ret = 1; } + MUTEX_EXIT(&is->is_lock); return ret; } static int fr_matchsrcdst(is, src, dst, fin, tcp) ipstate_t *is; -struct in_addr src, dst; +union i6addr src, dst; fr_info_t *fin; tcphdr_t *tcp; { @@ -554,7 +811,7 @@ tcphdr_t *tcp; u_short sp, dp; void *ifp; - rev = fin->fin_rev = (is->is_dst.s_addr != dst.s_addr); + rev = fin->fin_rev = IP6NEQ(is->is_dst, dst); ifp = fin->fin_ifp; out = fin->fin_out; @@ -563,7 +820,7 @@ tcphdr_t *tcp; sp = tcp->th_sport; dp = tcp->th_dport; } else { - flags = 0; + flags = is->is_flags & FI_WILDA; sp = 0; dp = 0; } @@ -590,15 +847,17 @@ tcphdr_t *tcp; ret = 0; if (rev == 0) { - if ((is->is_dst.s_addr == dst.s_addr) && - (is->is_src.s_addr == src.s_addr) && + if ( + (IP6EQ(is->is_dst, dst) || (flags & FI_W_DADDR)) && + (IP6EQ(is->is_src, src) || (flags & FI_W_SADDR)) && (!tcp || ((sp == is->is_sport || flags & FI_W_SPORT) && (dp == is->is_dport || flags & FI_W_DPORT)))) { ret = 1; } } else { - if ((is->is_dst.s_addr == src.s_addr) && - (is->is_src.s_addr == dst.s_addr) && + if ( + (IP6EQ(is->is_dst, src) || (flags & FI_W_DADDR)) && + (IP6EQ(is->is_src, dst) || (flags & FI_W_SADDR)) && (!tcp || ((sp == is->is_dport || flags & FI_W_DPORT) && (dp == is->is_sport || flags & FI_W_SPORT)))) { ret = 1; @@ -641,25 +900,36 @@ tcphdr_t *tcp; is->is_maxdend = is->is_dend + 1; } is->is_flags &= ~(FI_W_SPORT|FI_W_DPORT); + ips_wild--; } + ret = -1; + if (!rev) { if (out) { if (!is->is_ifpout) - is->is_ifpout = ifp; + ret = 1; } else { if (!is->is_ifpin) - is->is_ifpin = ifp; + ret = 0; } } else { if (out) { if (!is->is_ifpin) - is->is_ifpin = ifp; + ret = 0; } else { if (!is->is_ifpout) - is->is_ifpout = ifp; + ret = 1; } } + + if (ret >= 0) { + is->is_ifp[ret] = ifp; +#ifdef _KERNEL + strncpy(is->is_ifname[out], IFNAME(fin->fin_ifp), + sizeof(is->is_ifname[1])); +#endif + } #ifdef _KERNEL if (ret >= 0) { strncpy(is->is_ifname[out], IFNAME(fin->fin_ifp), @@ -669,20 +939,50 @@ tcphdr_t *tcp; return 1; } -frentry_t *fr_checkicmpmatchingstate(ip, fin) +static int fr_matchicmpqueryreply(v, is, icmp) +int v; +ipstate_t *is; +icmphdr_t *icmp; +{ + if (v == 4) { + /* + * If we matched its type on the way in, then when going out + * it will still be the same type. + */ + if (((icmp->icmp_type == is->is_type) || + (icmpreplytype4[is->is_type] == icmp->icmp_type)) && + (icmp->icmp_id == is->is_icmp.ics_id) && + (icmp->icmp_seq == is->is_icmp.ics_seq)) { + return 1; + }; + } +#ifdef USE_INET6 + else if (is->is_v == 6) { + if ((is->is_type == ICMP6_ECHO_REPLY) && + (icmp->icmp_type == ICMP6_ECHO_REQUEST) && + (icmp->icmp_id == is->is_icmp.ics_id) && + (icmp->icmp_seq == is->is_icmp.ics_seq)) { + return 1; + }; + } +#endif + return 0; +} + +static frentry_t *fr_checkicmpmatchingstate(ip, fin) ip_t *ip; fr_info_t *fin; { - register struct in_addr dst, src; register ipstate_t *is, **isp; register u_short sport, dport; register u_char pr; + union i6addr dst, src; struct icmp *ic; u_short savelen; icmphdr_t *icmp; fr_info_t ofin; - tcphdr_t *tcp; int type, len; + tcphdr_t *tcp; frentry_t *fr; ip_t *oip; u_int hv; @@ -692,9 +992,10 @@ fr_info_t *fin; * Only a basic IP header (no options) should be with * an ICMP error header. */ - if ((ip->ip_hl != 5) || (ip->ip_len < ICMPERR_MINPKTLEN)) + if (((ip->ip_v != 4) || (ip->ip_hl != 5)) || + (fin->fin_plen < ICMPERR_MINPKTLEN)) return NULL; - ic = (struct icmp *)((char *)ip + fin->fin_hlen); + ic = (struct icmp *)fin->fin_dp; type = ic->icmp_type; /* * If it's not an error type, then return @@ -704,12 +1005,12 @@ fr_info_t *fin; (type != ICMP_PARAMPROB)) return NULL; - oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); - if (ip->ip_len < ICMPERR_MAXPKTLEN + ((oip->ip_hl - 5) << 2)) + oip = (ip_t *)((char *)ic + ICMPERR_ICMPHLEN); + if (fin->fin_plen < ICMPERR_MAXPKTLEN + ((oip->ip_hl - 5) << 2)) return NULL; /* - * Sanity Checks. + * Sanity checks. */ len = fin->fin_dlen - ICMPERR_ICMPHLEN; if ((len <= 0) || ((oip->ip_hl << 2) > len)) @@ -740,8 +1041,15 @@ fr_info_t *fin; } #endif - if (oip->ip_p == IPPROTO_ICMP) { + /* + * in the IPv4 case we must zero the i6addr union otherwise + * the IP6EQ and IP6NEQ macros produce the wrong results because + * of the 'junk' in the unused part of the union + */ + bzero((char *)&src, sizeof(src)); + bzero((char *)&dst, sizeof(dst)); + if (oip->ip_p == IPPROTO_ICMP) { icmp = (icmphdr_t *)((char *)oip + (oip->ip_hl << 2)); /* @@ -760,18 +1068,18 @@ fr_info_t *fin; /* * perform a lookup of the ICMP packet in the state table */ - hv = (pr = oip->ip_p); - hv += (src.s_addr = oip->ip_src.s_addr); - hv += (dst.s_addr = oip->ip_dst.s_addr); - if (icmp->icmp_type == ICMP_ECHO) { - hv += icmp->icmp_id; - hv += icmp->icmp_seq; - } + src.in4 = oip->ip_src; + hv += src.in4.s_addr; + dst.in4 = oip->ip_dst; + hv += dst.in4.s_addr; + hv += icmp->icmp_id; + hv += icmp->icmp_seq; hv %= fr_statesize; savelen = oip->ip_len; oip->ip_len = len; + ofin.fin_v = 4; fr_makefrip(oip->ip_hl << 2, oip, &ofin); oip->ip_len = savelen; ofin.fin_ifp = fin->fin_ifp; @@ -779,26 +1087,16 @@ fr_info_t *fin; ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_next) - if ((is->is_p == pr) && - fr_matchsrcdst(is, src, dst, &ofin, NULL)) { - /* - * in the state table ICMP query's are stored - * with the type of the corresponding ICMP - * response. Correct here - */ - if (((is->is_type == ICMP_ECHOREPLY) && - (icmp->icmp_id == is->is_icmp.ics_id) && - (icmp->icmp_seq == is->is_icmp.ics_seq) && - (icmp->icmp_type == ICMP_ECHO)) || - (is->is_type - 1 == ic->icmp_type)) { - ips_stats.iss_hits++; - is->is_pkts++; - is->is_bytes += ip->ip_len; - fr = is->is_rule; - RWLOCK_EXIT(&ipf_state); - return fr; - } + for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_hnext) + if ((is->is_p == pr) && (is->is_v == 4) && + fr_matchsrcdst(is, src, dst, &ofin, NULL) && + fr_matchicmpqueryreply(is->is_v, is, icmp)) { + ips_stats.iss_hits++; + is->is_pkts++; + is->is_bytes += ip->ip_len; + fr = is->is_rule; + RWLOCK_EXIT(&ipf_state); + return fr; } RWLOCK_EXIT(&ipf_state); return NULL; @@ -812,8 +1110,10 @@ fr_info_t *fin; sport = tcp->th_sport; hv = (pr = oip->ip_p); - hv += (src.s_addr = oip->ip_src.s_addr); - hv += (dst.s_addr = oip->ip_dst.s_addr); + src.in4 = oip->ip_src; + hv += src.in4.s_addr; + dst.in4 = oip->ip_dst; + hv += dst.in4.s_addr; hv += dport; hv += sport; hv %= fr_statesize; @@ -829,13 +1129,14 @@ fr_info_t *fin; */ savelen = oip->ip_len; oip->ip_len = len; + ofin.fin_v = 4; fr_makefrip(oip->ip_hl << 2, oip, &ofin); oip->ip_len = savelen; ofin.fin_ifp = fin->fin_ifp; ofin.fin_out = !fin->fin_out; ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_next) { + for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_hnext) { /* * Only allow this icmp though if the * encapsulated packet was allowed through the @@ -843,16 +1144,12 @@ fr_info_t *fin; * of info present does not allow for checking against * tcp internals such as seq and ack numbers. */ - if ((is->is_p == pr) && + if ((is->is_p == pr) && (is->is_v == 4) && fr_matchsrcdst(is, src, dst, &ofin, tcp)) { fr = is->is_rule; ips_stats.iss_hits++; - /* - * we must swap src and dst here because the icmp - * comes the other way around - */ is->is_pkts++; - is->is_bytes += ip->ip_len; + is->is_bytes += fin->fin_plen; /* * we deliberately do not touch the timeouts * for the accompanying state table entry. @@ -866,6 +1163,39 @@ fr_info_t *fin; return NULL; } + +static void fr_ipsmove(isp, is, hv) +ipstate_t **isp, *is; +u_int hv; +{ + u_int hvm; + + hvm = is->is_hv; + /* + * Remove the hash from the old location... + */ + if (is->is_hnext) + is->is_hnext->is_phnext = isp; + *isp = is->is_hnext; + if (ips_table[hvm] == NULL) + ips_stats.iss_inuse--; + + /* + * ...and put the hash in the new one. + */ + hvm = hv % fr_statesize; + is->is_hv = hvm; + isp = &ips_table[hvm]; + if (*isp) + (*isp)->is_phnext = &is->is_hnext; + else + ips_stats.iss_inuse++; + is->is_phnext = isp; + is->is_hnext = *isp; + *isp = is; +} + + /* * Check if a packet has a registered state. */ @@ -873,51 +1203,63 @@ frentry_t *fr_checkstate(ip, fin) ip_t *ip; fr_info_t *fin; { - register struct in_addr dst, src; + union i6addr dst, src; register ipstate_t *is, **isp; register u_char pr; - u_int hv, hvm, hlen, tryagain, pass; + u_int hv, hvm, hlen, tryagain, pass, v; struct icmp *ic; frentry_t *fr; tcphdr_t *tcp; - if ((ip->ip_off & IP_OFFMASK) || (fin->fin_fi.fi_fl & FI_SHORT)) + if (fr_state_lock || (fin->fin_off & IP_OFFMASK) || + (fin->fin_fi.fi_fl & FI_SHORT)) return NULL; is = NULL; hlen = fin->fin_hlen; tcp = (tcphdr_t *)((char *)ip + hlen); ic = (struct icmp *)tcp; - hv = (pr = ip->ip_p); - hv += (src.s_addr = ip->ip_src.s_addr); - hv += (dst.s_addr = ip->ip_dst.s_addr); + hv = (pr = fin->fin_fi.fi_p); + src = fin->fin_fi.fi_src; + dst = fin->fin_fi.fi_dst; + hv += src.in4.s_addr; + hv += dst.in4.s_addr; /* * Search the hash table for matching packet header info. */ - switch (ip->ip_p) + v = fin->fin_fi.fi_v; + switch (fin->fin_fi.fi_p) { +#ifdef USE_INET6 + case IPPROTO_ICMPV6 : + if (v == 6) { + if (fin->fin_out) + hv -= dst.in4.s_addr; + else + hv -= src.in4.s_addr; + if ((ic->icmp_type == ICMP6_ECHO_REQUEST) || + (ic->icmp_type == ICMP6_ECHO_REPLY)) { + hv += ic->icmp_id; + hv += ic->icmp_seq; + } + } +#endif case IPPROTO_ICMP : - if ((ic->icmp_type == ICMP_ECHO) || - (ic->icmp_type == ICMP_ECHOREPLY)) { + if (v == 4) { hv += ic->icmp_id; hv += ic->icmp_seq; } hv %= fr_statesize; READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_next) - if ((is->is_p == pr) && - fr_matchsrcdst(is, src, dst, fin, NULL)) { - if ((is->is_type == ICMP_ECHOREPLY) && - (ic->icmp_type == ICMP_ECHO) && - (ic->icmp_id == is->is_icmp.ics_id) && - (ic->icmp_seq == is->is_icmp.ics_seq)) - ; - else if (is->is_type != ic->icmp_type) - continue; + for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_hnext) { + if ((is->is_p == pr) && (is->is_v == v) && + fr_matchsrcdst(is, src, dst, fin, NULL) && + fr_matchicmpqueryreply(v, is, ic)) { is->is_age = fr_icmptimeout; break; } + } if (is != NULL) break; RWLOCK_EXIT(&ipf_state); @@ -925,13 +1267,18 @@ fr_info_t *fin; * No matching icmp state entry. Perhaps this is a * response to another state entry. */ - fr = fr_checkicmpmatchingstate(ip, fin); +#ifdef USE_INET6 + if (v == 6) + fr = fr_checkicmp6matchingstate((ip6_t *)ip, fin); + else +#endif + fr = fr_checkicmpmatchingstate(ip, fin); if (fr) return fr; break; case IPPROTO_TCP : { - register u_short dport = tcp->th_dport, sport = tcp->th_sport; + register u_short dport, sport; register int i; i = tcp->th_flags; @@ -941,65 +1288,42 @@ fr_info_t *fin; if ((i & TH_RST) && ((i & (TH_FIN|TH_SYN|TH_RST)) != TH_RST)) break; + case IPPROTO_UDP : + dport = tcp->th_dport; + sport = tcp->th_sport; tryagain = 0; -retry_tcp: + hv += dport; + hv += sport; + READ_ENTER(&ipf_state); +retry_tcpudp: hvm = hv % fr_statesize; - WRITE_ENTER(&ipf_state); - for (isp = &ips_table[hvm]; (is = *isp); - isp = &is->is_next) - if ((is->is_p == pr) && + for (isp = &ips_table[hvm]; (is = *isp); isp = &is->is_hnext) + if ((is->is_p == pr) && (is->is_v == v) && fr_matchsrcdst(is, src, dst, fin, tcp)) { - if (fr_tcpstate(is, fin, ip, tcp)) { -#ifndef _KERNEL - if (tcp->th_flags & TCP_CLOSE) { - *isp = is->is_next; - isp = &ips_table[hvm]; - if (ips_table[hvm] == NULL) - ips_stats.iss_inuse--; - ips_num--; + if ((pr == IPPROTO_TCP)) { + if (!fr_tcpstate(is, fin, ip, tcp)) { + continue; } -#endif - break; } - is = NULL; break; } - if (is != NULL) - break; - RWLOCK_EXIT(&ipf_state); - hv += dport; - hv += sport; - if (tryagain == 0) { - tryagain = 1; - goto retry_tcp; - } - break; - } - case IPPROTO_UDP : - { - register u_short dport = tcp->th_dport, sport = tcp->th_sport; - - tryagain = 0; -retry_udp: - hvm = hv % fr_statesize; - /* - * Nothing else to match on but ports. and IP#'s - */ - READ_ENTER(&ipf_state); - for (is = ips_table[hvm]; is; is = is->is_next) - if ((is->is_p == pr) && - fr_matchsrcdst(is, src, dst, fin, tcp)) { - is->is_age = fr_udptimeout; - break; + if (is != NULL) { + if (tryagain && + !(is->is_flags & (FI_WILDP|FI_WILDA))) { + hv += dport; + hv += sport; + fr_ipsmove(isp, is, hv); + MUTEX_DOWNGRADE(&ipf_state); } - if (is != NULL) break; + } RWLOCK_EXIT(&ipf_state); - hv += dport; - hv += sport; - if (tryagain == 0) { + if (!tryagain && ips_wild) { + hv -= dport; + hv -= sport; tryagain = 1; - goto retry_udp; + WRITE_ENTER(&ipf_state); + goto retry_tcpudp; } break; } @@ -1007,14 +1331,14 @@ retry_udp: break; } if (is == NULL) { - ATOMIC_INC(ips_stats.iss_miss); + ATOMIC_INCL(ips_stats.iss_miss); return NULL; } - MUTEX_ENTER(&ipf_rw); - is->is_bytes += ip->ip_len; + MUTEX_ENTER(&is->is_lock); + is->is_bytes += fin->fin_plen; ips_stats.iss_hits++; is->is_pkts++; - MUTEX_EXIT(&ipf_rw); + MUTEX_EXIT(&is->is_lock); fr = is->is_rule; fin->fin_fr = fr; pass = is->is_pass; @@ -1029,18 +1353,56 @@ retry_udp: } +void ip_statesync(ifp) +void *ifp; +{ + register ipstate_t *is; + + WRITE_ENTER(&ipf_state); + for (is = ips_list; is; is = is->is_next) { + if (is->is_ifpin == ifp) { + is->is_ifpin = GETUNIT(is->is_ifname[0], is->is_v); + if (!is->is_ifpin) + is->is_ifpin = (void *)-1; + } + if (is->is_ifpout == ifp) { + is->is_ifpout = GETUNIT(is->is_ifname[1], is->is_v); + if (!is->is_ifpout) + is->is_ifpout = (void *)-1; + } + } + RWLOCK_EXIT(&ipf_state); +} + + static void fr_delstate(is) ipstate_t *is; { frentry_t *fr; + if (is->is_flags & (FI_WILDP|FI_WILDA)) + ips_wild--; + if (is->is_next) + is->is_next->is_pnext = is->is_pnext; + *is->is_pnext = is->is_next; + if (is->is_hnext) + is->is_hnext->is_phnext = is->is_phnext; + *is->is_phnext = is->is_hnext; + if (ips_table[is->is_hv] == NULL) + ips_stats.iss_inuse--; + fr = is->is_rule; if (fr != NULL) { - ATOMIC_DEC(fr->fr_ref); - if (fr->fr_ref == 0) + ATOMIC_DEC32(fr->fr_ref); + if (fr->fr_ref == 0) { KFREE(fr); + } } +#ifdef _KERNEL + MUTEX_DESTROY(&is->is_lock); +#endif KFREE(is); + ips_num--; } @@ -1049,16 +1411,11 @@ ipstate_t *is; */ void fr_stateunload() { - register int i; - register ipstate_t *is, **isp; + register ipstate_t *is; WRITE_ENTER(&ipf_state); - for (i = fr_statesize - 1; i >= 0; i--) - for (isp = &ips_table[i]; (is = *isp); ) { - *isp = is->is_next; - fr_delstate(is); - ips_num--; - } + while ((is = ips_list)) + fr_delstate(is); ips_stats.iss_inuse = 0; ips_num = 0; RWLOCK_EXIT(&ipf_state); @@ -1073,7 +1430,6 @@ void fr_stateunload() */ void fr_timeoutstate() { - register int i; register ipstate_t *is, **isp; #if defined(_KERNEL) && !SOLARIS int s; @@ -1081,23 +1437,18 @@ void fr_timeoutstate() SPL_NET(s); WRITE_ENTER(&ipf_state); - for (i = fr_statesize - 1; i >= 0; i--) - for (isp = &ips_table[i]; (is = *isp); ) - if (is->is_age && !--is->is_age) { - *isp = is->is_next; - if (is->is_p == IPPROTO_TCP) - ips_stats.iss_fin++; - else - ips_stats.iss_expire++; - if (ips_table[i] == NULL) - ips_stats.iss_inuse--; + for (isp = &ips_list; (is = *isp); ) + if (is->is_age && !--is->is_age) { + if (is->is_p == IPPROTO_TCP) + ips_stats.iss_fin++; + else + ips_stats.iss_expire++; #ifdef IPFILTER_LOG - ipstate_log(is, ISL_EXPIRE); + ipstate_log(is, ISL_EXPIRE); #endif - fr_delstate(is); - ips_num--; - } else - isp = &is->is_next; + fr_delstate(is); + } else + isp = &is->is_next; RWLOCK_EXIT(&ipf_state); SPL_X(s); if (fr_state_doflush) { @@ -1110,11 +1461,31 @@ void fr_timeoutstate() /* * Original idea freom Pradeep Krishnan for use primarily with NAT code. * (pkrishna@netcom.com) + * + * Rewritten by Arjan de Vet <Arjan.deVet@adv.iae.nl>, 2000-07-29: + * + * - (try to) base state transitions on real evidence only, + * i.e. packets that are sent and have been received by ipfilter; + * diagram 18.12 of TCP/IP volume 1 by W. Richard Stevens was used. + * + * - deal with half-closed connections correctly; + * + * - store the state of the source in state[0] such that ipfstat + * displays the state as source/dest instead of dest/source; the calls + * to fr_tcp_age have been changed accordingly. + * + * Parameters: + * + * state[0] = state of source (host that initiated connection) + * state[1] = state of dest (host that accepted the connection) + * + * dir == 0 : a packet from source to dest + * dir == 1 : a packet from dest to source + * */ -void fr_tcp_age(age, state, ip, fin, dir) +void fr_tcp_age(age, state, fin, dir) u_long *age; u_char *state; -ip_t *ip; fr_info_t *fin; int dir; { @@ -1124,7 +1495,7 @@ int dir; ostate = state[1 - dir]; - dlen = ip->ip_len - fin->fin_hlen - (tcp->th_off << 2); + dlen = fin->fin_plen - fin->fin_hlen - (tcp->th_off << 2); if (flags & TH_RST) { if (!(tcp->th_flags & TH_PUSH) && !dlen) { @@ -1137,67 +1508,192 @@ int dir; return; } - *age = fr_tcptimeout; /* 1 min */ + *age = fr_tcptimeout; /* default 4 mins */ switch(state[dir]) { - case TCPS_CLOSED: + case TCPS_CLOSED: /* 0 */ + if ((flags & TH_OPENING) == TH_OPENING) { + /* + * 'dir' received an S and sends SA in response, + * CLOSED -> SYN_RECEIVED + */ + state[dir] = TCPS_SYN_RECEIVED; + *age = fr_tcptimeout; + } else if ((flags & (TH_SYN|TH_ACK)) == TH_SYN) { + /* 'dir' sent S, CLOSED -> SYN_SENT */ + state[dir] = TCPS_SYN_SENT; + *age = fr_tcptimeout; + } + /* + * The next piece of code makes it possible to get + * already established connections into the state table + * after a restart or reload of the filter rules; this + * 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) { + /* we saw an A, guess 'dir' is in ESTABLISHED mode */ state[dir] = TCPS_ESTABLISHED; *age = fr_tcpidletimeout; } - case TCPS_FIN_WAIT_2: - if ((flags & TH_OPENING) == TH_OPENING) + /* + * TODO: besides regular ACK packets we can have other + * packets as well; it is yet to be determined how we + * should initialize the states in those cases + */ + break; + + case TCPS_LISTEN: /* 1 */ + /* NOT USED */ + break; + + case TCPS_SYN_SENT: /* 2 */ + 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; + } else if (flags & TH_FIN) { + /* + * We see an F from 'dir' which is in SYN_SENT + * state and wants to close its side of the + * connection; SYN_SENT -> FIN_WAIT_1 + */ + state[dir] = TCPS_FIN_WAIT_1; + *age = fr_tcpidletimeout; /* or fr_tcptimeout? */ + } else if ((flags & TH_OPENING) == TH_OPENING) { + /* + * We see an SA from 'dir' which is already in + * SYN_SENT state, this means we have a + * simultaneous open; SYN_SENT -> SYN_RECEIVED + */ state[dir] = TCPS_SYN_RECEIVED; - else if (flags & TH_SYN) - state[dir] = TCPS_SYN_SENT; + *age = fr_tcptimeout; + } break; - case TCPS_SYN_RECEIVED: - case TCPS_SYN_SENT: - if ((flags & (TH_FIN|TH_ACK)) == TH_ACK) { + + case TCPS_SYN_RECEIVED: /* 3 */ + if ((flags & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK) { + /* + * We see an A from 'dir' which was in SYN_RECEIVED + * state so it must now be in established state, + * SYN_RECEIVED -> ESTABLISHED + */ state[dir] = TCPS_ESTABLISHED; *age = fr_tcpidletimeout; - } else if ((flags & (TH_FIN|TH_ACK)) == (TH_FIN|TH_ACK)) { - state[dir] = TCPS_CLOSE_WAIT; - if (!(flags & TH_PUSH) && !dlen && - ostate > TCPS_ESTABLISHED) - *age = fr_tcplastack; - else - *age = fr_tcpclosewait; + } else if (flags & TH_FIN) { + /* + * We see an F from 'dir' which is in SYN_RECEIVED + * state and wants to close its side of the connection; + * SYN_RECEIVED -> FIN_WAIT_1 + */ + state[dir] = TCPS_FIN_WAIT_1; + *age = fr_tcpidletimeout; } break; - case TCPS_ESTABLISHED: + + case TCPS_ESTABLISHED: /* 4 */ if (flags & TH_FIN) { - state[dir] = TCPS_CLOSE_WAIT; - if (!(flags & TH_PUSH) && !dlen && - ostate > TCPS_ESTABLISHED) - *age = fr_tcplastack; - else - *age = fr_tcpclosewait; - } else { - if (ostate < TCPS_CLOSE_WAIT) + /* + * 'dir' closed its side of the connection; this + * gives us a half-closed connection; + * ESTABLISHED -> FIN_WAIT_1 + */ + state[dir] = TCPS_FIN_WAIT_1; + *age = fr_tcphalfclosed; + } else if (flags & TH_ACK) { + /* an ACK, should we exclude other flags here? */ + if (ostate == TCPS_FIN_WAIT_1) { + /* + * We know the other side did an active close, + * so we are ACKing the recvd FIN packet (does + * the window matching code guarantee this?) + * and go into CLOSE_WAIT state; this gives us + * a half-closed connection + */ + state[dir] = TCPS_CLOSE_WAIT; + *age = fr_tcphalfclosed; + } else if (ostate < TCPS_CLOSE_WAIT) + /* + * Still a fully established connection, + * reset timeout + */ *age = fr_tcpidletimeout; } break; - case TCPS_CLOSE_WAIT: - if ((flags & TH_FIN) && !(flags & TH_PUSH) && !dlen && - ostate > TCPS_ESTABLISHED) { + + case TCPS_CLOSE_WAIT: /* 5 */ + if (flags & TH_FIN) { + /* + * Application closed and 'dir' sent a FIN, we're now + * going into LAST_ACK state + */ *age = fr_tcplastack; state[dir] = TCPS_LAST_ACK; + } else { + /* + * We remain in CLOSE_WAIT because the other side has + * closed already and we did not close our side yet; + * reset timeout + */ + *age = fr_tcphalfclosed; + } + break; + + case TCPS_FIN_WAIT_1: /* 6 */ + if ((flags & TH_ACK) && ostate > TCPS_CLOSE_WAIT) { + /* + * If the other side is not active anymore it has sent + * us a FIN packet that we are ack'ing now with an ACK; + * this means both sides have now closed the connection + * and we go into TIME_WAIT + */ + /* + * XXX: how do we know we really are ACKing the FIN + * packet here? does the window code guarantee that? + */ + state[dir] = TCPS_TIME_WAIT; + *age = fr_tcptimeout; } else - *age = fr_tcpclosewait; + /* + * 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; break; - case TCPS_LAST_ACK: + + case TCPS_CLOSING: /* 7 */ + /* NOT USED */ + break; + + case TCPS_LAST_ACK: /* 8 */ if (flags & TH_ACK) { - state[dir] = TCPS_FIN_WAIT_2; - if (!(flags & TH_PUSH) && !dlen && - ostate > TCPS_ESTABLISHED) + if ((flags & TH_PUSH) || dlen) + /* + * There is still data to be delivered, reset + * timeout + */ *age = fr_tcplastack; - else { - *age = fr_tcpclosewait; - state[dir] = TCPS_CLOSE_WAIT; - } } + /* + * We cannot detect when we go out of LAST_ACK state to CLOSED + * because that is based on the reception of ACK packets; + * ipfilter can only detect that a packet has been sent by a + * host + */ + break; + + case TCPS_FIN_WAIT_2: /* 9 */ + /* NOT USED */ + break; + + case TCPS_TIME_WAIT: /* 10 */ + /* we're in 2MSL timeout now */ break; } } @@ -1219,6 +1715,7 @@ u_int type; ipsl.isl_src = is->is_src; ipsl.isl_dst = is->is_dst; ipsl.isl_p = is->is_p; + ipsl.isl_v = is->is_v; ipsl.isl_flags = is->is_flags; if (ipsl.isl_p == IPPROTO_TCP || ipsl.isl_p == IPPROTO_UDP) { ipsl.isl_sport = is->is_sport; @@ -1242,25 +1739,162 @@ u_int type; #endif -void ip_statesync(ifp) -void *ifp; +#ifdef USE_INET6 +frentry_t *fr_checkicmp6matchingstate(ip, fin) +ip6_t *ip; +fr_info_t *fin; { - register ipstate_t *is; - register int i; + register ipstate_t *is, **isp; + register u_short sport, dport; + register u_char pr; + struct icmp6_hdr *ic, *oic; + union i6addr dst, src; + u_short savelen; + fr_info_t ofin; + tcphdr_t *tcp; + frentry_t *fr; + ip6_t *oip; + int type; + u_int hv; - WRITE_ENTER(&ipf_state); - for (i = fr_statesize - 1; i >= 0; i--) - for (is = ips_table[i]; is != NULL; is = is->is_next) { - if (is->is_ifpin == ifp) { - is->is_ifpin = GETUNIT(is->is_ifname[0]); - if (!is->is_ifpin) - is->is_ifpin = (void *)-1; - } - if (is->is_ifpout == ifp) { - is->is_ifpout = GETUNIT(is->is_ifname[1]); - if (!is->is_ifpout) - is->is_ifpout = (void *)-1; + /* + * Does it at least have the return (basic) IP header ? + * Only a basic IP header (no options) should be with + * an ICMP error header. + */ + if ((fin->fin_v != 6) || (fin->fin_plen < ICMP6ERR_MINPKTLEN)) + return NULL; + ic = (struct icmp6_hdr *)fin->fin_dp; + type = ic->icmp6_type; + /* + * If it's not an error type, then return + */ + if ((type != ICMP6_DST_UNREACH) && (type != ICMP6_PACKET_TOO_BIG) && + (type != ICMP6_TIME_EXCEEDED) && (type != ICMP6_PARAM_PROB)) + return NULL; + + oip = (ip6_t *)((char *)ic + ICMPERR_ICMPHLEN); + if (fin->fin_plen < sizeof(*oip)) + return NULL; + + if (oip->ip6_nxt == IPPROTO_ICMPV6) { + oic = (struct icmp6_hdr *)(oip + 1); + /* + * a ICMP error can only be generated as a result of an + * ICMP query, not as the response on an ICMP error + * + * XXX theoretically ICMP_ECHOREP and the other reply's are + * ICMP query's as well, but adding them here seems strange XXX + */ + if (!(oic->icmp6_type & ICMP6_INFOMSG_MASK)) + return NULL; + + /* + * perform a lookup of the ICMP packet in the state table + */ + hv = (pr = oip->ip6_nxt); + src.in6 = oip->ip6_src; + hv += src.in4.s_addr; + dst.in6 = oip->ip6_dst; + hv += dst.in4.s_addr; + hv += oic->icmp6_id; + hv += oic->icmp6_seq; + hv %= fr_statesize; + + oip->ip6_plen = ntohs(oip->ip6_plen); + ofin.fin_v = 6; + fr_makefrip(sizeof(*oip), (ip_t *)oip, &ofin); + oip->ip6_plen = htons(oip->ip6_plen); + ofin.fin_ifp = fin->fin_ifp; + ofin.fin_out = !fin->fin_out; + ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ + + READ_ENTER(&ipf_state); + for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_hnext) + if ((is->is_p == pr) && + (oic->icmp6_id == is->is_icmp.ics_id) && + (oic->icmp6_seq == is->is_icmp.ics_seq) && + fr_matchsrcdst(is, src, dst, &ofin, NULL)) { + /* + * in the state table ICMP query's are stored + * with the type of the corresponding ICMP + * response. Correct here + */ + if (((is->is_type == ICMP6_ECHO_REPLY) && + (oic->icmp6_type == ICMP6_ECHO_REQUEST)) || + (is->is_type - 1 == oic->icmp6_type )) { + ips_stats.iss_hits++; + is->is_pkts++; + is->is_bytes += fin->fin_plen; + return is->is_rule; + } } + RWLOCK_EXIT(&ipf_state); + + return NULL; + }; + + if ((oip->ip6_nxt != IPPROTO_TCP) && (oip->ip6_nxt != IPPROTO_UDP)) + return NULL; + tcp = (tcphdr_t *)(oip + 1); + dport = tcp->th_dport; + sport = tcp->th_sport; + + hv = (pr = oip->ip6_nxt); + src.in6 = oip->ip6_src; + hv += src.in4.s_addr; + dst.in6 = oip->ip6_dst; + hv += dst.in4.s_addr; + hv += dport; + hv += sport; + hv %= fr_statesize; + /* + * we make an fin entry to be able to feed it to + * matchsrcdst note that not all fields are encessary + * but this is the cleanest way. Note further we fill + * in fin_mp such that if someone uses it we'll get + * a kernel panic. fr_matchsrcdst does not use this. + * + * watch out here, as ip is in host order and oip in network + * order. Any change we make must be undone afterwards. + */ + savelen = oip->ip6_plen; + oip->ip6_plen = ip->ip6_plen - sizeof(*ip) - ICMPERR_ICMPHLEN; + ofin.fin_v = 6; + fr_makefrip(sizeof(*oip), (ip_t *)oip, &ofin); + oip->ip6_plen = savelen; + ofin.fin_ifp = fin->fin_ifp; + ofin.fin_out = !fin->fin_out; + ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ + READ_ENTER(&ipf_state); + for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_hnext) { + /* + * Only allow this icmp though if the + * encapsulated packet was allowed through the + * other way around. Note that the minimal amount + * of info present does not allow for checking against + * tcp internals such as seq and ack numbers. + */ + if ((is->is_p == pr) && (is->is_v == 6) && + fr_matchsrcdst(is, src, dst, &ofin, tcp)) { + fr = is->is_rule; + ips_stats.iss_hits++; + /* + * we must swap src and dst here because the icmp + * comes the other way around + */ + is->is_pkts++; + is->is_bytes += fin->fin_plen; + /* + * we deliberately do not touch the timeouts + * for the accompanying state table entry. + * It remains to be seen if that is correct. XXX + */ + RWLOCK_EXIT(&ipf_state); + return fr; } + } RWLOCK_EXIT(&ipf_state); + return NULL; } +#endif diff --git a/sys/netinet/ip_state.h b/sys/netinet/ip_state.h index edfcc8d6e3a..a7cbaaac28d 100644 --- a/sys/netinet/ip_state.h +++ b/sys/netinet/ip_state.h @@ -1,25 +1,26 @@ -/* $OpenBSD: ip_state.h,v 1.15 2000/08/10 05:50:26 kjell Exp $ */ +/* $OpenBSD: ip_state.h,v 1.16 2001/01/17 04:47:16 fgsch Exp $ */ /* - * Copyright (C) 1995-1998 by Darren Reed. + * Copyright (C) 1995-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. * * @(#)ip_state.h 1.3 1/12/96 (C) 1995 Darren Reed - * $IPFilter: ip_state.h,v 2.1.2.5 2000/07/08 02:15:56 darrenr Exp $ + * $IPFilter: ip_state.h,v 2.13.2.2 2000/08/23 11:01:31 darrenr Exp $ */ #ifndef __IP_STATE_H__ #define __IP_STATE_H__ -#ifndef IPSTATE_SIZE -# define IPSTATE_SIZE 5737 +#if defined(__STDC__) || defined(__GNUC__) +# define SIOCDELST _IOW('r', 61, struct ipstate *) +#else +# define SIOCDELST _IOW(r, 61, struct ipstate *) #endif -#ifndef IPSTATE_MAX -# define IPSTATE_MAX 4013 /* Maximum number of states held */ -#endif +#define IPSTATE_SIZE 5737 +#define IPSTATE_MAX 4013 /* Maximum number of states held */ #define PAIRS(s1,d1,s2,d2) ((((s1) == (s2)) && ((d1) == (d2))) ||\ (((s1) == (d2)) && ((d1) == (s2)))) @@ -53,16 +54,20 @@ typedef struct tcpstate { typedef struct ipstate { struct ipstate *is_next; + struct ipstate **is_pnext; + struct ipstate *is_hnext; + struct ipstate **is_phnext; u_long is_age; u_int is_pass; U_QUAD_T is_pkts; U_QUAD_T is_bytes; - void *is_ifpin; - void *is_ifpout; + void *is_ifp[2]; frentry_t *is_rule; - struct in_addr is_src; - struct in_addr is_dst; + union i6addr is_src; + union i6addr is_dst; u_char is_p; /* Protocol */ + u_char is_v; + u_int is_hv; u_32_t is_flags; u_32_t is_opt; /* packet options set */ u_32_t is_optmsk; /* " " mask */ @@ -76,15 +81,20 @@ typedef struct ipstate { udpstate_t is_us; } is_ps; char is_ifname[2][IFNAMSIZ]; +#if SOLARIS || defined(__sgi) + kmutex_t is_lock; +#endif } ipstate_t; -#define is_icmp is_ps.is_ics -#define is_type is_icmp.ics_type -#define is_code is_icmp.ics_code -#define is_tcp is_ps.is_ts -#define is_udp is_ps.is_us -#define is_send is_tcp.ts_data[0].td_end -#define is_dend is_tcp.ts_data[1].td_end +#define is_saddr is_src.in4.s_addr +#define is_daddr is_dst.in4.s_addr +#define is_icmp is_ps.is_ics +#define is_type is_icmp.ics_type +#define is_code is_icmp.ics_code +#define is_tcp is_ps.is_ts +#define is_udp is_ps.is_us +#define is_send is_tcp.ts_data[0].td_end +#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_maxsend is_tcp.ts_data[0].td_maxend @@ -92,6 +102,8 @@ typedef struct ipstate { #define is_sport is_tcp.ts_sport #define is_dport is_tcp.ts_dport #define is_state is_tcp.ts_state +#define is_ifpin is_ifp[0] +#define is_ifpout is_ifp[1] #define TH_OPENING (TH_SYN|TH_ACK) /* @@ -103,21 +115,30 @@ typedef struct ipstate { * Bits 8,9 are used to indicate wildcard source/destination port matching. */ +typedef struct ipstate_save { + void *ips_next; + struct ipstate ips_is; + struct frentry ips_fr; +} ipstate_save_t; + +#define ips_rule ips_is.is_rule + typedef struct ipslog { U_QUAD_T isl_pkts; U_QUAD_T isl_bytes; - struct in_addr isl_src; - struct in_addr isl_dst; - u_char isl_p; - u_char isl_flags; - u_char isl_state[2]; + union i6addr isl_src; + union i6addr isl_dst; u_short isl_type; union { u_short isl_filler[2]; u_short isl_ports[2]; u_short isl_icmp; } isl_ps; + u_char isl_v; + u_char isl_p; + u_char isl_flags; + u_char isl_state[2]; } ipslog_t; #define isl_sport isl_ps.isl_ports[0] @@ -127,6 +148,7 @@ typedef struct ipslog { #define ISL_NEW 0 #define ISL_EXPIRE 0xffff #define ISL_FLUSH 0xfffe +#define ISL_REMOVE 0xfffd typedef struct ips_stat { @@ -144,6 +166,7 @@ typedef struct ips_stat { u_long iss_logfail; u_long iss_inuse; ipstate_t **iss_table; + ipstate_t *iss_list; } ips_stat_t; @@ -152,15 +175,17 @@ extern u_long fr_tcpclosewait; extern u_long fr_tcplastack; extern u_long fr_tcptimeout; extern u_long fr_tcpclosed; +extern u_long fr_tcphalfclosed; extern u_long fr_udptimeout; extern u_long fr_icmptimeout; +extern int fr_state_lock; extern int fr_stateinit __P((void)); extern int fr_tcpstate __P((ipstate_t *, fr_info_t *, ip_t *, tcphdr_t *)); extern ipstate_t *fr_addstate __P((ip_t *, fr_info_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 *, ip_t *, fr_info_t *, int)); +extern void fr_tcp_age __P((u_long *, u_char *, fr_info_t *, 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/netinet/ipl.h b/sys/netinet/ipl.h index 1fd9dfb8feb..e05da33c5a3 100644 --- a/sys/netinet/ipl.h +++ b/sys/netinet/ipl.h @@ -1,18 +1,19 @@ -/* $OpenBSD: ipl.h,v 1.12 2000/08/10 05:50:26 kjell Exp $ */ +/* $OpenBSD: ipl.h,v 1.13 2001/01/17 04:47:17 fgsch Exp $ */ /* - * Copyright (C) 1993-1999 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. * * @(#)ipl.h 1.21 6/5/96 + * $IPFilter: ipl.h,v 2.15.2.16 2000/12/17 12:28:50 darrenr Exp $ */ #ifndef __IPL_H__ #define __IPL_H__ -#define IPL_VERSION "IP Filter: v3.3.18" +#define IPL_VERSION "IP Filter: v3.4.15" #endif |