1145516Sdarrenr/*
2255332Scy * Copyright (C) 2012 by Darren Reed.
3145516Sdarrenr *
4145516Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
5145516Sdarrenr */
6145516Sdarrenr#if defined(KERNEL) || defined(_KERNEL)
7145516Sdarrenr# undef KERNEL
8145516Sdarrenr# undef _KERNEL
9145516Sdarrenr# define        KERNEL	1
10145516Sdarrenr# define        _KERNEL	1
11145516Sdarrenr#endif
12145516Sdarrenr#include <sys/param.h>
13145516Sdarrenr#if defined(__hpux) && (HPUXREV >= 1111) && !defined(_KERNEL)
14145516Sdarrenr# include <sys/kern_svcs.h>
15145516Sdarrenr#endif
16145516Sdarrenr#include <sys/types.h>
17145516Sdarrenr#include <sys/time.h>
18145516Sdarrenr#include <sys/errno.h>
19145516Sdarrenr#if !defined(_KERNEL)
20145516Sdarrenr# include <stdlib.h>
21145516Sdarrenr# include <string.h>
22145516Sdarrenr# define _KERNEL
23145516Sdarrenr# ifdef __OpenBSD__
24145516Sdarrenrstruct file;
25145516Sdarrenr# endif
26145516Sdarrenr# include <sys/uio.h>
27145516Sdarrenr# undef _KERNEL
28145516Sdarrenr#else
29145516Sdarrenr# include <sys/systm.h>
30145516Sdarrenr# if !defined(__svr4__) && !defined(__SVR4)
31145516Sdarrenr#  include <sys/mbuf.h>
32145516Sdarrenr# endif
33145516Sdarrenr#endif
34145516Sdarrenr#include <sys/socket.h>
35153872Sguido#if !defined(__hpux) && !defined(__osf__) && !defined(linux) && !defined(AIX)
36145516Sdarrenr# include <sys/ioccom.h>
37145516Sdarrenr#endif
38145516Sdarrenr#ifdef __FreeBSD__
39145516Sdarrenr# include <sys/filio.h>
40145516Sdarrenr# include <sys/malloc.h>
41145516Sdarrenr#else
42145516Sdarrenr# include <sys/ioctl.h>
43145516Sdarrenr#endif
44145516Sdarrenr
45145516Sdarrenr#include <netinet/in.h>
46145516Sdarrenr#include <netinet/in_systm.h>
47145516Sdarrenr#include <netinet/ip.h>
48145516Sdarrenr#include <netinet/tcp.h>
49145516Sdarrenr
50145516Sdarrenr#include <net/if.h>
51145516Sdarrenr
52145516Sdarrenr
53145516Sdarrenr#include "netinet/ip_compat.h"
54145516Sdarrenr#include "netinet/ip_fil.h"
55145516Sdarrenr#include "netinet/ip_state.h"
56145516Sdarrenr#include "netinet/ip_scan.h"
57145516Sdarrenr/* END OF INCLUDES */
58145516Sdarrenr
59145516Sdarrenr#if !defined(lint)
60145516Sdarrenrstatic const char sccsid[] = "@(#)ip_state.c	1.8 6/5/96 (C) 1993-2000 Darren Reed";
61255332Scystatic const char rcsid[] = "@(#)$Id$";
62145516Sdarrenr#endif
63145516Sdarrenr
64145516Sdarrenr#ifdef	IPFILTER_SCAN	/* endif at bottom of file */
65145516Sdarrenr
66145516Sdarrenr
67255332Scyipscan_t	*ipf_scan_list = NULL,
68255332Scy		*ipf_scan_tail = NULL;
69255332Scyipscanstat_t	ipf_scan_stat;
70145516Sdarrenr# ifdef USE_MUTEXES
71255332Scyipfrwlock_t	ipf_scan_rwlock;
72145516Sdarrenr# endif
73145516Sdarrenr
74145516Sdarrenr# ifndef isalpha
75145516Sdarrenr#  define	isalpha(x)	(((x) >= 'A' && 'Z' >= (x)) || \
76145516Sdarrenr				 ((x) >= 'a' && 'z' >= (x)))
77145516Sdarrenr# endif
78145516Sdarrenr
79145516Sdarrenr
80255332Scyint ipf_scan_add __P((caddr_t));
81255332Scyint ipf_scan_remove __P((caddr_t));
82255332Scystruct ipscan *ipf_scan_lookup __P((char *));
83255332Scyint ipf_scan_matchstr __P((sinfo_t *, char *, int));
84255332Scyint ipf_scan_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *));
85255332Scyint ipf_scan_match __P((ipstate_t *));
86145516Sdarrenr
87255332Scystatic int	ipf_scan_inited = 0;
88145516Sdarrenr
89145516Sdarrenr
90255332Scyint
91255332Scyipf_scan_init()
92145516Sdarrenr{
93255332Scy	RWLOCK_INIT(&ipf_scan_rwlock, "ip scan rwlock");
94255332Scy	ipf_scan_inited = 1;
95145516Sdarrenr	return 0;
96145516Sdarrenr}
97145516Sdarrenr
98145516Sdarrenr
99255332Scyvoid
100255332Scyipf_scan_unload(ipf_main_softc_t *arg)
101145516Sdarrenr{
102255332Scy	if (ipf_scan_inited == 1) {
103255332Scy		RW_DESTROY(&ipf_scan_rwlock);
104255332Scy		ipf_scan_inited = 0;
105161351Sguido	}
106145516Sdarrenr}
107145516Sdarrenr
108145516Sdarrenr
109255332Scyint
110255332Scyipf_scan_add(data)
111255332Scy	caddr_t data;
112145516Sdarrenr{
113145516Sdarrenr	ipscan_t *i, *isc;
114145516Sdarrenr	int err;
115145516Sdarrenr
116145516Sdarrenr	KMALLOC(isc, ipscan_t *);
117255332Scy	if (!isc) {
118255332Scy		ipf_interror = 90001;
119145516Sdarrenr		return ENOMEM;
120255332Scy	}
121145516Sdarrenr
122145516Sdarrenr	err = copyinptr(data, isc, sizeof(*isc));
123170263Sdarrenr	if (err) {
124170263Sdarrenr		KFREE(isc);
125145516Sdarrenr		return err;
126170263Sdarrenr	}
127145516Sdarrenr
128255332Scy	WRITE_ENTER(&ipf_scan_rwlock);
129145516Sdarrenr
130255332Scy	i = ipf_scan_lookup(isc->ipsc_tag);
131255332Scy	if (i != NULL) {
132255332Scy		RWLOCK_EXIT(&ipf_scan_rwlock);
133145516Sdarrenr		KFREE(isc);
134255332Scy		ipf_interror = 90002;
135145516Sdarrenr		return EEXIST;
136145516Sdarrenr	}
137145516Sdarrenr
138255332Scy	if (ipf_scan_tail) {
139255332Scy		ipf_scan_tail->ipsc_next = isc;
140255332Scy		isc->ipsc_pnext = &ipf_scan_tail->ipsc_next;
141255332Scy		ipf_scan_tail = isc;
142145516Sdarrenr	} else {
143255332Scy		ipf_scan_list = isc;
144255332Scy		ipf_scan_tail = isc;
145255332Scy		isc->ipsc_pnext = &ipf_scan_list;
146145516Sdarrenr	}
147145516Sdarrenr	isc->ipsc_next = NULL;
148145516Sdarrenr
149145516Sdarrenr	isc->ipsc_hits = 0;
150145516Sdarrenr	isc->ipsc_fref = 0;
151145516Sdarrenr	isc->ipsc_sref = 0;
152145516Sdarrenr	isc->ipsc_active = 0;
153145516Sdarrenr
154255332Scy	ipf_scan_stat.iscs_entries++;
155255332Scy	RWLOCK_EXIT(&ipf_scan_rwlock);
156145516Sdarrenr	return 0;
157145516Sdarrenr}
158145516Sdarrenr
159145516Sdarrenr
160255332Scyint
161255332Scyipf_scan_remove(data)
162255332Scy	caddr_t data;
163145516Sdarrenr{
164145516Sdarrenr	ipscan_t isc, *i;
165145516Sdarrenr	int err;
166145516Sdarrenr
167145516Sdarrenr	err = copyinptr(data, &isc, sizeof(isc));
168145516Sdarrenr	if (err)
169145516Sdarrenr		return err;
170145516Sdarrenr
171255332Scy	WRITE_ENTER(&ipf_scan_rwlock);
172145516Sdarrenr
173255332Scy	i = ipf_scan_lookup(isc.ipsc_tag);
174145516Sdarrenr	if (i == NULL)
175145516Sdarrenr		err = ENOENT;
176145516Sdarrenr	else {
177145516Sdarrenr		if (i->ipsc_fref) {
178255332Scy			RWLOCK_EXIT(&ipf_scan_rwlock);
179255332Scy			ipf_interror = 90003;
180145516Sdarrenr			return EBUSY;
181145516Sdarrenr		}
182145516Sdarrenr
183145516Sdarrenr		*i->ipsc_pnext = i->ipsc_next;
184145516Sdarrenr		if (i->ipsc_next)
185145516Sdarrenr			i->ipsc_next->ipsc_pnext = i->ipsc_pnext;
186145516Sdarrenr		else {
187255332Scy			if (i->ipsc_pnext == &ipf_scan_list)
188255332Scy				ipf_scan_tail = NULL;
189145516Sdarrenr			else
190255332Scy				ipf_scan_tail = *(*i->ipsc_pnext)->ipsc_pnext;
191145516Sdarrenr		}
192145516Sdarrenr
193255332Scy		ipf_scan_stat.iscs_entries--;
194145516Sdarrenr		KFREE(i);
195145516Sdarrenr	}
196255332Scy	RWLOCK_EXIT(&ipf_scan_rwlock);
197145516Sdarrenr	return err;
198145516Sdarrenr}
199145516Sdarrenr
200145516Sdarrenr
201255332Scystruct ipscan *
202255332Scyipf_scan_lookup(tag)
203255332Scy	char *tag;
204145516Sdarrenr{
205145516Sdarrenr	ipscan_t *i;
206145516Sdarrenr
207255332Scy	for (i = ipf_scan_list; i; i = i->ipsc_next)
208145516Sdarrenr		if (!strcmp(i->ipsc_tag, tag))
209145516Sdarrenr			return i;
210145516Sdarrenr	return NULL;
211145516Sdarrenr}
212145516Sdarrenr
213145516Sdarrenr
214255332Scyint
215255332Scyipf_scan_attachfr(fr)
216255332Scy	struct frentry *fr;
217145516Sdarrenr{
218145516Sdarrenr	ipscan_t *i;
219145516Sdarrenr
220255332Scy	if (fr->fr_isctag != -1) {
221255332Scy		READ_ENTER(&ipf_scan_rwlock);
222255332Scy		i = ipf_scan_lookup(fr->fr_isctag + fr->fr_names);
223145516Sdarrenr		if (i != NULL) {
224145516Sdarrenr			ATOMIC_INC32(i->ipsc_fref);
225145516Sdarrenr		}
226255332Scy		RWLOCK_EXIT(&ipf_scan_rwlock);
227255332Scy		if (i == NULL) {
228255332Scy			ipf_interror = 90004;
229145516Sdarrenr			return ENOENT;
230255332Scy		}
231145516Sdarrenr		fr->fr_isc = i;
232145516Sdarrenr	}
233145516Sdarrenr	return 0;
234145516Sdarrenr}
235145516Sdarrenr
236145516Sdarrenr
237255332Scyint
238255332Scyipf_scan_attachis(is)
239255332Scy	struct ipstate *is;
240145516Sdarrenr{
241145516Sdarrenr	frentry_t *fr;
242145516Sdarrenr	ipscan_t *i;
243145516Sdarrenr
244255332Scy	READ_ENTER(&ipf_scan_rwlock);
245145516Sdarrenr	fr = is->is_rule;
246255332Scy	if (fr != NULL) {
247145516Sdarrenr		i = fr->fr_isc;
248170263Sdarrenr		if ((i != NULL) && (i != (ipscan_t *)-1)) {
249145516Sdarrenr			is->is_isc = i;
250170263Sdarrenr			ATOMIC_INC32(i->ipsc_sref);
251170263Sdarrenr			if (i->ipsc_clen)
252170263Sdarrenr				is->is_flags |= IS_SC_CLIENT;
253170263Sdarrenr			else
254170263Sdarrenr				is->is_flags |= IS_SC_MATCHC;
255170263Sdarrenr			if (i->ipsc_slen)
256170263Sdarrenr				is->is_flags |= IS_SC_SERVER;
257170263Sdarrenr			else
258170263Sdarrenr				is->is_flags |= IS_SC_MATCHS;
259145516Sdarrenr		}
260145516Sdarrenr	}
261255332Scy	RWLOCK_EXIT(&ipf_scan_rwlock);
262145516Sdarrenr	return 0;
263145516Sdarrenr}
264145516Sdarrenr
265145516Sdarrenr
266255332Scyint
267255332Scyipf_scan_detachfr(fr)
268255332Scy	struct frentry *fr;
269145516Sdarrenr{
270145516Sdarrenr	ipscan_t *i;
271145516Sdarrenr
272145516Sdarrenr	i = fr->fr_isc;
273145516Sdarrenr	if (i != NULL) {
274145516Sdarrenr		ATOMIC_DEC32(i->ipsc_fref);
275145516Sdarrenr	}
276145516Sdarrenr	return 0;
277145516Sdarrenr}
278145516Sdarrenr
279145516Sdarrenr
280255332Scyint
281255332Scyipf_scan_detachis(is)
282255332Scy	struct ipstate *is;
283145516Sdarrenr{
284145516Sdarrenr	ipscan_t *i;
285145516Sdarrenr
286255332Scy	READ_ENTER(&ipf_scan_rwlock);
287145516Sdarrenr	if ((i = is->is_isc) && (i != (ipscan_t *)-1)) {
288145516Sdarrenr		ATOMIC_DEC32(i->ipsc_sref);
289145516Sdarrenr		is->is_isc = NULL;
290145516Sdarrenr		is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER);
291145516Sdarrenr	}
292255332Scy	RWLOCK_EXIT(&ipf_scan_rwlock);
293145516Sdarrenr	return 0;
294145516Sdarrenr}
295145516Sdarrenr
296145516Sdarrenr
297145516Sdarrenr/*
298145516Sdarrenr * 'string' compare for scanning
299145516Sdarrenr */
300255332Scyint
301255332Scyipf_scan_matchstr(sp, str, n)
302255332Scy	sinfo_t *sp;
303255332Scy	char *str;
304255332Scy	int n;
305145516Sdarrenr{
306145516Sdarrenr	char *s, *t, *up;
307145516Sdarrenr	int i = n;
308145516Sdarrenr
309145516Sdarrenr	if (i > sp->s_len)
310145516Sdarrenr		i = sp->s_len;
311145516Sdarrenr	up = str;
312145516Sdarrenr
313145516Sdarrenr	for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++)
314145516Sdarrenr		switch ((int)*t)
315145516Sdarrenr		{
316145516Sdarrenr		case '.' :
317145516Sdarrenr			if (*s != *up)
318145516Sdarrenr				return 1;
319145516Sdarrenr			break;
320145516Sdarrenr		case '?' :
321145516Sdarrenr			if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f)))
322145516Sdarrenr				return 1;
323145516Sdarrenr			break;
324145516Sdarrenr		case '*' :
325145516Sdarrenr			break;
326145516Sdarrenr		}
327145516Sdarrenr	return 0;
328145516Sdarrenr}
329145516Sdarrenr
330145516Sdarrenr
331145516Sdarrenr/*
332145516Sdarrenr * Returns 3 if both server and client match, 2 if just server,
333145516Sdarrenr * 1 if just client
334145516Sdarrenr */
335255332Scyint
336255332Scyipf_scan_matchisc(isc, is, cl, sl, maxm)
337255332Scy	ipscan_t *isc;
338255332Scy	ipstate_t *is;
339255332Scy	int cl, sl, maxm[2];
340145516Sdarrenr{
341145516Sdarrenr	int i, j, k, n, ret = 0, flags;
342145516Sdarrenr
343145516Sdarrenr	flags = is->is_flags;
344145516Sdarrenr
345145516Sdarrenr	/*
346145516Sdarrenr	 * If we've already matched more than what is on offer, then
347145516Sdarrenr	 * assume we have a better match already and forget this one.
348145516Sdarrenr	 */
349145516Sdarrenr	if (maxm != NULL) {
350145516Sdarrenr		if (isc->ipsc_clen < maxm[0])
351145516Sdarrenr			return 0;
352145516Sdarrenr		if (isc->ipsc_slen < maxm[1])
353145516Sdarrenr			return 0;
354145516Sdarrenr		j = maxm[0];
355145516Sdarrenr		k = maxm[1];
356145516Sdarrenr	} else {
357145516Sdarrenr		j = 0;
358145516Sdarrenr		k = 0;
359145516Sdarrenr	}
360145516Sdarrenr
361145516Sdarrenr	if (!isc->ipsc_clen)
362145516Sdarrenr		ret = 1;
363145516Sdarrenr	else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) &&
364145516Sdarrenr		 cl && isc->ipsc_clen) {
365145516Sdarrenr		i = 0;
366145516Sdarrenr		n = MIN(cl, isc->ipsc_clen);
367145516Sdarrenr		if ((n > 0) && (!maxm || (n >= maxm[1]))) {
368255332Scy			if (!ipf_scan_matchstr(&isc->ipsc_cl,
369255332Scy					       is->is_sbuf[0], n)) {
370145516Sdarrenr				i++;
371145516Sdarrenr				ret |= 1;
372145516Sdarrenr				if (n > j)
373145516Sdarrenr					j = n;
374145516Sdarrenr			}
375145516Sdarrenr		}
376145516Sdarrenr	}
377145516Sdarrenr
378145516Sdarrenr	if (!isc->ipsc_slen)
379145516Sdarrenr		ret |= 2;
380145516Sdarrenr	else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) &&
381145516Sdarrenr		 sl && isc->ipsc_slen) {
382145516Sdarrenr		i = 0;
383145516Sdarrenr		n = MIN(cl, isc->ipsc_slen);
384145516Sdarrenr		if ((n > 0) && (!maxm || (n >= maxm[1]))) {
385255332Scy			if (!ipf_scan_matchstr(&isc->ipsc_sl,
386255332Scy					       is->is_sbuf[1], n)) {
387145516Sdarrenr				i++;
388145516Sdarrenr				ret |= 2;
389145516Sdarrenr				if (n > k)
390145516Sdarrenr					k = n;
391145516Sdarrenr			}
392145516Sdarrenr		}
393145516Sdarrenr	}
394145516Sdarrenr
395145516Sdarrenr	if (maxm && (ret == 3)) {
396145516Sdarrenr		maxm[0] = j;
397145516Sdarrenr		maxm[1] = k;
398145516Sdarrenr	}
399145516Sdarrenr	return ret;
400145516Sdarrenr}
401145516Sdarrenr
402145516Sdarrenr
403255332Scyint
404255332Scyipf_scan_match(is)
405255332Scy	ipstate_t *is;
406145516Sdarrenr{
407145516Sdarrenr	int i, j, k, n, cl, sl, maxm[2];
408145516Sdarrenr	ipscan_t *isc, *lm;
409145516Sdarrenr	tcpdata_t *t;
410145516Sdarrenr
411145516Sdarrenr	for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1)
412145516Sdarrenr		cl++;
413145516Sdarrenr	for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1)
414145516Sdarrenr		sl++;
415145516Sdarrenr
416145516Sdarrenr	j = 0;
417145516Sdarrenr	isc = is->is_isc;
418145516Sdarrenr	if (isc != NULL) {
419145516Sdarrenr		/*
420145516Sdarrenr		 * Known object to scan for.
421145516Sdarrenr		 */
422255332Scy		i = ipf_scan_matchisc(isc, is, cl, sl, NULL);
423145516Sdarrenr		if (i & 1) {
424145516Sdarrenr			is->is_flags |= IS_SC_MATCHC;
425145516Sdarrenr			is->is_flags &= ~IS_SC_CLIENT;
426145516Sdarrenr		} else if (cl >= isc->ipsc_clen)
427145516Sdarrenr			is->is_flags &= ~IS_SC_CLIENT;
428145516Sdarrenr		if (i & 2) {
429145516Sdarrenr			is->is_flags |= IS_SC_MATCHS;
430145516Sdarrenr			is->is_flags &= ~IS_SC_SERVER;
431145516Sdarrenr		} else if (sl >= isc->ipsc_slen)
432145516Sdarrenr			is->is_flags &= ~IS_SC_SERVER;
433145516Sdarrenr	} else {
434145516Sdarrenr		i = 0;
435145516Sdarrenr		lm = NULL;
436145516Sdarrenr		maxm[0] = 0;
437145516Sdarrenr		maxm[1] = 0;
438255332Scy		for (k = 0, isc = ipf_scan_list; isc; isc = isc->ipsc_next) {
439255332Scy			i = ipf_scan_matchisc(isc, is, cl, sl, maxm);
440145516Sdarrenr			if (i) {
441145516Sdarrenr				/*
442145516Sdarrenr				 * We only want to remember the best match
443145516Sdarrenr				 * and the number of times we get a best
444145516Sdarrenr				 * match.
445145516Sdarrenr				 */
446145516Sdarrenr				if ((j == 3) && (i < 3))
447145516Sdarrenr					continue;
448145516Sdarrenr				if ((i == 3) && (j != 3))
449145516Sdarrenr					k = 1;
450145516Sdarrenr				else
451145516Sdarrenr					k++;
452145516Sdarrenr				j = i;
453145516Sdarrenr				lm = isc;
454145516Sdarrenr			}
455145516Sdarrenr		}
456145516Sdarrenr		if (k == 1)
457145516Sdarrenr			isc = lm;
458161351Sguido		if (isc == NULL)
459161351Sguido			return 0;
460145516Sdarrenr
461145516Sdarrenr		/*
462145516Sdarrenr		 * No matches or partial matches, so reset the respective
463145516Sdarrenr		 * search flag.
464145516Sdarrenr		 */
465145516Sdarrenr		if (!(j & 1))
466145516Sdarrenr			is->is_flags &= ~IS_SC_CLIENT;
467145516Sdarrenr
468145516Sdarrenr		if (!(j & 2))
469145516Sdarrenr			is->is_flags &= ~IS_SC_SERVER;
470145516Sdarrenr
471145516Sdarrenr		/*
472145516Sdarrenr		 * If we found the best match, then set flags appropriately.
473145516Sdarrenr		 */
474145516Sdarrenr		if ((j == 3) && (k == 1)) {
475145516Sdarrenr			is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT);
476145516Sdarrenr			is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC);
477145516Sdarrenr		}
478145516Sdarrenr	}
479145516Sdarrenr
480145516Sdarrenr	/*
481145516Sdarrenr	 * If the acknowledged side of a connection has moved past the data in
482145516Sdarrenr	 * which we are interested, then reset respective flag.
483145516Sdarrenr	 */
484145516Sdarrenr	t = &is->is_tcp.ts_data[0];
485145516Sdarrenr	if (t->td_end > is->is_s0[0] + 15)
486145516Sdarrenr		is->is_flags &= ~IS_SC_CLIENT;
487145516Sdarrenr
488145516Sdarrenr	t = &is->is_tcp.ts_data[1];
489145516Sdarrenr	if (t->td_end > is->is_s0[1] + 15)
490145516Sdarrenr		is->is_flags &= ~IS_SC_SERVER;
491145516Sdarrenr
492145516Sdarrenr	/*
493145516Sdarrenr	 * Matching complete ?
494145516Sdarrenr	 */
495145516Sdarrenr	j = ISC_A_NONE;
496145516Sdarrenr	if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) {
497145516Sdarrenr		j = isc->ipsc_action;
498255332Scy		ipf_scan_stat.iscs_acted++;
499145516Sdarrenr	} else if ((is->is_isc != NULL) &&
500145516Sdarrenr		   ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) &&
501145516Sdarrenr		   !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) {
502145516Sdarrenr		/*
503145516Sdarrenr		 * Matching failed...
504145516Sdarrenr		 */
505145516Sdarrenr		j = isc->ipsc_else;
506255332Scy		ipf_scan_stat.iscs_else++;
507145516Sdarrenr	}
508145516Sdarrenr
509145516Sdarrenr	switch (j)
510145516Sdarrenr	{
511145516Sdarrenr	case  ISC_A_CLOSE :
512145516Sdarrenr		/*
513145516Sdarrenr		 * If as a result of a successful match we are to
514145516Sdarrenr		 * close a connection, change the "keep state" info.
515145516Sdarrenr		 * to block packets and generate TCP RST's.
516145516Sdarrenr		 */
517145516Sdarrenr		is->is_pass &= ~FR_RETICMP;
518145516Sdarrenr		is->is_pass |= FR_RETRST;
519145516Sdarrenr		break;
520145516Sdarrenr	default :
521145516Sdarrenr		break;
522145516Sdarrenr	}
523145516Sdarrenr
524145516Sdarrenr	return i;
525145516Sdarrenr}
526145516Sdarrenr
527145516Sdarrenr
528145516Sdarrenr/*
529145516Sdarrenr * check if a packet matches what we're scanning for
530145516Sdarrenr */
531255332Scyint
532255332Scyipf_scan_packet(fin, is)
533255332Scy	fr_info_t *fin;
534255332Scy	ipstate_t *is;
535145516Sdarrenr{
536145516Sdarrenr	int i, j, rv, dlen, off, thoff;
537145516Sdarrenr	u_32_t seq, s0;
538145516Sdarrenr	tcphdr_t *tcp;
539145516Sdarrenr
540145516Sdarrenr	rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src);
541145516Sdarrenr	tcp = fin->fin_dp;
542145516Sdarrenr	seq = ntohl(tcp->th_seq);
543145516Sdarrenr
544145516Sdarrenr	if (!is->is_s0[rv])
545145516Sdarrenr		return 1;
546145516Sdarrenr
547145516Sdarrenr	/*
548145516Sdarrenr	 * check if this packet has more data that falls within the first
549145516Sdarrenr	 * 16 bytes sent in either direction.
550145516Sdarrenr	 */
551145516Sdarrenr	s0 = is->is_s0[rv];
552145516Sdarrenr	off = seq - s0;
553145516Sdarrenr	if ((off > 15) || (off < 0))
554145516Sdarrenr		return 1;
555145516Sdarrenr	thoff = TCP_OFF(tcp) << 2;
556145516Sdarrenr	dlen = fin->fin_dlen - thoff;
557145516Sdarrenr	if (dlen <= 0)
558145516Sdarrenr		return 1;
559145516Sdarrenr	if (dlen > 16)
560145516Sdarrenr		dlen = 16;
561145516Sdarrenr	if (off + dlen > 16)
562145516Sdarrenr		dlen = 16 - off;
563145516Sdarrenr
564145516Sdarrenr	j = 0xffff >> (16 - dlen);
565145516Sdarrenr	i = (0xffff & j) << off;
566145516Sdarrenr#ifdef _KERNEL
567153872Sguido	COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_plen - fin->fin_dlen + thoff,
568153872Sguido		 dlen, (caddr_t)is->is_sbuf[rv] + off);
569145516Sdarrenr#endif
570145516Sdarrenr	is->is_smsk[rv] |= i;
571145516Sdarrenr	for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1)
572145516Sdarrenr		j++;
573145516Sdarrenr	if (j == 0)
574145516Sdarrenr		return 1;
575145516Sdarrenr
576255332Scy	(void) ipf_scan_match(is);
577145516Sdarrenr#if 0
578145516Sdarrenr	/*
579145516Sdarrenr	 * There is the potential here for plain text passwords to get
580145516Sdarrenr	 * buffered and stored for some time...
581145516Sdarrenr	 */
582145516Sdarrenr	if (!(is->is_flags & IS_SC_CLIENT))
583145516Sdarrenr		bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0]));
584145516Sdarrenr	if (!(is->is_flags & IS_SC_SERVER))
585145516Sdarrenr		bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1]));
586145516Sdarrenr#endif
587145516Sdarrenr	return 0;
588145516Sdarrenr}
589145516Sdarrenr
590145516Sdarrenr
591255332Scyint
592255332Scyipf_scan_ioctl(data, cmd, mode, uid, ctx)
593255332Scy	caddr_t data;
594255332Scy	ioctlcmd_t cmd;
595255332Scy	int mode, uid;
596255332Scy	void *ctx;
597145516Sdarrenr{
598145516Sdarrenr	ipscanstat_t ipscs;
599145516Sdarrenr	int err = 0;
600145516Sdarrenr
601145516Sdarrenr	switch (cmd)
602145516Sdarrenr	{
603145516Sdarrenr	case SIOCADSCA :
604255332Scy		err = ipf_scan_add(data);
605145516Sdarrenr		break;
606145516Sdarrenr	case SIOCRMSCA :
607255332Scy		err = ipf_scan_remove(data);
608145516Sdarrenr		break;
609145516Sdarrenr	case SIOCGSCST :
610255332Scy		bcopy((char *)&ipf_scan_stat, (char *)&ipscs, sizeof(ipscs));
611255332Scy		ipscs.iscs_list = ipf_scan_list;
612172771Sdarrenr		err = BCOPYOUT(&ipscs, data, sizeof(ipscs));
613255332Scy		if (err != 0) {
614255332Scy			ipf_interror = 90005;
615172771Sdarrenr			err = EFAULT;
616255332Scy		}
617145516Sdarrenr		break;
618145516Sdarrenr	default :
619145516Sdarrenr		err = EINVAL;
620145516Sdarrenr		break;
621145516Sdarrenr	}
622145516Sdarrenr
623145516Sdarrenr	return err;
624145516Sdarrenr}
625145516Sdarrenr#endif	/* IPFILTER_SCAN */
626