1 /* $NetBSD: ip_scan.c,v 1.2 2007/06/16 10:52:25 martin Exp $ */
4 * Copyright (C) 1995-2001 by Darren Reed.
6 * See the IPFILTER.LICENCE file for details on licencing.
8 #if defined(KERNEL) || defined(_KERNEL)
14 #include <sys/param.h>
15 #if defined(__hpux) && (HPUXREV >= 1111) && !defined(_KERNEL)
16 # include <sys/kern_svcs.h>
18 #include <sys/types.h>
20 #include <sys/errno.h>
31 # include <sys/systm.h>
32 # if !defined(__svr4__) && !defined(__SVR4)
33 # include <sys/mbuf.h>
36 #include <sys/socket.h>
37 #if !defined(__hpux) && !defined(__osf__) && !defined(linux) && !defined(AIX)
38 # include <sys/ioccom.h>
41 # include <sys/filio.h>
42 # include <sys/malloc.h>
44 # include <sys/ioctl.h>
47 #include <netinet/in.h>
48 #include <netinet/in_systm.h>
49 #include <netinet/ip.h>
50 #include <netinet/tcp.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"
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";
66 #ifdef IPFILTER_SCAN /* endif at bottom of file */
69 ipscan_t
*ipsc_list
= NULL
,
71 ipscanstat_t ipsc_stat
;
73 ipfrwlock_t ipsc_rwlock
;
77 # define isalpha(x) (((x) >= 'A' && 'Z' >= (x)) || \
78 ((x) >= 'a' && 'z' >= (x)))
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;
94 RWLOCK_INIT(&ipsc_rwlock
, "ip scan rwlock");
102 if (ipsc_inited
== 1) {
103 RW_DESTROY(&ipsc_rwlock
);
115 KMALLOC(isc
, ipscan_t
*);
119 err
= copyinptr(data
, isc
, sizeof(*isc
));
125 WRITE_ENTER(&ipsc_rwlock
);
127 i
= ipsc_lookup(isc
->ipsc_tag
);
129 RWLOCK_EXIT(&ipsc_rwlock
);
135 ipsc_tail
->ipsc_next
= isc
;
136 isc
->ipsc_pnext
= &ipsc_tail
->ipsc_next
;
141 isc
->ipsc_pnext
= &ipsc_list
;
143 isc
->ipsc_next
= NULL
;
148 isc
->ipsc_active
= 0;
150 ipsc_stat
.iscs_entries
++;
151 RWLOCK_EXIT(&ipsc_rwlock
);
156 int ipsc_delete(data
)
162 err
= copyinptr(data
, &isc
, sizeof(isc
));
166 WRITE_ENTER(&ipsc_rwlock
);
168 i
= ipsc_lookup(isc
.ipsc_tag
);
173 RWLOCK_EXIT(&ipsc_rwlock
);
177 *i
->ipsc_pnext
= i
->ipsc_next
;
179 i
->ipsc_next
->ipsc_pnext
= i
->ipsc_pnext
;
181 if (i
->ipsc_pnext
== &ipsc_list
)
184 ipsc_tail
= *(*i
->ipsc_pnext
)->ipsc_pnext
;
187 ipsc_stat
.iscs_entries
--;
190 RWLOCK_EXIT(&ipsc_rwlock
);
195 struct ipscan
*ipsc_lookup(tag
)
200 for (i
= ipsc_list
; i
; i
= i
->ipsc_next
)
201 if (!strcmp(i
->ipsc_tag
, tag
))
207 int ipsc_attachfr(fr
)
212 if (fr
->fr_isctag
[0]) {
213 READ_ENTER(&ipsc_rwlock
);
214 i
= ipsc_lookup(fr
->fr_isctag
);
216 ATOMIC_INC32(i
->ipsc_fref
);
218 RWLOCK_EXIT(&ipsc_rwlock
);
227 int ipsc_attachis(is
)
233 READ_ENTER(&ipsc_rwlock
);
237 if ((i
!= NULL
) && (i
!= (ipscan_t
*)-1)) {
239 ATOMIC_INC32(i
->ipsc_sref
);
241 is
->is_flags
|= IS_SC_CLIENT
;
243 is
->is_flags
|= IS_SC_MATCHC
;
245 is
->is_flags
|= IS_SC_SERVER
;
247 is
->is_flags
|= IS_SC_MATCHS
;
250 RWLOCK_EXIT(&ipsc_rwlock
);
255 int ipsc_detachfr(fr
)
262 ATOMIC_DEC32(i
->ipsc_fref
);
268 int ipsc_detachis(is
)
273 READ_ENTER(&ipsc_rwlock
);
274 if ((i
= is
->is_isc
) && (i
!= (ipscan_t
*)-1)) {
275 ATOMIC_DEC32(i
->ipsc_sref
);
277 is
->is_flags
&= ~(IS_SC_CLIENT
|IS_SC_SERVER
);
279 RWLOCK_EXIT(&ipsc_rwlock
);
285 * 'string' compare for scanning
287 int ipsc_matchstr(sp
, str
, n
)
299 for (s
= sp
->s_txt
, t
= sp
->s_msk
; i
; i
--, s
++, t
++, up
++)
307 if (!ISALPHA(*up
) || ((*s
& 0x5f) != (*up
& 0x5f)))
318 * Returns 3 if both server and client match, 2 if just server,
321 int ipsc_matchisc(isc
, is
, cl
, sl
, maxm
)
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.
335 if (isc
->ipsc_clen
< maxm
[0])
337 if (isc
->ipsc_slen
< maxm
[1])
348 else if (((flags
& (IS_SC_MATCHC
|IS_SC_CLIENT
)) == IS_SC_CLIENT
) &&
349 cl
&& isc
->ipsc_clen
) {
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
)) {
364 else if (((flags
& (IS_SC_MATCHS
|IS_SC_SERVER
)) == IS_SC_SERVER
) &&
365 sl
&& isc
->ipsc_slen
) {
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
)) {
378 if (maxm
&& (ret
== 3)) {
389 int i
, j
, k
, n
, cl
, sl
, maxm
[2];
393 for (cl
= 0, n
= is
->is_smsk
[0]; n
& 1; n
>>= 1)
395 for (sl
= 0, n
= is
->is_smsk
[1]; n
& 1; n
>>= 1)
402 * Known object to scan for.
404 i
= ipsc_matchisc(isc
, is
, cl
, sl
, NULL
);
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
;
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
;
420 for (k
= 0, isc
= ipsc_list
; isc
; isc
= isc
->ipsc_next
) {
421 i
= ipsc_matchisc(isc
, is
, cl
, sl
, maxm
);
424 * We only want to remember the best match
425 * and the number of times we get a best
428 if ((j
== 3) && (i
< 3))
430 if ((i
== 3) && (j
!= 3))
444 * No matches or partial matches, so reset the respective
448 is
->is_flags
&= ~IS_SC_CLIENT
;
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 ?
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
))) {
488 ipsc_stat
.iscs_else
++;
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
;
511 * check if a packet matches what we're scanning for
513 int ipsc_packet(fin
, is
)
517 int i
, j
, rv
, dlen
, off
, thoff
;
521 rv
= !IP6_EQ(&fin
->fin_fi
.fi_src
, &is
->is_src
);
523 seq
= ntohl(tcp
->th_seq
);
529 * check if this packet has more data that falls within the first
530 * 16 bytes sent in either direction.
534 if ((off
> 15) || (off
< 0))
536 thoff
= TCP_OFF(tcp
) << 2;
537 dlen
= fin
->fin_dlen
- thoff
;
545 j
= 0xffff >> (16 - dlen
);
546 i
= (0xffff & j
) << off
;
548 COPYDATA(*(mb_t
**)fin
->fin_mp
, fin
->fin_plen
- fin
->fin_dlen
+ thoff
,
549 dlen
, (caddr_t
)is
->is_sbuf
[rv
] + off
);
551 is
->is_smsk
[rv
] |= i
;
552 for (j
= 0, i
= is
->is_smsk
[rv
]; i
& 1; i
>>= 1)
557 (void) ipsc_match(is
);
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]));
572 int fr_scan_ioctl(data
, cmd
, mode
, uid
, ctx
)
584 err
= ipsc_add(data
);
587 err
= ipsc_delete(data
);
590 bcopy((char *)&ipsc_stat
, (char *)&ipscs
, sizeof(ipscs
));
591 ipscs
.iscs_list
= ipsc_list
;
592 err
= BCOPYOUT(&ipscs
, data
, sizeof(ipscs
));
603 #endif /* IPFILTER_SCAN */