Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / ipf / ip_scan.c
blob1b05af90da5fa820824c3451b408878d80f50915
1 /* $NetBSD: ip_scan.c,v 1.2 2007/06/16 10:52:25 martin Exp $ */
3 /*
4 * Copyright (C) 1995-2001 by Darren Reed.
6 * See the IPFILTER.LICENCE file for details on licencing.
7 */
8 #if defined(KERNEL) || defined(_KERNEL)
9 # undef KERNEL
10 # undef _KERNEL
11 # define KERNEL 1
12 # define _KERNEL 1
13 #endif
14 #include <sys/param.h>
15 #if defined(__hpux) && (HPUXREV >= 1111) && !defined(_KERNEL)
16 # include <sys/kern_svcs.h>
17 #endif
18 #include <sys/types.h>
19 #include <sys/time.h>
20 #include <sys/errno.h>
21 #if !defined(_KERNEL)
22 # include <stdlib.h>
23 # include <string.h>
24 # define _KERNEL
25 # ifdef __OpenBSD__
26 struct file;
27 # endif
28 # include <sys/uio.h>
29 # undef _KERNEL
30 #else
31 # include <sys/systm.h>
32 # if !defined(__svr4__) && !defined(__SVR4)
33 # include <sys/mbuf.h>
34 # endif
35 #endif
36 #include <sys/socket.h>
37 #if !defined(__hpux) && !defined(__osf__) && !defined(linux) && !defined(AIX)
38 # include <sys/ioccom.h>
39 #endif
40 #ifdef __FreeBSD__
41 # include <sys/filio.h>
42 # include <sys/malloc.h>
43 #else
44 # include <sys/ioctl.h>
45 #endif
47 #include <netinet/in.h>
48 #include <netinet/in_systm.h>
49 #include <netinet/ip.h>
50 #include <netinet/tcp.h>
52 #include <net/if.h>
55 #include "netinet/ip_compat.h"
56 #include "netinet/ip_fil.h"
57 #include "netinet/ip_state.h"
58 #include "netinet/ip_scan.h"
59 /* END OF INCLUDES */
61 #if !defined(lint)
62 static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed";
63 static const char rcsid[] = "@(#)Id: ip_scan.c,v 2.40.2.10 2007/06/02 21:22:28 darrenr Exp";
64 #endif
66 #ifdef IPFILTER_SCAN /* endif at bottom of file */
69 ipscan_t *ipsc_list = NULL,
70 *ipsc_tail = NULL;
71 ipscanstat_t ipsc_stat;
72 # ifdef USE_MUTEXES
73 ipfrwlock_t ipsc_rwlock;
74 # endif
76 # ifndef isalpha
77 # define isalpha(x) (((x) >= 'A' && 'Z' >= (x)) || \
78 ((x) >= 'a' && 'z' >= (x)))
79 # endif
82 int ipsc_add __P((caddr_t));
83 int ipsc_delete __P((caddr_t));
84 struct ipscan *ipsc_lookup __P((char *));
85 int ipsc_matchstr __P((sinfo_t *, char *, int));
86 int ipsc_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *));
87 int ipsc_match __P((ipstate_t *));
89 static int ipsc_inited = 0;
92 int ipsc_init()
94 RWLOCK_INIT(&ipsc_rwlock, "ip scan rwlock");
95 ipsc_inited = 1;
96 return 0;
100 void fr_scanunload()
102 if (ipsc_inited == 1) {
103 RW_DESTROY(&ipsc_rwlock);
104 ipsc_inited = 0;
109 int ipsc_add(data)
110 caddr_t data;
112 ipscan_t *i, *isc;
113 int err;
115 KMALLOC(isc, ipscan_t *);
116 if (!isc)
117 return ENOMEM;
119 err = copyinptr(data, isc, sizeof(*isc));
120 if (err) {
121 KFREE(isc);
122 return err;
125 WRITE_ENTER(&ipsc_rwlock);
127 i = ipsc_lookup(isc->ipsc_tag);
128 if (i) {
129 RWLOCK_EXIT(&ipsc_rwlock);
130 KFREE(isc);
131 return EEXIST;
134 if (ipsc_tail) {
135 ipsc_tail->ipsc_next = isc;
136 isc->ipsc_pnext = &ipsc_tail->ipsc_next;
137 ipsc_tail = isc;
138 } else {
139 ipsc_list = isc;
140 ipsc_tail = isc;
141 isc->ipsc_pnext = &ipsc_list;
143 isc->ipsc_next = NULL;
145 isc->ipsc_hits = 0;
146 isc->ipsc_fref = 0;
147 isc->ipsc_sref = 0;
148 isc->ipsc_active = 0;
150 ipsc_stat.iscs_entries++;
151 RWLOCK_EXIT(&ipsc_rwlock);
152 return 0;
156 int ipsc_delete(data)
157 caddr_t data;
159 ipscan_t isc, *i;
160 int err;
162 err = copyinptr(data, &isc, sizeof(isc));
163 if (err)
164 return err;
166 WRITE_ENTER(&ipsc_rwlock);
168 i = ipsc_lookup(isc.ipsc_tag);
169 if (i == NULL)
170 err = ENOENT;
171 else {
172 if (i->ipsc_fref) {
173 RWLOCK_EXIT(&ipsc_rwlock);
174 return EBUSY;
177 *i->ipsc_pnext = i->ipsc_next;
178 if (i->ipsc_next)
179 i->ipsc_next->ipsc_pnext = i->ipsc_pnext;
180 else {
181 if (i->ipsc_pnext == &ipsc_list)
182 ipsc_tail = NULL;
183 else
184 ipsc_tail = *(*i->ipsc_pnext)->ipsc_pnext;
187 ipsc_stat.iscs_entries--;
188 KFREE(i);
190 RWLOCK_EXIT(&ipsc_rwlock);
191 return err;
195 struct ipscan *ipsc_lookup(tag)
196 char *tag;
198 ipscan_t *i;
200 for (i = ipsc_list; i; i = i->ipsc_next)
201 if (!strcmp(i->ipsc_tag, tag))
202 return i;
203 return NULL;
207 int ipsc_attachfr(fr)
208 struct frentry *fr;
210 ipscan_t *i;
212 if (fr->fr_isctag[0]) {
213 READ_ENTER(&ipsc_rwlock);
214 i = ipsc_lookup(fr->fr_isctag);
215 if (i != NULL) {
216 ATOMIC_INC32(i->ipsc_fref);
218 RWLOCK_EXIT(&ipsc_rwlock);
219 if (i == NULL)
220 return ENOENT;
221 fr->fr_isc = i;
223 return 0;
227 int ipsc_attachis(is)
228 struct ipstate *is;
230 frentry_t *fr;
231 ipscan_t *i;
233 READ_ENTER(&ipsc_rwlock);
234 fr = is->is_rule;
235 if (fr) {
236 i = fr->fr_isc;
237 if ((i != NULL) && (i != (ipscan_t *)-1)) {
238 is->is_isc = i;
239 ATOMIC_INC32(i->ipsc_sref);
240 if (i->ipsc_clen)
241 is->is_flags |= IS_SC_CLIENT;
242 else
243 is->is_flags |= IS_SC_MATCHC;
244 if (i->ipsc_slen)
245 is->is_flags |= IS_SC_SERVER;
246 else
247 is->is_flags |= IS_SC_MATCHS;
250 RWLOCK_EXIT(&ipsc_rwlock);
251 return 0;
255 int ipsc_detachfr(fr)
256 struct frentry *fr;
258 ipscan_t *i;
260 i = fr->fr_isc;
261 if (i != NULL) {
262 ATOMIC_DEC32(i->ipsc_fref);
264 return 0;
268 int ipsc_detachis(is)
269 struct ipstate *is;
271 ipscan_t *i;
273 READ_ENTER(&ipsc_rwlock);
274 if ((i = is->is_isc) && (i != (ipscan_t *)-1)) {
275 ATOMIC_DEC32(i->ipsc_sref);
276 is->is_isc = NULL;
277 is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER);
279 RWLOCK_EXIT(&ipsc_rwlock);
280 return 0;
285 * 'string' compare for scanning
287 int ipsc_matchstr(sp, str, n)
288 sinfo_t *sp;
289 char *str;
290 int n;
292 char *s, *t, *up;
293 int i = n;
295 if (i > sp->s_len)
296 i = sp->s_len;
297 up = str;
299 for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++)
300 switch ((int)*t)
302 case '.' :
303 if (*s != *up)
304 return 1;
305 break;
306 case '?' :
307 if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f)))
308 return 1;
309 break;
310 case '*' :
311 break;
313 return 0;
318 * Returns 3 if both server and client match, 2 if just server,
319 * 1 if just client
321 int ipsc_matchisc(isc, is, cl, sl, maxm)
322 ipscan_t *isc;
323 ipstate_t *is;
324 int cl, sl, maxm[2];
326 int i, j, k, n, ret = 0, flags;
328 flags = is->is_flags;
331 * If we've already matched more than what is on offer, then
332 * assume we have a better match already and forget this one.
334 if (maxm != NULL) {
335 if (isc->ipsc_clen < maxm[0])
336 return 0;
337 if (isc->ipsc_slen < maxm[1])
338 return 0;
339 j = maxm[0];
340 k = maxm[1];
341 } else {
342 j = 0;
343 k = 0;
346 if (!isc->ipsc_clen)
347 ret = 1;
348 else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) &&
349 cl && isc->ipsc_clen) {
350 i = 0;
351 n = MIN(cl, isc->ipsc_clen);
352 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
353 if (!ipsc_matchstr(&isc->ipsc_cl, is->is_sbuf[0], n)) {
354 i++;
355 ret |= 1;
356 if (n > j)
357 j = n;
362 if (!isc->ipsc_slen)
363 ret |= 2;
364 else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) &&
365 sl && isc->ipsc_slen) {
366 i = 0;
367 n = MIN(cl, isc->ipsc_slen);
368 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
369 if (!ipsc_matchstr(&isc->ipsc_sl, is->is_sbuf[1], n)) {
370 i++;
371 ret |= 2;
372 if (n > k)
373 k = n;
378 if (maxm && (ret == 3)) {
379 maxm[0] = j;
380 maxm[1] = k;
382 return ret;
386 int ipsc_match(is)
387 ipstate_t *is;
389 int i, j, k, n, cl, sl, maxm[2];
390 ipscan_t *isc, *lm;
391 tcpdata_t *t;
393 for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1)
394 cl++;
395 for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1)
396 sl++;
398 j = 0;
399 isc = is->is_isc;
400 if (isc != NULL) {
402 * Known object to scan for.
404 i = ipsc_matchisc(isc, is, cl, sl, NULL);
405 if (i & 1) {
406 is->is_flags |= IS_SC_MATCHC;
407 is->is_flags &= ~IS_SC_CLIENT;
408 } else if (cl >= isc->ipsc_clen)
409 is->is_flags &= ~IS_SC_CLIENT;
410 if (i & 2) {
411 is->is_flags |= IS_SC_MATCHS;
412 is->is_flags &= ~IS_SC_SERVER;
413 } else if (sl >= isc->ipsc_slen)
414 is->is_flags &= ~IS_SC_SERVER;
415 } else {
416 i = 0;
417 lm = NULL;
418 maxm[0] = 0;
419 maxm[1] = 0;
420 for (k = 0, isc = ipsc_list; isc; isc = isc->ipsc_next) {
421 i = ipsc_matchisc(isc, is, cl, sl, maxm);
422 if (i) {
424 * We only want to remember the best match
425 * and the number of times we get a best
426 * match.
428 if ((j == 3) && (i < 3))
429 continue;
430 if ((i == 3) && (j != 3))
431 k = 1;
432 else
433 k++;
434 j = i;
435 lm = isc;
438 if (k == 1)
439 isc = lm;
440 if (isc == NULL)
441 return 0;
444 * No matches or partial matches, so reset the respective
445 * search flag.
447 if (!(j & 1))
448 is->is_flags &= ~IS_SC_CLIENT;
450 if (!(j & 2))
451 is->is_flags &= ~IS_SC_SERVER;
454 * If we found the best match, then set flags appropriately.
456 if ((j == 3) && (k == 1)) {
457 is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT);
458 is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC);
463 * If the acknowledged side of a connection has moved past the data in
464 * which we are interested, then reset respective flag.
466 t = &is->is_tcp.ts_data[0];
467 if (t->td_end > is->is_s0[0] + 15)
468 is->is_flags &= ~IS_SC_CLIENT;
470 t = &is->is_tcp.ts_data[1];
471 if (t->td_end > is->is_s0[1] + 15)
472 is->is_flags &= ~IS_SC_SERVER;
475 * Matching complete ?
477 j = ISC_A_NONE;
478 if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) {
479 j = isc->ipsc_action;
480 ipsc_stat.iscs_acted++;
481 } else if ((is->is_isc != NULL) &&
482 ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) &&
483 !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) {
485 * Matching failed...
487 j = isc->ipsc_else;
488 ipsc_stat.iscs_else++;
491 switch (j)
493 case ISC_A_CLOSE :
495 * If as a result of a successful match we are to
496 * close a connection, change the "keep state" info.
497 * to block packets and generate TCP RST's.
499 is->is_pass &= ~FR_RETICMP;
500 is->is_pass |= FR_RETRST;
501 break;
502 default :
503 break;
506 return i;
511 * check if a packet matches what we're scanning for
513 int ipsc_packet(fin, is)
514 fr_info_t *fin;
515 ipstate_t *is;
517 int i, j, rv, dlen, off, thoff;
518 u_32_t seq, s0;
519 tcphdr_t *tcp;
521 rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src);
522 tcp = fin->fin_dp;
523 seq = ntohl(tcp->th_seq);
525 if (!is->is_s0[rv])
526 return 1;
529 * check if this packet has more data that falls within the first
530 * 16 bytes sent in either direction.
532 s0 = is->is_s0[rv];
533 off = seq - s0;
534 if ((off > 15) || (off < 0))
535 return 1;
536 thoff = TCP_OFF(tcp) << 2;
537 dlen = fin->fin_dlen - thoff;
538 if (dlen <= 0)
539 return 1;
540 if (dlen > 16)
541 dlen = 16;
542 if (off + dlen > 16)
543 dlen = 16 - off;
545 j = 0xffff >> (16 - dlen);
546 i = (0xffff & j) << off;
547 #ifdef _KERNEL
548 COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_plen - fin->fin_dlen + thoff,
549 dlen, (caddr_t)is->is_sbuf[rv] + off);
550 #endif
551 is->is_smsk[rv] |= i;
552 for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1)
553 j++;
554 if (j == 0)
555 return 1;
557 (void) ipsc_match(is);
558 #if 0
560 * There is the potential here for plain text passwords to get
561 * buffered and stored for some time...
563 if (!(is->is_flags & IS_SC_CLIENT))
564 bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0]));
565 if (!(is->is_flags & IS_SC_SERVER))
566 bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1]));
567 #endif
568 return 0;
572 int fr_scan_ioctl(data, cmd, mode, uid, ctx)
573 caddr_t data;
574 ioctlcmd_t cmd;
575 int mode, uid;
576 void *ctx;
578 ipscanstat_t ipscs;
579 int err;
581 switch (cmd)
583 case SIOCADSCA :
584 err = ipsc_add(data);
585 break;
586 case SIOCRMSCA :
587 err = ipsc_delete(data);
588 break;
589 case SIOCGSCST :
590 bcopy((char *)&ipsc_stat, (char *)&ipscs, sizeof(ipscs));
591 ipscs.iscs_list = ipsc_list;
592 err = BCOPYOUT(&ipscs, data, sizeof(ipscs));
593 if (err != 0)
594 err = EFAULT;
595 break;
596 default :
597 err = EINVAL;
598 break;
601 return err;
603 #endif /* IPFILTER_SCAN */