2 * Copyright (C) 1995-2001 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
6 #if defined(KERNEL) || defined(_KERNEL)
12 #include <sys/param.h>
13 #if defined(__hpux) && (HPUXREV >= 1111) && !defined(_KERNEL)
14 # include <sys/kern_svcs.h>
16 #include <sys/types.h>
18 #include <sys/errno.h>
29 # include <sys/systm.h>
30 # if !defined(__svr4__) && !defined(__SVR4)
31 # include <sys/mbuf.h>
34 #include <sys/socket.h>
35 #if !defined(__hpux) && !defined(__osf__) && !defined(linux) && !defined(AIX)
36 # include <sys/ioccom.h>
39 # include <sys/filio.h>
40 # include <sys/malloc.h>
42 # include <sys/ioctl.h>
45 #include <netinet/in.h>
46 #include <netinet/in_systm.h>
47 #include <netinet/ip.h>
48 #include <netinet/tcp.h>
53 #include "netinet/ip_compat.h"
54 #include "netinet/ip_fil.h"
55 #include "netinet/ip_state.h"
56 #include "netinet/ip_scan.h"
60 static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed";
61 static const char rcsid[] = "@(#)$Id: ip_scan.c,v 2.40.2.10 2007/06/02 21:22:28 darrenr Exp $";
64 #ifdef IPFILTER_SCAN /* endif at bottom of file */
67 ipscan_t *ipsc_list = NULL,
69 ipscanstat_t ipsc_stat;
71 ipfrwlock_t ipsc_rwlock;
75 # define isalpha(x) (((x) >= 'A' && 'Z' >= (x)) || \
76 ((x) >= 'a' && 'z' >= (x)))
80 int ipsc_add __P((caddr_t));
81 int ipsc_delete __P((caddr_t));
82 struct ipscan *ipsc_lookup __P((char *));
83 int ipsc_matchstr __P((sinfo_t *, char *, int));
84 int ipsc_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *));
85 int ipsc_match __P((ipstate_t *));
87 static int ipsc_inited = 0;
92 RWLOCK_INIT(&ipsc_rwlock, "ip scan rwlock");
100 if (ipsc_inited == 1) {
101 RW_DESTROY(&ipsc_rwlock);
113 KMALLOC(isc, ipscan_t *);
117 err = copyinptr(data, isc, sizeof(*isc));
123 WRITE_ENTER(&ipsc_rwlock);
125 i = ipsc_lookup(isc->ipsc_tag);
127 RWLOCK_EXIT(&ipsc_rwlock);
133 ipsc_tail->ipsc_next = isc;
134 isc->ipsc_pnext = &ipsc_tail->ipsc_next;
139 isc->ipsc_pnext = &ipsc_list;
141 isc->ipsc_next = NULL;
146 isc->ipsc_active = 0;
148 ipsc_stat.iscs_entries++;
149 RWLOCK_EXIT(&ipsc_rwlock);
154 int ipsc_delete(data)
160 err = copyinptr(data, &isc, sizeof(isc));
164 WRITE_ENTER(&ipsc_rwlock);
166 i = ipsc_lookup(isc.ipsc_tag);
171 RWLOCK_EXIT(&ipsc_rwlock);
175 *i->ipsc_pnext = i->ipsc_next;
177 i->ipsc_next->ipsc_pnext = i->ipsc_pnext;
179 if (i->ipsc_pnext == &ipsc_list)
182 ipsc_tail = *(*i->ipsc_pnext)->ipsc_pnext;
185 ipsc_stat.iscs_entries--;
188 RWLOCK_EXIT(&ipsc_rwlock);
193 struct ipscan *ipsc_lookup(tag)
198 for (i = ipsc_list; i; i = i->ipsc_next)
199 if (!strcmp(i->ipsc_tag, tag))
205 int ipsc_attachfr(fr)
210 if (fr->fr_isctag[0]) {
211 READ_ENTER(&ipsc_rwlock);
212 i = ipsc_lookup(fr->fr_isctag);
214 ATOMIC_INC32(i->ipsc_fref);
216 RWLOCK_EXIT(&ipsc_rwlock);
225 int ipsc_attachis(is)
231 READ_ENTER(&ipsc_rwlock);
235 if ((i != NULL) && (i != (ipscan_t *)-1)) {
237 ATOMIC_INC32(i->ipsc_sref);
239 is->is_flags |= IS_SC_CLIENT;
241 is->is_flags |= IS_SC_MATCHC;
243 is->is_flags |= IS_SC_SERVER;
245 is->is_flags |= IS_SC_MATCHS;
248 RWLOCK_EXIT(&ipsc_rwlock);
253 int ipsc_detachfr(fr)
260 ATOMIC_DEC32(i->ipsc_fref);
266 int ipsc_detachis(is)
271 READ_ENTER(&ipsc_rwlock);
272 if ((i = is->is_isc) && (i != (ipscan_t *)-1)) {
273 ATOMIC_DEC32(i->ipsc_sref);
275 is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER);
277 RWLOCK_EXIT(&ipsc_rwlock);
283 * 'string' compare for scanning
285 int ipsc_matchstr(sp, str, n)
297 for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++)
305 if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f)))
316 * Returns 3 if both server and client match, 2 if just server,
319 int ipsc_matchisc(isc, is, cl, sl, maxm)
324 int i, j, k, n, ret = 0, flags;
326 flags = is->is_flags;
329 * If we've already matched more than what is on offer, then
330 * assume we have a better match already and forget this one.
333 if (isc->ipsc_clen < maxm[0])
335 if (isc->ipsc_slen < maxm[1])
346 else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) &&
347 cl && isc->ipsc_clen) {
349 n = MIN(cl, isc->ipsc_clen);
350 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
351 if (!ipsc_matchstr(&isc->ipsc_cl, is->is_sbuf[0], n)) {
362 else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) &&
363 sl && isc->ipsc_slen) {
365 n = MIN(cl, isc->ipsc_slen);
366 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
367 if (!ipsc_matchstr(&isc->ipsc_sl, is->is_sbuf[1], n)) {
376 if (maxm && (ret == 3)) {
387 int i, j, k, n, cl, sl, maxm[2];
391 for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1)
393 for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1)
400 * Known object to scan for.
402 i = ipsc_matchisc(isc, is, cl, sl, NULL);
404 is->is_flags |= IS_SC_MATCHC;
405 is->is_flags &= ~IS_SC_CLIENT;
406 } else if (cl >= isc->ipsc_clen)
407 is->is_flags &= ~IS_SC_CLIENT;
409 is->is_flags |= IS_SC_MATCHS;
410 is->is_flags &= ~IS_SC_SERVER;
411 } else if (sl >= isc->ipsc_slen)
412 is->is_flags &= ~IS_SC_SERVER;
418 for (k = 0, isc = ipsc_list; isc; isc = isc->ipsc_next) {
419 i = ipsc_matchisc(isc, is, cl, sl, maxm);
422 * We only want to remember the best match
423 * and the number of times we get a best
426 if ((j == 3) && (i < 3))
428 if ((i == 3) && (j != 3))
442 * No matches or partial matches, so reset the respective
446 is->is_flags &= ~IS_SC_CLIENT;
449 is->is_flags &= ~IS_SC_SERVER;
452 * If we found the best match, then set flags appropriately.
454 if ((j == 3) && (k == 1)) {
455 is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT);
456 is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC);
461 * If the acknowledged side of a connection has moved past the data in
462 * which we are interested, then reset respective flag.
464 t = &is->is_tcp.ts_data[0];
465 if (t->td_end > is->is_s0[0] + 15)
466 is->is_flags &= ~IS_SC_CLIENT;
468 t = &is->is_tcp.ts_data[1];
469 if (t->td_end > is->is_s0[1] + 15)
470 is->is_flags &= ~IS_SC_SERVER;
473 * Matching complete ?
476 if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) {
477 j = isc->ipsc_action;
478 ipsc_stat.iscs_acted++;
479 } else if ((is->is_isc != NULL) &&
480 ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) &&
481 !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) {
486 ipsc_stat.iscs_else++;
493 * If as a result of a successful match we are to
494 * close a connection, change the "keep state" info.
495 * to block packets and generate TCP RST's.
497 is->is_pass &= ~FR_RETICMP;
498 is->is_pass |= FR_RETRST;
509 * check if a packet matches what we're scanning for
511 int ipsc_packet(fin, is)
515 int i, j, rv, dlen, off, thoff;
519 rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src);
521 seq = ntohl(tcp->th_seq);
527 * check if this packet has more data that falls within the first
528 * 16 bytes sent in either direction.
532 if ((off > 15) || (off < 0))
534 thoff = TCP_OFF(tcp) << 2;
535 dlen = fin->fin_dlen - thoff;
543 j = 0xffff >> (16 - dlen);
544 i = (0xffff & j) << off;
546 COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_plen - fin->fin_dlen + thoff,
547 dlen, (caddr_t)is->is_sbuf[rv] + off);
549 is->is_smsk[rv] |= i;
550 for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1)
555 (void) ipsc_match(is);
558 * There is the potential here for plain text passwords to get
559 * buffered and stored for some time...
561 if (!(is->is_flags & IS_SC_CLIENT))
562 bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0]));
563 if (!(is->is_flags & IS_SC_SERVER))
564 bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1]));
570 int fr_scan_ioctl(data, cmd, mode, uid, ctx)
582 err = ipsc_add(data);
585 err = ipsc_delete(data);
588 bcopy((char *)&ipsc_stat, (char *)&ipscs, sizeof(ipscs));
589 ipscs.iscs_list = ipsc_list;
590 err = BCOPYOUT(&ipscs, data, sizeof(ipscs));
601 #endif /* IPFILTER_SCAN */