summaryrefslogtreecommitdiffstats
path: root/sys/contrib/ipfilter/netinet/fil.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/ipfilter/netinet/fil.c')
-rw-r--r--sys/contrib/ipfilter/netinet/fil.c1298
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;
+}
OpenPOWER on IntegriCloud