Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / dist / ipf / netinet / ip_scan.c
blobc88bfec9a73d98f6940e559ba61ff4ec72e1ffd4
1 /* $NetBSD$ */
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 #if defined(__NetBSD__)
63 #include <sys/cdefs.h>
64 __KERNEL_RCSID(0, "$NetBSD$");
65 #else
66 static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed";
67 static const char rcsid[] = "@(#)Id: ip_scan.c,v 2.40.2.10 2007/06/02 21:22:28 darrenr Exp";
68 #endif
69 #endif
71 #ifdef IPFILTER_SCAN /* endif at bottom of file */
74 ipscan_t *ipsc_list = NULL,
75 *ipsc_tail = NULL;
76 ipscanstat_t ipsc_stat;
77 # ifdef USE_MUTEXES
78 ipfrwlock_t ipsc_rwlock;
79 # endif
81 # ifndef isalpha
82 # define isalpha(x) (((x) >= 'A' && 'Z' >= (x)) || \
83 ((x) >= 'a' && 'z' >= (x)))
84 # endif
87 int ipsc_add __P((void *));
88 int ipsc_delete __P((void *));
89 struct ipscan *ipsc_lookup __P((char *));
90 int ipsc_matchstr __P((sinfo_t *, char *, int));
91 int ipsc_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *));
92 int ipsc_match __P((ipstate_t *));
94 static int ipsc_inited = 0;
97 int ipsc_init()
99 RWLOCK_INIT(&ipsc_rwlock, "ip scan rwlock");
100 ipsc_inited = 1;
101 return 0;
105 void fr_scanunload()
107 if (ipsc_inited == 1) {
108 RW_DESTROY(&ipsc_rwlock);
109 ipsc_inited = 0;
114 int ipsc_add(data)
115 void *data;
117 ipscan_t *i, *isc;
118 int err;
120 KMALLOC(isc, ipscan_t *);
121 if (!isc)
122 return ENOMEM;
124 err = copyinptr(data, isc, sizeof(*isc));
125 if (err) {
126 KFREE(isc);
127 return err;
130 WRITE_ENTER(&ipsc_rwlock);
132 i = ipsc_lookup(isc->ipsc_tag);
133 if (i) {
134 RWLOCK_EXIT(&ipsc_rwlock);
135 KFREE(isc);
136 return EEXIST;
139 if (ipsc_tail) {
140 ipsc_tail->ipsc_next = isc;
141 isc->ipsc_pnext = &ipsc_tail->ipsc_next;
142 ipsc_tail = isc;
143 } else {
144 ipsc_list = isc;
145 ipsc_tail = isc;
146 isc->ipsc_pnext = &ipsc_list;
148 isc->ipsc_next = NULL;
150 isc->ipsc_hits = 0;
151 isc->ipsc_fref = 0;
152 isc->ipsc_sref = 0;
153 isc->ipsc_active = 0;
155 ipsc_stat.iscs_entries++;
156 RWLOCK_EXIT(&ipsc_rwlock);
157 return 0;
161 int ipsc_delete(data)
162 void *data;
164 ipscan_t isc, *i;
165 int err;
167 err = copyinptr(data, &isc, sizeof(isc));
168 if (err)
169 return err;
171 WRITE_ENTER(&ipsc_rwlock);
173 i = ipsc_lookup(isc.ipsc_tag);
174 if (i == NULL)
175 err = ENOENT;
176 else {
177 if (i->ipsc_fref) {
178 RWLOCK_EXIT(&ipsc_rwlock);
179 return EBUSY;
182 *i->ipsc_pnext = i->ipsc_next;
183 if (i->ipsc_next)
184 i->ipsc_next->ipsc_pnext = i->ipsc_pnext;
185 else {
186 if (i->ipsc_pnext == &ipsc_list)
187 ipsc_tail = NULL;
188 else
189 ipsc_tail = *(*i->ipsc_pnext)->ipsc_pnext;
192 ipsc_stat.iscs_entries--;
193 KFREE(i);
195 RWLOCK_EXIT(&ipsc_rwlock);
196 return err;
200 struct ipscan *ipsc_lookup(tag)
201 char *tag;
203 ipscan_t *i;
205 for (i = ipsc_list; i; i = i->ipsc_next)
206 if (!strcmp(i->ipsc_tag, tag))
207 return i;
208 return NULL;
212 int ipsc_attachfr(fr)
213 struct frentry *fr;
215 ipscan_t *i;
217 if (fr->fr_isctag[0]) {
218 READ_ENTER(&ipsc_rwlock);
219 i = ipsc_lookup(fr->fr_isctag);
220 if (i != NULL) {
221 ATOMIC_INC32(i->ipsc_fref);
223 RWLOCK_EXIT(&ipsc_rwlock);
224 if (i == NULL)
225 return ENOENT;
226 fr->fr_isc = i;
228 return 0;
232 int ipsc_attachis(is)
233 struct ipstate *is;
235 frentry_t *fr;
236 ipscan_t *i;
238 READ_ENTER(&ipsc_rwlock);
239 fr = is->is_rule;
240 if (fr) {
241 i = fr->fr_isc;
242 if ((i != NULL) && (i != (ipscan_t *)-1)) {
243 is->is_isc = i;
244 ATOMIC_INC32(i->ipsc_sref);
245 if (i->ipsc_clen)
246 is->is_flags |= IS_SC_CLIENT;
247 else
248 is->is_flags |= IS_SC_MATCHC;
249 if (i->ipsc_slen)
250 is->is_flags |= IS_SC_SERVER;
251 else
252 is->is_flags |= IS_SC_MATCHS;
255 RWLOCK_EXIT(&ipsc_rwlock);
256 return 0;
260 int ipsc_detachfr(fr)
261 struct frentry *fr;
263 ipscan_t *i;
265 i = fr->fr_isc;
266 if (i != NULL) {
267 ATOMIC_DEC32(i->ipsc_fref);
269 return 0;
273 int ipsc_detachis(is)
274 struct ipstate *is;
276 ipscan_t *i;
278 READ_ENTER(&ipsc_rwlock);
279 if ((i = is->is_isc) && (i != (ipscan_t *)-1)) {
280 ATOMIC_DEC32(i->ipsc_sref);
281 is->is_isc = NULL;
282 is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER);
284 RWLOCK_EXIT(&ipsc_rwlock);
285 return 0;
290 * 'string' compare for scanning
292 int ipsc_matchstr(sp, str, n)
293 sinfo_t *sp;
294 char *str;
295 int n;
297 char *s, *t, *up;
298 int i = n;
300 if (i > sp->s_len)
301 i = sp->s_len;
302 up = str;
304 for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++)
305 switch ((int)*t)
307 case '.' :
308 if (*s != *up)
309 return 1;
310 break;
311 case '?' :
312 if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f)))
313 return 1;
314 break;
315 case '*' :
316 break;
318 return 0;
323 * Returns 3 if both server and client match, 2 if just server,
324 * 1 if just client
326 int ipsc_matchisc(isc, is, cl, sl, maxm)
327 ipscan_t *isc;
328 ipstate_t *is;
329 int cl, sl, maxm[2];
331 int i, j, k, n, ret = 0, flags;
333 flags = is->is_flags;
336 * If we've already matched more than what is on offer, then
337 * assume we have a better match already and forget this one.
339 if (maxm != NULL) {
340 if (isc->ipsc_clen < maxm[0])
341 return 0;
342 if (isc->ipsc_slen < maxm[1])
343 return 0;
344 j = maxm[0];
345 k = maxm[1];
346 } else {
347 j = 0;
348 k = 0;
351 if (!isc->ipsc_clen)
352 ret = 1;
353 else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) &&
354 cl && isc->ipsc_clen) {
355 i = 0;
356 n = MIN(cl, isc->ipsc_clen);
357 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
358 if (!ipsc_matchstr(&isc->ipsc_cl, is->is_sbuf[0], n)) {
359 i++;
360 ret |= 1;
361 if (n > j)
362 j = n;
367 if (!isc->ipsc_slen)
368 ret |= 2;
369 else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) &&
370 sl && isc->ipsc_slen) {
371 i = 0;
372 n = MIN(cl, isc->ipsc_slen);
373 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
374 if (!ipsc_matchstr(&isc->ipsc_sl, is->is_sbuf[1], n)) {
375 i++;
376 ret |= 2;
377 if (n > k)
378 k = n;
383 if (maxm && (ret == 3)) {
384 maxm[0] = j;
385 maxm[1] = k;
387 return ret;
391 int ipsc_match(is)
392 ipstate_t *is;
394 int i, j, k, n, cl, sl, maxm[2];
395 ipscan_t *isc, *lm;
396 tcpdata_t *t;
398 for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1)
399 cl++;
400 for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1)
401 sl++;
403 j = 0;
404 isc = is->is_isc;
405 if (isc != NULL) {
407 * Known object to scan for.
409 i = ipsc_matchisc(isc, is, cl, sl, NULL);
410 if (i & 1) {
411 is->is_flags |= IS_SC_MATCHC;
412 is->is_flags &= ~IS_SC_CLIENT;
413 } else if (cl >= isc->ipsc_clen)
414 is->is_flags &= ~IS_SC_CLIENT;
415 if (i & 2) {
416 is->is_flags |= IS_SC_MATCHS;
417 is->is_flags &= ~IS_SC_SERVER;
418 } else if (sl >= isc->ipsc_slen)
419 is->is_flags &= ~IS_SC_SERVER;
420 } else {
421 i = 0;
422 lm = NULL;
423 maxm[0] = 0;
424 maxm[1] = 0;
425 for (k = 0, isc = ipsc_list; isc; isc = isc->ipsc_next) {
426 i = ipsc_matchisc(isc, is, cl, sl, maxm);
427 if (i) {
429 * We only want to remember the best match
430 * and the number of times we get a best
431 * match.
433 if ((j == 3) && (i < 3))
434 continue;
435 if ((i == 3) && (j != 3))
436 k = 1;
437 else
438 k++;
439 j = i;
440 lm = isc;
443 if (k == 1)
444 isc = lm;
445 if (isc == NULL)
446 return 0;
449 * No matches or partial matches, so reset the respective
450 * search flag.
452 if (!(j & 1))
453 is->is_flags &= ~IS_SC_CLIENT;
455 if (!(j & 2))
456 is->is_flags &= ~IS_SC_SERVER;
459 * If we found the best match, then set flags appropriately.
461 if ((j == 3) && (k == 1)) {
462 is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT);
463 is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC);
468 * If the acknowledged side of a connection has moved past the data in
469 * which we are interested, then reset respective flag.
471 t = &is->is_tcp.ts_data[0];
472 if (t->td_end > is->is_s0[0] + 15)
473 is->is_flags &= ~IS_SC_CLIENT;
475 t = &is->is_tcp.ts_data[1];
476 if (t->td_end > is->is_s0[1] + 15)
477 is->is_flags &= ~IS_SC_SERVER;
480 * Matching complete ?
482 j = ISC_A_NONE;
483 if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) {
484 j = isc->ipsc_action;
485 ipsc_stat.iscs_acted++;
486 } else if ((is->is_isc != NULL) &&
487 ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) &&
488 !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) {
490 * Matching failed...
492 j = isc->ipsc_else;
493 ipsc_stat.iscs_else++;
496 switch (j)
498 case ISC_A_CLOSE :
500 * If as a result of a successful match we are to
501 * close a connection, change the "keep state" info.
502 * to block packets and generate TCP RST's.
504 is->is_pass &= ~FR_RETICMP;
505 is->is_pass |= FR_RETRST;
506 break;
507 default :
508 break;
511 return i;
516 * check if a packet matches what we're scanning for
518 int ipsc_packet(fin, is)
519 fr_info_t *fin;
520 ipstate_t *is;
522 int i, j, rv, dlen, off, thoff;
523 u_32_t seq, s0;
524 tcphdr_t *tcp;
526 rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src);
527 tcp = fin->fin_dp;
528 seq = ntohl(tcp->th_seq);
530 if (!is->is_s0[rv])
531 return 1;
534 * check if this packet has more data that falls within the first
535 * 16 bytes sent in either direction.
537 s0 = is->is_s0[rv];
538 off = seq - s0;
539 if ((off > 15) || (off < 0))
540 return 1;
541 thoff = TCP_OFF(tcp) << 2;
542 dlen = fin->fin_dlen - thoff;
543 if (dlen <= 0)
544 return 1;
545 if (dlen > 16)
546 dlen = 16;
547 if (off + dlen > 16)
548 dlen = 16 - off;
550 j = 0xffff >> (16 - dlen);
551 i = (0xffff & j) << off;
552 #ifdef _KERNEL
553 COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_plen - fin->fin_dlen + thoff,
554 dlen, (void *)is->is_sbuf[rv] + off);
555 #endif
556 is->is_smsk[rv] |= i;
557 for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1)
558 j++;
559 if (j == 0)
560 return 1;
562 (void) ipsc_match(is);
563 #if 0
565 * There is the potential here for plain text passwords to get
566 * buffered and stored for some time...
568 if (!(is->is_flags & IS_SC_CLIENT))
569 bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0]));
570 if (!(is->is_flags & IS_SC_SERVER))
571 bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1]));
572 #endif
573 return 0;
577 int fr_scan_ioctl(data, cmd, mode, uid, ctx)
578 void * data;
579 ioctlcmd_t cmd;
580 int mode, uid;
581 void *ctx;
583 ipscanstat_t ipscs;
584 int err = 0;
586 switch (cmd)
588 case SIOCADSCA :
589 err = ipsc_add(data);
590 break;
591 case SIOCRMSCA :
592 err = ipsc_delete(data);
593 break;
594 case SIOCGSCST :
595 bcopy((char *)&ipsc_stat, (char *)&ipscs, sizeof(ipscs));
596 ipscs.iscs_list = ipsc_list;
597 err = BCOPYOUT(&ipscs, data, sizeof(ipscs));
598 if (err != 0)
599 err = EFAULT;
600 break;
601 default :
602 err = EINVAL;
603 break;
606 return err;
608 #endif /* IPFILTER_SCAN */