diff options
author | darrenr <darrenr@FreeBSD.org> | 2007-06-04 02:54:36 +0000 |
---|---|---|
committer | darrenr <darrenr@FreeBSD.org> | 2007-06-04 02:54:36 +0000 |
commit | a33069b5324be7fb6d5c0a0d785bb0e10eb0aa36 (patch) | |
tree | 28d6fb710df6e0ddec4933e69ec29d2ecd78a134 /sys/contrib/ipfilter/netinet/fil.c | |
parent | 1dd4fa592dfed4984b91696b53e64e8c075f63eb (diff) | |
download | FreeBSD-src-a33069b5324be7fb6d5c0a0d785bb0e10eb0aa36.zip FreeBSD-src-a33069b5324be7fb6d5c0a0d785bb0e10eb0aa36.tar.gz |
Merge IPFilter 4.1.23 back to HEAD
See src/contrib/ipfilter/HISTORY for details of changes since 4.1.13
Diffstat (limited to 'sys/contrib/ipfilter/netinet/fil.c')
-rw-r--r-- | sys/contrib/ipfilter/netinet/fil.c | 1298 |
1 files changed, 1125 insertions, 173 deletions
diff --git a/sys/contrib/ipfilter/netinet/fil.c b/sys/contrib/ipfilter/netinet/fil.c index ee21a94..7c015ea 100644 --- a/sys/contrib/ipfilter/netinet/fil.c +++ b/sys/contrib/ipfilter/netinet/fil.c @@ -17,7 +17,11 @@ #include <sys/time.h> #if defined(__NetBSD__) # if (NetBSD >= 199905) && !defined(IPFILTER_LKM) && defined(_KERNEL) -# include "opt_ipfilter_log.h" +# if (__NetBSD_Version__ < 399001400) +# include "opt_ipfilter_log.h" +# else +# include "opt_ipfilter.h" +# endif # endif #endif #if defined(_KERNEL) && defined(__FreeBSD_version) && \ @@ -34,6 +38,9 @@ #else # include <sys/ioctl.h> #endif +#if (defined(__SVR4) || defined(__svr4__)) && defined(sun) +# include <sys/filio.h> +#endif #if !defined(_AIX51) # include <sys/fcntl.h> #endif @@ -81,7 +88,11 @@ struct file; # endif # include "radix_ipf.h" #endif -#include <net/route.h> +#ifdef __osf__ +# include "radix_ipf.h" +#else +# include <net/route.h> +#endif #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> @@ -100,6 +111,9 @@ struct file; #ifdef __hpux # undef _NET_ROUTE_INCLUDED #endif +#ifdef __osf__ +# undef _RADIX_H_ +#endif #include "netinet/ip_compat.h" #ifdef USE_INET6 # include <netinet/icmp6.h> @@ -154,7 +168,7 @@ extern int opts; fr_info_t frcache[2][8]; -struct filterstats frstats[2] = { { 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; +struct filterstats frstats[2]; struct frentry *ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } }, *ipfilter6[2][2] = { { NULL, NULL }, { NULL, NULL } }, *ipacct6[2][2] = { { NULL, NULL }, { NULL, NULL } }, @@ -235,11 +249,14 @@ static INLINE int frpr_udpcommon __P((fr_info_t *)); static int fr_updateipid __P((fr_info_t *)); #ifdef IPFILTER_LOOKUP static int fr_grpmapinit __P((frentry_t *fr)); -static INLINE void *fr_resolvelookup __P((u_int, u_int, lookupfunc_t *)); +static INLINE void *fr_resolvelookup __P((u_int, u_int, i6addr_t *, lookupfunc_t *)); #endif static void frsynclist __P((frentry_t *, void *)); static ipftuneable_t *fr_findtunebyname __P((const char *)); static ipftuneable_t *fr_findtunebycookie __P((void *, void **)); +static int ipf_geniter __P((ipftoken_t *, ipfgeniter_t *)); +static int ipf_frruleiter __P((void *, int, void *)); +static void ipf_unlinktoken __P((ipftoken_t *)); /* @@ -319,7 +336,7 @@ static ipfunc_resolve_t fr_availfuncs[] = { { "fr_srcgrpmap", fr_srcgrpmap, fr_grpmapinit }, { "fr_dstgrpmap", fr_dstgrpmap, fr_grpmapinit }, #endif - { "", NULL } + { "", NULL, NULL } }; @@ -532,7 +549,16 @@ int multiple, proto; return IPPROTO_NONE; hdr = fin->fin_dp; - shift = 8 + (hdr->ip6e_len << 3); + switch (proto) + { + case IPPROTO_FRAGMENT : + shift = 8; + break; + default : + shift = 8 + (hdr->ip6e_len << 3); + break; + } + if (shift > fin->fin_dlen) { /* Nasty extension header length? */ fin->fin_flx |= FI_BAD; return IPPROTO_NONE; @@ -551,6 +577,7 @@ int multiple, proto; break; } + fin->fin_exthdr = fin->fin_dp; fin->fin_dp = (char *)fin->fin_dp + shift; fin->fin_dlen -= shift; @@ -600,24 +627,22 @@ static INLINE int frpr_routing6(fin) fr_info_t *fin; { struct ip6_ext *hdr; - int shift; if (frpr_ipv6exthdr(fin, 0, IPPROTO_ROUTING) == IPPROTO_NONE) return IPPROTO_NONE; + hdr = fin->fin_exthdr; - hdr = fin->fin_dp; - shift = 8 + (hdr->ip6e_len << 3); - /* - * Nasty extension header length? - */ - if ((shift < sizeof(struct ip6_hdr)) || - ((shift - sizeof(struct ip6_hdr)) & 15)) { + if ((hdr->ip6e_len & 1) != 0) { + /* + * The routing header data is made up of 128 bit IPv6 addresses + * which means it must be a multiple of 2 lots of 8 in length. + */ fin->fin_flx |= FI_BAD; /* * Compensate for the changes made in frpr_ipv6exthdr() */ - fin->fin_dlen += shift; - fin->fin_dp = (char *)fin->fin_dp - shift; + fin->fin_dlen += 8 + (hdr->ip6e_len << 3); + fin->fin_dp = hdr; return IPPROTO_NONE; } @@ -643,16 +668,20 @@ static INLINE void frpr_fragment6(fin) fr_info_t *fin; { struct ip6_frag *frag; + int extoff; fin->fin_flx |= FI_FRAG; if (frpr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT) == IPPROTO_NONE) return; + extoff = (char *)fin->fin_exthdr - (char *)fin->fin_dp; + if (frpr_pullup(fin, sizeof(*frag)) == -1) return; - frag = fin->fin_dp; + fin->fin_exthdr = (char *)fin->fin_dp + extoff; + frag = fin->fin_exthdr; /* * Fragment but no fragmentation info set? Bad packet... */ @@ -702,10 +731,12 @@ fr_info_t *fin; int minicmpsz = sizeof(struct icmp6_hdr); struct icmp6_hdr *icmp6; - if (frpr_pullup(fin, ICMP6ERR_MINPKTLEN + 8 - sizeof(ip6_t)) == -1) + if (frpr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1) return; if (fin->fin_dlen > 1) { + ip6_t *ip6; + icmp6 = fin->fin_dp; fin->fin_data[0] = *(u_short *)icmp6; @@ -720,12 +751,26 @@ fr_info_t *fin; case ICMP6_PACKET_TOO_BIG : case ICMP6_TIME_EXCEEDED : case ICMP6_PARAM_PROB : + fin->fin_flx |= FI_ICMPERR; if ((fin->fin_m != NULL) && (M_LEN(fin->fin_m) < fin->fin_plen)) { if (fr_coalesce(fin) != 1) return; } - fin->fin_flx |= FI_ICMPERR; + + if (frpr_pullup(fin, ICMP6ERR_MINPKTLEN) == -1) + return; + + /* + * If the destination of this packet doesn't match the + * source of the original packet then this packet is + * not correct. + */ + ip6 = (ip6_t *)((char *)icmp6 + ICMPERR_ICMPHLEN); + if (IP6_NEQ(&fin->fin_fi.fi_dst, + (i6addr_t *)&ip6->ip6_src)) + fin->fin_flx |= FI_BAD; + minicmpsz = ICMP6ERR_IPICMPHLEN - sizeof(ip6_t); break; default : @@ -752,8 +797,13 @@ fr_info_t *fin; frpr_short6(fin, sizeof(struct udphdr)); - if (frpr_udpcommon(fin) == 0) + if (frpr_udpcommon(fin) == 0) { + u_char p = fin->fin_p; + + fin->fin_p = IPPROTO_UDP; fr_checkv6sum(fin); + fin->fin_p = p; + } } @@ -772,8 +822,13 @@ fr_info_t *fin; frpr_short6(fin, sizeof(struct tcphdr)); - if (frpr_tcpcommon(fin) == 0) + if (frpr_tcpcommon(fin) == 0) { + u_char p = fin->fin_p; + + fin->fin_p = IPPROTO_TCP; fr_checkv6sum(fin); + fin->fin_p = p; + } } @@ -862,18 +917,26 @@ static INLINE int frpr_pullup(fin, plen) fr_info_t *fin; int plen; { -#if defined(_KERNEL) if (fin->fin_m != NULL) { if (fin->fin_dp != NULL) plen += (char *)fin->fin_dp - ((char *)fin->fin_ip + fin->fin_hlen); plen += fin->fin_hlen; if (M_LEN(fin->fin_m) < plen) { +#if defined(_KERNEL) if (fr_pullup(fin->fin_m, fin, plen) == NULL) return -1; +#else + /* + * Fake fr_pullup failing + */ + *fin->fin_mp = NULL; + fin->fin_m = NULL; + fin->fin_ip = NULL; + return -1; +#endif } } -#endif return 0; } @@ -987,6 +1050,22 @@ fr_info_t *fin; oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0) fin->fin_flx |= FI_BAD; + + /* + * If the destination of this packet doesn't match the + * source of the original packet then this packet is + * not correct. + */ + if (oip->ip_src.s_addr != fin->fin_daddr) + fin->fin_flx |= FI_BAD; + + /* + * If the destination of this packet doesn't match the + * source of the original packet then this packet is + * not correct. + */ + if (oip->ip_src.s_addr != fin->fin_daddr) + fin->fin_flx |= FI_BAD; break; default : break; @@ -1054,14 +1133,27 @@ fr_info_t *fin; */ if ((flags & TH_URG) != 0 && (tcp->th_urp == 0)) { fin->fin_flx |= FI_BAD; +#if 0 } else if ((flags & TH_URG) == 0 && (tcp->th_urp != 0)) { - /* Ignore this case, it shows up in "real" traffic with */ - /* bogus values in the urgent pointer field. */ - ; + /* + * Ignore this case (#if 0) as it shows up in "real" + * traffic with bogus values in the urgent pointer field. + */ + fin->fin_flx |= FI_BAD; +#endif } else if (((flags & (TH_SYN|TH_FIN)) != 0) && ((flags & (TH_RST|TH_ACK)) == TH_RST)) { /* TH_FIN|TH_RST|TH_ACK seems to appear "naturally" */ fin->fin_flx |= FI_BAD; +#if 1 + } else if (((flags & TH_SYN) != 0) && + ((flags & (TH_URG|TH_PUSH)) != 0)) { + /* + * SYN with URG and PUSH set is not for normal TCP but it is + * possible(?) with T/TCP...but who uses T/TCP? + */ + fin->fin_flx |= FI_BAD; +#endif } else if (!(flags & TH_ACK)) { /* * If the ack bit isn't set, then either the SYN or @@ -1347,13 +1439,16 @@ fr_info_t *fin; */ off &= IP_MF|IP_OFFMASK; if (off != 0) { + int morefrag = off & IP_MF; + fi->fi_flx |= FI_FRAG; off &= IP_OFFMASK; if (off != 0) { fin->fin_flx |= FI_FRAGBODY; off <<= 3; if ((off + fin->fin_dlen > 65535) || - (fin->fin_dlen == 0) || (fin->fin_dlen & 7)) { + (fin->fin_dlen == 0) || + ((morefrag != 0) && ((fin->fin_dlen & 7) != 0))) { /* * The length of the packet, starting at its * offset cannot exceed 65535 (0xffff) as the @@ -1507,18 +1602,24 @@ fr_info_t *fin; fin->fin_rule = 0xffffffff; fin->fin_group[0] = -1; fin->fin_group[1] = '\0'; - fin->fin_dlen = fin->fin_plen - hlen; fin->fin_dp = (char *)ip + hlen; v = fin->fin_v; - if (v == 4) + if (v == 4) { + fin->fin_plen = ip->ip_len; + fin->fin_dlen = fin->fin_plen - hlen; + frpr_ipv4hdr(fin); #ifdef USE_INET6 - else if (v == 6) { + } else if (v == 6) { + fin->fin_plen = ntohs(((ip6_t *)ip)->ip6_plen); + fin->fin_dlen = fin->fin_plen; + fin->fin_plen += hlen; + if (frpr_ipv6hdr(fin) == -1) return -1; - } #endif + } if (fin->fin_ip == NULL) return -1; return 0; @@ -1680,7 +1781,7 @@ int portcmp; */ i = ((*lip & *lm) != *ld); FR_DEBUG(("0. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (i) return 1; @@ -1691,7 +1792,7 @@ int portcmp; lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("1. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (i) return 1; @@ -1714,20 +1815,20 @@ int portcmp; #endif i = ((*lip & *lm) != *ld); FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2b. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2c. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2d. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + ntohl(*lip), ntohl(*lm), ntohl(*ld))); } else { lip += 3; lm += 3; @@ -1756,20 +1857,20 @@ int portcmp; #endif i = ((*lip & *lm) != *ld); FR_DEBUG(("3a. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3b. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3c. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3d. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + ntohl(*lip), ntohl(*lm), ntohl(*ld))); } else { lip += 3; lm += 3; @@ -2297,9 +2398,6 @@ int out; int v = IP_V(ip); mb_t *mc = NULL; mb_t *m; -#ifdef USE_INET6 - ip6_t *ip6; -#endif /* * The first part of fr_check() deals with making sure that what goes * into the filtering engine makes some sense. Information about the @@ -2395,13 +2493,10 @@ int out; * structures to handle comfortably, for now, so just drop * them. */ - ip6 = (ip6_t *)ip; - fin->fin_plen = ntohs(ip6->ip6_plen); - if (fin->fin_plen == 0) { + if (((ip6_t *)ip)->ip6_plen == 0) { pass = FR_BLOCK|FR_NOMATCH; goto finished; } - fin->fin_plen += sizeof(ip6_t); } else #endif { @@ -2409,7 +2504,6 @@ int out; ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); #endif - fin->fin_plen = ip->ip_len; } if (fr_makefrip(hlen, ip, fin) == -1) { @@ -2439,8 +2533,7 @@ int out; } #ifdef USE_INET6 else if (v == 6) { - ip6 = (ip6_t *)ip; - if (ip6->ip6_hlim < fr_minttl) { + if (((ip6_t *)ip)->ip6_hlim < fr_minttl) { ATOMIC_INCL(frstats[0].fr_badttl); fin->fin_flx |= FI_LOWTTL; } @@ -2464,8 +2557,7 @@ int out; fr = fr_checkauth(fin, &pass); if (!out) { if (fr_checknatin(fin, &pass) == -1) { - RWLOCK_EXIT(&ipf_mutex); - goto finished; + goto filterdone; } } if (!out) @@ -2507,8 +2599,7 @@ int out; (void) fr_acctpkt(fin, NULL); if (fr_checknatout(fin, &pass) == -1) { - RWLOCK_EXIT(&ipf_mutex); - goto finished; + ; } else if ((fr_update_ipid != 0) && (v == 4)) { if (fr_updateipid(fin) == -1) { ATOMIC_INCL(frstats[1].fr_ipud); @@ -2520,20 +2611,26 @@ int out; } } +filterdone: #ifdef IPFILTER_LOG if ((fr_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { (void) fr_dolog(fin, &pass); } #endif + /* + * The FI_STATE flag is cleared here so that calling fr_checkstate + * will work when called from inside of fr_fastroute. Although + * there is a similar flag, FI_NATED, for NAT, it does have the same + * impact on code execution. + */ if (fin->fin_state != NULL) { - fr_statederef(fin, (ipstate_t **)&fin->fin_state); - fin->fin_state = NULL; + fr_statederef((ipstate_t **)&fin->fin_state); + fin->fin_flx ^= FI_STATE; } if (fin->fin_nat != NULL) { fr_natderef((nat_t **)&fin->fin_nat); - fin->fin_nat = NULL; } /* @@ -2788,6 +2885,7 @@ int len; /* ip(I) - pointer to IP header */ /* l4proto(I) - protocol to caclulate checksum for */ /* l4hdr(I) - pointer to layer 4 header */ +/* l3len(I) - length of layer 4 data plus layer 3 header */ /* */ /* Calculates the TCP checksum for the packet held in "m", using the data */ /* in the IP header "ip" to seed it. */ @@ -2796,6 +2894,8 @@ int len; /* and the TCP header. We also assume that data blocks aren't allocated in */ /* odd sizes. */ /* */ +/* For IPv6, l3len excludes extension header size. */ +/* */ /* Expects ip_len to be in host byte order when called. */ /* ------------------------------------------------------------------------ */ u_short fr_cksum(m, ip, l4proto, l4hdr, l3len) @@ -2838,9 +2938,9 @@ void *l4hdr; } else if (IP_V(ip) == 6) { ip6 = (ip6_t *)ip; hlen = sizeof(*ip6); - slen = ntohs(l3len); + slen = l3len - hlen; sum = htons((u_short)l4proto); - sum += slen; + sum += htons(slen); sp = (u_short *)&ip6->ip6_src; sum += *sp++; /* ip6_src */ sum += *sp++; @@ -3026,6 +3126,12 @@ nodata: # endif /* defined(BSD) || defined(sun) */ # endif /* MENTAT */ #else /* _KERNEL */ + /* + * Add up IP Header portion + */ + if (sp != (u_short *)l4hdr) + sp = (u_short *)l4hdr; + for (; slen > 1; slen -= 2) sum += *sp++; if (slen) @@ -3071,7 +3177,7 @@ nodata: * SUCH DAMAGE. * * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 - * $Id: fil.c,v 2.243.2.78 2006/03/29 11:19:54 darrenr Exp $ + * $Id: fil.c,v 2.243.2.109 2007/05/31 12:27:33 darrenr Exp $ */ /* * Copy data from an mbuf chain starting "off" bytes from the beginning, @@ -3662,13 +3768,15 @@ void *ifp; if (fr->fr_type == FR_T_IPF && fr->fr_satype == FRI_LOOKUP && fr->fr_srcptr == NULL) { fr->fr_srcptr = fr_resolvelookup(fr->fr_srctype, - fr->fr_srcnum, + fr->fr_srcsubtype, + &fr->fr_slookup, &fr->fr_srcfunc); } if (fr->fr_type == FR_T_IPF && fr->fr_datype == FRI_LOOKUP && fr->fr_dstptr == NULL) { fr->fr_dstptr = fr_resolvelookup(fr->fr_dsttype, - fr->fr_dstnum, + fr->fr_dstsubtype, + &fr->fr_dlookup, &fr->fr_dstfunc); } #endif @@ -3740,17 +3848,19 @@ void *src, *dst; size_t size; { caddr_t ca; - int err; + int error; # if SOLARIS - err = COPYIN(src, (caddr_t)&ca, sizeof(ca)); - if (err != 0) - return err; + error = COPYIN(src, (caddr_t)&ca, sizeof(ca)); + if (error != 0) + return error; # else bcopy(src, (caddr_t)&ca, sizeof(ca)); # endif - err = COPYIN(ca, dst, size); - return err; + error = COPYIN(ca, dst, size); + if (error != 0) + error = EFAULT; + return error; } @@ -3770,11 +3880,13 @@ void *src, *dst; size_t size; { caddr_t ca; - int err; + int error; bcopy(dst, (caddr_t)&ca, sizeof(ca)); - err = COPYOUT(src, ca, size); - return err; + error = COPYOUT(src, ca, size); + if (error != 0) + error = EFAULT; + return error; } #endif @@ -3950,7 +4062,8 @@ int rev; /* Function: fr_resolvelookup */ /* Returns: void * - NULL = failure, else success. */ /* Parameters: type(I) - type of lookup these parameters are for. */ -/* number(I) - table number to use when searching */ +/* subtype(I) - whether the info below contains number/name */ +/* info(I) - pointer to name/number of the lookup data */ /* funcptr(IO) - pointer to pointer for storing IP address */ /* searching function. */ /* */ @@ -3959,20 +4072,35 @@ int rev; /* call to do the IP address search will be change, regardless of whether */ /* or not the "table" number exists. */ /* ------------------------------------------------------------------------ */ -static void *fr_resolvelookup(type, number, funcptr) -u_int type, number; +static void *fr_resolvelookup(type, subtype, info, funcptr) +u_int type, subtype; +i6addr_t *info; lookupfunc_t *funcptr; { - char name[FR_GROUPLEN]; + char label[FR_GROUPLEN], *name; iphtable_t *iph; ip_pool_t *ipo; void *ptr; + if (subtype == 0) { #if defined(SNPRINTF) && defined(_KERNEL) - SNPRINTF(name, sizeof(name), "%u", number); + SNPRINTF(label, sizeof(label), "%u", info->iplookupnum); #else - (void) sprintf(name, "%u", number); + (void) sprintf(label, "%u", info->iplookupnum); #endif + name = label; + } else if (subtype == 1) { + /* + * Because iplookupname is currently only a 12 character + * string and FR_GROUPLEN is 16, copy all of it into the + * label buffer and add on a NULL at the end. + */ + strncpy(label, info->iplookupname, sizeof(info->iplookupname)); + label[sizeof(info->iplookupname)] = '\0'; + name = label; + } else { + return NULL; + } READ_ENTER(&ip_poolrw); @@ -4144,16 +4272,6 @@ caddr_t data; fprev = &fg->fg_start; } - ftail = fprev; - for (f = *ftail; (f = *ftail) != NULL; ftail = &f->fr_next) { - if (fp->fr_collect <= f->fr_collect) { - ftail = fprev; - f = NULL; - break; - } - fprev = ftail; - } - /* * Copy in extra data for the rule. */ @@ -4163,6 +4281,8 @@ caddr_t data; if (!ptr) return ENOMEM; error = COPYIN(uptr, ptr, fp->fr_dsize); + if (error != 0) + error = EFAULT; } else { ptr = uptr; error = 0; @@ -4221,8 +4341,11 @@ caddr_t data; #ifdef IPFILTER_LOOKUP case FRI_LOOKUP : fp->fr_srcptr = fr_resolvelookup(fp->fr_srctype, - fp->fr_srcnum, + fp->fr_srcsubtype, + &fp->fr_slookup, &fp->fr_srcfunc); + if (fp->fr_srcptr == NULL) + return ESRCH; break; #endif default : @@ -4246,8 +4369,11 @@ caddr_t data; #ifdef IPFILTER_LOOKUP case FRI_LOOKUP : fp->fr_dstptr = fr_resolvelookup(fp->fr_dsttype, - fp->fr_dstnum, + fp->fr_dstsubtype, + &fp->fr_dlookup, &fp->fr_dstfunc); + if (fp->fr_dstptr == NULL) + return ESRCH; break; #endif default : @@ -4288,6 +4414,20 @@ caddr_t data; fp->fr_cksum += *p; WRITE_ENTER(&ipf_mutex); + + /* + * Now that the filter rule lists are locked, we can walk the + * chain of them without fear. + */ + ftail = fprev; + for (f = *ftail; (f = *ftail) != NULL; ftail = &f->fr_next) { + if (fp->fr_collect <= f->fr_collect) { + ftail = fprev; + f = NULL; + break; + } + fprev = ftail; + } bzero((char *)frcache, sizeof(frcache)); for (; (f = *ftail) != NULL; ftail = &f->fr_next) { @@ -4331,6 +4471,8 @@ caddr_t data; if ((f->fr_dsize != 0) && (uptr != NULL)) error = COPYOUT(f->fr_data, uptr, f->fr_dsize); + if (error != 0) + error = EFAULT; if (error == 0) { f->fr_hits = 0; f->fr_bytes = 0; @@ -4427,7 +4569,7 @@ caddr_t data; fr_fixskip(ftail, f, -1); *ftail = f->fr_next; f->fr_next = NULL; - (void)fr_derefrule(&f); + (void) fr_derefrule(&f); } } else { /* @@ -4576,7 +4718,7 @@ void *data; #if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__FreeBSD__)) || \ - (defined(__FreeBSD__) && (__FreeBSD_version < 490000)) || \ + (defined(__FreeBSD__) && (__FreeBSD_version < 501000)) || \ (defined(__NetBSD__) && (__NetBSD_Version__ < 105000000)) || \ (defined(__OpenBSD__) && (OpenBSD < 200006)) /* @@ -4641,6 +4783,7 @@ frentry_t **frp; frentry_t *fr; fr = *frp; + *frp = NULL; MUTEX_ENTER(&fr->fr_lock); fr->fr_ref--; @@ -4666,7 +4809,6 @@ frentry_t **frp; } else { MUTEX_EXIT(&fr->fr_lock); } - *frp = NULL; return -1; } @@ -5196,55 +5338,47 @@ char *buffer; /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command */ /* mode(I) - mode value */ +/* uid(I) - uid making the ioctl call */ +/* ctx(I) - pointer to context data */ /* */ /* Based on the value of unit, call the appropriate ioctl handler or return */ /* EIO if ipfilter is not running. Also checks if write perms are req'd */ /* for the device in order to execute the ioctl. */ /* ------------------------------------------------------------------------ */ -int fr_ioctlswitch(unit, data, cmd, mode) -int unit, mode; +int fr_ioctlswitch(unit, data, cmd, mode, uid, ctx) +int unit, mode, uid; ioctlcmd_t cmd; -void *data; +void *data, *ctx; { int error = 0; switch (unit) { case IPL_LOGIPF : - error = -1; + error = fr_ipf_ioctl(data, cmd, mode, uid, ctx); break; case IPL_LOGNAT : if (fr_running > 0) - error = fr_nat_ioctl(data, cmd, mode); + error = fr_nat_ioctl(data, cmd, mode, uid, ctx); else error = EIO; break; case IPL_LOGSTATE : if (fr_running > 0) - error = fr_state_ioctl(data, cmd, mode); + error = fr_state_ioctl(data, cmd, mode, uid, ctx); else error = EIO; break; case IPL_LOGAUTH : - if (fr_running > 0) { - if ((cmd == (ioctlcmd_t)SIOCADAFR) || - (cmd == (ioctlcmd_t)SIOCRMAFR)) { - if (!(mode & FWRITE)) { - error = EPERM; - } else { - error = frrequest(unit, cmd, data, - fr_active, 1); - } - } else { - error = fr_auth_ioctl(data, cmd, mode); - } - } else + if (fr_running > 0) + error = fr_auth_ioctl(data, cmd, mode, uid, ctx); + else error = EIO; break; case IPL_LOGSYNC : #ifdef IPFILTER_SYNC if (fr_running > 0) - error = fr_sync_ioctl(data, cmd, mode); + error = fr_sync_ioctl(data, cmd, mode, uid, ctx); else #endif error = EIO; @@ -5252,7 +5386,7 @@ void *data; case IPL_LOGSCAN : #ifdef IPFILTER_SCAN if (fr_running > 0) - error = fr_scan_ioctl(data, cmd, mode); + error = fr_scan_ioctl(data, cmd, mode, uid, ctx); else #endif error = EIO; @@ -5260,7 +5394,7 @@ void *data; case IPL_LOGLOOKUP : #ifdef IPFILTER_LOOKUP if (fr_running > 0) - error = ip_lookup_ioctl(data, cmd, mode); + error = ip_lookup_ioctl(data, cmd, mode, uid, ctx); else #endif error = EIO; @@ -5278,9 +5412,7 @@ void *data; * This array defines the expected size of objects coming into the kernel * for the various recognised object types. */ -#define NUM_OBJ_TYPES 14 - -static int fr_objbytes[NUM_OBJ_TYPES][2] = { +static int fr_objbytes[IPFOBJ_COUNT][2] = { { 1, sizeof(struct frentry) }, /* frentry */ { 0, sizeof(struct friostat) }, { 0, sizeof(struct fr_info) }, @@ -5294,7 +5426,13 @@ static int fr_objbytes[NUM_OBJ_TYPES][2] = { { 1, sizeof(struct ipstate) }, /* ipstate */ { 0, sizeof(struct ips_stat) }, { 0, sizeof(struct frauth) }, - { 0, sizeof(struct ipftune) } + { 0, sizeof(struct ipftune) }, + { 0, sizeof(struct nat) }, /* nat_t */ + { 0, sizeof(struct ipfruleiter) }, + { 0, sizeof(struct ipfgeniter) }, + { 0, sizeof(struct ipftable) }, + { 0, sizeof(struct ipflookupiter) }, + { 0, sizeof(struct ipftq) * IPF_TCP_NSTATES }, }; @@ -5317,7 +5455,7 @@ int type; ipfobj_t obj; int error = 0; - if ((type < 0) || (type > NUM_OBJ_TYPES-1)) + if ((type < 0) || (type >= IPFOBJ_COUNT)) return EINVAL; BCOPYIN((caddr_t)data, (caddr_t)&obj, sizeof(obj)); @@ -5329,8 +5467,9 @@ int type; if ((fr_objbytes[type][0] & 1) != 0) { if (obj.ipfo_size < fr_objbytes[type][1]) return EINVAL; - } else if (obj.ipfo_size != fr_objbytes[type][1]) + } else if (obj.ipfo_size != fr_objbytes[type][1]) { return EINVAL; + } #else if (obj.ipfo_rev != IPFILTER_VERSION) /* XXX compatibility hook here */ @@ -5351,6 +5490,8 @@ int type; error = COPYIN((caddr_t)obj.ipfo_ptr, (caddr_t)ptr, obj.ipfo_size); } + if (error != 0) + error = EFAULT; return error; } @@ -5377,7 +5518,7 @@ int type, sz; ipfobj_t obj; int error; - if ((type < 0) || (type > NUM_OBJ_TYPES-1)) + if ((type < 0) || (type >= IPFOBJ_COUNT)) return EINVAL; if (((fr_objbytes[type][0] & 1) == 0) || (sz < fr_objbytes[type][1])) return EINVAL; @@ -5400,6 +5541,8 @@ int type, sz; #endif error = COPYIN((caddr_t)obj.ipfo_ptr, (caddr_t)ptr, sz); + if (error != 0) + error = EFAULT; return error; } @@ -5426,7 +5569,7 @@ int type, sz; ipfobj_t obj; int error; - if ((type < 0) || (type > NUM_OBJ_TYPES-1) || + if ((type < 0) || (type > IPFOBJ_COUNT) || ((fr_objbytes[type][0] & 1) == 0) || (sz < fr_objbytes[type][1])) return EINVAL; @@ -5449,6 +5592,8 @@ int type, sz; #endif error = COPYOUT((caddr_t)ptr, (caddr_t)obj.ipfo_ptr, sz); + if (error != 0) + error = EFAULT; return error; } @@ -5472,7 +5617,7 @@ int type; ipfobj_t obj; int error; - if ((type < 0) || (type > NUM_OBJ_TYPES-1)) + if ((type < 0) || (type > IPFOBJ_COUNT)) return EINVAL; BCOPYIN((caddr_t)data, (caddr_t)&obj, sizeof(obj)); @@ -5500,6 +5645,8 @@ int type; #endif error = COPYOUT((caddr_t)ptr, (caddr_t)obj.ipfo_ptr, obj.ipfo_size); + if (error != 0) + error = EFAULT; return error; } @@ -5570,9 +5717,11 @@ fr_info_t *fin; if (csump != NULL) hdrsum = *csump; - if (dosum) + if (dosum) { sum = fr_cksum(fin->fin_m, fin->fin_ip, - fin->fin_p, fin->fin_dp, fin->fin_plen); + fin->fin_p, fin->fin_dp, + fin->fin_dlen + fin->fin_hlen); + } #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID) } #endif @@ -5773,100 +5922,104 @@ fr_info_t *fin; ipftuneable_t ipf_tuneables[] = { /* filtering */ { { &fr_flags }, "fr_flags", 0, 0xffffffff, - sizeof(fr_flags), 0 }, + sizeof(fr_flags), 0, NULL }, { { &fr_active }, "fr_active", 0, 0, - sizeof(fr_active), IPFT_RDONLY }, + sizeof(fr_active), IPFT_RDONLY, NULL }, { { &fr_control_forwarding }, "fr_control_forwarding", 0, 1, - sizeof(fr_control_forwarding), 0 }, + sizeof(fr_control_forwarding), 0, NULL }, { { &fr_update_ipid }, "fr_update_ipid", 0, 1, - sizeof(fr_update_ipid), 0 }, + sizeof(fr_update_ipid), 0, NULL }, { { &fr_chksrc }, "fr_chksrc", 0, 1, - sizeof(fr_chksrc), 0 }, + sizeof(fr_chksrc), 0, NULL }, { { &fr_minttl }, "fr_minttl", 0, 1, - sizeof(fr_minttl), 0 }, + sizeof(fr_minttl), 0, NULL }, { { &fr_icmpminfragmtu }, "fr_icmpminfragmtu", 0, 1, - sizeof(fr_icmpminfragmtu), 0 }, + sizeof(fr_icmpminfragmtu), 0, NULL }, { { &fr_pass }, "fr_pass", 0, 0xffffffff, - sizeof(fr_pass), 0 }, + sizeof(fr_pass), 0, NULL }, /* state */ { { &fr_tcpidletimeout }, "fr_tcpidletimeout", 1, 0x7fffffff, - sizeof(fr_tcpidletimeout), IPFT_WRDISABLED }, + sizeof(fr_tcpidletimeout), IPFT_WRDISABLED, NULL }, { { &fr_tcpclosewait }, "fr_tcpclosewait", 1, 0x7fffffff, - sizeof(fr_tcpclosewait), IPFT_WRDISABLED }, + sizeof(fr_tcpclosewait), IPFT_WRDISABLED, NULL }, { { &fr_tcplastack }, "fr_tcplastack", 1, 0x7fffffff, - sizeof(fr_tcplastack), IPFT_WRDISABLED }, + sizeof(fr_tcplastack), IPFT_WRDISABLED, NULL }, { { &fr_tcptimeout }, "fr_tcptimeout", 1, 0x7fffffff, - sizeof(fr_tcptimeout), IPFT_WRDISABLED }, + sizeof(fr_tcptimeout), IPFT_WRDISABLED, NULL }, { { &fr_tcpclosed }, "fr_tcpclosed", 1, 0x7fffffff, - sizeof(fr_tcpclosed), IPFT_WRDISABLED }, + sizeof(fr_tcpclosed), IPFT_WRDISABLED, NULL }, { { &fr_tcphalfclosed }, "fr_tcphalfclosed", 1, 0x7fffffff, - sizeof(fr_tcphalfclosed), IPFT_WRDISABLED }, + sizeof(fr_tcphalfclosed), IPFT_WRDISABLED, NULL }, { { &fr_udptimeout }, "fr_udptimeout", 1, 0x7fffffff, - sizeof(fr_udptimeout), IPFT_WRDISABLED }, + sizeof(fr_udptimeout), IPFT_WRDISABLED, NULL }, { { &fr_udpacktimeout }, "fr_udpacktimeout", 1, 0x7fffffff, - sizeof(fr_udpacktimeout), IPFT_WRDISABLED }, + sizeof(fr_udpacktimeout), IPFT_WRDISABLED, NULL }, { { &fr_icmptimeout }, "fr_icmptimeout", 1, 0x7fffffff, - sizeof(fr_icmptimeout), IPFT_WRDISABLED }, + sizeof(fr_icmptimeout), IPFT_WRDISABLED, NULL }, { { &fr_icmpacktimeout }, "fr_icmpacktimeout", 1, 0x7fffffff, - sizeof(fr_icmpacktimeout), IPFT_WRDISABLED }, + sizeof(fr_icmpacktimeout), IPFT_WRDISABLED, NULL }, { { &fr_iptimeout }, "fr_iptimeout", 1, 0x7fffffff, - sizeof(fr_iptimeout), IPFT_WRDISABLED }, + sizeof(fr_iptimeout), IPFT_WRDISABLED, NULL }, { { &fr_statemax }, "fr_statemax", 1, 0x7fffffff, - sizeof(fr_statemax), 0 }, + sizeof(fr_statemax), 0, NULL }, { { &fr_statesize }, "fr_statesize", 1, 0x7fffffff, - sizeof(fr_statesize), IPFT_WRDISABLED }, + sizeof(fr_statesize), IPFT_WRDISABLED, NULL }, { { &fr_state_lock }, "fr_state_lock", 0, 1, - sizeof(fr_state_lock), IPFT_RDONLY }, + sizeof(fr_state_lock), IPFT_RDONLY, NULL }, { { &fr_state_maxbucket }, "fr_state_maxbucket", 1, 0x7fffffff, - sizeof(fr_state_maxbucket), IPFT_WRDISABLED }, + sizeof(fr_state_maxbucket), IPFT_WRDISABLED, NULL }, { { &fr_state_maxbucket_reset }, "fr_state_maxbucket_reset", 0, 1, - sizeof(fr_state_maxbucket_reset), IPFT_WRDISABLED }, + sizeof(fr_state_maxbucket_reset), IPFT_WRDISABLED, NULL }, { { &ipstate_logging }, "ipstate_logging", 0, 1, - sizeof(ipstate_logging), 0 }, + sizeof(ipstate_logging), 0, NULL }, /* nat */ { { &fr_nat_lock }, "fr_nat_lock", 0, 1, - sizeof(fr_nat_lock), IPFT_RDONLY }, + sizeof(fr_nat_lock), IPFT_RDONLY, NULL }, { { &ipf_nattable_sz }, "ipf_nattable_sz", 1, 0x7fffffff, - sizeof(ipf_nattable_sz), IPFT_WRDISABLED }, + sizeof(ipf_nattable_sz), IPFT_WRDISABLED, NULL }, { { &ipf_nattable_max }, "ipf_nattable_max", 1, 0x7fffffff, - sizeof(ipf_nattable_max), 0 }, + sizeof(ipf_nattable_max), 0, NULL }, { { &ipf_natrules_sz }, "ipf_natrules_sz", 1, 0x7fffffff, - sizeof(ipf_natrules_sz), IPFT_WRDISABLED }, + sizeof(ipf_natrules_sz), IPFT_WRDISABLED, NULL }, { { &ipf_rdrrules_sz }, "ipf_rdrrules_sz", 1, 0x7fffffff, - sizeof(ipf_rdrrules_sz), IPFT_WRDISABLED }, + sizeof(ipf_rdrrules_sz), IPFT_WRDISABLED, NULL }, { { &ipf_hostmap_sz }, "ipf_hostmap_sz", 1, 0x7fffffff, - sizeof(ipf_hostmap_sz), IPFT_WRDISABLED }, + sizeof(ipf_hostmap_sz), IPFT_WRDISABLED, NULL }, { { &fr_nat_maxbucket }, "fr_nat_maxbucket", 1, 0x7fffffff, - sizeof(fr_nat_maxbucket), IPFT_WRDISABLED }, + sizeof(fr_nat_maxbucket), IPFT_WRDISABLED, NULL }, { { &fr_nat_maxbucket_reset }, "fr_nat_maxbucket_reset", 0, 1, - sizeof(fr_nat_maxbucket_reset), IPFT_WRDISABLED }, + sizeof(fr_nat_maxbucket_reset), IPFT_WRDISABLED, NULL }, { { &nat_logging }, "nat_logging", 0, 1, - sizeof(nat_logging), 0 }, + sizeof(nat_logging), 0, NULL }, { { &fr_defnatage }, "fr_defnatage", 1, 0x7fffffff, - sizeof(fr_defnatage), IPFT_WRDISABLED }, + sizeof(fr_defnatage), IPFT_WRDISABLED, NULL }, { { &fr_defnatipage }, "fr_defnatipage", 1, 0x7fffffff, - sizeof(fr_defnatipage), IPFT_WRDISABLED }, + sizeof(fr_defnatipage), IPFT_WRDISABLED, NULL }, { { &fr_defnaticmpage }, "fr_defnaticmpage", 1, 0x7fffffff, - sizeof(fr_defnaticmpage), IPFT_WRDISABLED }, + sizeof(fr_defnaticmpage), IPFT_WRDISABLED, NULL }, + { { &fr_nat_doflush }, "fr_nat_doflush", 0, 1, + sizeof(fr_nat_doflush), 0, NULL }, + /* proxy */ + { { &ipf_proxy_debug }, "ipf_proxy_debug", 0, 10, + sizeof(ipf_proxy_debug), 0, 0 }, /* frag */ { { &ipfr_size }, "ipfr_size", 1, 0x7fffffff, - sizeof(ipfr_size), IPFT_WRDISABLED }, + sizeof(ipfr_size), IPFT_WRDISABLED, NULL }, { { &fr_ipfrttl }, "fr_ipfrttl", 1, 0x7fffffff, - sizeof(fr_ipfrttl), IPFT_WRDISABLED }, + sizeof(fr_ipfrttl), IPFT_WRDISABLED, NULL }, #ifdef IPFILTER_LOG /* log */ { { &ipl_suppress }, "ipl_suppress", 0, 1, - sizeof(ipl_suppress), 0 }, - { { &ipl_buffer_sz }, "ipl_buffer_sz", 0, 0, - sizeof(ipl_buffer_sz), IPFT_RDONLY }, + sizeof(ipl_suppress), 0, NULL }, { { &ipl_logmax }, "ipl_logmax", 0, 0x7fffffff, - sizeof(ipl_logmax), IPFT_WRDISABLED }, + sizeof(ipl_logmax), IPFT_WRDISABLED, NULL }, { { &ipl_logall }, "ipl_logall", 0, 1, - sizeof(ipl_logall), 0 }, + sizeof(ipl_logall), 0, NULL }, { { &ipl_logsize }, "ipl_logsize", 0, 0x80000, - sizeof(ipl_logsize), 0 }, + sizeof(ipl_logsize), 0, NULL }, #endif - { { NULL }, NULL, 0, 0 } + { { NULL }, NULL, 0, 0, + 0, 0, NULL } }; static ipftuneable_t *ipf_tunelist = NULL; @@ -6177,6 +6330,8 @@ int fr_initialise() { int i; + bzero(&frstats, sizeof(frstats)); + #ifdef IPFILTER_LOG i = fr_loginit(); if (i < 0) @@ -6285,7 +6440,7 @@ caddr_t data; return EFAULT; WRITE_ENTER(&ipf_mutex); - bzero((char *)frstats, sizeof(*frstats) * 2); + bzero(&frstats, sizeof(frstats)); RWLOCK_EXIT(&ipf_mutex); return 0; @@ -6364,3 +6519,800 @@ int v; nic = (void *)-1; return nic; } + + +ipftoken_t *ipftokenhead = NULL, **ipftokentail = &ipftokenhead; + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_expiretokens */ +/* Returns: None. */ +/* Parameters: None. */ +/* */ +/* This function is run every ipf tick to see if there are any tokens that */ +/* have been held for too long and need to be freed up. */ +/* ------------------------------------------------------------------------ */ +void ipf_expiretokens() +{ + ipftoken_t *it; + void *data; + + WRITE_ENTER(&ipf_tokens); + while ((it = ipftokenhead) != NULL) { + if (it->ipt_die > fr_ticks) + break; + + data = it->ipt_data; + + ipf_freetoken(it); + } + RWLOCK_EXIT(&ipf_tokens); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_deltoken */ +/* Returns: int - 0 = success, else error */ +/* Parameters: type(I) - the token type to match */ +/* uid(I) - uid owning the token */ +/* ptr(I) - context pointer for the token */ +/* */ +/* This function looks for a a token in the current list that matches up */ +/* the fields (type, uid, ptr). If none is found, ESRCH is returned, else */ +/* call ipf_freetoken() to remove it from the list. */ +/* ------------------------------------------------------------------------ */ +int ipf_deltoken(type, uid, ptr) +int type, uid; +void *ptr; +{ + ipftoken_t *it; + int error = ESRCH; + + WRITE_ENTER(&ipf_tokens); + for (it = ipftokenhead; it != NULL; it = it->ipt_next) + if (ptr == it->ipt_ctx && type == it->ipt_type && + uid == it->ipt_uid) { + ipf_freetoken(it); + error = 0; + break; + } + RWLOCK_EXIT(&ipf_tokens); + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_findtoken */ +/* Returns: ipftoken_t * - NULL if no memory, else pointer to token */ +/* Parameters: type(I) - the token type to match */ +/* uid(I) - uid owning the token */ +/* ptr(I) - context pointer for the token */ +/* */ +/* This function looks for a live token in the list of current tokens that */ +/* matches the tuple (type, uid, ptr). If one cannot be found then one is */ +/* allocated. If one is found then it is moved to the top of the list of */ +/* currently active tokens. */ +/* */ +/* NOTE: It is by design that this function returns holding a read lock on */ +/* ipf_tokens. Callers must make sure they release it! */ +/* ------------------------------------------------------------------------ */ +ipftoken_t *ipf_findtoken(type, uid, ptr) +int type, uid; +void *ptr; +{ + ipftoken_t *it, *new; + + KMALLOC(new, ipftoken_t *); + + WRITE_ENTER(&ipf_tokens); + for (it = ipftokenhead; it != NULL; it = it->ipt_next) { + if (it->ipt_alive == 0) + continue; + if (ptr == it->ipt_ctx && type == it->ipt_type && + uid == it->ipt_uid) + break; + } + + if (it == NULL) { + it = new; + new = NULL; + if (it == NULL) + return NULL; + it->ipt_data = NULL; + it->ipt_ctx = ptr; + it->ipt_uid = uid; + it->ipt_type = type; + it->ipt_next = NULL; + it->ipt_alive = 1; + } else { + if (new != NULL) { + KFREE(new); + new = NULL; + } + + ipf_unlinktoken(it); + } + it->ipt_pnext = ipftokentail; + *ipftokentail = it; + ipftokentail = &it->ipt_next; + it->ipt_next = NULL; + + it->ipt_die = fr_ticks + 2; + + MUTEX_DOWNGRADE(&ipf_tokens); + + return it; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_unlinktoken */ +/* Returns: None. */ +/* Parameters: token(I) - pointer to token structure */ +/* */ +/* This function unlinks a token structure from the linked list of tokens */ +/* that "own" it. The head pointer never needs to be explicitly adjusted */ +/* but the tail does due to the linked list implementation. */ +/* ------------------------------------------------------------------------ */ +static void ipf_unlinktoken(token) +ipftoken_t *token; +{ + + if (ipftokentail == &token->ipt_next) + ipftokentail = token->ipt_pnext; + + *token->ipt_pnext = token->ipt_next; + if (token->ipt_next != NULL) + token->ipt_next->ipt_pnext = token->ipt_pnext; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_freetoken */ +/* Returns: None. */ +/* Parameters: token(I) - pointer to token structure */ +/* */ +/* This function unlinks a token from the linked list and on the path to */ +/* free'ing the data, it calls the dereference function that is associated */ +/* with the type of data pointed to by the token as it is considered to */ +/* hold a reference to it. */ +/* ------------------------------------------------------------------------ */ +void ipf_freetoken(token) +ipftoken_t *token; +{ + void *data, **datap; + + ipf_unlinktoken(token); + + data = token->ipt_data; + datap = &data; + + if ((data != NULL) && (data != (void *)-1)) { + switch (token->ipt_type) + { + case IPFGENITER_IPF : + (void) fr_derefrule((frentry_t **)datap); + break; + case IPFGENITER_IPNAT : + WRITE_ENTER(&ipf_nat); + fr_ipnatderef((ipnat_t **)datap); + RWLOCK_EXIT(&ipf_nat); + break; + case IPFGENITER_NAT : + fr_natderef((nat_t **)datap); + break; + case IPFGENITER_STATE : + fr_statederef((ipstate_t **)datap); + break; + case IPFGENITER_FRAG : +#ifdef USE_MUTEXES + fr_fragderef((ipfr_t **)datap, &ipf_frag); +#else + fr_fragderef((ipfr_t **)datap); +#endif + break; + case IPFGENITER_NATFRAG : +#ifdef USE_MUTEXES + fr_fragderef((ipfr_t **)datap, &ipf_natfrag); +#else + fr_fragderef((ipfr_t **)datap); +#endif + break; + case IPFGENITER_HOSTMAP : + fr_hostmapdel((hostmap_t **)datap); + break; + default : +#ifdef IPFILTER_LOOKUP + ip_lookup_iterderef(token->ipt_type, data); +#endif + break; + } + } + + KFREE(token); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_getnextrule */ +/* Returns: int - 0 = success, else error */ +/* Parameters: t(I) - pointer to destination information to resolve */ +/* ptr(I) - pointer to ipfobj_t to copyin from user space */ +/* */ +/* This function's first job is to bring in the ipfruleiter_t structure via */ +/* the ipfobj_t structure to determine what should be the next rule to */ +/* return. Once the ipfruleiter_t has been brought in, it then tries to */ +/* find the 'next rule'. This may include searching rule group lists or */ +/* just be as simple as looking at the 'next' field in the rule structure. */ +/* When we have found the rule to return, increase its reference count and */ +/* if we used an existing rule to get here, decrease its reference count. */ +/* ------------------------------------------------------------------------ */ +int ipf_getnextrule(ipftoken_t *t, void *ptr) +{ + frentry_t *fr, *next, zero; + int error, count, out; + ipfruleiter_t it; + frgroup_t *fg; + char *dst; + + if (t == NULL || ptr == NULL) + return EFAULT; + error = fr_inobj(ptr, &it, IPFOBJ_IPFITER); + if (error != 0) + return error; + if ((it.iri_inout < 0) || (it.iri_inout > 3)) + return EINVAL; + if ((it.iri_active != 0) && (it.iri_active != 1)) + return EINVAL; + if (it.iri_nrules == 0) + return ENOSPC; + if (it.iri_rule == NULL) + return EFAULT; + + out = it.iri_inout & F_OUT; + fr = t->ipt_data; + READ_ENTER(&ipf_mutex); + if (fr == NULL) { + if (*it.iri_group == '\0') { + if ((it.iri_inout & F_ACIN) != 0) { + if (it.iri_v == 4) + next = ipacct[out][it.iri_active]; + else + next = ipacct6[out][it.iri_active]; + } else { + if (it.iri_v == 4) + next = ipfilter[out][it.iri_active]; + else + next = ipfilter6[out][it.iri_active]; + } + } else { + fg = fr_findgroup(it.iri_group, IPL_LOGIPF, + it.iri_active, NULL); + if (fg != NULL) + next = fg->fg_start; + else + next = NULL; + } + } else { + next = fr->fr_next; + } + + dst = (char *)it.iri_rule; + /* + * The ipfruleiter may ask for more than 1 rule at a time to be + * copied out, so long as that many exist in the list to start with! + */ + for (count = it.iri_nrules; count > 0; count--) { + if (next != NULL) { + MUTEX_ENTER(&next->fr_lock); + next->fr_ref++; + MUTEX_EXIT(&next->fr_lock); + t->ipt_data = next; + } else { + bzero(&zero, sizeof(zero)); + next = &zero; + ipf_freetoken(t); + fr = NULL; + t = NULL; + count = 1; + } + RWLOCK_EXIT(&ipf_mutex); + + if (fr != NULL) { + (void) fr_derefrule(&fr); + } + + error = COPYOUT(next, dst, sizeof(*next)); + if (error != 0) + return EFAULT; + + if (next->fr_data != NULL) { + dst += sizeof(*next); + error = COPYOUT(next->fr_data, dst, next->fr_dsize); + if (error != 0) + error = EFAULT; + else + dst += next->fr_dsize; + } + + if ((count == 1) || (next->fr_next == NULL) || (error != 0)) + break; + + READ_ENTER(&ipf_mutex); + fr = next; + next = fr->fr_next; + } + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_frruleiter */ +/* Returns: int - 0 = success, else error */ +/* Parameters: data(I) - the token type to match */ +/* uid(I) - uid owning the token */ +/* ptr(I) - context pointer for the token */ +/* */ +/* This function serves as a stepping stone between fr_ipf_ioctl and */ +/* ipf_getnextrule. It's role is to find the right token in the kernel for */ +/* the process doing the ioctl and use that to ask for the next rule. */ +/* ------------------------------------------------------------------------ */ +static int ipf_frruleiter(data, uid, ctx) +void *data, *ctx; +int uid; +{ + ipftoken_t *token; + int error; + + token = ipf_findtoken(IPFGENITER_IPF, uid, ctx); + if (token != NULL) + error = ipf_getnextrule(token, data); + else + error = EFAULT; + RWLOCK_EXIT(&ipf_tokens); + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_geniter */ +/* Returns: int - 0 = success, else error */ +/* Parameters: token(I) - pointer to ipftoken_t structure */ +/* itp(I) - */ +/* */ +/* ------------------------------------------------------------------------ */ +static int ipf_geniter(token, itp) +ipftoken_t *token; +ipfgeniter_t *itp; +{ + int error; + + switch (itp->igi_type) + { + case IPFGENITER_FRAG : +#ifdef USE_MUTEXES + error = fr_nextfrag(token, itp, + &ipfr_list, &ipfr_tail, &ipf_frag); +#else + error = fr_nextfrag(token, itp, &ipfr_list, &ipfr_tail); +#endif + break; + default : + error = EINVAL; + break; + } + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_genericiter */ +/* Returns: int - 0 = success, else error */ +/* Parameters: data(I) - the token type to match */ +/* uid(I) - uid owning the token */ +/* ptr(I) - context pointer for the token */ +/* */ +/* ------------------------------------------------------------------------ */ +int ipf_genericiter(data, uid, ctx) +void *data, *ctx; +int uid; +{ + ipftoken_t *token; + ipfgeniter_t iter; + int error; + + error = fr_inobj(data, &iter, IPFOBJ_GENITER); + if (error != 0) + return error; + + token = ipf_findtoken(iter.igi_type, uid, ctx); + if (token != NULL) { + token->ipt_subtype = iter.igi_type; + error = ipf_geniter(token, &iter); + } else + error = EFAULT; + RWLOCK_EXIT(&ipf_tokens); + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_ipf_ioctl */ +/* Returns: int - 0 = success, else error */ +/* Parameters: data(I) - the token type to match */ +/* cmd(I) - the ioctl command number */ +/* mode(I) - mode flags for the ioctl */ +/* uid(I) - uid owning the token */ +/* ptr(I) - context pointer for the token */ +/* */ +/* This function handles all of the ioctl command that are actually isssued */ +/* to the /dev/ipl device. */ +/* ------------------------------------------------------------------------ */ +int fr_ipf_ioctl(data, cmd, mode, uid, ctx) +caddr_t data; +ioctlcmd_t cmd; +int mode, uid; +void *ctx; +{ + friostat_t fio; + int error, tmp; + SPL_INT(s); + + switch (cmd) + { + case SIOCFRENB : + if (!(mode & FWRITE)) + error = EPERM; + else { + error = BCOPYIN((caddr_t)data, (caddr_t)&tmp, + sizeof(tmp)); + if (error != 0) { + error = EFAULT; + break; + } + + RWLOCK_EXIT(&ipf_global); + WRITE_ENTER(&ipf_global); + if (tmp) { + if (fr_running > 0) + error = 0; + else + error = ipfattach(); + if (error == 0) + fr_running = 1; + else + (void) ipfdetach(); + } else { + error = ipfdetach(); + if (error == 0) + fr_running = -1; + } + } + break; + + case SIOCIPFSET : + if (!(mode & FWRITE)) { + error = EPERM; + break; + } + /* FALLTHRU */ + case SIOCIPFGETNEXT : + case SIOCIPFGET : + error = fr_ipftune(cmd, (void *)data); + break; + + case SIOCSETFF : + if (!(mode & FWRITE)) + error = EPERM; + else { + error = BCOPYIN((caddr_t)data, (caddr_t)&fr_flags, + sizeof(fr_flags)); + if (error != 0) + error = EFAULT; + } + break; + + case SIOCGETFF : + error = BCOPYOUT((caddr_t)&fr_flags, (caddr_t)data, + sizeof(fr_flags)); + if (error != 0) + error = EFAULT; + break; + + case SIOCFUNCL : + error = fr_resolvefunc((void *)data); + break; + + case SIOCINAFR : + case SIOCRMAFR : + case SIOCADAFR : + case SIOCZRLST : + if (!(mode & FWRITE)) + error = EPERM; + else + error = frrequest(IPL_LOGIPF, cmd, (caddr_t)data, + fr_active, 1); + break; + + case SIOCINIFR : + case SIOCRMIFR : + case SIOCADIFR : + if (!(mode & FWRITE)) + error = EPERM; + else + error = frrequest(IPL_LOGIPF, cmd, (caddr_t)data, + 1 - fr_active, 1); + break; + + case SIOCSWAPA : + if (!(mode & FWRITE)) + error = EPERM; + else { + WRITE_ENTER(&ipf_mutex); + bzero((char *)frcache, sizeof(frcache[0]) * 2); + error = BCOPYOUT((caddr_t)&fr_active, (caddr_t)data, + sizeof(fr_active)); + if (error != 0) + error = EFAULT; + else + fr_active = 1 - fr_active; + RWLOCK_EXIT(&ipf_mutex); + } + break; + + case SIOCGETFS : + fr_getstat(&fio); + error = fr_outobj((void *)data, &fio, IPFOBJ_IPFSTAT); + break; + + case SIOCFRZST : + if (!(mode & FWRITE)) + error = EPERM; + else + error = fr_zerostats((caddr_t)data); + break; + + case SIOCIPFFL : + if (!(mode & FWRITE)) + error = EPERM; + else { + error = BCOPYIN((caddr_t)data, (caddr_t)&tmp, + sizeof(tmp)); + if (!error) { + tmp = frflush(IPL_LOGIPF, 4, tmp); + error = BCOPYOUT((caddr_t)&tmp, (caddr_t)data, + sizeof(tmp)); + if (error != 0) + error = EFAULT; + } else + error = EFAULT; + } + break; + +#ifdef USE_INET6 + case SIOCIPFL6 : + if (!(mode & FWRITE)) + error = EPERM; + else { + error = BCOPYIN((caddr_t)data, (caddr_t)&tmp, + sizeof(tmp)); + if (!error) { + tmp = frflush(IPL_LOGIPF, 6, tmp); + error = BCOPYOUT((caddr_t)&tmp, (caddr_t)data, + sizeof(tmp)); + if (error != 0) + error = EFAULT; + } else + error = EFAULT; + } + break; +#endif + + case SIOCSTLCK : + error = BCOPYIN((caddr_t)data, (caddr_t)&tmp, sizeof(tmp)); + if (error == 0) { + 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)) + error = EPERM; + else { + tmp = ipflog_clear(IPL_LOGIPF); + error = BCOPYOUT((caddr_t)&tmp, (caddr_t)data, + sizeof(tmp)); + if (error) + error = EFAULT; + } + break; +#endif /* IPFILTER_LOG */ + + case SIOCFRSYN : + if (!(mode & FWRITE)) + error = EPERM; + else { + RWLOCK_EXIT(&ipf_global); + WRITE_ENTER(&ipf_global); +#ifdef MENTAT + error = ipfsync(); +#else + frsync(NULL); + error = 0; +#endif + + } + break; + + case SIOCGFRST : + error = fr_outobj((void *)data, fr_fragstats(), + IPFOBJ_FRAGSTAT); + break; + +#ifdef IPFILTER_LOG + case FIONREAD : + tmp = (int)iplused[IPL_LOGIPF]; + + error = BCOPYOUT((caddr_t)&tmp, (caddr_t)data, sizeof(tmp)); + break; +#endif + + case SIOCIPFITER : + SPL_SCHED(s); + error = ipf_frruleiter(data, uid, ctx); + SPL_X(s); + break; + + case SIOCGENITER : + SPL_SCHED(s); + error = ipf_genericiter(data, uid, ctx); + SPL_X(s); + break; + break; + + case SIOCIPFDELTOK : + SPL_SCHED(s); + error = BCOPYIN((caddr_t)data, (caddr_t)&tmp, sizeof(tmp)); + if (error == 0) + error = ipf_deltoken(tmp, uid, ctx); + SPL_X(s); + break; + break; + + default : + error = EINVAL; + break; + } + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_queueflush */ +/* Returns: int - number of entries flushed (0 = none) */ +/* Parameters: deletefn(I) - function to call to delete entry */ +/* ipfqs(I) - top of the list of ipf internal queues */ +/* userqs(I) - top of the list of user defined timeouts */ +/* */ +/* This fucntion gets called when the state/NAT hash tables fill up and we */ +/* need to try a bit harder to free up some space. The algorithm used is */ +/* to look for the oldest entries on each timeout queue and free them if */ +/* they are within the given window we are considering. Where the window */ +/* starts and the steps taken to increase its size depend upon how long ipf */ +/* has been running (fr_ticks.) Anything modified in the last 30 seconds */ +/* is not touched. */ +/* touched */ +/* die fr_ticks 30*1.5 1800*1.5 | 43200*1.5 */ +/* | | | | | | */ +/* future <--+----------+--------+-----------+-----+-----+-----------> past */ +/* now \_int=30s_/ \_int=1hr_/ \_int=12hr */ +/* */ +/* Points to note: */ +/* - tqe_die is the time, in the future, when entries die. */ +/* - tqe_die - fr_ticks is how long left the connection has to live in ipf */ +/* ticks. */ +/* - tqe_touched is when the entry was last used by NAT/state */ +/* - the closer tqe_touched is to fr_ticks, the further tqe_die will be for */ +/* any given timeout queue and vice versa. */ +/* - both tqe_die and tqe_touched increase over time */ +/* - timeout queues are sorted with the highest value of tqe_die at the */ +/* bottom and therefore the smallest values of each are at the top */ +/* */ +/* We start by setting up a maximum range to scan for things to move of */ +/* iend (newest) to istart (oldest) in chunks of "interval". If nothing is */ +/* found in that range, "interval" is adjusted (so long as it isn't 30) and */ +/* we start again with a new value for "iend" and "istart". The downside */ +/* of the current implementation is that it may return removing just 1 entry*/ +/* every time (pathological case) where it could remove more. */ +/* ------------------------------------------------------------------------ */ +int ipf_queueflush(deletefn, ipfqs, userqs) +ipftq_delete_fn_t deletefn; +ipftq_t *ipfqs, *userqs; +{ + u_long interval, istart, iend; + ipftq_t *ifq, *ifqnext; + ipftqent_t *tqe, *tqn; + int removed; + + /* + * NOTE: Use of "* 15 / 10" is required here because if "* 1.5" is + * used then the operations are upgraded to floating point + * and kernels don't like floating point... + */ + if (fr_ticks > IPF_TTLVAL(43200 * 15 / 10)) { + istart = IPF_TTLVAL(86400 * 4); + interval = IPF_TTLVAL(43200); + } else if (fr_ticks > IPF_TTLVAL(1800 * 15 / 10)) { + istart = IPF_TTLVAL(43200); + interval = IPF_TTLVAL(1800); + } else if (fr_ticks > IPF_TTLVAL(30 * 15 / 10)) { + istart = IPF_TTLVAL(1800); + interval = IPF_TTLVAL(30); + } else { + return 0; + } + if (istart > fr_ticks) { + istart = (fr_ticks / interval) * interval; + } + + iend = fr_ticks - interval; + if (istart > iend) + istart = iend - interval; + removed = 0; + + while (removed == 0) { + u_long try; + + try = fr_ticks - istart; + + for (ifq = ipfqs; ifq != NULL; ifq = ifq->ifq_next) { + for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { + if (try < tqe->tqe_touched) + break; + tqn = tqe->tqe_next; + if ((*deletefn)(tqe->tqe_parent) == 0) + removed++; + } + } + + for (ifq = userqs; ifq != NULL; ifq = ifqnext) { + ifqnext = ifq->ifq_next; + + for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { + if (try < tqe->tqe_touched) + break; + tqn = tqe->tqe_next; + if ((*deletefn)(tqe->tqe_parent) == 0) + removed++; + } + } + + istart -= interval; + if (try >= iend) { + if (interval == IPF_TTLVAL(43200)) { + interval = IPF_TTLVAL(1800); + } else if (interval == IPF_TTLVAL(1800)) { + interval = IPF_TTLVAL(30); + } else { + break; + } + if (interval >= fr_ticks) + break; + + iend = fr_ticks - interval; + } + } + + return removed; +} |