summaryrefslogtreecommitdiffstats
path: root/ip_scan.c
diff options
context:
space:
mode:
authorcy <cy@FreeBSD.org>2013-07-19 05:41:57 +0000
committercy <cy@FreeBSD.org>2013-07-19 05:41:57 +0000
commit672af8808c0e7c15f330b401482f9271c2eb3fa6 (patch)
tree225b5acf68c01bc6a260b386c2b2dbf4fa2839e3 /ip_scan.c
parent71e82d94e82560b20789833f60056506de34de8b (diff)
downloadFreeBSD-src-672af8808c0e7c15f330b401482f9271c2eb3fa6.zip
FreeBSD-src-672af8808c0e7c15f330b401482f9271c2eb3fa6.tar.gz
As per the developers handbook (5.3.1 step 1), prepare the vendor trees for
import of new ipfilter vendor sources by flattening them. To keep the tags consistent with dist, the tags are also flattened. Approved by: glebius (Mentor)
Diffstat (limited to 'ip_scan.c')
-rw-r--r--ip_scan.c594
1 files changed, 594 insertions, 0 deletions
diff --git a/ip_scan.c b/ip_scan.c
new file mode 100644
index 0000000..37f6d58
--- /dev/null
+++ b/ip_scan.c
@@ -0,0 +1,594 @@
+/* $NetBSD$ */
+
+/*
+ * Copyright (C) 1995-2001 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ */
+#if defined(KERNEL) || defined(_KERNEL)
+# undef KERNEL
+# undef _KERNEL
+# define KERNEL 1
+# define _KERNEL 1
+#endif
+#include <sys/param.h>
+#if defined(__hpux) && (HPUXREV >= 1111) && !defined(_KERNEL)
+# include <sys/kern_svcs.h>
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#if !defined(_KERNEL)
+# include <stdlib.h>
+# include <string.h>
+# define _KERNEL
+# ifdef __OpenBSD__
+struct file;
+# endif
+# include <sys/uio.h>
+# undef _KERNEL
+#else
+# include <sys/systm.h>
+# if !defined(__svr4__) && !defined(__SVR4)
+# include <sys/mbuf.h>
+# endif
+#endif
+#include <sys/socket.h>
+#if !defined(__hpux) && !defined(__osf__) && !defined(linux)
+# include <sys/ioccom.h>
+#endif
+#ifdef __FreeBSD__
+# include <sys/filio.h>
+# include <sys/malloc.h>
+#else
+# include <sys/ioctl.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <net/if.h>
+
+
+#include "netinet/ip_compat.h"
+#include "netinet/ip_fil.h"
+#include "netinet/ip_state.h"
+#include "netinet/ip_scan.h"
+/* END OF INCLUDES */
+
+#if !defined(lint)
+static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed";
+static const char rcsid[] = "@(#)Id: ip_scan.c,v 2.40.2.2 2005/01/18 10:13:16 darrenr Exp";
+#endif
+
+#ifdef IPFILTER_SCAN /* endif at bottom of file */
+
+
+ipscan_t *ipsc_list = NULL,
+ *ipsc_tail = NULL;
+ipscanstat_t ipsc_stat;
+# ifdef USE_MUTEXES
+ipfrwlock_t ipsc_rwlock;
+# endif
+
+# ifndef isalpha
+# define isalpha(x) (((x) >= 'A' && 'Z' >= (x)) || \
+ ((x) >= 'a' && 'z' >= (x)))
+# endif
+
+
+int ipsc_add __P((caddr_t));
+int ipsc_delete __P((caddr_t));
+struct ipscan *ipsc_lookup __P((char *));
+int ipsc_matchstr __P((sinfo_t *, char *, int));
+int ipsc_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *));
+int ipsc_match __P((ipstate_t *));
+
+
+
+int ipsc_init()
+{
+ RWLOCK_INIT(&ipsc_rwlock, "ip scan rwlock");
+ return 0;
+}
+
+
+void fr_scanunload()
+{
+ RW_DESTROY(&ipsc_rwlock);
+}
+
+
+int ipsc_add(data)
+caddr_t data;
+{
+ ipscan_t *i, *isc;
+ int err;
+
+ KMALLOC(isc, ipscan_t *);
+ if (!isc)
+ return ENOMEM;
+
+ err = copyinptr(data, isc, sizeof(*isc));
+ if (err)
+ return err;
+
+ WRITE_ENTER(&ipsc_rwlock);
+
+ i = ipsc_lookup(isc->ipsc_tag);
+ if (i) {
+ RWLOCK_EXIT(&ipsc_rwlock);
+ KFREE(isc);
+ return EEXIST;
+ }
+
+ if (ipsc_tail) {
+ ipsc_tail->ipsc_next = isc;
+ isc->ipsc_pnext = &ipsc_tail->ipsc_next;
+ ipsc_tail = isc;
+ } else {
+ ipsc_list = isc;
+ ipsc_tail = isc;
+ isc->ipsc_pnext = &ipsc_list;
+ }
+ isc->ipsc_next = NULL;
+
+ isc->ipsc_hits = 0;
+ isc->ipsc_fref = 0;
+ isc->ipsc_sref = 0;
+ isc->ipsc_active = 0;
+
+ ipsc_stat.iscs_entries++;
+ RWLOCK_EXIT(&ipsc_rwlock);
+ return 0;
+}
+
+
+int ipsc_delete(data)
+caddr_t data;
+{
+ ipscan_t isc, *i;
+ int err;
+
+ err = copyinptr(data, &isc, sizeof(isc));
+ if (err)
+ return err;
+
+ WRITE_ENTER(&ipsc_rwlock);
+
+ i = ipsc_lookup(isc.ipsc_tag);
+ if (i == NULL)
+ err = ENOENT;
+ else {
+ if (i->ipsc_fref) {
+ RWLOCK_EXIT(&ipsc_rwlock);
+ return EBUSY;
+ }
+
+ *i->ipsc_pnext = i->ipsc_next;
+ if (i->ipsc_next)
+ i->ipsc_next->ipsc_pnext = i->ipsc_pnext;
+ else {
+ if (i->ipsc_pnext == &ipsc_list)
+ ipsc_tail = NULL;
+ else
+ ipsc_tail = *(*i->ipsc_pnext)->ipsc_pnext;
+ }
+
+ ipsc_stat.iscs_entries--;
+ KFREE(i);
+ }
+ RWLOCK_EXIT(&ipsc_rwlock);
+ return err;
+}
+
+
+struct ipscan *ipsc_lookup(tag)
+char *tag;
+{
+ ipscan_t *i;
+
+ for (i = ipsc_list; i; i = i->ipsc_next)
+ if (!strcmp(i->ipsc_tag, tag))
+ return i;
+ return NULL;
+}
+
+
+int ipsc_attachfr(fr)
+struct frentry *fr;
+{
+ ipscan_t *i;
+
+ if (fr->fr_isctag[0]) {
+ READ_ENTER(&ipsc_rwlock);
+ i = ipsc_lookup(fr->fr_isctag);
+ if (i != NULL) {
+ ATOMIC_INC32(i->ipsc_fref);
+ }
+ RWLOCK_EXIT(&ipsc_rwlock);
+ if (i == NULL)
+ return ENOENT;
+ fr->fr_isc = i;
+ }
+ return 0;
+}
+
+
+int ipsc_attachis(is)
+struct ipstate *is;
+{
+ frentry_t *fr;
+ ipscan_t *i;
+
+ READ_ENTER(&ipsc_rwlock);
+ fr = is->is_rule;
+ if (fr) {
+ i = fr->fr_isc;
+ if (!i || (i != (ipscan_t *)-1)) {
+ is->is_isc = i;
+ if (i) {
+ ATOMIC_INC32(i->ipsc_sref);
+ if (i->ipsc_clen)
+ is->is_flags |= IS_SC_CLIENT;
+ else
+ is->is_flags |= IS_SC_MATCHC;
+ if (i->ipsc_slen)
+ is->is_flags |= IS_SC_SERVER;
+ else
+ is->is_flags |= IS_SC_MATCHS;
+ } else
+ is->is_flags |= (IS_SC_CLIENT|IS_SC_SERVER);
+ }
+ }
+ RWLOCK_EXIT(&ipsc_rwlock);
+ return 0;
+}
+
+
+int ipsc_detachfr(fr)
+struct frentry *fr;
+{
+ ipscan_t *i;
+
+ i = fr->fr_isc;
+ if (i != NULL) {
+ ATOMIC_DEC32(i->ipsc_fref);
+ }
+ return 0;
+}
+
+
+int ipsc_detachis(is)
+struct ipstate *is;
+{
+ ipscan_t *i;
+
+ READ_ENTER(&ipsc_rwlock);
+ if ((i = is->is_isc) && (i != (ipscan_t *)-1)) {
+ ATOMIC_DEC32(i->ipsc_sref);
+ is->is_isc = NULL;
+ is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER);
+ }
+ RWLOCK_EXIT(&ipsc_rwlock);
+ return 0;
+}
+
+
+/*
+ * 'string' compare for scanning
+ */
+int ipsc_matchstr(sp, str, n)
+sinfo_t *sp;
+char *str;
+int n;
+{
+ char *s, *t, *up;
+ int i = n;
+
+ if (i > sp->s_len)
+ i = sp->s_len;
+ up = str;
+
+ for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++)
+ switch ((int)*t)
+ {
+ case '.' :
+ if (*s != *up)
+ return 1;
+ break;
+ case '?' :
+ if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f)))
+ return 1;
+ break;
+ case '*' :
+ break;
+ }
+ return 0;
+}
+
+
+/*
+ * Returns 3 if both server and client match, 2 if just server,
+ * 1 if just client
+ */
+int ipsc_matchisc(isc, is, cl, sl, maxm)
+ipscan_t *isc;
+ipstate_t *is;
+int cl, sl, maxm[2];
+{
+ int i, j, k, n, ret = 0, flags;
+
+ flags = is->is_flags;
+
+ /*
+ * If we've already matched more than what is on offer, then
+ * assume we have a better match already and forget this one.
+ */
+ if (maxm != NULL) {
+ if (isc->ipsc_clen < maxm[0])
+ return 0;
+ if (isc->ipsc_slen < maxm[1])
+ return 0;
+ j = maxm[0];
+ k = maxm[1];
+ } else {
+ j = 0;
+ k = 0;
+ }
+
+ if (!isc->ipsc_clen)
+ ret = 1;
+ else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) &&
+ cl && isc->ipsc_clen) {
+ i = 0;
+ n = MIN(cl, isc->ipsc_clen);
+ if ((n > 0) && (!maxm || (n >= maxm[1]))) {
+ if (!ipsc_matchstr(&isc->ipsc_cl, is->is_sbuf[0], n)) {
+ i++;
+ ret |= 1;
+ if (n > j)
+ j = n;
+ }
+ }
+ }
+
+ if (!isc->ipsc_slen)
+ ret |= 2;
+ else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) &&
+ sl && isc->ipsc_slen) {
+ i = 0;
+ n = MIN(cl, isc->ipsc_slen);
+ if ((n > 0) && (!maxm || (n >= maxm[1]))) {
+ if (!ipsc_matchstr(&isc->ipsc_sl, is->is_sbuf[1], n)) {
+ i++;
+ ret |= 2;
+ if (n > k)
+ k = n;
+ }
+ }
+ }
+
+ if (maxm && (ret == 3)) {
+ maxm[0] = j;
+ maxm[1] = k;
+ }
+ return ret;
+}
+
+
+int ipsc_match(is)
+ipstate_t *is;
+{
+ int i, j, k, n, cl, sl, maxm[2];
+ ipscan_t *isc, *lm;
+ tcpdata_t *t;
+
+ for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1)
+ cl++;
+ for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1)
+ sl++;
+
+ j = 0;
+ isc = is->is_isc;
+ if (isc != NULL) {
+ /*
+ * Known object to scan for.
+ */
+ i = ipsc_matchisc(isc, is, cl, sl, NULL);
+ if (i & 1) {
+ is->is_flags |= IS_SC_MATCHC;
+ is->is_flags &= ~IS_SC_CLIENT;
+ } else if (cl >= isc->ipsc_clen)
+ is->is_flags &= ~IS_SC_CLIENT;
+ if (i & 2) {
+ is->is_flags |= IS_SC_MATCHS;
+ is->is_flags &= ~IS_SC_SERVER;
+ } else if (sl >= isc->ipsc_slen)
+ is->is_flags &= ~IS_SC_SERVER;
+ } else {
+ i = 0;
+ lm = NULL;
+ maxm[0] = 0;
+ maxm[1] = 0;
+ for (k = 0, isc = ipsc_list; isc; isc = isc->ipsc_next) {
+ i = ipsc_matchisc(isc, is, cl, sl, maxm);
+ if (i) {
+ /*
+ * We only want to remember the best match
+ * and the number of times we get a best
+ * match.
+ */
+ if ((j == 3) && (i < 3))
+ continue;
+ if ((i == 3) && (j != 3))
+ k = 1;
+ else
+ k++;
+ j = i;
+ lm = isc;
+ }
+ }
+ if (k == 1)
+ isc = lm;
+
+ /*
+ * No matches or partial matches, so reset the respective
+ * search flag.
+ */
+ if (!(j & 1))
+ is->is_flags &= ~IS_SC_CLIENT;
+
+ if (!(j & 2))
+ is->is_flags &= ~IS_SC_SERVER;
+
+ /*
+ * If we found the best match, then set flags appropriately.
+ */
+ if ((j == 3) && (k == 1)) {
+ is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT);
+ is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC);
+ }
+ }
+
+ /*
+ * If the acknowledged side of a connection has moved past the data in
+ * which we are interested, then reset respective flag.
+ */
+ t = &is->is_tcp.ts_data[0];
+ if (t->td_end > is->is_s0[0] + 15)
+ is->is_flags &= ~IS_SC_CLIENT;
+
+ t = &is->is_tcp.ts_data[1];
+ if (t->td_end > is->is_s0[1] + 15)
+ is->is_flags &= ~IS_SC_SERVER;
+
+ /*
+ * Matching complete ?
+ */
+ j = ISC_A_NONE;
+ if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) {
+ j = isc->ipsc_action;
+ ipsc_stat.iscs_acted++;
+ } else if ((is->is_isc != NULL) &&
+ ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) &&
+ !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) {
+ /*
+ * Matching failed...
+ */
+ j = isc->ipsc_else;
+ ipsc_stat.iscs_else++;
+ }
+
+ switch (j)
+ {
+ case ISC_A_CLOSE :
+ /*
+ * If as a result of a successful match we are to
+ * close a connection, change the "keep state" info.
+ * to block packets and generate TCP RST's.
+ */
+ is->is_pass &= ~FR_RETICMP;
+ is->is_pass |= FR_RETRST;
+ break;
+ default :
+ break;
+ }
+
+ return i;
+}
+
+
+/*
+ * check if a packet matches what we're scanning for
+ */
+int ipsc_packet(fin, is)
+fr_info_t *fin;
+ipstate_t *is;
+{
+ int i, j, rv, dlen, off, thoff;
+ u_32_t seq, s0;
+ tcphdr_t *tcp;
+
+ rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src);
+ tcp = fin->fin_dp;
+ seq = ntohl(tcp->th_seq);
+
+ if (!is->is_s0[rv])
+ return 1;
+
+ /*
+ * check if this packet has more data that falls within the first
+ * 16 bytes sent in either direction.
+ */
+ s0 = is->is_s0[rv];
+ off = seq - s0;
+ if ((off > 15) || (off < 0))
+ return 1;
+ thoff = TCP_OFF(tcp) << 2;
+ dlen = fin->fin_dlen - thoff;
+ if (dlen <= 0)
+ return 1;
+ if (dlen > 16)
+ dlen = 16;
+ if (off + dlen > 16)
+ dlen = 16 - off;
+
+ j = 0xffff >> (16 - dlen);
+ i = (0xffff & j) << off;
+#ifdef _KERNEL
+ COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_hlen + thoff, dlen,
+ (caddr_t)is->is_sbuf[rv] + off);
+#endif
+ is->is_smsk[rv] |= i;
+ for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1)
+ j++;
+ if (j == 0)
+ return 1;
+
+ (void) ipsc_match(is);
+#if 0
+ /*
+ * There is the potential here for plain text passwords to get
+ * buffered and stored for some time...
+ */
+ if (!(is->is_flags & IS_SC_CLIENT))
+ bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0]));
+ if (!(is->is_flags & IS_SC_SERVER))
+ bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1]));
+#endif
+ return 0;
+}
+
+
+int fr_scan_ioctl(data, cmd, mode)
+caddr_t data;
+ioctlcmd_t cmd;
+int mode;
+{
+ ipscanstat_t ipscs;
+ int err = 0;
+
+ switch (cmd)
+ {
+ case SIOCADSCA :
+ err = ipsc_add(data);
+ break;
+ case SIOCRMSCA :
+ err = ipsc_delete(data);
+ break;
+ case SIOCGSCST :
+ bcopy((char *)&ipsc_stat, (char *)&ipscs, sizeof(ipscs));
+ ipscs.iscs_list = ipsc_list;
+ BCOPYOUT(&ipscs, data, sizeof(ipscs));
+ break;
+ default :
+ err = EINVAL;
+ break;
+ }
+
+ return err;
+}
+#endif /* IPFILTER_SCAN */
OpenPOWER on IntegriCloud