Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / dist / ipf / netinet / fil.c
blob763fc151b55d7c1dbf2b2868d3c995987b34225a
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 1993-2003 by Darren Reed.
6 * See the IPFILTER.LICENCE file for details on licencing.
8 * Copyright 2008 Sun Microsystems, Inc.
9 */
10 #if defined(KERNEL) || defined(_KERNEL)
11 # undef KERNEL
12 # undef _KERNEL
13 # define KERNEL 1
14 # define _KERNEL 1
15 #endif
16 #include <sys/errno.h>
17 #include <sys/types.h>
18 #include <sys/param.h>
19 #include <sys/time.h>
20 #if defined(__NetBSD__)
21 # if (NetBSD >= 199905) && !defined(IPFILTER_LKM) && defined(_KERNEL)
22 # if (__NetBSD_Version__ < 301000000)
23 # include "opt_ipfilter_log.h"
24 # else
25 # include <sys/cdefs.h>
26 # include "opt_ipfilter.h"
27 # endif
28 # endif
29 #endif
30 #if defined(_KERNEL) && defined(__FreeBSD_version) && \
31 (__FreeBSD_version >= 220000)
32 # if (__FreeBSD_version >= 400000)
33 # if !defined(IPFILTER_LKM)
34 # include "opt_inet6.h"
35 # endif
36 # if (__FreeBSD_version == 400019)
37 # define CSUM_DELAY_DATA
38 # endif
39 # endif
40 # include <sys/filio.h>
41 #else
42 # include <sys/ioctl.h>
43 #endif
44 #if (defined(__SVR4) || defined(__svr4__)) && defined(sun)
45 # include <sys/filio.h>
46 #endif
47 #if !defined(_AIX51)
48 # include <sys/fcntl.h>
49 #endif
50 #if defined(_KERNEL)
51 # include <sys/systm.h>
52 # include <sys/file.h>
53 #else
54 # include <stdio.h>
55 # include <string.h>
56 # include <stdlib.h>
57 # include <stddef.h>
58 # include <sys/file.h>
59 # define _KERNEL
60 # ifdef __OpenBSD__
61 struct file;
62 # endif
63 # include <sys/uio.h>
64 # undef _KERNEL
65 #endif
66 #if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux) && \
67 !defined(linux)
68 # include <sys/mbuf.h>
69 #else
70 # if !defined(linux)
71 # include <sys/byteorder.h>
72 # endif
73 # if (SOLARIS2 < 5) && defined(sun)
74 # include <sys/dditypes.h>
75 # endif
76 #endif
77 #ifdef __hpux
78 # define _NET_ROUTE_INCLUDED
79 #endif
80 #if !defined(linux)
81 # include <sys/protosw.h>
82 #endif
83 #include <sys/socket.h>
84 #include <net/if.h>
85 #ifdef sun
86 # include <net/af.h>
87 #endif
88 #if !defined(_KERNEL) && (defined(__FreeBSD__) || defined(SOLARIS2))
89 # if (__FreeBSD_version >= 504000)
90 # undef _RADIX_H_
91 # endif
92 # include "radix_ipf.h"
93 #endif
94 #ifdef __osf__
95 # include "radix_ipf.h"
96 #else
97 # include <net/route.h>
98 #endif
99 #include <netinet/in.h>
100 #include <netinet/in_systm.h>
101 #include <netinet/ip.h>
102 #if !defined(linux)
103 # include <netinet/ip_var.h>
104 #endif
105 #if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */
106 # include <sys/hashing.h>
107 # include <netinet/in_var.h>
108 #endif
109 #include <netinet/tcp.h>
110 #if (!defined(__sgi) && !defined(AIX)) || defined(_KERNEL)
111 # include <netinet/udp.h>
112 # include <netinet/ip_icmp.h>
113 #endif
114 #ifdef __hpux
115 # undef _NET_ROUTE_INCLUDED
116 #endif
117 #ifdef __osf__
118 # undef _RADIX_H_
119 #endif
120 #include "netinet/ip_compat.h"
121 #ifdef USE_INET6
122 # include <netinet/icmp6.h>
123 # if !SOLARIS && defined(_KERNEL) && !defined(__osf__) && !defined(__hpux)
124 # include <netinet6/in6_var.h>
125 # endif
126 #endif
127 #include <netinet/tcpip.h>
128 #include "netinet/ip_fil.h"
129 #include "netinet/ip_nat.h"
130 #include "netinet/ip_frag.h"
131 #include "netinet/ip_state.h"
132 #include "netinet/ip_proxy.h"
133 #include "netinet/ip_auth.h"
134 #ifdef IPFILTER_SCAN
135 # include "netinet/ip_scan.h"
136 #endif
137 #ifdef IPFILTER_SYNC
138 # include "netinet/ip_sync.h"
139 #endif
140 #include "netinet/ip_pool.h"
141 #include "netinet/ip_htable.h"
142 #ifdef IPFILTER_COMPILED
143 # include "netinet/ip_rules.h"
144 #endif
145 #if defined(IPFILTER_BPF) && defined(_KERNEL)
146 # include <net/bpf.h>
147 #endif
148 #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
149 # include <sys/malloc.h>
150 # if defined(_KERNEL) && !defined(IPFILTER_LKM)
151 # include "opt_ipfilter.h"
152 # endif
153 #endif
154 #include "netinet/ipl.h"
155 /* END OF INCLUDES */
157 #if !defined(lint)
158 #if defined(__NetBSD__)
159 #include <sys/cdefs.h>
160 __KERNEL_RCSID(0, "$NetBSD$");
161 #else
162 static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-2000 Darren Reed";
163 static const char rcsid[] = "@(#)Id: fil.c,v 2.243.2.147 2009/07/21 22:25:28 darrenr Exp";
164 #endif
165 #endif
167 #ifndef _KERNEL
168 # include "ipf.h"
169 # include "ipt.h"
170 # include "bpf-ipf.h"
171 extern int opts;
172 #endif /* _KERNEL */
175 fr_info_t frcache[2][8];
176 struct filterstats frstats[2] = { { .fr_pass = 0 }, { .fr_pass = 0 } };
177 struct frentry *ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } },
178 *ipfilter6[2][2] = { { NULL, NULL }, { NULL, NULL } },
179 *ipacct6[2][2] = { { NULL, NULL }, { NULL, NULL } },
180 *ipacct[2][2] = { { NULL, NULL }, { NULL, NULL } },
181 *ipnatrules[2][2] = { { NULL, NULL }, { NULL, NULL } };
182 struct frgroup *ipfgroups[IPL_LOGSIZE][2];
183 char ipfilter_version[] = IPL_VERSION;
184 int fr_refcnt = 0;
186 * For fr_running:
187 * 0 == loading, 1 = running, -1 = disabled, -2 = unloading
189 int fr_running = 0;
190 int fr_flags = IPF_LOGGING;
191 int fr_active = 0;
192 int fr_control_forwarding = 0;
193 int fr_update_ipid = 0;
194 u_short fr_ip_id = 0;
195 int fr_chksrc = 0; /* causes a system crash if enabled */
196 int fr_minttl = 4;
197 int fr_icmpminfragmtu = 68;
198 u_long fr_frouteok[2] = {0, 0};
199 u_long fr_userifqs = 0;
200 u_long fr_badcoalesces[2] = {0, 0};
201 u_char ipf_iss_secret[32];
202 #if defined(IPFILTER_DEFAULT_BLOCK)
203 int fr_pass = FR_BLOCK|FR_NOMATCH;
204 #else
205 int fr_pass = (IPF_DEFAULT_PASS)|FR_NOMATCH;
206 #endif
207 int fr_features = 0
208 #ifdef IPFILTER_LKM
209 | IPF_FEAT_LKM
210 #endif
211 #ifdef IPFILTER_LOG
212 | IPF_FEAT_LOG
213 #endif
214 #ifdef IPFILTER_LOOKUP
215 | IPF_FEAT_LOOKUP
216 #endif
217 #ifdef IPFILTER_BPF
218 | IPF_FEAT_BPF
219 #endif
220 #ifdef IPFILTER_COMPILED
221 | IPF_FEAT_COMPILED
222 #endif
223 #ifdef IPFILTER_CKSUM
224 | IPF_FEAT_CKSUM
225 #endif
226 #ifdef IPFILTER_SYNC
227 | IPF_FEAT_SYNC
228 #endif
229 #ifdef IPFILTER_SCAN
230 | IPF_FEAT_SCAN
231 #endif
232 #ifdef USE_INET6
233 | IPF_FEAT_IPV6
234 #endif
237 static INLINE int fr_ipfcheck __P((fr_info_t *, frentry_t *, int));
238 static int fr_portcheck __P((frpcmp_t *, u_short *));
239 static int frflushlist __P((int, minor_t, int *, frentry_t **));
240 static ipfunc_t fr_findfunc __P((ipfunc_t));
241 static frentry_t *fr_firewall __P((fr_info_t *, u_32_t *));
242 static int fr_funcinit __P((frentry_t *fr));
243 static INLINE void frpr_ah __P((fr_info_t *));
244 static INLINE void frpr_esp __P((fr_info_t *));
245 static INLINE void frpr_gre __P((fr_info_t *));
246 static INLINE void frpr_udp __P((fr_info_t *));
247 static INLINE void frpr_tcp __P((fr_info_t *));
248 static INLINE void frpr_icmp __P((fr_info_t *));
249 static INLINE void frpr_ipv4hdr __P((fr_info_t *));
250 static INLINE int frpr_pullup __P((fr_info_t *, int));
251 static INLINE void frpr_short __P((fr_info_t *, int));
252 static INLINE int frpr_tcpcommon __P((fr_info_t *));
253 static INLINE int frpr_udpcommon __P((fr_info_t *));
254 static int fr_updateipid __P((fr_info_t *));
255 #ifdef IPFILTER_LOOKUP
256 static int fr_grpmapinit __P((frentry_t *fr));
257 static INLINE void *fr_resolvelookup __P((u_int, u_int, i6addr_t *, lookupfunc_t *));
258 #endif
259 static void frsynclist __P((frentry_t *, void *));
260 static ipftuneable_t *fr_findtunebyname __P((const char *));
261 static ipftuneable_t *fr_findtunebycookie __P((void *, void **));
262 static int ipf_geniter __P((ipftoken_t *, ipfgeniter_t *));
263 static int ipf_frruleiter __P((void *, int, void *));
264 static void ipf_unlinktoken __P((ipftoken_t *));
268 * bit values for identifying presence of individual IP options
269 * All of these tables should be ordered by increasing key value on the left
270 * hand side to allow for binary searching of the array and include a trailer
271 * with a 0 for the bitmask for linear searches to easily find the end with.
273 const struct optlist ipopts[20] = {
274 { IPOPT_NOP, 0x000001 },
275 { IPOPT_RR, 0x000002 },
276 { IPOPT_ZSU, 0x000004 },
277 { IPOPT_MTUP, 0x000008 },
278 { IPOPT_MTUR, 0x000010 },
279 { IPOPT_ENCODE, 0x000020 },
280 { IPOPT_TS, 0x000040 },
281 { IPOPT_TR, 0x000080 },
282 { IPOPT_SECURITY, 0x000100 },
283 { IPOPT_LSRR, 0x000200 },
284 { IPOPT_E_SEC, 0x000400 },
285 { IPOPT_CIPSO, 0x000800 },
286 { IPOPT_SATID, 0x001000 },
287 { IPOPT_SSRR, 0x002000 },
288 { IPOPT_ADDEXT, 0x004000 },
289 { IPOPT_VISA, 0x008000 },
290 { IPOPT_IMITD, 0x010000 },
291 { IPOPT_EIP, 0x020000 },
292 { IPOPT_FINN, 0x040000 },
293 { 0, 0x000000 }
296 #ifdef USE_INET6
297 struct optlist ip6exthdr[] = {
298 { IPPROTO_HOPOPTS, 0x000001 },
299 { IPPROTO_IPV6, 0x000002 },
300 { IPPROTO_ROUTING, 0x000004 },
301 { IPPROTO_FRAGMENT, 0x000008 },
302 { IPPROTO_ESP, 0x000010 },
303 { IPPROTO_AH, 0x000020 },
304 { IPPROTO_NONE, 0x000040 },
305 { IPPROTO_DSTOPTS, 0x000080 },
306 { IPPROTO_MOBILITY, 0x000100 },
307 { 0, 0 }
309 #endif
311 struct optlist tcpopts[] = {
312 { TCPOPT_NOP, 0x000001 },
313 { TCPOPT_MAXSEG, 0x000002 },
314 { TCPOPT_WINDOW, 0x000004 },
315 { TCPOPT_SACK_PERMITTED, 0x000008 },
316 { TCPOPT_SACK, 0x000010 },
317 { TCPOPT_TIMESTAMP, 0x000020 },
318 { 0, 0x000000 }
322 * bit values for identifying presence of individual IP security options
324 const struct optlist secopt[8] = {
325 { IPSO_CLASS_RES4, 0x01 },
326 { IPSO_CLASS_TOPS, 0x02 },
327 { IPSO_CLASS_SECR, 0x04 },
328 { IPSO_CLASS_RES3, 0x08 },
329 { IPSO_CLASS_CONF, 0x10 },
330 { IPSO_CLASS_UNCL, 0x20 },
331 { IPSO_CLASS_RES2, 0x40 },
332 { IPSO_CLASS_RES1, 0x80 }
337 * Table of functions available for use with call rules.
339 static ipfunc_resolve_t fr_availfuncs[] = {
340 #ifdef IPFILTER_LOOKUP
341 { "fr_srcgrpmap", fr_srcgrpmap, fr_grpmapinit },
342 { "fr_dstgrpmap", fr_dstgrpmap, fr_grpmapinit },
343 #endif
344 { "", NULL, NULL }
349 * The next section of code is a a collection of small routines that set
350 * fields in the fr_info_t structure passed based on properties of the
351 * current packet. There are different routines for the same protocol
352 * for each of IPv4 and IPv6. Adding a new protocol, for which there
353 * will "special" inspection for setup, is now more easily done by adding
354 * a new routine and expanding the frpr_ipinit*() function rather than by
355 * adding more code to a growing switch statement.
357 #ifdef USE_INET6
358 static INLINE int frpr_ah6 __P((fr_info_t *));
359 static INLINE void frpr_esp6 __P((fr_info_t *));
360 static INLINE void frpr_gre6 __P((fr_info_t *));
361 static INLINE void frpr_udp6 __P((fr_info_t *));
362 static INLINE void frpr_tcp6 __P((fr_info_t *));
363 static INLINE void frpr_icmp6 __P((fr_info_t *));
364 static INLINE void frpr_ipv6hdr __P((fr_info_t *));
365 static INLINE void frpr_short6 __P((fr_info_t *, int));
366 static INLINE int frpr_hopopts6 __P((fr_info_t *));
367 static INLINE int frpr_mobility6 __P((fr_info_t *));
368 static INLINE int frpr_routing6 __P((fr_info_t *));
369 static INLINE int frpr_dstopts6 __P((fr_info_t *));
370 static INLINE int frpr_fragment6 __P((fr_info_t *));
371 static INLINE int frpr_ipv6exthdr __P((fr_info_t *, int, int));
374 /* ------------------------------------------------------------------------ */
375 /* Function: frpr_short6 */
376 /* Returns: void */
377 /* Parameters: fin(I) - pointer to packet information */
378 /* */
379 /* IPv6 Only */
380 /* This is function enforces the 'is a packet too short to be legit' rule */
381 /* for IPv6 and marks the packet with FI_SHORT if so. See function comment */
382 /* for frpr_short() for more details. */
383 /* ------------------------------------------------------------------------ */
384 static INLINE void frpr_short6(fin, xmin)
385 fr_info_t *fin;
386 int xmin;
389 if (fin->fin_dlen < xmin)
390 fin->fin_flx |= FI_SHORT;
394 /* ------------------------------------------------------------------------ */
395 /* Function: frpr_ipv6hdr */
396 /* Returns: void */
397 /* Parameters: fin(I) - pointer to packet information */
398 /* */
399 /* IPv6 Only */
400 /* Copy values from the IPv6 header into the fr_info_t struct and call the */
401 /* per-protocol analyzer if it exists. In validating the packet, a protocol*/
402 /* analyzer may pullup or free the packet itself so we need to be vigiliant */
403 /* of that possibility arising. */
404 /* ------------------------------------------------------------------------ */
405 static INLINE void frpr_ipv6hdr(fin)
406 fr_info_t *fin;
408 ip6_t *ip6 = (ip6_t *)fin->fin_ip;
409 int p, go = 1, i, hdrcount;
410 fr_ip_t *fi = &fin->fin_fi;
412 fin->fin_off = 0;
414 fi->fi_tos = 0;
415 fi->fi_optmsk = 0;
416 fi->fi_secmsk = 0;
417 fi->fi_auth = 0;
419 p = ip6->ip6_nxt;
420 fi->fi_ttl = ip6->ip6_hlim;
421 fi->fi_src.in6 = ip6->ip6_src;
422 fi->fi_dst.in6 = ip6->ip6_dst;
423 fin->fin_id = (u_short)(ip6->ip6_flow & 0xffff);
425 hdrcount = 0;
426 while (go && !(fin->fin_flx & FI_SHORT)) {
427 switch (p)
429 case IPPROTO_UDP :
430 frpr_udp6(fin);
431 go = 0;
432 break;
434 case IPPROTO_TCP :
435 frpr_tcp6(fin);
436 go = 0;
437 break;
439 case IPPROTO_ICMPV6 :
440 frpr_icmp6(fin);
441 go = 0;
442 break;
444 case IPPROTO_GRE :
445 frpr_gre6(fin);
446 go = 0;
447 break;
449 case IPPROTO_HOPOPTS :
450 p = frpr_hopopts6(fin);
451 break;
453 case IPPROTO_MOBILITY :
454 p = frpr_mobility6(fin);
455 break;
457 case IPPROTO_DSTOPTS :
458 p = frpr_dstopts6(fin);
459 break;
461 case IPPROTO_ROUTING :
462 p = frpr_routing6(fin);
463 break;
465 case IPPROTO_AH :
466 p = frpr_ah6(fin);
467 break;
469 case IPPROTO_ESP :
470 frpr_esp6(fin);
471 go = 0;
472 break;
474 case IPPROTO_IPV6 :
475 for (i = 0; ip6exthdr[i].ol_bit != 0; i++)
476 if (ip6exthdr[i].ol_val == p) {
477 fin->fin_flx |= ip6exthdr[i].ol_bit;
478 break;
480 go = 0;
481 break;
483 case IPPROTO_NONE :
484 go = 0;
485 break;
487 case IPPROTO_FRAGMENT :
488 p = frpr_fragment6(fin);
489 if (fin->fin_off != 0)
490 go = 0;
491 break;
493 default :
494 go = 0;
495 break;
497 hdrcount++;
500 * It is important to note that at this point, for the
501 * extension headers (go != 0), the entire header may not have
502 * been pulled up when the code gets to this point. This is
503 * only done for "go != 0" because the other header handlers
504 * will all pullup their complete header. The other indicator
505 * of an incomplete packet is that this was just an extension
506 * header.
508 if ((go != 0) && (p != IPPROTO_NONE) &&
509 (frpr_pullup(fin, 0) == -1)) {
510 p = IPPROTO_NONE;
511 go = 0;
514 fi->fi_p = p;
518 /* ------------------------------------------------------------------------ */
519 /* Function: frpr_ipv6exthdr */
520 /* Returns: int - value of the next header or IPPROTO_NONE if error */
521 /* Parameters: fin(I) - pointer to packet information */
522 /* multiple(I) - flag indicating yes/no if multiple occurances */
523 /* of this extension header are allowed. */
524 /* proto(I) - protocol number for this extension header */
525 /* */
526 /* IPv6 Only */
527 /* This function expects to find an IPv6 extension header at fin_dp. */
528 /* There must be at least 8 bytes of data at fin_dp for there to be a valid */
529 /* extension header present. If a good one is found, fin_dp is advanced to */
530 /* point at the first piece of data after the extension header, fin_exthdr */
531 /* points to the start of the extension header and the "protocol" of the */
532 /* *NEXT* header is returned. */
533 /* ------------------------------------------------------------------------ */
534 static INLINE int frpr_ipv6exthdr(fin, multiple, proto)
535 fr_info_t *fin;
536 int multiple, proto;
538 struct ip6_ext *hdr;
539 u_short shift;
540 int i;
542 fin->fin_flx |= FI_V6EXTHDR;
544 /* 8 is default length of extension hdr */
545 if ((fin->fin_dlen - 8) < 0) {
546 fin->fin_flx |= FI_SHORT;
547 return IPPROTO_NONE;
550 if (frpr_pullup(fin, 8) == -1)
551 return IPPROTO_NONE;
553 hdr = fin->fin_dp;
554 switch (proto)
556 case IPPROTO_FRAGMENT :
557 shift = 8;
558 break;
559 default :
560 shift = 8 + (hdr->ip6e_len << 3);
561 break;
564 if (shift > fin->fin_dlen) { /* Nasty extension header length? */
565 fin->fin_flx |= FI_BAD;
566 return IPPROTO_NONE;
569 for (i = 0; ip6exthdr[i].ol_bit != 0; i++)
570 if (ip6exthdr[i].ol_val == proto) {
572 * Most IPv6 extension headers are only allowed once.
574 if ((multiple == 0) &&
575 ((fin->fin_optmsk & ip6exthdr[i].ol_bit) != 0))
576 fin->fin_flx |= FI_BAD;
577 else
578 fin->fin_optmsk |= ip6exthdr[i].ol_bit;
579 break;
582 fin->fin_exthdr = fin->fin_dp;
583 fin->fin_dp = (char *)fin->fin_dp + shift;
584 fin->fin_dlen -= shift;
586 return hdr->ip6e_nxt;
590 /* ------------------------------------------------------------------------ */
591 /* Function: frpr_hopopts6 */
592 /* Returns: int - value of the next header or IPPROTO_NONE if error */
593 /* Parameters: fin(I) - pointer to packet information */
594 /* */
595 /* IPv6 Only */
596 /* This is function checks pending hop by hop options extension header */
597 /* ------------------------------------------------------------------------ */
598 static INLINE int frpr_hopopts6(fin)
599 fr_info_t *fin;
601 return frpr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS);
605 /* ------------------------------------------------------------------------ */
606 /* Function: frpr_mobility6 */
607 /* Returns: int - value of the next header or IPPROTO_NONE if error */
608 /* Parameters: fin(I) - pointer to packet information */
609 /* */
610 /* IPv6 Only */
611 /* This is function checks the IPv6 mobility extension header */
612 /* ------------------------------------------------------------------------ */
613 static INLINE int frpr_mobility6(fin)
614 fr_info_t *fin;
616 return frpr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY);
620 /* ------------------------------------------------------------------------ */
621 /* Function: frpr_routing6 */
622 /* Returns: int - value of the next header or IPPROTO_NONE if error */
623 /* Parameters: fin(I) - pointer to packet information */
624 /* */
625 /* IPv6 Only */
626 /* This is function checks pending routing extension header */
627 /* ------------------------------------------------------------------------ */
628 static INLINE int frpr_routing6(fin)
629 fr_info_t *fin;
631 struct ip6_ext *hdr;
633 if (frpr_ipv6exthdr(fin, 0, IPPROTO_ROUTING) == IPPROTO_NONE)
634 return IPPROTO_NONE;
635 hdr = fin->fin_exthdr;
637 if ((hdr->ip6e_len & 1) != 0) {
639 * The routing header data is made up of 128 bit IPv6 addresses
640 * which means it must be a multiple of 2 lots of 8 in length.
642 fin->fin_flx |= FI_BAD;
645 return hdr->ip6e_nxt;
649 /* ------------------------------------------------------------------------ */
650 /* Function: frpr_fragment6 */
651 /* Returns: int - value of the next header or IPPROTO_NONE if error */
652 /* Parameters: fin(I) - pointer to packet information */
653 /* */
654 /* IPv6 Only */
655 /* Examine the IPv6 fragment header and extract fragment offset information.*/
656 /* */
657 /* We don't know where the transport layer header (or whatever is next is), */
658 /* as it could be behind destination options (amongst others). Because */
659 /* there is no fragment cache, there is no knowledge about whether or not an*/
660 /* upper layer header has been seen (or where it ends) and thus we are not */
661 /* able to continue processing beyond this header with any confidence. */
662 /* ------------------------------------------------------------------------ */
663 static INLINE int frpr_fragment6(fin)
664 fr_info_t *fin;
666 struct ip6_frag *frag;
668 fin->fin_flx |= FI_FRAG;
671 * A fragmented IPv6 packet implies that there must be something
672 * else after the fragment.
674 if (frpr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT) == IPPROTO_NONE)
675 return IPPROTO_NONE;
677 frag = fin->fin_exthdr;
680 * If this fragment isn't the last then the packet length must
681 * be a multiple of 8.
683 if ((frag->ip6f_offlg & IP6F_MORE_FRAG) != 0) {
684 fin->fin_flx |= FI_MOREFRAG;
686 if ((fin->fin_plen & 0x7) != 0)
687 fin->fin_flx |= FI_BAD;
690 fin->fin_off = ntohs(frag->ip6f_offlg & IP6F_OFF_MASK);
691 if (fin->fin_off != 0)
692 fin->fin_flx |= FI_FRAGBODY;
695 * Jumbograms aren't handled, so the max. length is 64k
697 if ((fin->fin_off << 3) + fin->fin_dlen > 65535)
698 fin->fin_flx |= FI_BAD;
700 return frag->ip6f_nxt;
704 /* ------------------------------------------------------------------------ */
705 /* Function: frpr_dstopts6 */
706 /* Returns: int - value of the next header or IPPROTO_NONE if error */
707 /* Parameters: fin(I) - pointer to packet information */
708 /* nextheader(I) - stores next header value */
709 /* */
710 /* IPv6 Only */
711 /* This is function checks pending destination options extension header */
712 /* ------------------------------------------------------------------------ */
713 static INLINE int frpr_dstopts6(fin)
714 fr_info_t *fin;
716 return frpr_ipv6exthdr(fin, 1, IPPROTO_DSTOPTS);
720 /* ------------------------------------------------------------------------ */
721 /* Function: frpr_icmp6 */
722 /* Returns: void */
723 /* Parameters: fin(I) - pointer to packet information */
724 /* */
725 /* IPv6 Only */
726 /* This routine is mainly concerned with determining the minimum valid size */
727 /* for an ICMPv6 packet. */
728 /* ------------------------------------------------------------------------ */
729 static INLINE void frpr_icmp6(fin)
730 fr_info_t *fin;
732 int minicmpsz = sizeof(struct icmp6_hdr);
733 struct icmp6_hdr *icmp6;
735 if (frpr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1)
736 return;
738 if (fin->fin_dlen > 1) {
739 ip6_t *ip6;
741 icmp6 = fin->fin_dp;
743 fin->fin_data[0] = *(u_short *)icmp6;
745 switch (icmp6->icmp6_type)
747 case ICMP6_ECHO_REPLY :
748 case ICMP6_ECHO_REQUEST :
749 minicmpsz = ICMP6ERR_MINPKTLEN - sizeof(ip6_t);
750 break;
751 case ICMP6_DST_UNREACH :
752 case ICMP6_PACKET_TOO_BIG :
753 case ICMP6_TIME_EXCEEDED :
754 case ICMP6_PARAM_PROB :
755 fin->fin_flx |= FI_ICMPERR;
756 minicmpsz = ICMP6ERR_IPICMPHLEN - sizeof(ip6_t);
757 if (fin->fin_plen < ICMP6ERR_IPICMPHLEN)
758 break;
760 if (M_LEN(fin->fin_m) < fin->fin_plen) {
761 if (fr_coalesce(fin) != 1)
762 return;
766 * If the destination of this packet doesn't match the
767 * source of the original packet then this packet is
768 * not correct.
770 icmp6 = fin->fin_dp;
771 ip6 = (ip6_t *)((char *)icmp6 + ICMPERR_ICMPHLEN);
772 if (IP6_NEQ(&fin->fin_fi.fi_dst,
773 &ip6->ip6_src))
774 fin->fin_flx |= FI_BAD;
776 break;
777 default :
778 break;
782 frpr_short6(fin, minicmpsz);
786 /* ------------------------------------------------------------------------ */
787 /* Function: frpr_udp6 */
788 /* Returns: void */
789 /* Parameters: fin(I) - pointer to packet information */
790 /* */
791 /* IPv6 Only */
792 /* Analyse the packet for IPv6/UDP properties. */
793 /* Is not expected to be called for fragmented packets. */
794 /* ------------------------------------------------------------------------ */
795 static INLINE void frpr_udp6(fin)
796 fr_info_t *fin;
799 frpr_short6(fin, sizeof(struct udphdr));
801 if (frpr_udpcommon(fin) == 0) {
802 u_char p = fin->fin_p;
804 fin->fin_p = IPPROTO_UDP;
805 fr_checkv6sum(fin);
806 fin->fin_p = p;
811 /* ------------------------------------------------------------------------ */
812 /* Function: frpr_tcp6 */
813 /* Returns: void */
814 /* Parameters: fin(I) - pointer to packet information */
815 /* */
816 /* IPv6 Only */
817 /* Analyse the packet for IPv6/TCP properties. */
818 /* Is not expected to be called for fragmented packets. */
819 /* ------------------------------------------------------------------------ */
820 static INLINE void frpr_tcp6(fin)
821 fr_info_t *fin;
824 frpr_short6(fin, sizeof(struct tcphdr));
826 if (frpr_tcpcommon(fin) == 0) {
827 u_char p = fin->fin_p;
829 fin->fin_p = IPPROTO_TCP;
830 fr_checkv6sum(fin);
831 fin->fin_p = p;
836 /* ------------------------------------------------------------------------ */
837 /* Function: frpr_esp6 */
838 /* Returns: void */
839 /* Parameters: fin(I) - pointer to packet information */
840 /* */
841 /* IPv6 Only */
842 /* Analyse the packet for ESP properties. */
843 /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */
844 /* even though the newer ESP packets must also have a sequence number that */
845 /* is 32bits as well, it is not possible(?) to determine the version from a */
846 /* simple packet header. */
847 /* ------------------------------------------------------------------------ */
848 static INLINE void frpr_esp6(fin)
849 fr_info_t *fin;
852 frpr_short6(fin, sizeof(grehdr_t));
854 (void) frpr_pullup(fin, 8);
858 /* ------------------------------------------------------------------------ */
859 /* Function: frpr_ah6 */
860 /* Returns: void */
861 /* Parameters: fin(I) - pointer to packet information */
862 /* */
863 /* IPv6 Only */
864 /* Analyse the packet for AH properties. */
865 /* The minimum length is taken to be the combination of all fields in the */
866 /* header being present and no authentication data (null algorithm used.) */
867 /* ------------------------------------------------------------------------ */
868 static INLINE int frpr_ah6(fin)
869 fr_info_t *fin;
871 authhdr_t *ah;
873 frpr_short6(fin, 12);
875 if (frpr_pullup(fin, sizeof(*ah)) == -1)
876 return IPPROTO_NONE;
878 ah = (authhdr_t *)fin->fin_dp;
879 return ah->ah_next;
883 /* ------------------------------------------------------------------------ */
884 /* Function: frpr_gre6 */
885 /* Returns: void */
886 /* Parameters: fin(I) - pointer to packet information */
887 /* */
888 /* Analyse the packet for GRE properties. */
889 /* ------------------------------------------------------------------------ */
890 static INLINE void frpr_gre6(fin)
891 fr_info_t *fin;
893 grehdr_t *gre;
895 frpr_short6(fin, sizeof(grehdr_t));
897 if (frpr_pullup(fin, sizeof(grehdr_t)) == -1)
898 return;
900 gre = fin->fin_dp;
901 if (GRE_REV(gre->gr_flags) == 1)
902 fin->fin_data[0] = gre->gr_call;
904 #endif /* USE_INET6 */
907 /* ------------------------------------------------------------------------ */
908 /* Function: frpr_pullup */
909 /* Returns: int - 0 == pullup succeeded, -1 == failure */
910 /* Parameters: fin(I) - pointer to packet information */
911 /* plen(I) - length (excluding L3 header) to pullup */
912 /* */
913 /* Short inline function to cut down on code duplication to perform a call */
914 /* to fr_pullup to ensure there is the required amount of data, */
915 /* consecutively in the packet buffer. */
916 /* */
917 /* This function pulls up 'extra' data at the location of fin_dp. fin_dp */
918 /* points to the first byte after the complete layer 3 header, which will */
919 /* include all of the known extension headers for IPv6 or options for IPv4. */
920 /* */
921 /* Since fr_pullup() expects the total length of bytes to be pulled up, it */
922 /* is necessary to add those we can already assume to be pulled up (fin_dp */
923 /* - fin_ip) to what is passed through. */
924 /* ------------------------------------------------------------------------ */
925 static INLINE int frpr_pullup(fin, plen)
926 fr_info_t *fin;
927 int plen;
929 if (fin->fin_m != NULL) {
930 if (fin->fin_dp != NULL)
931 plen += (char *)fin->fin_dp -
932 ((char *)fin->fin_ip + fin->fin_hlen);
933 plen += fin->fin_hlen;
934 if (M_LEN(fin->fin_m) < plen) {
935 #if defined(_KERNEL)
936 if (fr_pullup(fin->fin_m, fin, plen) == NULL)
937 return -1;
938 #else
940 * Fake fr_pullup failing
942 *fin->fin_mp = NULL;
943 fin->fin_m = NULL;
944 fin->fin_ip = NULL;
945 return -1;
946 #endif
949 return 0;
953 /* ------------------------------------------------------------------------ */
954 /* Function: frpr_short */
955 /* Returns: void */
956 /* Parameters: fin(I) - pointer to packet information */
957 /* xmin(I) - minimum header size */
958 /* */
959 /* Check if a packet is "short" as defined by xmin. The rule we are */
960 /* applying here is that the packet must not be fragmented within the layer */
961 /* 4 header. That is, it must not be a fragment that has its offset set to */
962 /* start within the layer 4 header (hdrmin) or if it is at offset 0, the */
963 /* entire layer 4 header must be present (min). */
964 /* ------------------------------------------------------------------------ */
965 static INLINE void frpr_short(fin, xmin)
966 fr_info_t *fin;
967 int xmin;
970 if (fin->fin_off == 0) {
971 if (fin->fin_dlen < xmin)
972 fin->fin_flx |= FI_SHORT;
973 } else if (fin->fin_off < xmin) {
974 fin->fin_flx |= FI_SHORT;
979 /* ------------------------------------------------------------------------ */
980 /* Function: frpr_icmp */
981 /* Returns: void */
982 /* Parameters: fin(I) - pointer to packet information */
983 /* */
984 /* IPv4 Only */
985 /* Do a sanity check on the packet for ICMP (v4). In nearly all cases, */
986 /* except extrememly bad packets, both type and code will be present. */
987 /* The expected minimum size of an ICMP packet is very much dependent on */
988 /* the type of it. */
989 /* */
990 /* XXX - other ICMP sanity checks? */
991 /* ------------------------------------------------------------------------ */
992 static INLINE void frpr_icmp(fin)
993 fr_info_t *fin;
995 int minicmpsz = sizeof(struct icmp);
996 icmphdr_t *icmp;
997 ip_t *oip;
999 if (fin->fin_off != 0) {
1000 frpr_short(fin, ICMPERR_ICMPHLEN);
1001 return;
1004 if (frpr_pullup(fin, ICMPERR_ICMPHLEN) == -1)
1005 return;
1007 icmp = fin->fin_dp;
1009 fin->fin_data[0] = *(u_short *)icmp;
1010 fin->fin_data[1] = icmp->icmp_id;
1012 switch (icmp->icmp_type)
1014 case ICMP_ECHOREPLY :
1015 case ICMP_ECHO :
1016 /* Router discovery messaes - RFC 1256 */
1017 case ICMP_ROUTERADVERT :
1018 case ICMP_ROUTERSOLICIT :
1019 minicmpsz = ICMP_MINLEN;
1020 break;
1022 * type(1) + code(1) + cksum(2) + id(2) seq(2) +
1023 * 3 * timestamp(3 * 4)
1025 case ICMP_TSTAMP :
1026 case ICMP_TSTAMPREPLY :
1027 minicmpsz = 20;
1028 break;
1030 * type(1) + code(1) + cksum(2) + id(2) seq(2) +
1031 * mask(4)
1033 case ICMP_MASKREQ :
1034 case ICMP_MASKREPLY :
1035 minicmpsz = 12;
1036 break;
1038 * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+)
1040 case ICMP_UNREACH :
1041 #ifdef icmp_nextmtu
1042 if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) {
1043 if (icmp->icmp_nextmtu < fr_icmpminfragmtu)
1044 fin->fin_flx |= FI_BAD;
1046 #endif
1047 case ICMP_SOURCEQUENCH :
1048 case ICMP_REDIRECT :
1049 case ICMP_TIMXCEED :
1050 case ICMP_PARAMPROB :
1051 fin->fin_flx |= FI_ICMPERR;
1052 if (fr_coalesce(fin) != 1)
1053 return;
1055 * ICMP error packets should not be generated for IP
1056 * packets that are a fragment that isn't the first
1057 * fragment.
1059 oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN);
1060 if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0)
1061 fin->fin_flx |= FI_BAD;
1064 * If the destination of this packet doesn't match the
1065 * source of the original packet then this packet is
1066 * not correct.
1068 if (oip->ip_src.s_addr != fin->fin_daddr)
1069 fin->fin_flx |= FI_BAD;
1072 * If the destination of this packet doesn't match the
1073 * source of the original packet then this packet is
1074 * not correct.
1076 if (oip->ip_src.s_addr != fin->fin_daddr)
1077 fin->fin_flx |= FI_BAD;
1078 break;
1079 default :
1080 break;
1083 frpr_short(fin, minicmpsz);
1085 if ((fin->fin_flx & FI_FRAG) == 0)
1086 fr_checkv4sum(fin);
1090 /* ------------------------------------------------------------------------ */
1091 /* Function: frpr_tcpcommon */
1092 /* Returns: int - 0 = header ok, 1 = bad packet, -1 = buffer error */
1093 /* Parameters: fin(I) - pointer to packet information */
1094 /* */
1095 /* TCP header sanity checking. Look for bad combinations of TCP flags, */
1096 /* and make some checks with how they interact with other fields. */
1097 /* If compiled with IPFILTER_CKSUM, check to see if the TCP checksum is */
1098 /* valid and mark the packet as bad if not. */
1099 /* ------------------------------------------------------------------------ */
1100 static INLINE int frpr_tcpcommon(fin)
1101 fr_info_t *fin;
1103 int flags, tlen;
1104 tcphdr_t *tcp;
1106 fin->fin_flx |= FI_TCPUDP;
1107 if (fin->fin_off != 0)
1108 return 0;
1110 if (frpr_pullup(fin, sizeof(*tcp)) == -1)
1111 return -1;
1112 tcp = fin->fin_dp;
1114 if (fin->fin_dlen > 3) {
1115 fin->fin_sport = ntohs(tcp->th_sport);
1116 fin->fin_dport = ntohs(tcp->th_dport);
1119 if ((fin->fin_flx & FI_SHORT) != 0)
1120 return 1;
1123 * Use of the TCP data offset *must* result in a value that is at
1124 * least the same size as the TCP header.
1126 tlen = TCP_OFF(tcp) << 2;
1127 if (tlen < sizeof(tcphdr_t)) {
1128 fin->fin_flx |= FI_BAD;
1129 return 1;
1132 flags = tcp->th_flags;
1133 fin->fin_tcpf = tcp->th_flags;
1136 * If the urgent flag is set, then the urgent pointer must
1137 * also be set and vice versa. Good TCP packets do not have
1138 * just one of these set.
1140 if ((flags & TH_URG) != 0 && (tcp->th_urp == 0)) {
1141 fin->fin_flx |= FI_BAD;
1142 #if 0
1143 } else if ((flags & TH_URG) == 0 && (tcp->th_urp != 0)) {
1145 * Ignore this case (#if 0) as it shows up in "real"
1146 * traffic with bogus values in the urgent pointer field.
1148 fin->fin_flx |= FI_BAD;
1149 #endif
1150 } else if (((flags & (TH_SYN|TH_FIN)) != 0) &&
1151 ((flags & (TH_RST|TH_ACK)) == TH_RST)) {
1152 /* TH_FIN|TH_RST|TH_ACK seems to appear "naturally" */
1153 fin->fin_flx |= FI_BAD;
1154 #if 1
1155 } else if (((flags & TH_SYN) != 0) &&
1156 ((flags & (TH_URG|TH_PUSH)) != 0)) {
1158 * SYN with URG and PUSH set is not for normal TCP but it is
1159 * possible(?) with T/TCP...but who uses T/TCP?
1161 fin->fin_flx |= FI_BAD;
1162 #endif
1163 } else if (!(flags & TH_ACK)) {
1165 * If the ack bit isn't set, then either the SYN or
1166 * RST bit must be set. If the SYN bit is set, then
1167 * we expect the ACK field to be 0. If the ACK is
1168 * not set and if URG, PSH or FIN are set, consdier
1169 * that to indicate a bad TCP packet.
1171 if ((flags == TH_SYN) && (tcp->th_ack != 0)) {
1173 * Cisco PIX sets the ACK field to a random value.
1174 * In light of this, do not set FI_BAD until a patch
1175 * is available from Cisco to ensure that
1176 * interoperability between existing systems is
1177 * achieved.
1179 /*fin->fin_flx |= FI_BAD*/;
1180 } else if (!(flags & (TH_RST|TH_SYN))) {
1181 fin->fin_flx |= FI_BAD;
1182 } else if ((flags & (TH_URG|TH_PUSH|TH_FIN)) != 0) {
1183 fin->fin_flx |= FI_BAD;
1188 * At this point, it's not exactly clear what is to be gained by
1189 * marking up which TCP options are and are not present. The one we
1190 * are most interested in is the TCP window scale. This is only in
1191 * a SYN packet [RFC1323] so we don't need this here...?
1192 * Now if we were to analyse the header for passive fingerprinting,
1193 * then that might add some weight to adding this...
1195 if (tlen == sizeof(tcphdr_t))
1196 return 0;
1198 if (frpr_pullup(fin, tlen) == -1)
1199 return -1;
1201 #if 0
1202 tcp = fin->fin_dp;
1203 ip = fin->fin_ip;
1204 s = (u_char *)(tcp + 1);
1205 off = IP_HL(ip) << 2;
1206 # ifdef _KERNEL
1207 if (fin->fin_mp != NULL) {
1208 mb_t *m = *fin->fin_mp;
1210 if (off + tlen > M_LEN(m))
1211 return;
1213 # endif
1214 for (tlen -= (int)sizeof(*tcp); tlen > 0; ) {
1215 opt = *s;
1216 if (opt == '\0')
1217 break;
1218 else if (opt == TCPOPT_NOP)
1219 ol = 1;
1220 else {
1221 if (tlen < 2)
1222 break;
1223 ol = (int)*(s + 1);
1224 if (ol < 2 || ol > tlen)
1225 break;
1228 for (i = 9, mv = 4; mv >= 0; ) {
1229 op = ipopts + i;
1230 if (opt == (u_char)op->ol_val) {
1231 optmsk |= op->ol_bit;
1232 break;
1235 tlen -= ol;
1236 s += ol;
1238 #endif /* 0 */
1240 return 0;
1245 /* ------------------------------------------------------------------------ */
1246 /* Function: frpr_udpcommon */
1247 /* Returns: int - 0 = header ok, 1 = bad packet */
1248 /* Parameters: fin(I) - pointer to packet information */
1249 /* */
1250 /* Extract the UDP source and destination ports, if present. If compiled */
1251 /* with IPFILTER_CKSUM, check to see if the UDP checksum is valid. */
1252 /* ------------------------------------------------------------------------ */
1253 static INLINE int frpr_udpcommon(fin)
1254 fr_info_t *fin;
1256 udphdr_t *udp;
1258 fin->fin_flx |= FI_TCPUDP;
1260 if (!fin->fin_off && (fin->fin_dlen > 3)) {
1261 if (frpr_pullup(fin, sizeof(*udp)) == -1) {
1262 fin->fin_flx |= FI_SHORT;
1263 return 1;
1266 udp = fin->fin_dp;
1268 fin->fin_sport = ntohs(udp->uh_sport);
1269 fin->fin_dport = ntohs(udp->uh_dport);
1272 return 0;
1276 /* ------------------------------------------------------------------------ */
1277 /* Function: frpr_tcp */
1278 /* Returns: void */
1279 /* Parameters: fin(I) - pointer to packet information */
1280 /* */
1281 /* IPv4 Only */
1282 /* Analyse the packet for IPv4/TCP properties. */
1283 /* ------------------------------------------------------------------------ */
1284 static INLINE void frpr_tcp(fin)
1285 fr_info_t *fin;
1288 frpr_short(fin, sizeof(tcphdr_t));
1290 if (frpr_tcpcommon(fin) == 0) {
1291 if ((fin->fin_flx & FI_FRAG) == 0)
1292 fr_checkv4sum(fin);
1297 /* ------------------------------------------------------------------------ */
1298 /* Function: frpr_udp */
1299 /* Returns: void */
1300 /* Parameters: fin(I) - pointer to packet information */
1301 /* */
1302 /* IPv4 Only */
1303 /* Analyse the packet for IPv4/UDP properties. */
1304 /* ------------------------------------------------------------------------ */
1305 static INLINE void frpr_udp(fin)
1306 fr_info_t *fin;
1309 frpr_short(fin, sizeof(udphdr_t));
1311 if (frpr_udpcommon(fin) == 0) {
1312 if ((fin->fin_flx & FI_FRAG) == 0)
1313 fr_checkv4sum(fin);
1318 /* ------------------------------------------------------------------------ */
1319 /* Function: frpr_esp */
1320 /* Returns: void */
1321 /* Parameters: fin(I) - pointer to packet information */
1322 /* */
1323 /* Analyse the packet for ESP properties. */
1324 /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */
1325 /* even though the newer ESP packets must also have a sequence number that */
1326 /* is 32bits as well, it is not possible(?) to determine the version from a */
1327 /* simple packet header. */
1328 /* ------------------------------------------------------------------------ */
1329 static INLINE void frpr_esp(fin)
1330 fr_info_t *fin;
1333 if (fin->fin_off == 0) {
1334 frpr_short(fin, 8);
1335 (void) frpr_pullup(fin, 8);
1341 /* ------------------------------------------------------------------------ */
1342 /* Function: frpr_ah */
1343 /* Returns: void */
1344 /* Parameters: fin(I) - pointer to packet information */
1345 /* */
1346 /* Analyse the packet for AH properties. */
1347 /* The minimum length is taken to be the combination of all fields in the */
1348 /* header being present and no authentication data (null algorithm used.) */
1349 /* ------------------------------------------------------------------------ */
1350 static INLINE void frpr_ah(fin)
1351 fr_info_t *fin;
1353 authhdr_t *ah;
1354 int len;
1356 frpr_short(fin, sizeof(*ah));
1358 if (((fin->fin_flx & FI_SHORT) != 0) || (fin->fin_off != 0))
1359 return;
1361 if (frpr_pullup(fin, sizeof(*ah)) == -1)
1362 return;
1364 ah = (authhdr_t *)fin->fin_dp;
1366 len = (ah->ah_plen + 2) << 2;
1367 frpr_short(fin, len);
1371 /* ------------------------------------------------------------------------ */
1372 /* Function: frpr_gre */
1373 /* Returns: void */
1374 /* Parameters: fin(I) - pointer to packet information */
1375 /* */
1376 /* Analyse the packet for GRE properties. */
1377 /* ------------------------------------------------------------------------ */
1378 static INLINE void frpr_gre(fin)
1379 fr_info_t *fin;
1381 grehdr_t *gre;
1383 frpr_short(fin, sizeof(*gre));
1385 if (fin->fin_off != 0)
1386 return;
1388 if (frpr_pullup(fin, sizeof(*gre)) == -1)
1389 return;
1391 if (fin->fin_off == 0) {
1392 gre = fin->fin_dp;
1393 if (GRE_REV(gre->gr_flags) == 1)
1394 fin->fin_data[0] = gre->gr_call;
1399 /* ------------------------------------------------------------------------ */
1400 /* Function: frpr_ipv4hdr */
1401 /* Returns: void */
1402 /* Parameters: fin(I) - pointer to packet information */
1403 /* */
1404 /* IPv4 Only */
1405 /* Analyze the IPv4 header and set fields in the fr_info_t structure. */
1406 /* Check all options present and flag their presence if any exist. */
1407 /* ------------------------------------------------------------------------ */
1408 static INLINE void frpr_ipv4hdr(fin)
1409 fr_info_t *fin;
1411 u_short optmsk = 0, secmsk = 0, auth = 0;
1412 int hlen, ol, mv, p, i;
1413 const struct optlist *op;
1414 u_char *s, opt;
1415 u_short off;
1416 fr_ip_t *fi;
1417 ip_t *ip;
1419 fi = &fin->fin_fi;
1420 hlen = fin->fin_hlen;
1422 ip = fin->fin_ip;
1423 p = ip->ip_p;
1424 fi->fi_p = p;
1425 fi->fi_tos = ip->ip_tos;
1426 fin->fin_id = ip->ip_id;
1427 off = ip->ip_off;
1429 /* Get both TTL and protocol */
1430 fi->fi_p = ip->ip_p;
1431 fi->fi_ttl = ip->ip_ttl;
1432 #if 0
1433 (*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4));
1434 #endif
1436 /* Zero out bits not used in IPv6 address */
1437 fi->fi_src.i6[1] = 0;
1438 fi->fi_src.i6[2] = 0;
1439 fi->fi_src.i6[3] = 0;
1440 fi->fi_dst.i6[1] = 0;
1441 fi->fi_dst.i6[2] = 0;
1442 fi->fi_dst.i6[3] = 0;
1444 fi->fi_saddr = ip->ip_src.s_addr;
1445 fi->fi_daddr = ip->ip_dst.s_addr;
1448 * set packet attribute flags based on the offset and
1449 * calculate the byte offset that it represents.
1451 off &= IP_MF|IP_OFFMASK;
1452 if (off != 0) {
1453 int morefrag = off & IP_MF;
1455 fi->fi_flx |= FI_FRAG;
1456 if (morefrag)
1457 fi->fi_flx |= FI_MOREFRAG;
1458 off &= IP_OFFMASK;
1459 if (off != 0) {
1460 fin->fin_flx |= FI_FRAGBODY;
1461 off <<= 3;
1462 if ((off + fin->fin_dlen > 65535) ||
1463 (fin->fin_dlen == 0) ||
1464 ((morefrag != 0) && ((fin->fin_dlen & 7) != 0))) {
1466 * The length of the packet, starting at its
1467 * offset cannot exceed 65535 (0xffff) as the
1468 * length of an IP packet is only 16 bits.
1470 * Any fragment that isn't the last fragment
1471 * must have a length greater than 0 and it
1472 * must be an even multiple of 8.
1474 fi->fi_flx |= FI_BAD;
1478 fin->fin_off = off;
1481 * Call per-protocol setup and checking
1483 switch (p)
1485 case IPPROTO_UDP :
1486 frpr_udp(fin);
1487 break;
1488 case IPPROTO_TCP :
1489 frpr_tcp(fin);
1490 break;
1491 case IPPROTO_ICMP :
1492 frpr_icmp(fin);
1493 break;
1494 case IPPROTO_AH :
1495 frpr_ah(fin);
1496 break;
1497 case IPPROTO_ESP :
1498 frpr_esp(fin);
1499 break;
1500 case IPPROTO_GRE :
1501 frpr_gre(fin);
1502 break;
1505 ip = fin->fin_ip;
1506 if (ip == NULL)
1507 return;
1510 * If it is a standard IP header (no options), set the flag fields
1511 * which relate to options to 0.
1513 if (hlen == sizeof(*ip)) {
1514 fi->fi_optmsk = 0;
1515 fi->fi_secmsk = 0;
1516 fi->fi_auth = 0;
1517 return;
1521 * So the IP header has some IP options attached. Walk the entire
1522 * list of options present with this packet and set flags to indicate
1523 * which ones are here and which ones are not. For the somewhat out
1524 * of date and obscure security classification options, set a flag to
1525 * represent which classification is present.
1527 fi->fi_flx |= FI_OPTIONS;
1529 for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) {
1530 opt = *s;
1531 if (opt == '\0')
1532 break;
1533 else if (opt == IPOPT_NOP)
1534 ol = 1;
1535 else {
1536 if (hlen < 2)
1537 break;
1538 ol = (int)*(s + 1);
1539 if (ol < 2 || ol > hlen)
1540 break;
1542 for (i = 9, mv = 4; mv >= 0; ) {
1543 op = ipopts + i;
1544 if ((opt == (u_char)op->ol_val) && (ol > 4)) {
1545 optmsk |= op->ol_bit;
1546 if (opt == IPOPT_SECURITY) {
1547 const struct optlist *sp;
1548 u_char sec;
1549 int j, m;
1551 sec = *(s + 2); /* classification */
1552 for (j = 3, m = 2; m >= 0; ) {
1553 sp = secopt + j;
1554 if (sec == sp->ol_val) {
1555 secmsk |= sp->ol_bit;
1556 auth = *(s + 3);
1557 auth *= 256;
1558 auth += *(s + 4);
1559 break;
1561 if (sec < sp->ol_val)
1562 j -= m;
1563 else
1564 j += m;
1565 m--;
1568 break;
1570 if (opt < op->ol_val)
1571 i -= mv;
1572 else
1573 i += mv;
1574 mv--;
1576 hlen -= ol;
1577 s += ol;
1583 if (auth && !(auth & 0x0100))
1584 auth &= 0xff00;
1585 fi->fi_optmsk = optmsk;
1586 fi->fi_secmsk = secmsk;
1587 fi->fi_auth = auth;
1591 /* ------------------------------------------------------------------------ */
1592 /* Function: fr_makefrip */
1593 /* Returns: void */
1594 /* Parameters: hlen(I) - length of IP packet header */
1595 /* ip(I) - pointer to the IP header */
1596 /* fin(IO) - pointer to packet information */
1597 /* */
1598 /* Compact the IP header into a structure which contains just the info. */
1599 /* which is useful for comparing IP headers with and store this information */
1600 /* in the fr_info_t structure pointer to by fin. At present, it is assumed */
1601 /* this function will be called with either an IPv4 or IPv6 packet. */
1602 /* ------------------------------------------------------------------------ */
1603 int fr_makefrip(hlen, ip, fin)
1604 int hlen;
1605 ip_t *ip;
1606 fr_info_t *fin;
1608 int v;
1610 fin->fin_depth = 0;
1611 fin->fin_hlen = (u_short)hlen;
1612 fin->fin_ip = ip;
1613 fin->fin_rule = 0xffffffff;
1614 fin->fin_group[0] = -1;
1615 fin->fin_group[1] = '\0';
1616 fin->fin_dp = (char *)ip + hlen;
1618 v = fin->fin_v;
1619 if (v == 4) {
1620 fin->fin_plen = ip->ip_len;
1621 fin->fin_dlen = fin->fin_plen - hlen;
1623 frpr_ipv4hdr(fin);
1624 #ifdef USE_INET6
1625 } else if (v == 6) {
1626 fin->fin_plen = ntohs(((ip6_t *)ip)->ip6_plen);
1627 fin->fin_dlen = fin->fin_plen;
1628 fin->fin_plen += hlen;
1630 frpr_ipv6hdr(fin);
1631 #endif
1633 if (fin->fin_ip == NULL)
1634 return -1;
1635 return 0;
1639 /* ------------------------------------------------------------------------ */
1640 /* Function: fr_portcheck */
1641 /* Returns: int - 1 == port matched, 0 == port match failed */
1642 /* Parameters: frp(I) - pointer to port check `expression' */
1643 /* pop(I) - pointer to port number to evaluate */
1644 /* */
1645 /* Perform a comparison of a port number against some other(s), using a */
1646 /* structure with compare information stored in it. */
1647 /* ------------------------------------------------------------------------ */
1648 static INLINE int fr_portcheck(frp, pop)
1649 frpcmp_t *frp;
1650 u_short *pop;
1652 u_short tup, po;
1653 int err = 1;
1655 tup = *pop;
1656 po = frp->frp_port;
1659 * Do opposite test to that required and continue if that succeeds.
1661 switch (frp->frp_cmp)
1663 case FR_EQUAL :
1664 if (tup != po) /* EQUAL */
1665 err = 0;
1666 break;
1667 case FR_NEQUAL :
1668 if (tup == po) /* NOTEQUAL */
1669 err = 0;
1670 break;
1671 case FR_LESST :
1672 if (tup >= po) /* LESSTHAN */
1673 err = 0;
1674 break;
1675 case FR_GREATERT :
1676 if (tup <= po) /* GREATERTHAN */
1677 err = 0;
1678 break;
1679 case FR_LESSTE :
1680 if (tup > po) /* LT or EQ */
1681 err = 0;
1682 break;
1683 case FR_GREATERTE :
1684 if (tup < po) /* GT or EQ */
1685 err = 0;
1686 break;
1687 case FR_OUTRANGE :
1688 if (tup >= po && tup <= frp->frp_top) /* Out of range */
1689 err = 0;
1690 break;
1691 case FR_INRANGE :
1692 if (tup <= po || tup >= frp->frp_top) /* In range */
1693 err = 0;
1694 break;
1695 case FR_INCRANGE :
1696 if (tup < po || tup > frp->frp_top) /* Inclusive range */
1697 err = 0;
1698 break;
1699 default :
1700 break;
1702 return err;
1706 /* ------------------------------------------------------------------------ */
1707 /* Function: fr_tcpudpchk */
1708 /* Returns: int - 1 == protocol matched, 0 == check failed */
1709 /* Parameters: fin(I) - pointer to packet information */
1710 /* ft(I) - pointer to structure with comparison data */
1711 /* */
1712 /* Compares the current pcket (assuming it is TCP/UDP) information with a */
1713 /* structure containing information that we want to match against. */
1714 /* ------------------------------------------------------------------------ */
1715 int fr_tcpudpchk(fin, ft)
1716 fr_info_t *fin;
1717 frtuc_t *ft;
1719 int err = 1;
1722 * Both ports should *always* be in the first fragment.
1723 * So far, I cannot find any cases where they can not be.
1725 * compare destination ports
1727 if (ft->ftu_dcmp)
1728 err = fr_portcheck(&ft->ftu_dst, &fin->fin_dport);
1731 * compare source ports
1733 if (err && ft->ftu_scmp)
1734 err = fr_portcheck(&ft->ftu_src, &fin->fin_sport);
1737 * If we don't have all the TCP/UDP header, then how can we
1738 * expect to do any sort of match on it ? If we were looking for
1739 * TCP flags, then NO match. If not, then match (which should
1740 * satisfy the "short" class too).
1742 if (err && (fin->fin_p == IPPROTO_TCP)) {
1743 if (fin->fin_flx & FI_SHORT)
1744 return !(ft->ftu_tcpf | ft->ftu_tcpfm);
1746 * Match the flags ? If not, abort this match.
1748 if (ft->ftu_tcpfm &&
1749 ft->ftu_tcpf != (fin->fin_tcpf & ft->ftu_tcpfm)) {
1750 FR_DEBUG(("f. %#x & %#x != %#x\n", fin->fin_tcpf,
1751 ft->ftu_tcpfm, ft->ftu_tcpf));
1752 err = 0;
1755 return err;
1760 /* ------------------------------------------------------------------------ */
1761 /* Function: fr_ipfcheck */
1762 /* Returns: int - 0 == match, 1 == no match */
1763 /* Parameters: fin(I) - pointer to packet information */
1764 /* fr(I) - pointer to filter rule */
1765 /* portcmp(I) - flag indicating whether to attempt matching on */
1766 /* TCP/UDP port data. */
1767 /* */
1768 /* Check to see if a packet matches an IPFilter rule. Checks of addresses, */
1769 /* port numbers, etc, for "standard" IPFilter rules are all orchestrated in */
1770 /* this function. */
1771 /* ------------------------------------------------------------------------ */
1772 static INLINE int fr_ipfcheck(fin, fr, portcmp)
1773 fr_info_t *fin;
1774 frentry_t *fr;
1775 int portcmp;
1777 u_32_t *ld, *lm, *lip;
1778 fripf_t *fri;
1779 fr_ip_t *fi;
1780 int i;
1782 fi = &fin->fin_fi;
1783 fri = fr->fr_ipf;
1784 lip = (u_32_t *)fi;
1785 lm = (u_32_t *)&fri->fri_mip;
1786 ld = (u_32_t *)&fri->fri_ip;
1789 * first 32 bits to check coversion:
1790 * IP version, TOS, TTL, protocol
1792 i = ((*lip & *lm) != *ld);
1793 FR_DEBUG(("0. %#08x & %#08x != %#08x\n",
1794 ntohl(*lip), ntohl(*lm), ntohl(*ld)));
1795 if (i)
1796 return 1;
1799 * Next 32 bits is a constructed bitmask indicating which IP options
1800 * are present (if any) in this packet.
1802 lip++, lm++, ld++;
1803 i |= ((*lip & *lm) != *ld);
1804 FR_DEBUG(("1. %#08x & %#08x != %#08x\n",
1805 ntohl(*lip), ntohl(*lm), ntohl(*ld)));
1806 if (i)
1807 return 1;
1809 lip++, lm++, ld++;
1811 * Unrolled loops (4 each, for 32 bits) for address checks.
1814 * Check the source address.
1816 #ifdef IPFILTER_LOOKUP
1817 if (fr->fr_satype == FRI_LOOKUP) {
1818 i = (*fr->fr_srcfunc)(fr->fr_srcptr, fi->fi_v, lip);
1819 if (i == -1)
1820 return 1;
1821 lip += 3;
1822 lm += 3;
1823 ld += 3;
1824 } else {
1825 #endif
1826 i = ((*lip & *lm) != *ld);
1827 FR_DEBUG(("2a. %#08x & %#08x != %#08x\n",
1828 ntohl(*lip), ntohl(*lm), ntohl(*ld)));
1829 if (fi->fi_v == 6) {
1830 lip++, lm++, ld++;
1831 i |= ((*lip & *lm) != *ld);
1832 FR_DEBUG(("2b. %#08x & %#08x != %#08x\n",
1833 ntohl(*lip), ntohl(*lm), ntohl(*ld)));
1834 lip++, lm++, ld++;
1835 i |= ((*lip & *lm) != *ld);
1836 FR_DEBUG(("2c. %#08x & %#08x != %#08x\n",
1837 ntohl(*lip), ntohl(*lm), ntohl(*ld)));
1838 lip++, lm++, ld++;
1839 i |= ((*lip & *lm) != *ld);
1840 FR_DEBUG(("2d. %#08x & %#08x != %#08x\n",
1841 ntohl(*lip), ntohl(*lm), ntohl(*ld)));
1842 } else {
1843 lip += 3;
1844 lm += 3;
1845 ld += 3;
1847 #ifdef IPFILTER_LOOKUP
1849 #endif
1850 i ^= (fr->fr_flags & FR_NOTSRCIP) >> 6;
1851 if (i)
1852 return 1;
1855 * Check the destination address.
1857 lip++, lm++, ld++;
1858 #ifdef IPFILTER_LOOKUP
1859 if (fr->fr_datype == FRI_LOOKUP) {
1860 i = (*fr->fr_dstfunc)(fr->fr_dstptr, fi->fi_v, lip);
1861 if (i == -1)
1862 return 1;
1863 lip += 3;
1864 lm += 3;
1865 ld += 3;
1866 } else {
1867 #endif
1868 i = ((*lip & *lm) != *ld);
1869 FR_DEBUG(("3a. %#08x & %#08x != %#08x\n",
1870 ntohl(*lip), ntohl(*lm), ntohl(*ld)));
1871 if (fi->fi_v == 6) {
1872 lip++, lm++, ld++;
1873 i |= ((*lip & *lm) != *ld);
1874 FR_DEBUG(("3b. %#08x & %#08x != %#08x\n",
1875 ntohl(*lip), ntohl(*lm), ntohl(*ld)));
1876 lip++, lm++, ld++;
1877 i |= ((*lip & *lm) != *ld);
1878 FR_DEBUG(("3c. %#08x & %#08x != %#08x\n",
1879 ntohl(*lip), ntohl(*lm), ntohl(*ld)));
1880 lip++, lm++, ld++;
1881 i |= ((*lip & *lm) != *ld);
1882 FR_DEBUG(("3d. %#08x & %#08x != %#08x\n",
1883 ntohl(*lip), ntohl(*lm), ntohl(*ld)));
1884 } else {
1885 lip += 3;
1886 lm += 3;
1887 ld += 3;
1889 #ifdef IPFILTER_LOOKUP
1891 #endif
1892 i ^= (fr->fr_flags & FR_NOTDSTIP) >> 7;
1893 if (i)
1894 return 1;
1896 * IP addresses matched. The next 32bits contains:
1897 * mast of old IP header security & authentication bits.
1899 lip++, lm++, ld++;
1900 i |= ((*lip & *lm) != *ld);
1901 FR_DEBUG(("4. %#08x & %#08x != %#08x\n",
1902 *lip, *lm, *ld));
1905 * Next we have 32 bits of packet flags.
1907 lip++, lm++, ld++;
1908 i |= ((*lip & *lm) != *ld);
1909 FR_DEBUG(("5. %#08x & %#08x != %#08x\n",
1910 *lip, *lm, *ld));
1912 if (i == 0) {
1914 * If a fragment, then only the first has what we're
1915 * looking for here...
1917 if (portcmp) {
1918 if (!fr_tcpudpchk(fin, &fr->fr_tuc))
1919 i = 1;
1920 } else {
1921 if (fr->fr_dcmp || fr->fr_scmp ||
1922 fr->fr_tcpf || fr->fr_tcpfm)
1923 i = 1;
1924 if (fr->fr_icmpm || fr->fr_icmp) {
1925 if (((fi->fi_p != IPPROTO_ICMP) &&
1926 (fi->fi_p != IPPROTO_ICMPV6)) ||
1927 fin->fin_off || (fin->fin_dlen < 2))
1928 i = 1;
1929 else if ((fin->fin_data[0] & fr->fr_icmpm) !=
1930 fr->fr_icmp) {
1931 FR_DEBUG(("i. %#x & %#x != %#x\n",
1932 fin->fin_data[0],
1933 fr->fr_icmpm, fr->fr_icmp));
1934 i = 1;
1939 return i;
1943 /* ------------------------------------------------------------------------ */
1944 /* Function: fr_scanlist */
1945 /* Returns: int - result flags of scanning filter list */
1946 /* Parameters: fin(I) - pointer to packet information */
1947 /* pass(I) - default result to return for filtering */
1948 /* */
1949 /* Check the input/output list of rules for a match to the current packet. */
1950 /* If a match is found, the value of fr_flags from the rule becomes the */
1951 /* return value and fin->fin_fr points to the matched rule. */
1952 /* */
1953 /* This function may be called recusively upto 16 times (limit inbuilt.) */
1954 /* When unwinding, it should finish up with fin_depth as 0. */
1955 /* */
1956 /* Could be per interface, but this gets real nasty when you don't have, */
1957 /* or can't easily change, the kernel source code to . */
1958 /* ------------------------------------------------------------------------ */
1959 int fr_scanlist(fin, pass)
1960 fr_info_t *fin;
1961 u_32_t pass;
1963 int rulen, portcmp, off, skip;
1964 struct frentry *fr, *fnext;
1965 u_32_t passt, passo;
1968 * Do not allow nesting deeper than 16 levels.
1970 if (fin->fin_depth >= 16)
1971 return pass;
1973 fr = fin->fin_fr;
1976 * If there are no rules in this list, return now.
1978 if (fr == NULL)
1979 return pass;
1981 skip = 0;
1982 portcmp = 0;
1983 fin->fin_depth++;
1984 fin->fin_fr = NULL;
1985 off = fin->fin_off;
1987 if ((fin->fin_flx & FI_TCPUDP) && (fin->fin_dlen > 3) && !off)
1988 portcmp = 1;
1990 for (rulen = 0; fr; fr = fnext, rulen++) {
1991 fnext = fr->fr_next;
1992 if (skip != 0) {
1993 FR_VERBOSE(("%d (%#x)\n", skip, fr->fr_flags));
1994 skip--;
1995 continue;
1999 * In all checks below, a null (zero) value in the
2000 * filter struture is taken to mean a wildcard.
2002 * check that we are working for the right interface
2004 #ifdef _KERNEL
2005 if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp)
2006 continue;
2007 #else
2008 if (opts & (OPT_VERBOSE|OPT_DEBUG))
2009 printf("\n");
2010 FR_VERBOSE(("%c", FR_ISSKIP(pass) ? 's' :
2011 FR_ISPASS(pass) ? 'p' :
2012 FR_ISACCOUNT(pass) ? 'A' :
2013 FR_ISAUTH(pass) ? 'a' :
2014 (pass & FR_NOMATCH) ? 'n' :'b'));
2015 if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp)
2016 continue;
2017 FR_VERBOSE((":i"));
2018 #endif
2020 switch (fr->fr_type)
2022 case FR_T_IPF :
2023 case FR_T_IPF|FR_T_BUILTIN :
2024 if (fr_ipfcheck(fin, fr, portcmp))
2025 continue;
2026 break;
2027 #if defined(IPFILTER_BPF)
2028 case FR_T_BPFOPC :
2029 case FR_T_BPFOPC|FR_T_BUILTIN :
2031 u_char *mc;
2033 if (*fin->fin_mp == NULL)
2034 continue;
2035 if (fin->fin_v != fr->fr_v)
2036 continue;
2037 mc = (u_char *)fin->fin_m;
2038 if (!bpf_filter(fr->fr_data, mc, fin->fin_plen, 0))
2039 continue;
2040 break;
2042 #endif
2043 case FR_T_CALLFUNC|FR_T_BUILTIN :
2045 frentry_t *f;
2047 f = (*fr->fr_func)(fin, &pass);
2048 if (f != NULL)
2049 fr = f;
2050 else
2051 continue;
2052 break;
2054 default :
2055 break;
2058 if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) {
2059 if (fin->fin_nattag == NULL)
2060 continue;
2061 if (fr_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0)
2062 continue;
2064 FR_VERBOSE(("=%s.%d *", fr->fr_group, rulen));
2066 passt = fr->fr_flags;
2069 * Allowing a rule with the "keep state" flag set to match
2070 * packets that have been tagged "out of window" by the TCP
2071 * state tracking is foolish as the attempt to add a new
2072 * state entry to the table will fail.
2074 if ((passt & FR_KEEPSTATE) && (fin->fin_flx & FI_OOW))
2075 continue;
2078 * If the rule is a "call now" rule, then call the function
2079 * in the rule, if it exists and use the results from that.
2080 * If the function pointer is bad, just make like we ignore
2081 * it, except for increasing the hit counter.
2083 if ((passt & FR_CALLNOW) != 0) {
2084 frentry_t *frs;
2086 ATOMIC_INC64(fr->fr_hits);
2087 if ((fr->fr_func != NULL) &&
2088 (fr->fr_func == (ipfunc_t)-1))
2089 continue;
2091 frs = fin->fin_fr;
2092 fin->fin_fr = fr;
2093 fr = (*fr->fr_func)(fin, &passt);
2094 if (fr == NULL) {
2095 fin->fin_fr = frs;
2096 continue;
2098 passt = fr->fr_flags;
2100 fin->fin_fr = fr;
2102 #ifdef IPFILTER_LOG
2104 * Just log this packet...
2106 if ((passt & FR_LOGMASK) == FR_LOG) {
2107 if (ipflog(fin, passt) == -1) {
2108 if (passt & FR_LOGORBLOCK) {
2109 passt &= ~FR_CMDMASK;
2110 passt |= FR_BLOCK|FR_QUICK;
2112 ATOMIC_INCL(frstats[fin->fin_out].fr_skip);
2114 ATOMIC_INCL(frstats[fin->fin_out].fr_pkl);
2115 fin->fin_flx |= FI_DONTCACHE;
2117 #endif /* IPFILTER_LOG */
2118 fr->fr_bytes += (U_QUAD_T)fin->fin_plen;
2119 passo = pass;
2120 if (FR_ISSKIP(passt))
2121 skip = fr->fr_arg;
2122 else if ((passt & FR_LOGMASK) != FR_LOG)
2123 pass = passt;
2124 if (passt & (FR_RETICMP|FR_FAKEICMP))
2125 fin->fin_icode = fr->fr_icode;
2126 FR_DEBUG(("pass %#x\n", pass));
2127 ATOMIC_INC64(fr->fr_hits);
2128 fin->fin_rule = rulen;
2129 (void) strncpy(fin->fin_group, fr->fr_group, FR_GROUPLEN);
2130 if (fr->fr_grp != NULL) {
2131 fin->fin_fr = *fr->fr_grp;
2132 passt = fr_scanlist(fin, pass);
2133 if (fin->fin_fr == NULL) {
2134 fin->fin_rule = rulen;
2135 (void) strncpy(fin->fin_group, fr->fr_group,
2136 FR_GROUPLEN);
2137 fin->fin_fr = fr;
2138 passt = pass;
2140 pass = passt;
2143 if (passt & FR_QUICK) {
2145 * Finally, if we've asked to track state for this
2146 * packet, set it up. Add state for "quick" rules
2147 * here so that if the action fails we can consider
2148 * the rule to "not match" and keep on processing
2149 * filter rules.
2151 if ((pass & FR_KEEPSTATE) && !FR_ISAUTH(pass) &&
2152 !(fin->fin_flx & FI_STATE)) {
2153 int out = fin->fin_out;
2155 fin->fin_fr = fr;
2156 if (fr_addstate(fin, NULL, 0) != NULL) {
2157 ATOMIC_INCL(frstats[out].fr_ads);
2158 } else {
2159 ATOMIC_INCL(frstats[out].fr_bads);
2160 pass = passo;
2161 continue;
2164 break;
2167 fin->fin_depth--;
2168 return pass;
2172 /* ------------------------------------------------------------------------ */
2173 /* Function: fr_acctpkt */
2174 /* Returns: frentry_t* - always returns NULL */
2175 /* Parameters: fin(I) - pointer to packet information */
2176 /* passp(IO) - pointer to current/new filter decision (unused) */
2177 /* */
2178 /* Checks a packet against accounting rules, if there are any for the given */
2179 /* IP protocol version. */
2180 /* */
2181 /* N.B.: this function returns NULL to match the prototype used by other */
2182 /* functions called from the IPFilter "mainline" in fr_check(). */
2183 /* ------------------------------------------------------------------------ */
2184 frentry_t *fr_acctpkt(fin, passp)
2185 fr_info_t *fin;
2186 u_32_t *passp;
2188 char group[FR_GROUPLEN];
2189 frentry_t *fr, *frsave;
2190 u_32_t pass, rulen;
2192 passp = passp;
2193 #ifdef USE_INET6
2194 if (fin->fin_v == 6)
2195 fr = ipacct6[fin->fin_out][fr_active];
2196 else
2197 #endif
2198 fr = ipacct[fin->fin_out][fr_active];
2200 if (fr != NULL) {
2201 frsave = fin->fin_fr;
2202 bcopy(fin->fin_group, group, FR_GROUPLEN);
2203 rulen = fin->fin_rule;
2204 fin->fin_fr = fr;
2205 pass = fr_scanlist(fin, FR_NOMATCH);
2206 if (FR_ISACCOUNT(pass)) {
2207 ATOMIC_INCL(frstats[0].fr_acct);
2209 fin->fin_fr = frsave;
2210 bcopy(group, fin->fin_group, FR_GROUPLEN);
2211 fin->fin_rule = rulen;
2213 return NULL;
2217 /* ------------------------------------------------------------------------ */
2218 /* Function: fr_firewall */
2219 /* Returns: frentry_t* - returns pointer to matched rule, if no matches */
2220 /* were found, returns NULL. */
2221 /* Parameters: fin(I) - pointer to packet information */
2222 /* passp(IO) - pointer to current/new filter decision (unused) */
2223 /* */
2224 /* Applies an appropriate set of firewall rules to the packet, to see if */
2225 /* there are any matches. The first check is to see if a match can be seen */
2226 /* in the cache. If not, then search an appropriate list of rules. Once a */
2227 /* matching rule is found, take any appropriate actions as defined by the */
2228 /* rule - except logging. */
2229 /* ------------------------------------------------------------------------ */
2230 static frentry_t *fr_firewall(fin, passp)
2231 fr_info_t *fin;
2232 u_32_t *passp;
2234 frentry_t *fr;
2235 fr_info_t *fc;
2236 u_32_t pass;
2237 int out;
2239 out = fin->fin_out;
2240 pass = *passp;
2243 * If a packet is found in the auth table, then skip checking
2244 * the access lists for permission but we do need to consider
2245 * the result as if it were from the ACL's.
2247 fc = &frcache[out][CACHE_HASH(fin)];
2248 READ_ENTER(&ipf_frcache);
2249 if (!bcmp((char *)fin, (char *)fc, FI_CSIZE)) {
2251 * copy cached data so we can unlock the mutexes earlier.
2253 bcopy((char *)fc, (char *)fin, FI_COPYSIZE);
2254 RWLOCK_EXIT(&ipf_frcache);
2255 ATOMIC_INCL(frstats[out].fr_chit);
2257 if ((fr = fin->fin_fr) != NULL) {
2258 ATOMIC_INC64(fr->fr_hits);
2259 pass = fr->fr_flags;
2261 } else {
2262 RWLOCK_EXIT(&ipf_frcache);
2264 #ifdef USE_INET6
2265 if (fin->fin_v == 6)
2266 fin->fin_fr = ipfilter6[out][fr_active];
2267 else
2268 #endif
2269 fin->fin_fr = ipfilter[out][fr_active];
2270 if (fin->fin_fr != NULL)
2271 pass = fr_scanlist(fin, fr_pass);
2273 if (((pass & FR_KEEPSTATE) == 0) &&
2274 ((fin->fin_flx & FI_DONTCACHE) == 0)) {
2275 WRITE_ENTER(&ipf_frcache);
2276 bcopy((char *)fin, (char *)fc, FI_COPYSIZE);
2277 RWLOCK_EXIT(&ipf_frcache);
2279 if ((pass & FR_NOMATCH)) {
2280 ATOMIC_INCL(frstats[out].fr_nom);
2282 fr = fin->fin_fr;
2286 * Apply packets per second rate-limiting to a rule as required.
2288 if ((fr != NULL) && (fr->fr_pps != 0) &&
2289 !ppsratecheck(&fr->fr_lastpkt, &fr->fr_curpps, fr->fr_pps)) {
2290 pass &= ~(FR_CMDMASK|FR_DUP|FR_RETICMP|FR_RETRST);
2291 pass |= FR_BLOCK;
2292 ATOMIC_INCL(frstats[out].fr_ppshit);
2296 * If we fail to add a packet to the authorization queue, then we
2297 * drop the packet later. However, if it was added then pretend
2298 * we've dropped it already.
2300 if (FR_ISAUTH(pass)) {
2301 if (fr_newauth(fin->fin_m, fin) != 0) {
2302 #ifdef _KERNEL
2303 fin->fin_m = *fin->fin_mp = NULL;
2304 #else
2306 #endif
2307 fin->fin_error = 0;
2308 } else
2309 fin->fin_error = ENOSPC;
2312 if ((fr != NULL) && (fr->fr_func != NULL) &&
2313 (fr->fr_func != (ipfunc_t)-1) && !(pass & FR_CALLNOW))
2314 (void) (*fr->fr_func)(fin, &pass);
2317 * If a rule is a pre-auth rule, check again in the list of rules
2318 * loaded for authenticated use. It does not particulary matter
2319 * if this search fails because a "preauth" result, from a rule,
2320 * is treated as "not a pass", hence the packet is blocked.
2322 if (FR_ISPREAUTH(pass)) {
2323 if ((fin->fin_fr = ipauth) != NULL)
2324 pass = fr_scanlist(fin, fr_pass);
2328 * If the rule has "keep frag" and the packet is actually a fragment,
2329 * then create a fragment state entry.
2331 if ((pass & (FR_KEEPFRAG|FR_KEEPSTATE)) == FR_KEEPFRAG) {
2332 if (fin->fin_flx & FI_FRAG) {
2333 if (fr_newfrag(fin, pass) == -1) {
2334 ATOMIC_INCL(frstats[out].fr_bnfr);
2335 } else {
2336 ATOMIC_INCL(frstats[out].fr_nfr);
2338 } else {
2339 ATOMIC_INCL(frstats[out].fr_cfr);
2343 fr = fin->fin_fr;
2345 if (passp != NULL)
2346 *passp = pass;
2348 return fr;
2352 /* ------------------------------------------------------------------------ */
2353 /* Function: fr_check */
2354 /* Returns: int - 0 == packet allowed through, */
2355 /* User space: */
2356 /* -1 == packet blocked */
2357 /* 1 == packet not matched */
2358 /* -2 == requires authentication */
2359 /* Kernel: */
2360 /* > 0 == filter error # for packet */
2361 /* Parameters: ip(I) - pointer to start of IPv4/6 packet */
2362 /* hlen(I) - length of header */
2363 /* ifp(I) - pointer to interface this packet is on */
2364 /* out(I) - 0 == packet going in, 1 == packet going out */
2365 /* mp(IO) - pointer to caller's buffer pointer that holds this */
2366 /* IP packet. */
2367 /* Solaris & HP-UX ONLY : */
2368 /* qpi(I) - pointer to STREAMS queue information for this */
2369 /* interface & direction. */
2370 /* */
2371 /* fr_check() is the master function for all IPFilter packet processing. */
2372 /* It orchestrates: Network Address Translation (NAT), checking for packet */
2373 /* authorisation (or pre-authorisation), presence of related state info., */
2374 /* generating log entries, IP packet accounting, routing of packets as */
2375 /* directed by firewall rules and of course whether or not to allow the */
2376 /* packet to be further processed by the kernel. */
2377 /* */
2378 /* For packets blocked, the contents of "mp" will be NULL'd and the buffer */
2379 /* freed. Packets passed may be returned with the pointer pointed to by */
2380 /* by "mp" changed to a new buffer. */
2381 /* ------------------------------------------------------------------------ */
2382 int fr_check(ip, hlen, ifp, out
2383 #if defined(_KERNEL) && defined(MENTAT)
2384 , qif, mp)
2385 void *qif;
2386 #else
2387 , mp)
2388 #endif
2389 mb_t **mp;
2390 ip_t *ip;
2391 int hlen;
2392 void *ifp;
2393 int out;
2396 * The above really sucks, but short of writing a diff
2398 fr_info_t frinfo;
2399 fr_info_t *fin = &frinfo;
2400 u_32_t pass = fr_pass;
2401 frentry_t *fr = NULL;
2402 int v = IP_V(ip);
2403 mb_t *mc = NULL;
2404 mb_t *m;
2406 * The first part of fr_check() deals with making sure that what goes
2407 * into the filtering engine makes some sense. Information about the
2408 * the packet is distilled, collected into a fr_info_t structure and
2409 * the an attempt to ensure the buffer the packet is in is big enough
2410 * to hold all the required packet headers.
2412 #ifdef _KERNEL
2413 # ifdef MENTAT
2414 qpktinfo_t *qpi = qif;
2416 # if !defined(_INET_IP_STACK_H)
2417 if ((u_int)ip & 0x3)
2418 return 2;
2419 # endif
2420 # else
2421 SPL_INT(s);
2422 # endif
2424 if (fr_running <= 0) {
2425 return 0;
2428 bzero((char *)fin, sizeof(*fin));
2430 # ifdef MENTAT
2431 if (qpi->qpi_flags & QF_GROUP)
2432 fin->fin_flx |= FI_MBCAST;
2433 m = qpi->qpi_m;
2434 fin->fin_qfm = m;
2435 fin->fin_qpi = qpi;
2436 # else /* MENTAT */
2438 m = *mp;
2440 # if defined(M_MCAST)
2441 if ((m->m_flags & M_MCAST) != 0)
2442 fin->fin_flx |= FI_MBCAST|FI_MULTICAST;
2443 # endif
2444 # if defined(M_MLOOP)
2445 if ((m->m_flags & M_MLOOP) != 0)
2446 fin->fin_flx |= FI_MBCAST|FI_MULTICAST;
2447 # endif
2448 # if defined(M_BCAST)
2449 if ((m->m_flags & M_BCAST) != 0)
2450 fin->fin_flx |= FI_MBCAST|FI_BROADCAST;
2451 # endif
2452 # ifdef M_CANFASTFWD
2454 * XXX For now, IP Filter and fast-forwarding of cached flows
2455 * XXX are mutually exclusive. Eventually, IP Filter should
2456 * XXX get a "can-fast-forward" filter rule.
2458 m->m_flags &= ~M_CANFASTFWD;
2459 # endif /* M_CANFASTFWD */
2460 # ifdef CSUM_DELAY_DATA
2462 * disable delayed checksums.
2464 if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
2465 in_delayed_cksum(m);
2466 m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
2468 # endif /* CSUM_DELAY_DATA */
2469 # endif /* MENTAT */
2470 #else
2471 bzero((char *)fin, sizeof(*fin));
2472 m = *mp;
2473 #endif /* _KERNEL */
2475 fin->fin_v = v;
2476 fin->fin_m = m;
2477 fin->fin_ip = ip;
2478 fin->fin_mp = mp;
2479 fin->fin_out = out;
2480 fin->fin_ifp = ifp;
2481 fin->fin_error = ENETUNREACH;
2482 fin->fin_hlen = (u_short)hlen;
2483 fin->fin_dp = (char *)ip + hlen;
2485 fin->fin_ipoff = (char *)ip - MTOD(m, char *);
2487 SPL_NET(s);
2489 #ifdef USE_INET6
2490 if (v == 6) {
2491 ATOMIC_INCL(frstats[out].fr_ipv6);
2493 * Jumbo grams are quite likely too big for internal buffer
2494 * structures to handle comfortably, for now, so just drop
2495 * them.
2497 if (((ip6_t *)ip)->ip6_plen == 0) {
2498 pass = FR_BLOCK|FR_NOMATCH;
2499 goto finished;
2501 } else
2502 #endif
2504 #if (defined(OpenBSD) && (OpenBSD >= 200311)) && defined(_KERNEL)
2505 ip->ip_len = ntohs(ip->ip_len);
2506 ip->ip_off = ntohs(ip->ip_off);
2507 #endif
2510 if (fr_makefrip(hlen, ip, fin) == -1) {
2511 pass = FR_BLOCK|FR_NOMATCH;
2512 goto finished;
2516 * For at least IPv6 packets, if a m_pullup() fails then this pointer
2517 * becomes NULL and so we have no packet to free.
2519 if (*fin->fin_mp == NULL)
2520 goto finished;
2522 if (!out) {
2523 if (v == 4) {
2524 #ifdef _KERNEL
2525 if (fr_chksrc && !fr_verifysrc(fin)) {
2526 ATOMIC_INCL(frstats[0].fr_badsrc);
2527 fin->fin_flx |= FI_BADSRC;
2529 #endif
2530 if (fin->fin_ip->ip_ttl < fr_minttl) {
2531 ATOMIC_INCL(frstats[0].fr_badttl);
2532 fin->fin_flx |= FI_LOWTTL;
2535 #ifdef USE_INET6
2536 else if (v == 6) {
2537 if (((ip6_t *)ip)->ip6_hlim < fr_minttl) {
2538 ATOMIC_INCL(frstats[0].fr_badttl);
2539 fin->fin_flx |= FI_LOWTTL;
2542 #endif
2545 if (fin->fin_flx & FI_SHORT) {
2546 ATOMIC_INCL(frstats[out].fr_short);
2549 READ_ENTER(&ipf_mutex);
2551 if (!out) {
2552 if (fr_checknatin(fin, &pass) == -1) {
2553 goto filterdone;
2558 * Check auth now. This, combined with the check below to see if apass
2559 * is 0 is to ensure that we don't count the packet twice, which can
2560 * otherwise occur when we reprocess it. As it is, we only count it
2561 * after it has no auth. table matchup.
2563 * If a packet is found in the auth table, then skip checking
2564 * the access lists for permission but we do need to consider
2565 * the result as if it were from the ACL's. In addition, being
2566 * found in the auth table means it has been seen before, so do
2567 * not pass it through accounting (again), lest it be counted twice.
2569 fr = fr_checkauth(fin, &pass);
2570 if (!out && (fr == NULL))
2571 (void) fr_acctpkt(fin, NULL);
2573 if (fr == NULL) {
2574 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) == FI_FRAG) {
2575 fr = fr_knownfrag(fin, &pass);
2577 * Reset the keep state flag here so that we don't
2578 * try and add a new state entry because of it, leading
2579 * to a blocked packet because the add will fail.
2581 if (fr != NULL)
2582 pass &= ~FR_KEEPSTATE;
2584 if (fr == NULL)
2585 fr = fr_checkstate(fin, &pass);
2588 if ((pass & FR_NOMATCH) || (fr == NULL))
2589 fr = fr_firewall(fin, &pass);
2592 * If we've asked to track state for this packet, set it up.
2593 * Here rather than fr_firewall because fr_checkauth may decide
2594 * to return a packet for "keep state"
2596 if ((pass & FR_KEEPSTATE) && (fin->fin_m != NULL) &&
2597 !(fin->fin_flx & FI_STATE)) {
2598 if (fr_addstate(fin, NULL, 0) != NULL) {
2599 ATOMIC_INCL(frstats[out].fr_ads);
2600 } else {
2601 ATOMIC_INCL(frstats[out].fr_bads);
2602 if (FR_ISPASS(pass)) {
2603 pass &= ~FR_CMDMASK;
2604 pass |= FR_BLOCK;
2609 fin->fin_fr = fr;
2612 * Only count/translate packets which will be passed on, out the
2613 * interface.
2615 if (out && FR_ISPASS(pass)) {
2616 (void) fr_acctpkt(fin, NULL);
2618 if (fr_checknatout(fin, &pass) == -1) {
2620 } else if ((fr_update_ipid != 0) && (v == 4)) {
2621 if (fr_updateipid(fin) == -1) {
2622 ATOMIC_INCL(frstats[1].fr_ipud);
2623 pass &= ~FR_CMDMASK;
2624 pass |= FR_BLOCK;
2625 } else {
2626 ATOMIC_INCL(frstats[0].fr_ipud);
2631 filterdone:
2632 #ifdef IPFILTER_LOG
2633 if ((fr_flags & FF_LOGGING) || (pass & FR_LOGMASK)) {
2634 (void) fr_dolog(fin, &pass);
2636 #endif
2639 * The FI_STATE flag is cleared here so that calling fr_checkstate
2640 * will work when called from inside of fr_fastroute. Although
2641 * there is a similar flag, FI_NATED, for NAT, it does have the same
2642 * impact on code execution.
2644 fin->fin_flx &= ~FI_STATE;
2646 #if defined(FASTROUTE_RECURSION)
2648 * Up the reference on fr_lock and exit ipf_mutex. fr_fastroute
2649 * only frees up the lock on ipf_global and the generation of a
2650 * packet below could cause a recursive call into IPFilter.
2651 * Hang onto the filter rule just in case someone decides to remove
2652 * or flush it in the meantime.
2654 if (fr != NULL) {
2655 MUTEX_ENTER(&fr->fr_lock);
2656 fr->fr_ref++;
2657 MUTEX_EXIT(&fr->fr_lock);
2660 RWLOCK_EXIT(&ipf_mutex);
2661 #endif
2663 if ((pass & FR_RETMASK) != 0) {
2665 * Should we return an ICMP packet to indicate error
2666 * status passing through the packet filter ?
2667 * WARNING: ICMP error packets AND TCP RST packets should
2668 * ONLY be sent in repsonse to incoming packets. Sending them
2669 * in response to outbound packets can result in a panic on
2670 * some operating systems.
2672 if (!out) {
2673 if (pass & FR_RETICMP) {
2674 int dst;
2676 if ((pass & FR_RETMASK) == FR_FAKEICMP)
2677 dst = 1;
2678 else
2679 dst = 0;
2680 (void) fr_send_icmp_err(ICMP_UNREACH, fin, dst);
2681 ATOMIC_INCL(frstats[0].fr_ret);
2682 } else if (((pass & FR_RETMASK) == FR_RETRST) &&
2683 !(fin->fin_flx & FI_SHORT)) {
2684 if (((fin->fin_flx & FI_OOW) != 0) ||
2685 (fr_send_reset(fin) == 0)) {
2686 ATOMIC_INCL(frstats[1].fr_ret);
2691 * When using return-* with auth rules, the auth code
2692 * takes over disposing of this packet.
2694 if (FR_ISAUTH(pass) && (fin->fin_m != NULL)) {
2695 fin->fin_m = *fin->fin_mp = NULL;
2696 m = NULL;
2698 } else {
2699 if (pass & FR_RETRST)
2700 fin->fin_error = ECONNRESET;
2704 if (FR_ISBLOCK(pass) && (fin->fin_flx & FI_NEWNAT))
2705 nat_uncreate(fin);
2708 * If we didn't drop off the bottom of the list of rules (and thus
2709 * the 'current' rule fr is not NULL), then we may have some extra
2710 * instructions about what to do with a packet.
2711 * Once we're finished return to our caller, freeing the packet if
2712 * we are dropping it (* BSD ONLY *).
2714 if (fr != NULL) {
2715 frdest_t *fdp;
2718 * Generate a duplicated packet first because ipf_fastroute
2719 * can lead to fin_m being free'd... not good.
2721 if ((pass & FR_DUP) != 0) {
2722 mc = M_DUPLICATE(fin->fin_m);
2723 if (mc != NULL)
2724 (void) fr_fastroute(mc, &mc, fin, &fr->fr_dif);
2727 fdp = &fr->fr_tifs[fin->fin_rev];
2729 if (!out && (pass & FR_FASTROUTE)) {
2731 * For fastroute rule, no destioation interface defined
2732 * so pass NULL as the frdest_t parameter
2734 (void) fr_fastroute(fin->fin_m, mp, fin, NULL);
2735 m = *mp = NULL;
2736 } else if ((fdp->fd_ifp != NULL) &&
2737 (fdp->fd_ifp != (struct ifnet *)-1)) {
2738 /* this is for to rules: */
2739 (void) fr_fastroute(fin->fin_m, mp, fin, fdp);
2740 m = *mp = NULL;
2743 #if defined(FASTROUTE_RECURSION)
2744 (void) fr_derefrule(&fr);
2745 #endif
2747 #if !defined(FASTROUTE_RECURSION)
2748 RWLOCK_EXIT(&ipf_mutex);
2749 #endif
2751 finished:
2752 if (!FR_ISPASS(pass)) {
2753 ATOMIC_INCL(frstats[out].fr_block);
2754 if (*mp != NULL) {
2755 FREE_MB_T(*mp);
2756 m = *mp = NULL;
2758 } else {
2759 ATOMIC_INCL(frstats[out].fr_pass);
2760 #if defined(_KERNEL) && defined(__sgi)
2761 if ((fin->fin_hbuf != NULL) &&
2762 (mtod(fin->fin_m, struct ip *) != fin->fin_ip)) {
2763 COPYBACK(fin->fin_m, 0, fin->fin_plen, fin->fin_hbuf);
2765 #endif
2768 SPL_X(s);
2770 #ifdef _KERNEL
2771 # if (defined(OpenBSD) && (OpenBSD >= 200311))
2772 if (FR_ISPASS(pass) && (v == 4)) {
2773 ip = fin->fin_ip;
2774 ip->ip_len = ntohs(ip->ip_len);
2775 ip->ip_off = ntohs(ip->ip_off);
2777 # endif
2778 return (FR_ISPASS(pass)) ? 0 : fin->fin_error;
2779 #else /* _KERNEL */
2780 FR_VERBOSE(("fin_flx %#x pass %#x ", fin->fin_flx, pass));
2781 if ((pass & FR_NOMATCH) != 0)
2782 return 1;
2784 if ((pass & FR_RETMASK) != 0)
2785 switch (pass & FR_RETMASK)
2787 case FR_RETRST :
2788 return 3;
2789 case FR_RETICMP :
2790 return 4;
2791 case FR_FAKEICMP :
2792 return 5;
2795 switch (pass & FR_CMDMASK)
2797 case FR_PASS :
2798 return 0;
2799 case FR_BLOCK :
2800 return -1;
2801 case FR_AUTH :
2802 return -2;
2803 case FR_ACCOUNT :
2804 return -3;
2805 case FR_PREAUTH :
2806 return -4;
2808 return 2;
2809 #endif /* _KERNEL */
2813 #ifdef IPFILTER_LOG
2814 /* ------------------------------------------------------------------------ */
2815 /* Function: fr_dolog */
2816 /* Returns: frentry_t* - returns contents of fin_fr (no change made) */
2817 /* Parameters: fin(I) - pointer to packet information */
2818 /* passp(IO) - pointer to current/new filter decision (unused) */
2819 /* */
2820 /* Checks flags set to see how a packet should be logged, if it is to be */
2821 /* logged. Adjust statistics based on its success or not. */
2822 /* ------------------------------------------------------------------------ */
2823 frentry_t *fr_dolog(fin, passp)
2824 fr_info_t *fin;
2825 u_32_t *passp;
2827 u_32_t pass;
2828 int out;
2830 out = fin->fin_out;
2831 pass = *passp;
2833 if ((fr_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) {
2834 pass |= FF_LOGNOMATCH;
2835 ATOMIC_INCL(frstats[out].fr_npkl);
2836 goto logit;
2837 } else if (((pass & FR_LOGMASK) == FR_LOGP) ||
2838 (FR_ISPASS(pass) && (fr_flags & FF_LOGPASS))) {
2839 if ((pass & FR_LOGMASK) != FR_LOGP)
2840 pass |= FF_LOGPASS;
2841 ATOMIC_INCL(frstats[out].fr_ppkl);
2842 goto logit;
2843 } else if (((pass & FR_LOGMASK) == FR_LOGB) ||
2844 (FR_ISBLOCK(pass) && (fr_flags & FF_LOGBLOCK))) {
2845 if ((pass & FR_LOGMASK) != FR_LOGB)
2846 pass |= FF_LOGBLOCK;
2847 ATOMIC_INCL(frstats[out].fr_bpkl);
2848 logit:
2849 if (ipflog(fin, pass) == -1) {
2850 ATOMIC_INCL(frstats[out].fr_skip);
2853 * If the "or-block" option has been used then
2854 * block the packet if we failed to log it.
2856 if ((pass & FR_LOGORBLOCK) &&
2857 FR_ISPASS(pass)) {
2858 pass &= ~FR_CMDMASK;
2859 pass |= FR_BLOCK;
2862 *passp = pass;
2865 return fin->fin_fr;
2867 #endif /* IPFILTER_LOG */
2870 /* ------------------------------------------------------------------------ */
2871 /* Function: ipf_cksum */
2872 /* Returns: u_short - IP header checksum */
2873 /* Parameters: addr(I) - pointer to start of buffer to checksum */
2874 /* len(I) - length of buffer in bytes */
2875 /* */
2876 /* Calculate the two's complement 16 bit checksum of the buffer passed. */
2877 /* */
2878 /* N.B.: addr should be 16bit aligned. */
2879 /* ------------------------------------------------------------------------ */
2880 u_short ipf_cksum(addr, len)
2881 u_short *addr;
2882 int len;
2884 u_32_t sum = 0;
2886 for (sum = 0; len > 1; len -= 2)
2887 sum += *addr++;
2889 /* mop up an odd byte, if necessary */
2890 if (len == 1)
2891 sum += *(u_char *)addr;
2894 * add back carry outs from top 16 bits to low 16 bits
2896 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
2897 sum += (sum >> 16); /* add carry */
2898 return (u_short)(~sum);
2902 /* ------------------------------------------------------------------------ */
2903 /* Function: fr_cksum */
2904 /* Returns: u_short - layer 4 checksum */
2905 /* Parameters: m(I ) - pointer to buffer holding packet */
2906 /* ip(I) - pointer to IP header */
2907 /* l4proto(I) - protocol to caclulate checksum for */
2908 /* l4hdr(I) - pointer to layer 4 header */
2909 /* l3len(I) - length of layer 4 data plus layer 3 header */
2910 /* */
2911 /* Calculates the TCP checksum for the packet held in "m", using the data */
2912 /* in the IP header "ip" to seed it. */
2913 /* */
2914 /* NB: This function assumes we've pullup'd enough for all of the IP header */
2915 /* and the TCP header. We also assume that data blocks aren't allocated in */
2916 /* odd sizes. */
2917 /* */
2918 /* For IPv6, l3len excludes extension header size. */
2919 /* */
2920 /* Expects ip_len to be in host byte order when called. */
2921 /* ------------------------------------------------------------------------ */
2922 #ifdef INET
2923 u_short fr_cksum(m, ip, l4proto, l4hdr, l3len)
2924 mb_t *m;
2925 ip_t *ip;
2926 int l4proto, l3len;
2927 void *l4hdr;
2929 u_short *sp, slen, sumsave, l4hlen, *csump;
2930 u_int sum, sum2;
2931 int hlen;
2932 #ifdef USE_INET6
2933 ip6_t *ip6;
2934 #endif
2936 csump = NULL;
2937 sumsave = 0;
2938 l4hlen = 0;
2939 sp = NULL;
2940 slen = 0;
2941 hlen = 0;
2942 sum = 0;
2945 * Add up IP Header portion
2947 #ifdef USE_INET6
2948 if (IP_V(ip) == 4) {
2949 #endif
2950 hlen = IP_HL(ip) << 2;
2951 slen = l3len - hlen;
2952 sum = htons((u_short)l4proto);
2953 sum += htons(slen);
2954 sp = (u_short *)&ip->ip_src;
2955 sum += *sp++; /* ip_src */
2956 sum += *sp++;
2957 sum += *sp++; /* ip_dst */
2958 sum += *sp++;
2959 #ifdef USE_INET6
2960 } else if (IP_V(ip) == 6) {
2961 ip6 = (ip6_t *)ip;
2962 hlen = sizeof(*ip6);
2963 slen = l3len - hlen;
2964 sum = htons((u_short)l4proto);
2965 sum += htons(slen);
2966 sp = (u_short *)&ip6->ip6_src;
2967 sum += *sp++; /* ip6_src */
2968 sum += *sp++;
2969 sum += *sp++;
2970 sum += *sp++;
2971 sum += *sp++;
2972 sum += *sp++;
2973 sum += *sp++;
2974 sum += *sp++;
2975 sum += *sp++; /* ip6_dst */
2976 sum += *sp++;
2977 sum += *sp++;
2978 sum += *sp++;
2979 sum += *sp++;
2980 sum += *sp++;
2981 sum += *sp++;
2982 sum += *sp++;
2984 #endif
2986 switch (l4proto)
2988 case IPPROTO_UDP :
2989 csump = &((udphdr_t *)l4hdr)->uh_sum;
2990 l4hlen = sizeof(udphdr_t);
2991 break;
2993 case IPPROTO_TCP :
2994 csump = &((tcphdr_t *)l4hdr)->th_sum;
2995 l4hlen = sizeof(tcphdr_t);
2996 break;
2997 case IPPROTO_ICMP :
2998 csump = &((icmphdr_t *)l4hdr)->icmp_cksum;
2999 l4hlen = 4;
3000 sum = 0;
3001 break;
3002 default :
3003 break;
3006 if (csump != NULL) {
3007 sumsave = *csump;
3008 *csump = 0;
3011 l4hlen = l4hlen; /* LINT */
3013 #ifdef _KERNEL
3014 # ifdef MENTAT
3016 void *rp = m->b_rptr;
3018 if ((unsigned char *)ip > m->b_rptr && (unsigned char *)ip < m->b_wptr)
3019 m->b_rptr = (u_char *)ip;
3020 sum2 = ip_cksum(m, hlen, sum); /* hlen == offset */
3021 m->b_rptr = rp;
3022 sum2 = (u_short)(~sum2 & 0xffff);
3024 # else /* MENTAT */
3025 # if defined(BSD) || defined(sun)
3026 # if defined(BSD) && (BSD >= 199103)
3027 m->m_data += hlen;
3028 # else
3029 m->m_off += hlen;
3030 # endif
3031 m->m_len -= hlen;
3032 sum2 = in_cksum(m, slen);
3033 m->m_len += hlen;
3034 # if BSD >= 199103
3035 m->m_data -= hlen;
3036 # else
3037 m->m_off -= hlen;
3038 # endif
3040 * Both sum and sum2 are partial sums, so combine them together.
3042 sum += ~sum2 & 0xffff;
3043 while (sum > 0xffff)
3044 sum = (sum & 0xffff) + (sum >> 16);
3045 sum2 = ~sum & 0xffff;
3046 # else /* defined(BSD) || defined(sun) */
3048 union {
3049 u_char c[2];
3050 u_short s;
3051 } bytes;
3052 u_short len = ip->ip_len;
3053 # if defined(__sgi)
3054 int add;
3055 # endif
3058 * Add up IP Header portion
3060 if (sp != (u_short *)l4hdr)
3061 sp = (u_short *)l4hdr;
3063 switch (l4proto)
3065 case IPPROTO_UDP :
3066 sum += *sp++; /* sport */
3067 sum += *sp++; /* dport */
3068 sum += *sp++; /* udp length */
3069 sum += *sp++; /* checksum */
3070 break;
3072 case IPPROTO_TCP :
3073 sum += *sp++; /* sport */
3074 sum += *sp++; /* dport */
3075 sum += *sp++; /* seq */
3076 sum += *sp++;
3077 sum += *sp++; /* ack */
3078 sum += *sp++;
3079 sum += *sp++; /* off */
3080 sum += *sp++; /* win */
3081 sum += *sp++; /* checksum */
3082 sum += *sp++; /* urp */
3083 break;
3084 case IPPROTO_ICMP :
3085 sum = *sp++; /* type/code */
3086 sum += *sp++; /* checksum */
3087 break;
3090 # ifdef __sgi
3092 * In case we had to copy the IP & TCP header out of mbufs,
3093 * skip over the mbuf bits which are the header
3095 if ((void *)ip != mtod(m, void *)) {
3096 hlen = (void *)sp - (void *)ip;
3097 while (hlen) {
3098 add = MIN(hlen, m->m_len);
3099 sp = (u_short *)(mtod(m, void *) + add);
3100 hlen -= add;
3101 if (add == m->m_len) {
3102 m = m->m_next;
3103 if (!hlen) {
3104 if (!m)
3105 break;
3106 sp = mtod(m, u_short *);
3108 PANIC((!m),("fr_cksum(1): not enough data"));
3112 # endif
3114 len -= (l4hlen + hlen);
3115 if (len <= 0)
3116 goto nodata;
3118 while (len > 1) {
3119 if (((void *)sp - mtod(m, void *)) >= m->m_len) {
3120 m = m->m_next;
3121 PANIC((!m),("fr_cksum(2): not enough data"));
3122 sp = mtod(m, u_short *);
3124 if (((void *)(sp + 1) - mtod(m, void *)) > m->m_len) {
3125 bytes.c[0] = *(u_char *)sp;
3126 m = m->m_next;
3127 PANIC((!m),("fr_cksum(3): not enough data"));
3128 sp = mtod(m, u_short *);
3129 bytes.c[1] = *(u_char *)sp;
3130 sum += bytes.s;
3131 sp = (u_short *)((u_char *)sp + 1);
3133 if ((u_long)sp & 1) {
3134 bcopy((char *)sp++, (char *)&bytes.s, sizeof(bytes.s));
3135 sum += bytes.s;
3136 } else
3137 sum += *sp++;
3138 len -= 2;
3141 if (len != 0)
3142 sum += ntohs(*(u_char *)sp << 8);
3143 nodata:
3144 while (sum > 0xffff)
3145 sum = (sum & 0xffff) + (sum >> 16);
3146 sum2 = (u_short)(~sum & 0xffff);
3148 # endif /* defined(BSD) || defined(sun) */
3149 # endif /* MENTAT */
3150 #else /* _KERNEL */
3152 * Add up IP Header portion
3154 if (sp != (u_short *)l4hdr)
3155 sp = (u_short *)l4hdr;
3157 for (; slen > 1; slen -= 2)
3158 sum += *sp++;
3159 if (slen)
3160 sum += ntohs(*(u_char *)sp << 8);
3161 while (sum > 0xffff)
3162 sum = (sum & 0xffff) + (sum >> 16);
3163 sum2 = (u_short)(~sum & 0xffff);
3164 #endif /* _KERNEL */
3165 if (csump != NULL)
3166 *csump = sumsave;
3167 return sum2;
3169 #endif
3172 #if defined(_KERNEL) && ( ((defined(BSD) && (BSD < 199103)) && \
3173 !defined(MENTAT)) || defined(__sgi) ) && \
3174 !defined(linux) && !defined(_AIX51)
3176 * Copyright (c) 1982, 1986, 1988, 1991, 1993
3177 * The Regents of the University of California. All rights reserved.
3179 * Redistribution and use in source and binary forms, with or without
3180 * modification, are permitted provided that the following conditions
3181 * are met:
3182 * 1. Redistributions of source code must retain the above copyright
3183 * notice, this list of conditions and the following disclaimer.
3184 * 2. Redistributions in binary form must reproduce the above copyright
3185 * notice, this list of conditions and the following disclaimer in the
3186 * documentation and/or other materials provided with the distribution.
3187 * 3. Neither the name of the University nor the names of its contributors
3188 * may be used to endorse or promote products derived from this software
3189 * without specific prior written permission.
3191 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
3192 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3193 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3194 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3195 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3196 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3197 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3198 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3199 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3200 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3201 * SUCH DAMAGE.
3203 * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94
3204 * Id: fil.c,v 2.243.2.147 2009/07/21 22:25:28 darrenr Exp
3207 * Copy data from an mbuf chain starting "off" bytes from the beginning,
3208 * continuing for "len" bytes, into the indicated buffer.
3210 void
3211 m_copydata(m, off, len, cp)
3212 mb_t *m;
3213 int off;
3214 int len;
3215 void *cp;
3217 unsigned count;
3219 if (off < 0 || len < 0)
3220 panic("m_copydata");
3221 while (off > 0) {
3222 if (m == 0)
3223 panic("m_copydata");
3224 if (off < m->m_len)
3225 break;
3226 off -= m->m_len;
3227 m = m->m_next;
3229 while (len > 0) {
3230 if (m == 0)
3231 panic("m_copydata");
3232 count = MIN(m->m_len - off, len);
3233 bcopy(mtod(m, void *) + off, cp, count);
3234 len -= count;
3235 cp += count;
3236 off = 0;
3237 m = m->m_next;
3243 * Copy data from a buffer back into the indicated mbuf chain,
3244 * starting "off" bytes from the beginning, extending the mbuf
3245 * chain if necessary.
3247 void
3248 m_copyback(m0, off, len, cp)
3249 struct mbuf *m0;
3250 int off;
3251 int len;
3252 void *cp;
3254 int mlen;
3255 struct mbuf *m = m0, *n;
3256 int totlen = 0;
3258 if (m0 == 0)
3259 return;
3260 while (off > (mlen = m->m_len)) {
3261 off -= mlen;
3262 totlen += mlen;
3263 if (m->m_next == 0) {
3264 n = m_getclr(M_DONTWAIT, m->m_type);
3265 if (n == 0)
3266 goto out;
3267 n->m_len = min(MLEN, len + off);
3268 m->m_next = n;
3270 m = m->m_next;
3272 while (len > 0) {
3273 mlen = min(m->m_len - off, len);
3274 bcopy(cp, off + mtod(m, void *), (unsigned)mlen);
3275 cp += mlen;
3276 len -= mlen;
3277 mlen += off;
3278 off = 0;
3279 totlen += mlen;
3280 if (len == 0)
3281 break;
3282 if (m->m_next == 0) {
3283 n = m_get(M_DONTWAIT, m->m_type);
3284 if (n == 0)
3285 break;
3286 n->m_len = min(MLEN, len);
3287 m->m_next = n;
3289 m = m->m_next;
3291 out:
3292 #if 0
3293 if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
3294 m->m_pkthdr.len = totlen;
3295 #endif
3296 return;
3298 #endif /* (_KERNEL) && ( ((BSD < 199103) && !MENTAT) || __sgi) */
3301 /* ------------------------------------------------------------------------ */
3302 /* Function: fr_findgroup */
3303 /* Returns: frgroup_t * - NULL = group not found, else pointer to group */
3304 /* Parameters: group(I) - group name to search for */
3305 /* unit(I) - device to which this group belongs */
3306 /* set(I) - which set of rules (inactive/inactive) this is */
3307 /* fgpp(O) - pointer to place to store pointer to the pointer */
3308 /* to where to add the next (last) group or where */
3309 /* to delete group from. */
3310 /* */
3311 /* Search amongst the defined groups for a particular group number. */
3312 /* ------------------------------------------------------------------------ */
3313 frgroup_t *fr_findgroup(group, unit, set, fgpp)
3314 char *group;
3315 minor_t unit;
3316 int set;
3317 frgroup_t ***fgpp;
3319 frgroup_t *fg, **fgp;
3322 * Which list of groups to search in is dependent on which list of
3323 * rules are being operated on.
3325 fgp = &ipfgroups[unit][set];
3327 while ((fg = *fgp) != NULL) {
3328 if (strncmp(group, fg->fg_name, FR_GROUPLEN) == 0)
3329 break;
3330 else
3331 fgp = &fg->fg_next;
3333 if (fgpp != NULL)
3334 *fgpp = fgp;
3335 return fg;
3339 /* ------------------------------------------------------------------------ */
3340 /* Function: fr_addgroup */
3341 /* Returns: frgroup_t * - NULL == did not create group, */
3342 /* != NULL == pointer to the group */
3343 /* Parameters: num(I) - group number to add */
3344 /* head(I) - rule pointer that is using this as the head */
3345 /* flags(I) - rule flags which describe the type of rule it is */
3346 /* unit(I) - device to which this group will belong to */
3347 /* set(I) - which set of rules (inactive/inactive) this is */
3348 /* Write Locks: ipf_mutex */
3349 /* */
3350 /* Add a new group head, or if it already exists, increase the reference */
3351 /* count to it. */
3352 /* ------------------------------------------------------------------------ */
3353 frgroup_t *fr_addgroup(group, head, flags, unit, set)
3354 char *group;
3355 void *head;
3356 u_32_t flags;
3357 minor_t unit;
3358 int set;
3360 frgroup_t *fg, **fgp;
3361 u_32_t gflags;
3363 if (group == NULL)
3364 return NULL;
3366 if (unit == IPL_LOGIPF && *group == '\0')
3367 return NULL;
3369 fgp = NULL;
3370 gflags = flags & FR_INOUT;
3372 fg = fr_findgroup(group, unit, set, &fgp);
3373 if (fg != NULL) {
3374 if (fg->fg_flags == 0)
3375 fg->fg_flags = gflags;
3376 else if (gflags != fg->fg_flags)
3377 return NULL;
3378 fg->fg_ref++;
3379 return fg;
3381 KMALLOC(fg, frgroup_t *);
3382 if (fg != NULL) {
3383 fg->fg_head = head;
3384 fg->fg_start = NULL;
3385 fg->fg_next = *fgp;
3386 bcopy(group, fg->fg_name, FR_GROUPLEN);
3387 fg->fg_flags = gflags;
3388 fg->fg_ref = 1;
3389 *fgp = fg;
3391 return fg;
3395 /* ------------------------------------------------------------------------ */
3396 /* Function: fr_delgroup */
3397 /* Returns: Nil */
3398 /* Parameters: group(I) - group name to delete */
3399 /* unit(I) - device to which this group belongs */
3400 /* set(I) - which set of rules (inactive/inactive) this is */
3401 /* Write Locks: ipf_mutex */
3402 /* */
3403 /* Attempt to delete a group head. */
3404 /* Only do this when its reference count reaches 0. */
3405 /* ------------------------------------------------------------------------ */
3406 void fr_delgroup(group, unit, set)
3407 char *group;
3408 minor_t unit;
3409 int set;
3411 frgroup_t *fg, **fgp;
3413 fg = fr_findgroup(group, unit, set, &fgp);
3414 if (fg == NULL)
3415 return;
3417 fg->fg_ref--;
3418 if (fg->fg_ref == 0) {
3419 *fgp = fg->fg_next;
3420 KFREE(fg);
3425 /* ------------------------------------------------------------------------ */
3426 /* Function: fr_getrulen */
3427 /* Returns: frentry_t * - NULL == not found, else pointer to rule n */
3428 /* Parameters: unit(I) - device for which to count the rule's number */
3429 /* flags(I) - which set of rules to find the rule in */
3430 /* group(I) - group name */
3431 /* n(I) - rule number to find */
3432 /* */
3433 /* Find rule # n in group # g and return a pointer to it. Return NULl if */
3434 /* group # g doesn't exist or there are less than n rules in the group. */
3435 /* ------------------------------------------------------------------------ */
3436 frentry_t *fr_getrulen(unit, group, n)
3437 int unit;
3438 char *group;
3439 u_32_t n;
3441 frentry_t *fr;
3442 frgroup_t *fg;
3444 fg = fr_findgroup(group, unit, fr_active, NULL);
3445 if (fg == NULL)
3446 return NULL;
3447 for (fr = fg->fg_start; fr && n; fr = fr->fr_next, n--)
3449 if (n != 0)
3450 return NULL;
3451 return fr;
3455 /* ------------------------------------------------------------------------ */
3456 /* Function: frflushlist */
3457 /* Returns: int - >= 0 - number of flushed rules */
3458 /* Parameters: set(I) - which set of rules (inactive/inactive) this is */
3459 /* unit(I) - device for which to flush rules */
3460 /* flags(I) - which set of rules to flush */
3461 /* nfreedp(O) - pointer to int where flush count is stored */
3462 /* listp(I) - pointer to list to flush pointer */
3463 /* Write Locks: ipf_mutex */
3464 /* */
3465 /* Recursively flush rules from the list, descending groups as they are */
3466 /* encountered. if a rule is the head of a group and it has lost all its */
3467 /* group members, then also delete the group reference. nfreedp is needed */
3468 /* to store the accumulating count of rules removed, whereas the returned */
3469 /* value is just the number removed from the current list. The latter is */
3470 /* needed to correctly adjust reference counts on rules that define groups. */
3471 /* */
3472 /* NOTE: Rules not loaded from user space cannot be flushed. */
3473 /* ------------------------------------------------------------------------ */
3474 static int frflushlist(set, unit, nfreedp, listp)
3475 int set;
3476 minor_t unit;
3477 int *nfreedp;
3478 frentry_t **listp;
3480 int freed = 0;
3481 frentry_t *fp;
3483 while ((fp = *listp) != NULL) {
3484 if ((fp->fr_type & FR_T_BUILTIN) ||
3485 !(fp->fr_flags & FR_COPIED)) {
3486 listp = &fp->fr_next;
3487 continue;
3489 *listp = fp->fr_next;
3490 if (fp->fr_grp != NULL) {
3491 (void) frflushlist(set, unit, nfreedp, fp->fr_grp);
3494 if (fp->fr_grhead != NULL) {
3495 fr_delgroup(fp->fr_grhead, unit, set);
3496 *fp->fr_grhead = '\0';
3499 ASSERT(fp->fr_ref > 0);
3500 fp->fr_next = NULL;
3501 if (fr_derefrule(&fp) == 0)
3502 freed++;
3504 *nfreedp += freed;
3505 return freed;
3509 /* ------------------------------------------------------------------------ */
3510 /* Function: frflush */
3511 /* Returns: int - >= 0 - number of flushed rules */
3512 /* Parameters: unit(I) - device for which to flush rules */
3513 /* flags(I) - which set of rules to flush */
3514 /* */
3515 /* Calls flushlist() for all filter rules (accounting, firewall - both IPv4 */
3516 /* and IPv6) as defined by the value of flags. */
3517 /* ------------------------------------------------------------------------ */
3518 int frflush(unit, proto, flags)
3519 minor_t unit;
3520 int proto, flags;
3522 int flushed = 0, set;
3524 WRITE_ENTER(&ipf_mutex);
3525 bzero((char *)frcache, sizeof(frcache));
3527 set = fr_active;
3528 if ((flags & FR_INACTIVE) == FR_INACTIVE)
3529 set = 1 - set;
3531 if (flags & FR_OUTQUE) {
3532 if (proto == 0 || proto == 6) {
3533 (void) frflushlist(set, unit,
3534 &flushed, &ipfilter6[1][set]);
3535 (void) frflushlist(set, unit,
3536 &flushed, &ipacct6[1][set]);
3538 if (proto == 0 || proto == 4) {
3539 (void) frflushlist(set, unit,
3540 &flushed, &ipfilter[1][set]);
3541 (void) frflushlist(set, unit,
3542 &flushed, &ipacct[1][set]);
3545 if (flags & FR_INQUE) {
3546 if (proto == 0 || proto == 6) {
3547 (void) frflushlist(set, unit,
3548 &flushed, &ipfilter6[0][set]);
3549 (void) frflushlist(set, unit,
3550 &flushed, &ipacct6[0][set]);
3552 if (proto == 0 || proto == 4) {
3553 (void) frflushlist(set, unit,
3554 &flushed, &ipfilter[0][set]);
3555 (void) frflushlist(set, unit,
3556 &flushed, &ipacct[0][set]);
3559 RWLOCK_EXIT(&ipf_mutex);
3561 if (unit == IPL_LOGIPF) {
3562 int tmp;
3564 tmp = frflush(IPL_LOGCOUNT, proto, flags);
3565 if (tmp >= 0)
3566 flushed += tmp;
3568 return flushed;
3572 /* ------------------------------------------------------------------------ */
3573 /* Function: memstr */
3574 /* Returns: char * - NULL if failed, != NULL pointer to matching bytes */
3575 /* Parameters: src(I) - pointer to byte sequence to match */
3576 /* dst(I) - pointer to byte sequence to search */
3577 /* slen(I) - match length */
3578 /* dlen(I) - length available to search in */
3579 /* */
3580 /* Search dst for a sequence of bytes matching those at src and extend for */
3581 /* slen bytes. */
3582 /* ------------------------------------------------------------------------ */
3583 char *memstr(src, dst, slen, dlen)
3584 const char *src;
3585 char *dst;
3586 size_t slen, dlen;
3588 char *s = NULL;
3590 while (dlen >= slen) {
3591 if (memcmp(src, dst, slen) == 0) {
3592 s = dst;
3593 break;
3595 dst++;
3596 dlen--;
3598 return s;
3600 /* ------------------------------------------------------------------------ */
3601 /* Function: fr_fixskip */
3602 /* Returns: Nil */
3603 /* Parameters: listp(IO) - pointer to start of list with skip rule */
3604 /* rp(I) - rule added/removed with skip in it. */
3605 /* addremove(I) - adjustment (-1/+1) to make to skip count, */
3606 /* depending on whether a rule was just added */
3607 /* or removed. */
3608 /* */
3609 /* Adjust all the rules in a list which would have skip'd past the position */
3610 /* where we are inserting to skip to the right place given the change. */
3611 /* ------------------------------------------------------------------------ */
3612 void fr_fixskip(listp, rp, addremove)
3613 frentry_t **listp, *rp;
3614 int addremove;
3616 int rules, rn;
3617 frentry_t *fp;
3619 rules = 0;
3620 for (fp = *listp; (fp != NULL) && (fp != rp); fp = fp->fr_next)
3621 rules++;
3623 if (!fp)
3624 return;
3626 for (rn = 0, fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++)
3627 if (FR_ISSKIP(fp->fr_flags) && (rn + fp->fr_arg >= rules))
3628 fp->fr_arg += addremove;
3632 #ifdef _KERNEL
3633 /* ------------------------------------------------------------------------ */
3634 /* Function: count4bits */
3635 /* Returns: int - >= 0 - number of consecutive bits in input */
3636 /* Parameters: ip(I) - 32bit IP address */
3637 /* */
3638 /* IPv4 ONLY */
3639 /* count consecutive 1's in bit mask. If the mask generated by counting */
3640 /* consecutive 1's is different to that passed, return -1, else return # */
3641 /* of bits. */
3642 /* ------------------------------------------------------------------------ */
3643 int count4bits(ip)
3644 u_32_t ip;
3646 u_32_t ipn;
3647 int cnt = 0, i, j;
3649 ip = ipn = ntohl(ip);
3650 for (i = 32; i; i--, ipn *= 2)
3651 if (ipn & 0x80000000)
3652 cnt++;
3653 else
3654 break;
3655 ipn = 0;
3656 for (i = 32, j = cnt; i; i--, j--) {
3657 ipn *= 2;
3658 if (j > 0)
3659 ipn++;
3661 if (ipn == ip)
3662 return cnt;
3663 return -1;
3667 # if 0
3668 /* ------------------------------------------------------------------------ */
3669 /* Function: count6bits */
3670 /* Returns: int - >= 0 - number of consecutive bits in input */
3671 /* Parameters: msk(I) - pointer to start of IPv6 bitmask */
3672 /* */
3673 /* IPv6 ONLY */
3674 /* count consecutive 1's in bit mask. */
3675 /* ------------------------------------------------------------------------ */
3676 int count6bits(msk)
3677 u_32_t *msk;
3679 int i = 0, k;
3680 u_32_t j;
3682 for (k = 3; k >= 0; k--)
3683 if (msk[k] == 0xffffffff)
3684 i += 32;
3685 else {
3686 for (j = msk[k]; j; j <<= 1)
3687 if (j & 0x80000000)
3688 i++;
3690 return i;
3692 # endif
3693 #endif /* _KERNEL */
3696 /* ------------------------------------------------------------------------ */
3697 /* Function: frsynclist */
3698 /* Returns: void */
3699 /* Parameters: fr(I) - start of filter list to sync interface names for */
3700 /* ifp(I) - interface pointer for limiting sync lookups */
3701 /* Write Locks: ipf_mutex */
3702 /* */
3703 /* Walk through a list of filter rules and resolve any interface names into */
3704 /* pointers. Where dynamic addresses are used, also update the IP address */
3705 /* used in the rule. The interface pointer is used to limit the lookups to */
3706 /* a specific set of matching names if it is non-NULL. */
3707 /* ------------------------------------------------------------------------ */
3708 static void frsynclist(fr, ifp)
3709 frentry_t *fr;
3710 void *ifp;
3712 frdest_t *fdp;
3713 int v, i;
3715 for (; fr; fr = fr->fr_next) {
3716 v = fr->fr_v;
3719 * Lookup all the interface names that are part of the rule.
3721 for (i = 0; i < 4; i++) {
3722 if ((ifp != NULL) && (fr->fr_ifas[i] != ifp))
3723 continue;
3724 fr->fr_ifas[i] = fr_resolvenic(fr->fr_ifnames[i], v);
3727 if (fr->fr_type == FR_T_IPF) {
3728 if (fr->fr_satype != FRI_NORMAL &&
3729 fr->fr_satype != FRI_LOOKUP) {
3730 (void)fr_ifpaddr(v, fr->fr_satype,
3731 fr->fr_ifas[fr->fr_sifpidx],
3732 &fr->fr_src, &fr->fr_smsk);
3734 if (fr->fr_datype != FRI_NORMAL &&
3735 fr->fr_datype != FRI_LOOKUP) {
3736 (void)fr_ifpaddr(v, fr->fr_datype,
3737 fr->fr_ifas[fr->fr_difpidx],
3738 &fr->fr_dst, &fr->fr_dmsk);
3742 fdp = &fr->fr_tifs[0];
3743 if ((ifp == NULL) || (fdp->fd_ifp == ifp))
3744 fr_resolvedest(fdp, v);
3746 fdp = &fr->fr_tifs[1];
3747 if ((ifp == NULL) || (fdp->fd_ifp == ifp))
3748 fr_resolvedest(fdp, v);
3750 fdp = &fr->fr_dif;
3751 if ((ifp == NULL) || (fdp->fd_ifp == ifp)) {
3752 fr_resolvedest(fdp, v);
3754 fr->fr_flags &= ~FR_DUP;
3755 if ((fdp->fd_ifp != (void *)-1) &&
3756 (fdp->fd_ifp != NULL))
3757 fr->fr_flags |= FR_DUP;
3760 #ifdef IPFILTER_LOOKUP
3761 if (fr->fr_type == FR_T_IPF && fr->fr_satype == FRI_LOOKUP &&
3762 fr->fr_srcptr == NULL) {
3763 fr->fr_srcptr = fr_resolvelookup(fr->fr_srctype,
3764 fr->fr_srcsubtype,
3765 &fr->fr_slookup,
3766 &fr->fr_srcfunc);
3768 if (fr->fr_type == FR_T_IPF && fr->fr_datype == FRI_LOOKUP &&
3769 fr->fr_dstptr == NULL) {
3770 fr->fr_dstptr = fr_resolvelookup(fr->fr_dsttype,
3771 fr->fr_dstsubtype,
3772 &fr->fr_dlookup,
3773 &fr->fr_dstfunc);
3775 #endif
3780 #ifdef _KERNEL
3781 /* ------------------------------------------------------------------------ */
3782 /* Function: frsync */
3783 /* Returns: void */
3784 /* Parameters: Nil */
3785 /* */
3786 /* frsync() is called when we suspect that the interface list or */
3787 /* information about interfaces (like IP#) has changed. Go through all */
3788 /* filter rules, NAT entries and the state table and check if anything */
3789 /* needs to be changed/updated. */
3790 /* ------------------------------------------------------------------------ */
3791 void frsync(ifp)
3792 void *ifp;
3794 int i;
3796 # if !SOLARIS
3797 fr_natsync(ifp);
3798 fr_statesync(ifp);
3799 # endif
3801 WRITE_ENTER(&ipf_mutex);
3802 frsynclist(ipacct[0][fr_active], ifp);
3803 frsynclist(ipacct[1][fr_active], ifp);
3804 frsynclist(ipfilter[0][fr_active], ifp);
3805 frsynclist(ipfilter[1][fr_active], ifp);
3806 frsynclist(ipacct6[0][fr_active], ifp);
3807 frsynclist(ipacct6[1][fr_active], ifp);
3808 frsynclist(ipfilter6[0][fr_active], ifp);
3809 frsynclist(ipfilter6[1][fr_active], ifp);
3811 for (i = 0; i < IPL_LOGSIZE; i++) {
3812 frgroup_t *g;
3814 for (g = ipfgroups[i][0]; g != NULL; g = g->fg_next)
3815 frsynclist(g->fg_start, ifp);
3816 for (g = ipfgroups[i][1]; g != NULL; g = g->fg_next)
3817 frsynclist(g->fg_start, ifp);
3819 RWLOCK_EXIT(&ipf_mutex);
3824 * In the functions below, bcopy() is called because the pointer being
3825 * copied _from_ in this instance is a pointer to a char buf (which could
3826 * end up being unaligned) and on the kernel's local stack.
3828 /* ------------------------------------------------------------------------ */
3829 /* Function: copyinptr */
3830 /* Returns: int - 0 = success, else failure */
3831 /* Parameters: src(I) - pointer to the source address */
3832 /* dst(I) - destination address */
3833 /* size(I) - number of bytes to copy */
3834 /* */
3835 /* Copy a block of data in from user space, given a pointer to the pointer */
3836 /* to start copying from (src) and a pointer to where to store it (dst). */
3837 /* NB: src - pointer to user space pointer, dst - kernel space pointer */
3838 /* ------------------------------------------------------------------------ */
3839 int copyinptr(src, dst, size)
3840 void *src, *dst;
3841 size_t size;
3843 void *ca;
3844 int error;
3846 # if SOLARIS
3847 error = COPYIN(src, &ca, sizeof(ca));
3848 if (error != 0)
3849 return error;
3850 # else
3851 bcopy(src, (void *)&ca, sizeof(ca));
3852 # endif
3853 error = COPYIN(ca, dst, size);
3854 if (error != 0)
3855 error = EFAULT;
3856 return error;
3860 /* ------------------------------------------------------------------------ */
3861 /* Function: copyoutptr */
3862 /* Returns: int - 0 = success, else failure */
3863 /* Parameters: src(I) - pointer to the source address */
3864 /* dst(I) - destination address */
3865 /* size(I) - number of bytes to copy */
3866 /* */
3867 /* Copy a block of data out to user space, given a pointer to the pointer */
3868 /* to start copying from (src) and a pointer to where to store it (dst). */
3869 /* NB: src - kernel space pointer, dst - pointer to user space pointer. */
3870 /* ------------------------------------------------------------------------ */
3871 int copyoutptr(src, dst, size)
3872 void *src, *dst;
3873 size_t size;
3875 void *ca;
3876 int error;
3878 bcopy(dst, &ca, sizeof(ca));
3879 error = COPYOUT(src, ca, size);
3880 if (error != 0)
3881 error = EFAULT;
3882 return error;
3884 #endif
3887 /* ------------------------------------------------------------------------ */
3888 /* Function: fr_lock */
3889 /* Returns: int - 0 = success, else error */
3890 /* Parameters: data(I) - pointer to lock value to set */
3891 /* lockp(O) - pointer to location to store old lock value */
3892 /* */
3893 /* Get the new value for the lock integer, set it and return the old value */
3894 /* in *lockp. */
3895 /* ------------------------------------------------------------------------ */
3896 int fr_lock(data, lockp)
3897 void *data;
3898 int *lockp;
3900 int arg, err;
3902 err = BCOPYIN(data, &arg, sizeof(arg));
3903 if (err != 0)
3904 return EFAULT;
3905 err = BCOPYOUT(lockp, data, sizeof(*lockp));
3906 if (err != 0)
3907 return EFAULT;
3908 *lockp = arg;
3909 return 0;
3913 /* ------------------------------------------------------------------------ */
3914 /* Function: fr_getstat */
3915 /* Returns: Nil */
3916 /* Parameters: fiop(I) - pointer to ipfilter stats structure */
3917 /* */
3918 /* Stores a copy of current pointers, counters, etc, in the friostat */
3919 /* structure. */
3920 /* ------------------------------------------------------------------------ */
3921 void fr_getstat(fiop)
3922 friostat_t *fiop;
3924 int i, j;
3926 bcopy((char *)frstats, (char *)fiop->f_st, sizeof(filterstats_t) * 2);
3927 fiop->f_locks[IPL_LOGSTATE] = fr_state_lock;
3928 fiop->f_locks[IPL_LOGNAT] = fr_nat_lock;
3929 fiop->f_locks[IPL_LOGIPF] = fr_frag_lock;
3930 fiop->f_locks[IPL_LOGAUTH] = fr_auth_lock;
3932 for (i = 0; i < 2; i++)
3933 for (j = 0; j < 2; j++) {
3934 fiop->f_ipf[i][j] = ipfilter[i][j];
3935 fiop->f_acct[i][j] = ipacct[i][j];
3936 fiop->f_ipf6[i][j] = ipfilter6[i][j];
3937 fiop->f_acct6[i][j] = ipacct6[i][j];
3940 fiop->f_ticks = fr_ticks;
3941 fiop->f_active = fr_active;
3942 fiop->f_froute[0] = fr_frouteok[0];
3943 fiop->f_froute[1] = fr_frouteok[1];
3945 fiop->f_running = fr_running;
3946 for (i = 0; i < IPL_LOGSIZE; i++) {
3947 fiop->f_groups[i][0] = ipfgroups[i][0];
3948 fiop->f_groups[i][1] = ipfgroups[i][1];
3950 #ifdef IPFILTER_LOG
3951 fiop->f_logging = 1;
3952 #else
3953 fiop->f_logging = 0;
3954 #endif
3955 fiop->f_defpass = fr_pass;
3956 fiop->f_features = fr_features;
3957 (void) strncpy(fiop->f_version, ipfilter_version,
3958 sizeof(fiop->f_version));
3962 #ifdef USE_INET6
3963 int icmptoicmp6types[ICMP_MAXTYPE+1] = {
3964 ICMP6_ECHO_REPLY, /* 0: ICMP_ECHOREPLY */
3965 -1, /* 1: UNUSED */
3966 -1, /* 2: UNUSED */
3967 ICMP6_DST_UNREACH, /* 3: ICMP_UNREACH */
3968 -1, /* 4: ICMP_SOURCEQUENCH */
3969 ND_REDIRECT, /* 5: ICMP_REDIRECT */
3970 -1, /* 6: UNUSED */
3971 -1, /* 7: UNUSED */
3972 ICMP6_ECHO_REQUEST, /* 8: ICMP_ECHO */
3973 -1, /* 9: UNUSED */
3974 -1, /* 10: UNUSED */
3975 ICMP6_TIME_EXCEEDED, /* 11: ICMP_TIMXCEED */
3976 ICMP6_PARAM_PROB, /* 12: ICMP_PARAMPROB */
3977 -1, /* 13: ICMP_TSTAMP */
3978 -1, /* 14: ICMP_TSTAMPREPLY */
3979 -1, /* 15: ICMP_IREQ */
3980 -1, /* 16: ICMP_IREQREPLY */
3981 -1, /* 17: ICMP_MASKREQ */
3982 -1, /* 18: ICMP_MASKREPLY */
3986 int icmptoicmp6unreach[ICMP_MAX_UNREACH] = {
3987 ICMP6_DST_UNREACH_ADDR, /* 0: ICMP_UNREACH_NET */
3988 ICMP6_DST_UNREACH_ADDR, /* 1: ICMP_UNREACH_HOST */
3989 -1, /* 2: ICMP_UNREACH_PROTOCOL */
3990 ICMP6_DST_UNREACH_NOPORT, /* 3: ICMP_UNREACH_PORT */
3991 -1, /* 4: ICMP_UNREACH_NEEDFRAG */
3992 ICMP6_DST_UNREACH_NOTNEIGHBOR, /* 5: ICMP_UNREACH_SRCFAIL */
3993 ICMP6_DST_UNREACH_ADDR, /* 6: ICMP_UNREACH_NET_UNKNOWN */
3994 ICMP6_DST_UNREACH_ADDR, /* 7: ICMP_UNREACH_HOST_UNKNOWN */
3995 -1, /* 8: ICMP_UNREACH_ISOLATED */
3996 ICMP6_DST_UNREACH_ADMIN, /* 9: ICMP_UNREACH_NET_PROHIB */
3997 ICMP6_DST_UNREACH_ADMIN, /* 10: ICMP_UNREACH_HOST_PROHIB */
3998 -1, /* 11: ICMP_UNREACH_TOSNET */
3999 -1, /* 12: ICMP_UNREACH_TOSHOST */
4000 ICMP6_DST_UNREACH_ADMIN, /* 13: ICMP_UNREACH_ADMIN_PROHIBIT */
4002 int icmpreplytype6[ICMP6_MAXTYPE + 1];
4003 #endif
4005 int icmpreplytype4[ICMP_MAXTYPE + 1];
4008 /* ------------------------------------------------------------------------ */
4009 /* Function: fr_matchicmpqueryreply */
4010 /* Returns: int - 1 if "icmp" is a valid reply to "ic" else 0. */
4011 /* Parameters: v(I) - IP protocol version (4 or 6) */
4012 /* ic(I) - ICMP information */
4013 /* icmp(I) - ICMP packet header */
4014 /* rev(I) - direction (0 = forward/1 = reverse) of packet */
4015 /* */
4016 /* Check if the ICMP packet defined by the header pointed to by icmp is a */
4017 /* reply to one as described by what's in ic. If it is a match, return 1, */
4018 /* else return 0 for no match. */
4019 /* ------------------------------------------------------------------------ */
4020 int fr_matchicmpqueryreply(v, ic, icmp, rev)
4021 int v;
4022 icmpinfo_t *ic;
4023 icmphdr_t *icmp;
4024 int rev;
4026 int ictype;
4028 ictype = ic->ici_type;
4030 if (v == 4) {
4032 * If we matched its type on the way in, then when going out
4033 * it will still be the same type.
4035 if ((!rev && (icmp->icmp_type == ictype)) ||
4036 (rev && (icmpreplytype4[ictype] == icmp->icmp_type))) {
4037 if (icmp->icmp_type != ICMP_ECHOREPLY)
4038 return 1;
4039 if (icmp->icmp_id == ic->ici_id)
4040 return 1;
4043 #ifdef USE_INET6
4044 else if (v == 6) {
4045 if ((!rev && (icmp->icmp_type == ictype)) ||
4046 (rev && (icmpreplytype6[ictype] == icmp->icmp_type))) {
4047 if (icmp->icmp_type != ICMP6_ECHO_REPLY)
4048 return 1;
4049 if (icmp->icmp_id == ic->ici_id)
4050 return 1;
4053 #endif
4054 return 0;
4058 #ifdef IPFILTER_LOOKUP
4059 /* ------------------------------------------------------------------------ */
4060 /* Function: fr_resolvelookup */
4061 /* Returns: void * - NULL = failure, else success. */
4062 /* Parameters: type(I) - type of lookup these parameters are for. */
4063 /* subtype(I) - whether the info below contains number/name */
4064 /* info(I) - pointer to name/number of the lookup data */
4065 /* funcptr(IO) - pointer to pointer for storing IP address */
4066 /* searching function. */
4067 /* */
4068 /* Search for the "table" number passed in amongst those configured for */
4069 /* that particular type. If the type is recognised then the function to */
4070 /* call to do the IP address search will be change, regardless of whether */
4071 /* or not the "table" number exists. */
4072 /* ------------------------------------------------------------------------ */
4073 static void *fr_resolvelookup(type, subtype, info, funcptr)
4074 u_int type, subtype;
4075 i6addr_t *info;
4076 lookupfunc_t *funcptr;
4078 char label[FR_GROUPLEN], *name;
4079 iphtable_t *iph;
4080 ip_pool_t *ipo;
4081 void *ptr;
4083 if (subtype == 0) {
4084 #if defined(SNPRINTF) && defined(_KERNEL)
4085 SNPRINTF(label, sizeof(label), "%u", info->iplookupnum);
4086 #else
4087 (void) sprintf(label, "%u", info->iplookupnum);
4088 #endif
4089 name = label;
4090 } else if (subtype == 1) {
4092 * Because iplookupname is currently only a 12 character
4093 * string and FR_GROUPLEN is 16, copy all of it into the
4094 * label buffer and add on a NULL at the end.
4096 strncpy(label, info->iplookupname, sizeof(info->iplookupname));
4097 label[sizeof(info->iplookupname)] = '\0';
4098 name = label;
4099 } else {
4100 return NULL;
4103 READ_ENTER(&ip_poolrw);
4105 switch (type)
4107 case IPLT_POOL :
4108 # if (defined(__osf__) && defined(_KERNEL))
4109 ptr = NULL;
4110 *funcptr = NULL;
4111 # else
4112 ipo = ip_pool_find(IPL_LOGIPF, name);
4113 ptr = ipo;
4114 if (ipo != NULL) {
4115 ATOMIC_INC32(ipo->ipo_ref);
4117 *funcptr = ip_pool_search;
4118 # endif
4119 break;
4120 case IPLT_HASH :
4121 iph = fr_findhtable(IPL_LOGIPF, name);
4122 ptr = iph;
4123 if (iph != NULL) {
4124 ATOMIC_INC32(iph->iph_ref);
4126 *funcptr = fr_iphmfindip;
4127 break;
4128 default:
4129 ptr = NULL;
4130 *funcptr = NULL;
4131 break;
4133 RWLOCK_EXIT(&ip_poolrw);
4135 return ptr;
4137 #endif
4140 /* ------------------------------------------------------------------------ */
4141 /* Function: frrequest */
4142 /* Returns: int - 0 == success, > 0 == errno value */
4143 /* Parameters: unit(I) - device for which this is for */
4144 /* req(I) - ioctl command (SIOC*) */
4145 /* data(I) - pointr to ioctl data */
4146 /* set(I) - 1 or 0 (filter set) */
4147 /* makecopy(I) - flag indicating whether data points to a rule */
4148 /* in kernel space & hence doesn't need copying. */
4149 /* */
4150 /* This function handles all the requests which operate on the list of */
4151 /* filter rules. This includes adding, deleting, insertion. It is also */
4152 /* responsible for creating groups when a "head" rule is loaded. Interface */
4153 /* names are resolved here and other sanity checks are made on the content */
4154 /* of the rule structure being loaded. If a rule has user defined timeouts */
4155 /* then make sure they are created and initialised before exiting. */
4156 /* ------------------------------------------------------------------------ */
4157 int frrequest(unit, req, data, set, makecopy)
4158 int unit;
4159 ioctlcmd_t req;
4160 int set, makecopy;
4161 void *data;
4163 frentry_t frd, *fp, *f, **fprev, **ftail;
4164 int error = 0, in, v;
4165 void *ptr, *uptr;
4166 u_int *p, *pp;
4167 frgroup_t *fg;
4168 char *group;
4170 fg = NULL;
4171 fp = &frd;
4172 if (makecopy != 0) {
4173 error = fr_inobj(data, fp, IPFOBJ_FRENTRY);
4174 if (error)
4175 return EFAULT;
4176 if ((fp->fr_flags & FR_T_BUILTIN) != 0)
4177 return EINVAL;
4178 fp->fr_ref = 0;
4179 fp->fr_flags |= FR_COPIED;
4180 } else {
4181 fp = (frentry_t *)data;
4182 if ((fp->fr_type & FR_T_BUILTIN) == 0)
4183 return EINVAL;
4184 fp->fr_flags &= ~FR_COPIED;
4187 if (((fp->fr_dsize == 0) && (fp->fr_data != NULL)) ||
4188 ((fp->fr_dsize != 0) && (fp->fr_data == NULL)))
4189 return EINVAL;
4191 v = fp->fr_v;
4192 uptr = fp->fr_data;
4195 * Only filter rules for IPv4 or IPv6 are accepted.
4197 if (v == 4)
4198 /*EMPTY*/;
4199 #ifdef USE_INET6
4200 else if (v == 6)
4201 /*EMPTY*/;
4202 #endif
4203 else {
4204 return EINVAL;
4208 * If the rule is being loaded from user space, i.e. we had to copy it
4209 * into kernel space, then do not trust the function pointer in the
4210 * rule.
4212 if ((makecopy == 1) && (fp->fr_func != NULL)) {
4213 if (fr_findfunc(fp->fr_func) == NULL)
4214 return ESRCH;
4215 error = fr_funcinit(fp);
4216 if (error != 0)
4217 return error;
4220 ptr = NULL;
4222 * Check that the group number does exist and that its use (in/out)
4223 * matches what the rule is.
4225 if (!strncmp(fp->fr_grhead, "0", FR_GROUPLEN))
4226 *fp->fr_grhead = '\0';
4227 group = fp->fr_group;
4228 if (!strncmp(group, "0", FR_GROUPLEN))
4229 *group = '\0';
4231 if (FR_ISACCOUNT(fp->fr_flags))
4232 unit = IPL_LOGCOUNT;
4234 if ((req != (int)SIOCZRLST) && (*group != '\0')) {
4235 fg = fr_findgroup(group, unit, set, NULL);
4236 if (fg == NULL)
4237 return ESRCH;
4238 if (fg->fg_flags == 0)
4239 fg->fg_flags = fp->fr_flags & FR_INOUT;
4240 else if (fg->fg_flags != (fp->fr_flags & FR_INOUT))
4241 return ESRCH;
4244 in = (fp->fr_flags & FR_INQUE) ? 0 : 1;
4247 * Work out which rule list this change is being applied to.
4249 ftail = NULL;
4250 fprev = NULL;
4251 if (unit == IPL_LOGAUTH)
4252 fprev = &ipauth;
4253 else if (v == 4) {
4254 if (FR_ISACCOUNT(fp->fr_flags))
4255 fprev = &ipacct[in][set];
4256 else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0)
4257 fprev = &ipfilter[in][set];
4258 } else if (v == 6) {
4259 if (FR_ISACCOUNT(fp->fr_flags))
4260 fprev = &ipacct6[in][set];
4261 else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0)
4262 fprev = &ipfilter6[in][set];
4264 if (fprev == NULL)
4265 return ESRCH;
4267 if (*group != '\0') {
4268 if (!fg && !(fg = fr_findgroup(group, unit, set, NULL)))
4269 return ESRCH;
4270 fprev = &fg->fg_start;
4274 * Copy in extra data for the rule.
4276 if (fp->fr_dsize != 0) {
4277 if (makecopy != 0) {
4278 KMALLOCS(ptr, void *, fp->fr_dsize);
4279 if (!ptr)
4280 return ENOMEM;
4281 error = COPYIN(uptr, ptr, fp->fr_dsize);
4282 if (error != 0)
4283 error = EFAULT;
4284 } else {
4285 ptr = uptr;
4286 error = 0;
4288 if (error != 0) {
4289 KFREES(ptr, fp->fr_dsize);
4290 return ENOMEM;
4292 fp->fr_data = ptr;
4293 } else
4294 fp->fr_data = NULL;
4297 * Perform per-rule type sanity checks of their members.
4299 switch (fp->fr_type & ~FR_T_BUILTIN)
4301 #if defined(IPFILTER_BPF)
4302 case FR_T_BPFOPC :
4303 if (fp->fr_dsize == 0)
4304 return EINVAL;
4305 if (!bpf_validate(ptr, fp->fr_dsize/sizeof(struct bpf_insn))) {
4306 if (makecopy && fp->fr_data != NULL) {
4307 KFREES(fp->fr_data, fp->fr_dsize);
4309 return EINVAL;
4311 break;
4312 #endif
4313 case FR_T_IPF :
4314 if (fp->fr_dsize != sizeof(fripf_t))
4315 return EINVAL;
4318 * Allowing a rule with both "keep state" and "with oow" is
4319 * pointless because adding a state entry to the table will
4320 * fail with the out of window (oow) flag set.
4322 if ((fp->fr_flags & FR_KEEPSTATE) && (fp->fr_flx & FI_OOW))
4323 return EINVAL;
4325 switch (fp->fr_satype)
4327 case FRI_BROADCAST :
4328 case FRI_DYNAMIC :
4329 case FRI_NETWORK :
4330 case FRI_NETMASKED :
4331 case FRI_PEERADDR :
4332 if (fp->fr_sifpidx < 0 || fp->fr_sifpidx > 3) {
4333 if (makecopy && fp->fr_data != NULL) {
4334 KFREES(fp->fr_data, fp->fr_dsize);
4336 return EINVAL;
4338 break;
4339 #ifdef IPFILTER_LOOKUP
4340 case FRI_LOOKUP :
4341 fp->fr_srcptr = fr_resolvelookup(fp->fr_srctype,
4342 fp->fr_srcsubtype,
4343 &fp->fr_slookup,
4344 &fp->fr_srcfunc);
4345 if (fp->fr_srcptr == NULL)
4346 return ESRCH;
4347 break;
4348 #endif
4349 default :
4350 break;
4353 switch (fp->fr_datype)
4355 case FRI_BROADCAST :
4356 case FRI_DYNAMIC :
4357 case FRI_NETWORK :
4358 case FRI_NETMASKED :
4359 case FRI_PEERADDR :
4360 if (fp->fr_difpidx < 0 || fp->fr_difpidx > 3) {
4361 if (makecopy && fp->fr_data != NULL) {
4362 KFREES(fp->fr_data, fp->fr_dsize);
4364 return EINVAL;
4366 break;
4367 #ifdef IPFILTER_LOOKUP
4368 case FRI_LOOKUP :
4369 fp->fr_dstptr = fr_resolvelookup(fp->fr_dsttype,
4370 fp->fr_dstsubtype,
4371 &fp->fr_dlookup,
4372 &fp->fr_dstfunc);
4373 if (fp->fr_dstptr == NULL)
4374 return ESRCH;
4375 break;
4376 #endif
4377 default :
4378 break;
4380 break;
4381 case FR_T_NONE :
4382 break;
4383 case FR_T_CALLFUNC :
4384 break;
4385 case FR_T_COMPIPF :
4386 break;
4387 default :
4388 if (makecopy && fp->fr_data != NULL) {
4389 KFREES(fp->fr_data, fp->fr_dsize);
4391 return EINVAL;
4395 * Lookup all the interface names that are part of the rule.
4397 frsynclist(fp, NULL);
4398 fp->fr_statecnt = 0;
4401 * Look for an existing matching filter rule, but don't include the
4402 * next or interface pointer in the comparison (fr_next, fr_ifa).
4403 * This elminates rules which are indentical being loaded. Checksum
4404 * the constant part of the filter rule to make comparisons quicker
4405 * (this meaning no pointers are included).
4407 for (fp->fr_cksum = 0, p = (u_int *)&fp->fr_func, pp = &fp->fr_cksum;
4408 p < pp; p++)
4409 fp->fr_cksum += *p;
4410 pp = (u_int *)((char *)fp->fr_caddr + fp->fr_dsize);
4411 for (p = (u_int *)fp->fr_data; p < pp; p++)
4412 fp->fr_cksum += *p;
4414 WRITE_ENTER(&ipf_mutex);
4417 * Now that the filter rule lists are locked, we can walk the
4418 * chain of them without fear.
4420 ftail = fprev;
4421 for (f = *ftail; (f = *ftail) != NULL; ftail = &f->fr_next) {
4422 if (fp->fr_collect <= f->fr_collect) {
4423 ftail = fprev;
4424 f = NULL;
4425 break;
4427 fprev = ftail;
4429 bzero((char *)frcache, sizeof(frcache));
4431 for (; (f = *ftail) != NULL; ftail = &f->fr_next) {
4432 if ((fp->fr_cksum != f->fr_cksum) ||
4433 (f->fr_dsize != fp->fr_dsize))
4434 continue;
4435 if (bcmp((char *)&f->fr_func, (char *)&fp->fr_func, FR_CMPSIZ))
4436 continue;
4437 if ((!ptr && !f->fr_data) ||
4438 (ptr && f->fr_data &&
4439 !bcmp((char *)ptr, (char *)f->fr_data, f->fr_dsize)))
4440 break;
4444 * If zero'ing statistics, copy current to caller and zero.
4446 if (req == (ioctlcmd_t)SIOCZRLST) {
4447 if (f == NULL)
4448 error = ESRCH;
4449 else {
4451 * Copy and reduce lock because of impending copyout.
4452 * Well we should, but if we do then the atomicity of
4453 * this call and the correctness of fr_hits and
4454 * fr_bytes cannot be guaranteed. As it is, this code
4455 * only resets them to 0 if they are successfully
4456 * copied out into user space.
4458 bcopy((char *)f, (char *)fp, sizeof(*f));
4459 /* MUTEX_DOWNGRADE(&ipf_mutex); */
4462 * When we copy this rule back out, set the data
4463 * pointer to be what it was in user space.
4465 fp->fr_data = uptr;
4466 error = fr_outobj(data, fp, IPFOBJ_FRENTRY);
4468 if (error == 0) {
4469 if ((f->fr_dsize != 0) && (uptr != NULL))
4470 error = COPYOUT(f->fr_data, uptr,
4471 f->fr_dsize);
4472 if (error != 0)
4473 error = EFAULT;
4474 if (error == 0) {
4475 f->fr_hits = 0;
4476 f->fr_bytes = 0;
4481 if ((ptr != NULL) && (makecopy != 0)) {
4482 KFREES(ptr, fp->fr_dsize);
4484 RWLOCK_EXIT(&ipf_mutex);
4485 return error;
4488 if (!f) {
4490 * At the end of this, ftail must point to the place where the
4491 * new rule is to be saved/inserted/added.
4492 * For SIOCAD*FR, this should be the last rule in the group of
4493 * rules that have equal fr_collect fields.
4494 * For SIOCIN*FR, ...
4496 if (req == (ioctlcmd_t)SIOCADAFR ||
4497 req == (ioctlcmd_t)SIOCADIFR) {
4499 for (ftail = fprev; (f = *ftail) != NULL; ) {
4500 if (f->fr_collect > fp->fr_collect)
4501 break;
4502 ftail = &f->fr_next;
4504 f = NULL;
4505 ptr = NULL;
4506 error = 0;
4507 } else if (req == (ioctlcmd_t)SIOCINAFR ||
4508 req == (ioctlcmd_t)SIOCINIFR) {
4509 while ((f = *fprev) != NULL) {
4510 if (f->fr_collect >= fp->fr_collect)
4511 break;
4512 fprev = &f->fr_next;
4514 ftail = fprev;
4515 if (fp->fr_hits != 0) {
4516 while (fp->fr_hits && (f = *ftail)) {
4517 if (f->fr_collect != fp->fr_collect)
4518 break;
4519 fprev = ftail;
4520 ftail = &f->fr_next;
4521 fp->fr_hits--;
4524 f = NULL;
4525 ptr = NULL;
4526 error = 0;
4531 * Request to remove a rule.
4533 if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) {
4534 if (!f)
4535 error = ESRCH;
4536 else {
4538 * Do not allow activity from user space to interfere
4539 * with rules not loaded that way.
4541 if ((makecopy == 1) && !(f->fr_flags & FR_COPIED)) {
4542 error = EPERM;
4543 goto done;
4547 * Return EBUSY if the rule is being reference by
4548 * something else (eg state information.)
4550 if (f->fr_ref > 1) {
4551 error = EBUSY;
4552 goto done;
4554 #ifdef IPFILTER_SCAN
4555 if (f->fr_isctag[0] != '\0' &&
4556 (f->fr_isc != (struct ipscan *)-1))
4557 ipsc_detachfr(f);
4558 #endif
4559 if (unit == IPL_LOGAUTH) {
4560 error = fr_preauthcmd(req, f, ftail);
4561 goto done;
4563 if (*f->fr_grhead != '\0')
4564 fr_delgroup(f->fr_grhead, unit, set);
4565 fr_fixskip(ftail, f, -1);
4566 *ftail = f->fr_next;
4567 f->fr_next = NULL;
4568 (void) fr_derefrule(&f);
4570 } else {
4572 * Not removing, so we must be adding/inserting a rule.
4574 if (f)
4575 error = EEXIST;
4576 else {
4577 if (unit == IPL_LOGAUTH) {
4578 error = fr_preauthcmd(req, fp, ftail);
4579 goto done;
4581 if (makecopy) {
4582 KMALLOC(f, frentry_t *);
4583 } else
4584 f = fp;
4585 if (f != NULL) {
4586 if (fp != f)
4587 bcopy((char *)fp, (char *)f,
4588 sizeof(*f));
4589 MUTEX_NUKE(&f->fr_lock);
4590 MUTEX_INIT(&f->fr_lock, "filter rule lock");
4591 #ifdef IPFILTER_SCAN
4592 if (f->fr_isctag[0] != '\0' &&
4593 ipsc_attachfr(f))
4594 f->fr_isc = (struct ipscan *)-1;
4595 #endif
4596 f->fr_hits = 0;
4597 if (makecopy != 0)
4598 f->fr_ref = 1;
4599 f->fr_next = *ftail;
4600 *ftail = f;
4601 if (req == (ioctlcmd_t)SIOCINIFR ||
4602 req == (ioctlcmd_t)SIOCINAFR)
4603 fr_fixskip(ftail, f, 1);
4604 f->fr_grp = NULL;
4605 group = f->fr_grhead;
4606 if (*group != '\0') {
4607 fg = fr_addgroup(group, f, f->fr_flags,
4608 unit, set);
4609 if (fg != NULL)
4610 f->fr_grp = &fg->fg_start;
4612 } else
4613 error = ENOMEM;
4616 done:
4617 RWLOCK_EXIT(&ipf_mutex);
4618 if ((ptr != NULL) && (error != 0) && (makecopy != 0)) {
4619 KFREES(ptr, fp->fr_dsize);
4621 return (error);
4625 /* ------------------------------------------------------------------------ */
4626 /* Function: fr_funcinit */
4627 /* Returns: int - 0 == success, else ESRCH: cannot resolve rule details */
4628 /* Parameters: fr(I) - pointer to filter rule */
4629 /* */
4630 /* If a rule is a call rule, then check if the function it points to needs */
4631 /* an init function to be called now the rule has been loaded. */
4632 /* ------------------------------------------------------------------------ */
4633 static int fr_funcinit(fr)
4634 frentry_t *fr;
4636 ipfunc_resolve_t *ft;
4637 int err;
4639 err = ESRCH;
4641 for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4642 if (ft->ipfu_addr == fr->fr_func) {
4643 err = 0;
4644 if (ft->ipfu_init != NULL)
4645 err = (*ft->ipfu_init)(fr);
4646 break;
4648 return err;
4652 /* ------------------------------------------------------------------------ */
4653 /* Function: fr_findfunc */
4654 /* Returns: ipfunc_t - pointer to function if found, else NULL */
4655 /* Parameters: funcptr(I) - function pointer to lookup */
4656 /* */
4657 /* Look for a function in the table of known functions. */
4658 /* ------------------------------------------------------------------------ */
4659 static ipfunc_t fr_findfunc(funcptr)
4660 ipfunc_t funcptr;
4662 ipfunc_resolve_t *ft;
4664 for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4665 if (ft->ipfu_addr == funcptr)
4666 return funcptr;
4667 return NULL;
4671 /* ------------------------------------------------------------------------ */
4672 /* Function: fr_resolvefunc */
4673 /* Returns: int - 0 == success, else error */
4674 /* Parameters: data(IO) - ioctl data pointer to ipfunc_resolve_t struct */
4675 /* */
4676 /* Copy in a ipfunc_resolve_t structure and then fill in the missing field. */
4677 /* This will either be the function name (if the pointer is set) or the */
4678 /* function pointer if the name is set. When found, fill in the other one */
4679 /* so that the entire, complete, structure can be copied back to user space.*/
4680 /* ------------------------------------------------------------------------ */
4681 int fr_resolvefunc(data)
4682 void *data;
4684 ipfunc_resolve_t res, *ft;
4685 int err;
4687 err = BCOPYIN(data, &res, sizeof(res));
4688 if (err != 0)
4689 return EFAULT;
4691 if (res.ipfu_addr == NULL && res.ipfu_name[0] != '\0') {
4692 for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4693 if (strncmp(res.ipfu_name, ft->ipfu_name,
4694 sizeof(res.ipfu_name)) == 0) {
4695 res.ipfu_addr = ft->ipfu_addr;
4696 res.ipfu_init = ft->ipfu_init;
4697 if (COPYOUT(&res, data, sizeof(res)) != 0)
4698 return EFAULT;
4699 return 0;
4702 if (res.ipfu_addr != NULL && res.ipfu_name[0] == '\0') {
4703 for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4704 if (ft->ipfu_addr == res.ipfu_addr) {
4705 (void) strncpy(res.ipfu_name, ft->ipfu_name,
4706 sizeof(res.ipfu_name));
4707 res.ipfu_init = ft->ipfu_init;
4708 if (COPYOUT(&res, data, sizeof(res)) != 0)
4709 return EFAULT;
4710 return 0;
4713 return ESRCH;
4717 #if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__FreeBSD__)) || \
4718 (defined(__FreeBSD__) && (__FreeBSD_version < 501000)) || \
4719 (defined(__NetBSD__) && (__NetBSD_Version__ < 105000000)) || \
4720 (defined(__OpenBSD__) && (OpenBSD < 200006))
4722 * From: NetBSD
4723 * ppsratecheck(): packets (or events) per second limitation.
4726 ppsratecheck(lasttime, curpps, maxpps)
4727 struct timeval *lasttime;
4728 int *curpps;
4729 int maxpps; /* maximum pps allowed */
4731 struct timeval tv, delta;
4732 int rv;
4734 GETKTIME(&tv);
4736 delta.tv_sec = tv.tv_sec - lasttime->tv_sec;
4737 delta.tv_usec = tv.tv_usec - lasttime->tv_usec;
4738 if (delta.tv_usec < 0) {
4739 delta.tv_sec--;
4740 delta.tv_usec += 1000000;
4744 * check for 0,0 is so that the message will be seen at least once.
4745 * if more than one second have passed since the last update of
4746 * lasttime, reset the counter.
4748 * we do increment *curpps even in *curpps < maxpps case, as some may
4749 * try to use *curpps for stat purposes as well.
4751 if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) ||
4752 delta.tv_sec >= 1) {
4753 *lasttime = tv;
4754 *curpps = 0;
4755 rv = 1;
4756 } else if (maxpps < 0)
4757 rv = 1;
4758 else if (*curpps < maxpps)
4759 rv = 1;
4760 else
4761 rv = 0;
4762 *curpps = *curpps + 1;
4764 return (rv);
4766 #endif
4769 /* ------------------------------------------------------------------------ */
4770 /* Function: fr_derefrule */
4771 /* Returns: int - 0 == rule freed up, else rule not freed */
4772 /* Parameters: fr(I) - pointer to filter rule */
4773 /* */
4774 /* Decrement the reference counter to a rule by one. If it reaches zero, */
4775 /* free it and any associated storage space being used by it. */
4776 /* ------------------------------------------------------------------------ */
4777 int fr_derefrule(frp)
4778 frentry_t **frp;
4780 frentry_t *fr;
4782 fr = *frp;
4783 *frp = NULL;
4785 MUTEX_ENTER(&fr->fr_lock);
4786 fr->fr_ref--;
4787 if (fr->fr_ref == 0) {
4788 MUTEX_EXIT(&fr->fr_lock);
4789 MUTEX_DESTROY(&fr->fr_lock);
4791 #ifdef IPFILTER_LOOKUP
4792 if (fr->fr_type == FR_T_IPF && fr->fr_satype == FRI_LOOKUP)
4793 ip_lookup_deref(fr->fr_srctype, fr->fr_srcptr);
4794 if (fr->fr_type == FR_T_IPF && fr->fr_datype == FRI_LOOKUP)
4795 ip_lookup_deref(fr->fr_dsttype, fr->fr_dstptr);
4796 #endif
4798 if (fr->fr_dsize) {
4799 KFREES(fr->fr_data, fr->fr_dsize);
4801 if ((fr->fr_flags & FR_COPIED) != 0) {
4802 KFREE(fr);
4803 return 0;
4805 return 1;
4806 } else {
4807 MUTEX_EXIT(&fr->fr_lock);
4809 return -1;
4813 #ifdef IPFILTER_LOOKUP
4814 /* ------------------------------------------------------------------------ */
4815 /* Function: fr_grpmapinit */
4816 /* Returns: int - 0 == success, else ESRCH because table entry not found*/
4817 /* Parameters: fr(I) - pointer to rule to find hash table for */
4818 /* */
4819 /* Looks for group hash table fr_arg and stores a pointer to it in fr_ptr. */
4820 /* fr_ptr is later used by fr_srcgrpmap and fr_dstgrpmap. */
4821 /* ------------------------------------------------------------------------ */
4822 static int fr_grpmapinit(fr)
4823 frentry_t *fr;
4825 char name[FR_GROUPLEN];
4826 iphtable_t *iph;
4828 #if defined(SNPRINTF) && defined(_KERNEL)
4829 SNPRINTF(name, sizeof(name), "%d", fr->fr_arg);
4830 #else
4831 (void) sprintf(name, "%d", fr->fr_arg);
4832 #endif
4833 iph = fr_findhtable(IPL_LOGIPF, name);
4834 if (iph == NULL)
4835 return ESRCH;
4836 if ((iph->iph_flags & FR_INOUT) != (fr->fr_flags & FR_INOUT))
4837 return ESRCH;
4838 fr->fr_ptr = iph;
4839 return 0;
4843 /* ------------------------------------------------------------------------ */
4844 /* Function: fr_srcgrpmap */
4845 /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */
4846 /* Parameters: fin(I) - pointer to packet information */
4847 /* passp(IO) - pointer to current/new filter decision (unused) */
4848 /* */
4849 /* Look for a rule group head in a hash table, using the source address as */
4850 /* the key, and descend into that group and continue matching rules against */
4851 /* the packet. */
4852 /* ------------------------------------------------------------------------ */
4853 frentry_t *fr_srcgrpmap(fin, passp)
4854 fr_info_t *fin;
4855 u_32_t *passp;
4857 frgroup_t *fg;
4858 void *rval;
4860 rval = fr_iphmfindgroup(fin->fin_fr->fr_ptr, &fin->fin_src);
4861 if (rval == NULL)
4862 return NULL;
4864 fg = rval;
4865 fin->fin_fr = fg->fg_start;
4866 (void) fr_scanlist(fin, *passp);
4867 return fin->fin_fr;
4871 /* ------------------------------------------------------------------------ */
4872 /* Function: fr_dstgrpmap */
4873 /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */
4874 /* Parameters: fin(I) - pointer to packet information */
4875 /* passp(IO) - pointer to current/new filter decision (unused) */
4876 /* */
4877 /* Look for a rule group head in a hash table, using the destination */
4878 /* address as the key, and descend into that group and continue matching */
4879 /* rules against the packet. */
4880 /* ------------------------------------------------------------------------ */
4881 frentry_t *fr_dstgrpmap(fin, passp)
4882 fr_info_t *fin;
4883 u_32_t *passp;
4885 frgroup_t *fg;
4886 void *rval;
4888 rval = fr_iphmfindgroup(fin->fin_fr->fr_ptr, &fin->fin_dst);
4889 if (rval == NULL)
4890 return NULL;
4892 fg = rval;
4893 fin->fin_fr = fg->fg_start;
4894 (void) fr_scanlist(fin, *passp);
4895 return fin->fin_fr;
4897 #endif /* IPFILTER_LOOKUP */
4900 * Queue functions
4901 * ===============
4902 * These functions manage objects on queues for efficient timeouts. There are
4903 * a number of system defined queues as well as user defined timeouts. It is
4904 * expected that a lock is held in the domain in which the queue belongs
4905 * (i.e. either state or NAT) when calling any of these functions that prevents
4906 * fr_freetimeoutqueue() from being called at the same time as any other.
4910 /* ------------------------------------------------------------------------ */
4911 /* Function: fr_addtimeoutqueue */
4912 /* Returns: struct ifqtq * - NULL if malloc fails, else pointer to */
4913 /* timeout queue with given interval. */
4914 /* Parameters: parent(I) - pointer to pointer to parent node of this list */
4915 /* of interface queues. */
4916 /* seconds(I) - timeout value in seconds for this queue. */
4917 /* */
4918 /* This routine first looks for a timeout queue that matches the interval */
4919 /* being requested. If it finds one, increments the reference counter and */
4920 /* returns a pointer to it. If none are found, it allocates a new one and */
4921 /* inserts it at the top of the list. */
4922 /* */
4923 /* Locking. */
4924 /* It is assumed that the caller of this function has an appropriate lock */
4925 /* held (exclusively) in the domain that encompases 'parent'. */
4926 /* ------------------------------------------------------------------------ */
4927 ipftq_t *fr_addtimeoutqueue(parent, seconds)
4928 ipftq_t **parent;
4929 u_int seconds;
4931 ipftq_t *ifq;
4932 u_int period;
4934 period = seconds * IPF_HZ_DIVIDE;
4936 MUTEX_ENTER(&ipf_timeoutlock);
4937 for (ifq = *parent; ifq != NULL; ifq = ifq->ifq_next) {
4938 if (ifq->ifq_ttl == period) {
4940 * Reset the delete flag, if set, so the structure
4941 * gets reused rather than freed and reallocated.
4943 MUTEX_ENTER(&ifq->ifq_lock);
4944 ifq->ifq_flags &= ~IFQF_DELETE;
4945 ifq->ifq_ref++;
4946 MUTEX_EXIT(&ifq->ifq_lock);
4947 MUTEX_EXIT(&ipf_timeoutlock);
4949 return ifq;
4953 KMALLOC(ifq, ipftq_t *);
4954 if (ifq != NULL) {
4955 ifq->ifq_ttl = period;
4956 ifq->ifq_head = NULL;
4957 ifq->ifq_tail = &ifq->ifq_head;
4958 ifq->ifq_next = *parent;
4959 ifq->ifq_pnext = parent;
4960 ifq->ifq_ref = 1;
4961 ifq->ifq_flags = IFQF_USER;
4962 *parent = ifq;
4963 fr_userifqs++;
4964 MUTEX_NUKE(&ifq->ifq_lock);
4965 MUTEX_INIT(&ifq->ifq_lock, "ipftq mutex");
4967 MUTEX_EXIT(&ipf_timeoutlock);
4968 return ifq;
4972 /* ------------------------------------------------------------------------ */
4973 /* Function: fr_deletetimeoutqueue */
4974 /* Returns: int - new reference count value of the timeout queue */
4975 /* Parameters: ifq(I) - timeout queue which is losing a reference. */
4976 /* Locks: ifq->ifq_lock */
4977 /* */
4978 /* This routine must be called when we're discarding a pointer to a timeout */
4979 /* queue object, taking care of the reference counter. */
4980 /* */
4981 /* Now that this just sets a DELETE flag, it requires the expire code to */
4982 /* check the list of user defined timeout queues and call the free function */
4983 /* below (currently commented out) to stop memory leaking. It is done this */
4984 /* way because the locking may not be sufficient to safely do a free when */
4985 /* this function is called. */
4986 /* ------------------------------------------------------------------------ */
4987 int fr_deletetimeoutqueue(ifq)
4988 ipftq_t *ifq;
4991 ifq->ifq_ref--;
4992 if ((ifq->ifq_ref == 0) && ((ifq->ifq_flags & IFQF_USER) != 0)) {
4993 ifq->ifq_flags |= IFQF_DELETE;
4996 return ifq->ifq_ref;
5000 /* ------------------------------------------------------------------------ */
5001 /* Function: fr_freetimeoutqueue */
5002 /* Parameters: ifq(I) - timeout queue which is losing a reference. */
5003 /* Returns: Nil */
5004 /* */
5005 /* Locking: */
5006 /* It is assumed that the caller of this function has an appropriate lock */
5007 /* held (exclusively) in the domain that encompases the callers "domain". */
5008 /* The ifq_lock for this structure should not be held. */
5009 /* */
5010 /* Remove a user definde timeout queue from the list of queues it is in and */
5011 /* tidy up after this is done. */
5012 /* ------------------------------------------------------------------------ */
5013 void fr_freetimeoutqueue(ifq)
5014 ipftq_t *ifq;
5018 if (((ifq->ifq_flags & IFQF_DELETE) == 0) || (ifq->ifq_ref != 0) ||
5019 ((ifq->ifq_flags & IFQF_USER) == 0)) {
5020 printf("fr_freetimeoutqueue(%lx) flags 0x%x ttl %d ref %d\n",
5021 (u_long)ifq, ifq->ifq_flags, ifq->ifq_ttl,
5022 ifq->ifq_ref);
5023 return;
5027 * Remove from its position in the list.
5029 *ifq->ifq_pnext = ifq->ifq_next;
5030 if (ifq->ifq_next != NULL)
5031 ifq->ifq_next->ifq_pnext = ifq->ifq_pnext;
5033 MUTEX_DESTROY(&ifq->ifq_lock);
5034 ATOMIC_DEC(fr_userifqs);
5035 KFREE(ifq);
5039 /* ------------------------------------------------------------------------ */
5040 /* Function: fr_deletequeueentry */
5041 /* Returns: Nil */
5042 /* Parameters: tqe(I) - timeout queue entry to delete */
5043 /* ifq(I) - timeout queue to remove entry from */
5044 /* */
5045 /* Remove a tail queue entry from its queue and make it an orphan. */
5046 /* fr_deletetimeoutqueue is called to make sure the reference count on the */
5047 /* queue is correct. We can't, however, call fr_freetimeoutqueue because */
5048 /* the correct lock(s) may not be held that would make it safe to do so. */
5049 /* ------------------------------------------------------------------------ */
5050 void fr_deletequeueentry(tqe)
5051 ipftqent_t *tqe;
5053 ipftq_t *ifq;
5055 ifq = tqe->tqe_ifq;
5057 MUTEX_ENTER(&ifq->ifq_lock);
5059 if (tqe->tqe_pnext != NULL) {
5060 *tqe->tqe_pnext = tqe->tqe_next;
5061 if (tqe->tqe_next != NULL)
5062 tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
5063 else /* we must be the tail anyway */
5064 ifq->ifq_tail = tqe->tqe_pnext;
5066 tqe->tqe_pnext = NULL;
5067 tqe->tqe_ifq = NULL;
5070 (void) fr_deletetimeoutqueue(ifq);
5072 MUTEX_EXIT(&ifq->ifq_lock);
5076 /* ------------------------------------------------------------------------ */
5077 /* Function: fr_queuefront */
5078 /* Returns: Nil */
5079 /* Parameters: tqe(I) - pointer to timeout queue entry */
5080 /* */
5081 /* Move a queue entry to the front of the queue, if it isn't already there. */
5082 /* ------------------------------------------------------------------------ */
5083 void fr_queuefront(tqe)
5084 ipftqent_t *tqe;
5086 ipftq_t *ifq;
5088 ifq = tqe->tqe_ifq;
5089 if (ifq == NULL)
5090 return;
5092 MUTEX_ENTER(&ifq->ifq_lock);
5093 if (ifq->ifq_head != tqe) {
5094 *tqe->tqe_pnext = tqe->tqe_next;
5095 if (tqe->tqe_next)
5096 tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
5097 else
5098 ifq->ifq_tail = tqe->tqe_pnext;
5100 tqe->tqe_next = ifq->ifq_head;
5101 ifq->ifq_head->tqe_pnext = &tqe->tqe_next;
5102 ifq->ifq_head = tqe;
5103 tqe->tqe_pnext = &ifq->ifq_head;
5105 MUTEX_EXIT(&ifq->ifq_lock);
5109 /* ------------------------------------------------------------------------ */
5110 /* Function: fr_queueback */
5111 /* Returns: Nil */
5112 /* Parameters: tqe(I) - pointer to timeout queue entry */
5113 /* */
5114 /* Move a queue entry to the back of the queue, if it isn't already there. */
5115 /* ------------------------------------------------------------------------ */
5116 void fr_queueback(tqe)
5117 ipftqent_t *tqe;
5119 ipftq_t *ifq;
5121 ifq = tqe->tqe_ifq;
5122 if (ifq == NULL)
5123 return;
5124 tqe->tqe_die = fr_ticks + ifq->ifq_ttl;
5126 MUTEX_ENTER(&ifq->ifq_lock);
5127 if (tqe->tqe_next != NULL) { /* at the end already ? */
5129 * Remove from list
5131 *tqe->tqe_pnext = tqe->tqe_next;
5132 tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
5135 * Make it the last entry.
5137 tqe->tqe_next = NULL;
5138 tqe->tqe_pnext = ifq->ifq_tail;
5139 *ifq->ifq_tail = tqe;
5140 ifq->ifq_tail = &tqe->tqe_next;
5142 MUTEX_EXIT(&ifq->ifq_lock);
5146 /* ------------------------------------------------------------------------ */
5147 /* Function: fr_queueappend */
5148 /* Returns: Nil */
5149 /* Parameters: tqe(I) - pointer to timeout queue entry */
5150 /* ifq(I) - pointer to timeout queue */
5151 /* parent(I) - owing object pointer */
5152 /* */
5153 /* Add a new item to this queue and put it on the very end. */
5154 /* ------------------------------------------------------------------------ */
5155 void fr_queueappend(tqe, ifq, parent)
5156 ipftqent_t *tqe;
5157 ipftq_t *ifq;
5158 void *parent;
5161 MUTEX_ENTER(&ifq->ifq_lock);
5162 tqe->tqe_parent = parent;
5163 tqe->tqe_pnext = ifq->ifq_tail;
5164 *ifq->ifq_tail = tqe;
5165 ifq->ifq_tail = &tqe->tqe_next;
5166 tqe->tqe_next = NULL;
5167 tqe->tqe_ifq = ifq;
5168 tqe->tqe_die = fr_ticks + ifq->ifq_ttl;
5169 ifq->ifq_ref++;
5170 MUTEX_EXIT(&ifq->ifq_lock);
5174 /* ------------------------------------------------------------------------ */
5175 /* Function: fr_movequeue */
5176 /* Returns: Nil */
5177 /* Parameters: tq(I) - pointer to timeout queue information */
5178 /* oifp(I) - old timeout queue entry was on */
5179 /* nifp(I) - new timeout queue to put entry on */
5180 /* */
5181 /* Move a queue entry from one timeout queue to another timeout queue. */
5182 /* If it notices that the current entry is already last and does not need */
5183 /* to move queue, the return. */
5184 /* ------------------------------------------------------------------------ */
5185 void fr_movequeue(tqe, oifq, nifq)
5186 ipftqent_t *tqe;
5187 ipftq_t *oifq, *nifq;
5191 * If the queue hasn't changed and we last touched this entry at the
5192 * same ipf time, then we're not going to achieve anything by either
5193 * changing the ttl or moving it on the queue.
5195 if (oifq == nifq && tqe->tqe_touched == fr_ticks)
5196 return;
5199 * For any of this to be outside the lock, there is a risk that two
5200 * packets entering simultaneously, with one changing to a different
5201 * queue and one not, could end up with things in a bizarre state.
5203 MUTEX_ENTER(&oifq->ifq_lock);
5205 tqe->tqe_touched = fr_ticks;
5206 tqe->tqe_die = fr_ticks + nifq->ifq_ttl;
5208 * Is the operation here going to be a no-op ?
5210 if (oifq == nifq) {
5211 if ((tqe->tqe_next == NULL) ||
5212 (tqe->tqe_next->tqe_die == tqe->tqe_die)) {
5213 MUTEX_EXIT(&oifq->ifq_lock);
5214 return;
5219 * Remove from the old queue
5221 *tqe->tqe_pnext = tqe->tqe_next;
5222 if (tqe->tqe_next)
5223 tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
5224 else
5225 oifq->ifq_tail = tqe->tqe_pnext;
5226 tqe->tqe_next = NULL;
5229 * If we're moving from one queue to another, release the
5230 * lock on the old queue and get a lock on the new queue.
5231 * For user defined queues, if we're moving off it, call
5232 * delete in case it can now be freed.
5234 if (oifq != nifq) {
5235 tqe->tqe_ifq = NULL;
5237 (void) fr_deletetimeoutqueue(oifq);
5239 MUTEX_EXIT(&oifq->ifq_lock);
5241 MUTEX_ENTER(&nifq->ifq_lock);
5243 tqe->tqe_ifq = nifq;
5244 nifq->ifq_ref++;
5248 * Add to the bottom of the new queue
5250 tqe->tqe_pnext = nifq->ifq_tail;
5251 *nifq->ifq_tail = tqe;
5252 nifq->ifq_tail = &tqe->tqe_next;
5253 MUTEX_EXIT(&nifq->ifq_lock);
5257 /* ------------------------------------------------------------------------ */
5258 /* Function: fr_updateipid */
5259 /* Returns: int - 0 == success, -1 == error (packet should be droppped) */
5260 /* Parameters: fin(I) - pointer to packet information */
5261 /* */
5262 /* When we are doing NAT, change the IP of every packet to represent a */
5263 /* single sequence of packets coming from the host, hiding any host */
5264 /* specific sequencing that might otherwise be revealed. If the packet is */
5265 /* a fragment, then store the 'new' IPid in the fragment cache and look up */
5266 /* the fragment cache for non-leading fragments. If a non-leading fragment */
5267 /* has no match in the cache, return an error. */
5268 /* ------------------------------------------------------------------------ */
5269 static int fr_updateipid(fin)
5270 fr_info_t *fin;
5272 u_short id, ido, sums;
5273 u_32_t sumd, sum;
5274 ip_t *ip;
5276 if (fin->fin_off != 0) {
5277 sum = fr_ipid_knownfrag(fin);
5278 if (sum == 0xffffffff)
5279 return -1;
5280 sum &= 0xffff;
5281 id = (u_short)sum;
5282 } else {
5283 id = fr_nextipid(fin);
5284 if (fin->fin_off == 0 && (fin->fin_flx & FI_FRAG) != 0)
5285 (void) fr_ipid_newfrag(fin, (u_32_t)id);
5288 ip = fin->fin_ip;
5289 ido = ntohs(ip->ip_id);
5290 if (id == ido)
5291 return 0;
5292 ip->ip_id = htons(id);
5293 CALC_SUMD(ido, id, sumd); /* DESTRUCTIVE MACRO! id,ido change */
5294 sum = (~ntohs(ip->ip_sum)) & 0xffff;
5295 sum += sumd;
5296 sum = (sum >> 16) + (sum & 0xffff);
5297 sum = (sum >> 16) + (sum & 0xffff);
5298 sums = ~(u_short)sum;
5299 ip->ip_sum = htons(sums);
5300 return 0;
5304 #ifdef NEED_FRGETIFNAME
5305 /* ------------------------------------------------------------------------ */
5306 /* Function: fr_getifname */
5307 /* Returns: char * - pointer to interface name */
5308 /* Parameters: ifp(I) - pointer to network interface */
5309 /* buffer(O) - pointer to where to store interface name */
5310 /* */
5311 /* Constructs an interface name in the buffer passed. The buffer passed is */
5312 /* expected to be at least LIFNAMSIZ in bytes big. If buffer is passed in */
5313 /* as a NULL pointer then return a pointer to a static array. */
5314 /* ------------------------------------------------------------------------ */
5315 char *fr_getifname(ifp, buffer)
5316 struct ifnet *ifp;
5317 char *buffer;
5319 static char namebuf[LIFNAMSIZ];
5320 # if defined(MENTAT) || defined(__FreeBSD__) || defined(__osf__) || \
5321 defined(__sgi) || defined(linux) || defined(_AIX51) || \
5322 (defined(sun) && !defined(__SVR4) && !defined(__svr4__))
5323 int unit, space;
5324 char temp[20];
5325 char *s;
5326 # endif
5328 if (buffer == NULL)
5329 buffer = namebuf;
5330 (void) strncpy(buffer, ifp->if_name, LIFNAMSIZ);
5331 buffer[LIFNAMSIZ - 1] = '\0';
5332 # if defined(MENTAT) || defined(__FreeBSD__) || defined(__osf__) || \
5333 defined(__sgi) || defined(_AIX51) || \
5334 (defined(sun) && !defined(__SVR4) && !defined(__svr4__))
5335 for (s = buffer; *s; s++)
5337 unit = ifp->if_unit;
5338 space = LIFNAMSIZ - (s - buffer);
5339 if (space > 0) {
5340 # if defined(SNPRINTF) && defined(_KERNEL)
5341 SNPRINTF(temp, sizeof(temp), "%d", unit);
5342 # else
5343 (void) sprintf(temp, "%d", unit);
5344 # endif
5345 (void) strncpy(s, temp, space);
5347 # endif
5348 return buffer;
5350 #endif
5353 /* ------------------------------------------------------------------------ */
5354 /* Function: fr_ioctlswitch */
5355 /* Returns: int - -1 continue processing, else ioctl return value */
5356 /* Parameters: unit(I) - device unit opened */
5357 /* data(I) - pointer to ioctl data */
5358 /* cmd(I) - ioctl command */
5359 /* mode(I) - mode value */
5360 /* uid(I) - uid making the ioctl call */
5361 /* ctx(I) - pointer to context data */
5362 /* */
5363 /* Based on the value of unit, call the appropriate ioctl handler or return */
5364 /* EIO if ipfilter is not running. Also checks if write perms are req'd */
5365 /* for the device in order to execute the ioctl. */
5366 /* ------------------------------------------------------------------------ */
5367 int fr_ioctlswitch(unit, data, cmd, mode, uid, ctx)
5368 int unit, mode, uid;
5369 ioctlcmd_t cmd;
5370 void *data, *ctx;
5372 int error = 0;
5374 switch (unit)
5376 case IPL_LOGIPF :
5377 error = fr_ipf_ioctl(data, cmd, mode, uid, ctx);
5378 break;
5379 case IPL_LOGNAT :
5380 if (fr_running > 0)
5381 error = fr_nat_ioctl(data, cmd, mode, uid, ctx);
5382 else
5383 error = EIO;
5384 break;
5385 case IPL_LOGSTATE :
5386 if (fr_running > 0)
5387 error = fr_state_ioctl(data, cmd, mode, uid, ctx);
5388 else
5389 error = EIO;
5390 break;
5391 case IPL_LOGAUTH :
5392 if (fr_running > 0)
5393 error = fr_auth_ioctl(data, cmd, mode, uid, ctx);
5394 else
5395 error = EIO;
5396 break;
5397 case IPL_LOGSYNC :
5398 #ifdef IPFILTER_SYNC
5399 if (fr_running > 0)
5400 error = fr_sync_ioctl(data, cmd, mode, uid, ctx);
5401 else
5402 #endif
5403 error = EIO;
5404 break;
5405 case IPL_LOGSCAN :
5406 #ifdef IPFILTER_SCAN
5407 if (fr_running > 0)
5408 error = fr_scan_ioctl(data, cmd, mode, uid, ctx);
5409 else
5410 #endif
5411 error = EIO;
5412 break;
5413 case IPL_LOGLOOKUP :
5414 #ifdef IPFILTER_LOOKUP
5415 if (fr_running > 0)
5416 error = ip_lookup_ioctl(data, cmd, mode, uid, ctx);
5417 else
5418 #endif
5419 error = EIO;
5420 break;
5421 default :
5422 error = EIO;
5423 break;
5426 return error;
5431 * This array defines the expected size of objects coming into the kernel
5432 * for the various recognised object types.
5434 static int fr_objbytes[IPFOBJ_COUNT][2] = {
5435 { 1, sizeof(struct frentry) }, /* frentry */
5436 { 0, sizeof(struct friostat) },
5437 { 0, sizeof(struct fr_info) },
5438 { 0, sizeof(struct fr_authstat) },
5439 { 0, sizeof(struct ipfrstat) },
5440 { 0, sizeof(struct ipnat) },
5441 { 0, sizeof(struct natstat) },
5442 { 0, sizeof(struct ipstate_save) },
5443 { 1, sizeof(struct nat_save) }, /* nat_save */
5444 { 0, sizeof(struct natlookup) },
5445 { 1, sizeof(struct ipstate) }, /* ipstate */
5446 { 0, sizeof(struct ips_stat) },
5447 { 0, sizeof(struct frauth) },
5448 { 0, sizeof(struct ipftune) },
5449 { 0, sizeof(struct nat) }, /* nat_t */
5450 { 0, sizeof(struct ipfruleiter) },
5451 { 0, sizeof(struct ipfgeniter) },
5452 { 0, sizeof(struct ipftable) },
5453 { 0, sizeof(struct ipflookupiter) },
5454 { 0, sizeof(struct ipftq) * IPF_TCP_NSTATES },
5458 /* ------------------------------------------------------------------------ */
5459 /* Function: fr_inobj */
5460 /* Returns: int - 0 = success, else failure */
5461 /* Parameters: data(I) - pointer to ioctl data */
5462 /* ptr(I) - pointer to store real data in */
5463 /* type(I) - type of structure being moved */
5464 /* */
5465 /* Copy in the contents of what the ipfobj_t points to. In future, we */
5466 /* add things to check for version numbers, sizes, etc, to make it backward */
5467 /* compatible at the ABI for user land. */
5468 /* ------------------------------------------------------------------------ */
5469 int fr_inobj(data, ptr, type)
5470 void *data;
5471 void *ptr;
5472 int type;
5474 ipfobj_t obj;
5475 int error = 0;
5477 if ((type < 0) || (type >= IPFOBJ_COUNT))
5478 return EINVAL;
5480 error = BCOPYIN(data, &obj, sizeof(obj));
5481 if (error != 0)
5482 return EFAULT;
5484 if (obj.ipfo_type != type)
5485 return EINVAL;
5487 #ifndef IPFILTER_COMPAT
5488 if ((fr_objbytes[type][0] & 1) != 0) {
5489 if (obj.ipfo_size < fr_objbytes[type][1])
5490 return EINVAL;
5491 } else if (obj.ipfo_size != fr_objbytes[type][1]) {
5492 return EINVAL;
5494 #else
5495 if (obj.ipfo_rev != IPFILTER_VERSION)
5496 /* XXX compatibility hook here */
5498 if ((fr_objbytes[type][0] & 1) != 0) {
5499 if (obj.ipfo_size < fr_objbytes[type][1])
5500 /* XXX compatibility hook here */
5501 return EINVAL;
5502 } else if (obj.ipfo_size != fr_objbytes[type][1])
5503 /* XXX compatibility hook here */
5504 return EINVAL;
5505 #endif
5507 if ((fr_objbytes[type][0] & 1) != 0) {
5508 error = COPYIN((void *)obj.ipfo_ptr, (void *)ptr,
5509 fr_objbytes[type][1]);
5510 } else {
5511 error = COPYIN((void *)obj.ipfo_ptr, (void *)ptr,
5512 obj.ipfo_size);
5514 if (error != 0)
5515 error = EFAULT;
5516 return error;
5520 /* ------------------------------------------------------------------------ */
5521 /* Function: fr_inobjsz */
5522 /* Returns: int - 0 = success, else failure */
5523 /* Parameters: data(I) - pointer to ioctl data */
5524 /* ptr(I) - pointer to store real data in */
5525 /* type(I) - type of structure being moved */
5526 /* sz(I) - size of data to copy */
5527 /* */
5528 /* As per fr_inobj, except the size of the object to copy in is passed in */
5529 /* but it must not be smaller than the size defined for the type and the */
5530 /* type must allow for varied sized objects. The extra requirement here is */
5531 /* that sz must match the size of the object being passed in - this is not */
5532 /* not possible nor required in fr_inobj(). */
5533 /* ------------------------------------------------------------------------ */
5534 int fr_inobjsz(data, ptr, type, sz)
5535 void *data;
5536 void *ptr;
5537 int type, sz;
5539 ipfobj_t obj;
5540 int error;
5542 if ((type < 0) || (type >= IPFOBJ_COUNT))
5543 return EINVAL;
5544 if (((fr_objbytes[type][0] & 1) == 0) || (sz < fr_objbytes[type][1]))
5545 return EINVAL;
5547 error = BCOPYIN(data, &obj, sizeof(obj));
5548 if (error != 0)
5549 return EFAULT;
5551 if (obj.ipfo_type != type)
5552 return EINVAL;
5554 #ifndef IPFILTER_COMPAT
5555 if (obj.ipfo_size != sz)
5556 return EINVAL;
5557 #else
5558 if (obj.ipfo_rev != IPFILTER_VERSION)
5559 /* XXX compatibility hook here */
5561 if (obj.ipfo_size != sz)
5562 /* XXX compatibility hook here */
5563 return EINVAL;
5564 #endif
5566 error = COPYIN((void *)obj.ipfo_ptr, (void *)ptr, sz);
5567 if (error != 0)
5568 error = EFAULT;
5569 return error;
5573 /* ------------------------------------------------------------------------ */
5574 /* Function: fr_outobjsz */
5575 /* Returns: int - 0 = success, else failure */
5576 /* Parameters: data(I) - pointer to ioctl data */
5577 /* ptr(I) - pointer to store real data in */
5578 /* type(I) - type of structure being moved */
5579 /* sz(I) - size of data to copy */
5580 /* */
5581 /* As per fr_outobj, except the size of the object to copy out is passed in */
5582 /* but it must not be smaller than the size defined for the type and the */
5583 /* type must allow for varied sized objects. The extra requirement here is */
5584 /* that sz must match the size of the object being passed in - this is not */
5585 /* not possible nor required in fr_outobj(). */
5586 /* ------------------------------------------------------------------------ */
5587 int fr_outobjsz(data, ptr, type, sz)
5588 void *data;
5589 void *ptr;
5590 int type, sz;
5592 ipfobj_t obj;
5593 int error;
5595 if ((type < 0) || (type >= IPFOBJ_COUNT) ||
5596 ((fr_objbytes[type][0] & 1) == 0) ||
5597 (sz < fr_objbytes[type][1]))
5598 return EINVAL;
5600 error = BCOPYIN(data, &obj, sizeof(obj));
5601 if (error != 0)
5602 return EFAULT;
5604 if (obj.ipfo_type != type)
5605 return EINVAL;
5607 #ifndef IPFILTER_COMPAT
5608 if (obj.ipfo_size != sz)
5609 return EINVAL;
5610 #else
5611 if (obj.ipfo_rev != IPFILTER_VERSION)
5612 /* XXX compatibility hook here */
5614 if (obj.ipfo_size != sz)
5615 /* XXX compatibility hook here */
5616 return EINVAL;
5617 #endif
5619 error = COPYOUT((void *)ptr, (void *)obj.ipfo_ptr, sz);
5620 if (error != 0)
5621 error = EFAULT;
5622 return error;
5626 /* ------------------------------------------------------------------------ */
5627 /* Function: fr_outobj */
5628 /* Returns: int - 0 = success, else failure */
5629 /* Parameters: data(I) - pointer to ioctl data */
5630 /* ptr(I) - pointer to store real data in */
5631 /* type(I) - type of structure being moved */
5632 /* */
5633 /* Copy out the contents of what ptr is to where ipfobj points to. In */
5634 /* future, we add things to check for version numbers, sizes, etc, to make */
5635 /* it backward compatible at the ABI for user land. */
5636 /* ------------------------------------------------------------------------ */
5637 int fr_outobj(data, ptr, type)
5638 void *data;
5639 void *ptr;
5640 int type;
5642 ipfobj_t obj;
5643 int error;
5645 if ((type < 0) || (type >= IPFOBJ_COUNT))
5646 return EINVAL;
5648 error = BCOPYIN(data, &obj, sizeof(obj));
5649 if (error != 0)
5650 return EFAULT;
5652 if (obj.ipfo_type != type)
5653 return EINVAL;
5655 #ifndef IPFILTER_COMPAT
5656 if ((fr_objbytes[type][0] & 1) != 0) {
5657 if (obj.ipfo_size < fr_objbytes[type][1])
5658 return EINVAL;
5659 } else if (obj.ipfo_size != fr_objbytes[type][1])
5660 return EINVAL;
5661 #else
5662 if (obj.ipfo_rev != IPFILTER_VERSION)
5663 /* XXX compatibility hook here */
5665 if ((fr_objbytes[type][0] & 1) != 0) {
5666 if (obj.ipfo_size < fr_objbytes[type][1])
5667 /* XXX compatibility hook here */
5668 return EINVAL;
5669 } else if (obj.ipfo_size != fr_objbytes[type][1])
5670 /* XXX compatibility hook here */
5671 return EINVAL;
5672 #endif
5674 error = COPYOUT((void *)ptr, (void *)obj.ipfo_ptr, obj.ipfo_size);
5675 if (error != 0)
5676 error = EFAULT;
5677 return error;
5681 /* ------------------------------------------------------------------------ */
5682 /* Function: fr_checkl4sum */
5683 /* Returns: int - 0 = good, -1 = bad, 1 = cannot check */
5684 /* Parameters: fin(I) - pointer to packet information */
5685 /* */
5686 /* If possible, calculate the layer 4 checksum for the packet. If this is */
5687 /* not possible, return without indicating a failure or success but in a */
5688 /* way that is ditinguishable. */
5689 /* ------------------------------------------------------------------------ */
5690 int fr_checkl4sum(fin)
5691 fr_info_t *fin;
5693 u_short sum, hdrsum, *csump;
5694 udphdr_t *udp;
5695 int dosum;
5697 if ((fin->fin_flx & FI_NOCKSUM) != 0)
5698 return 0;
5700 if (fin->fin_cksum == 1)
5701 return 0;
5703 if (fin->fin_cksum == -1)
5704 return -1;
5707 * If the TCP packet isn't a fragment, isn't too short and otherwise
5708 * isn't already considered "bad", then validate the checksum. If
5709 * this check fails then considered the packet to be "bad".
5711 if ((fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) != 0)
5712 return 1;
5714 csump = NULL;
5715 hdrsum = 0;
5716 dosum = 0;
5717 sum = 0;
5719 #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID)
5720 if (dohwcksum && ((*fin->fin_mp)->b_ick_flag == ICK_VALID)) {
5721 hdrsum = 0;
5722 sum = 0;
5723 } else {
5724 #endif
5725 switch (fin->fin_p)
5727 case IPPROTO_TCP :
5728 csump = &((tcphdr_t *)fin->fin_dp)->th_sum;
5729 dosum = 1;
5730 break;
5732 case IPPROTO_UDP :
5733 udp = fin->fin_dp;
5734 if (udp->uh_sum != 0) {
5735 csump = &udp->uh_sum;
5736 dosum = 1;
5738 break;
5740 case IPPROTO_ICMP :
5741 csump = &((struct icmp *)fin->fin_dp)->icmp_cksum;
5742 dosum = 1;
5743 break;
5745 default :
5746 return 1;
5747 /*NOTREACHED*/
5750 if (csump != NULL)
5751 hdrsum = *csump;
5753 if (dosum) {
5754 #ifdef INET
5755 sum = fr_cksum(fin->fin_m, fin->fin_ip,
5756 fin->fin_p, fin->fin_dp,
5757 fin->fin_dlen + fin->fin_hlen);
5758 #else
5759 return 1;
5760 #endif
5762 #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID)
5764 #endif
5765 #if !defined(_KERNEL)
5766 if (sum == hdrsum) {
5767 FR_DEBUG(("checkl4sum: %hx == %hx\n", sum, hdrsum));
5768 } else {
5769 FR_DEBUG(("checkl4sum: %hx != %hx\n", sum, hdrsum));
5771 #endif
5772 if (hdrsum == sum) {
5773 fin->fin_cksum = 1;
5774 return 0;
5776 fin->fin_cksum = -1;
5777 return -1;
5781 /* ------------------------------------------------------------------------ */
5782 /* Function: fr_ifpfillv4addr */
5783 /* Returns: int - 0 = address update, -1 = address not updated */
5784 /* Parameters: atype(I) - type of network address update to perform */
5785 /* sin(I) - pointer to source of address information */
5786 /* mask(I) - pointer to source of netmask information */
5787 /* inp(I) - pointer to destination address store */
5788 /* inpmask(I) - pointer to destination netmask store */
5789 /* */
5790 /* Given a type of network address update (atype) to perform, copy */
5791 /* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */
5792 /* netmask update is performed unless FRI_NETMASKED is passed as atype, in */
5793 /* which case the operation fails. For all values of atype other than */
5794 /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */
5795 /* value. */
5796 /* ------------------------------------------------------------------------ */
5797 int fr_ifpfillv4addr(atype, sin, mask, inp, inpmask)
5798 int atype;
5799 struct sockaddr_in *sin, *mask;
5800 struct in_addr *inp, *inpmask;
5802 if (inpmask != NULL && atype != FRI_NETMASKED)
5803 inpmask->s_addr = 0xffffffff;
5805 if (atype == FRI_NETWORK || atype == FRI_NETMASKED) {
5806 if (atype == FRI_NETMASKED) {
5807 if (inpmask == NULL)
5808 return -1;
5809 inpmask->s_addr = mask->sin_addr.s_addr;
5811 inp->s_addr = sin->sin_addr.s_addr & mask->sin_addr.s_addr;
5812 } else {
5813 inp->s_addr = sin->sin_addr.s_addr;
5815 return 0;
5819 #ifdef USE_INET6
5820 /* ------------------------------------------------------------------------ */
5821 /* Function: fr_ifpfillv6addr */
5822 /* Returns: int - 0 = address update, -1 = address not updated */
5823 /* Parameters: atype(I) - type of network address update to perform */
5824 /* sin(I) - pointer to source of address information */
5825 /* mask(I) - pointer to source of netmask information */
5826 /* inp(I) - pointer to destination address store */
5827 /* inpmask(I) - pointer to destination netmask store */
5828 /* */
5829 /* Given a type of network address update (atype) to perform, copy */
5830 /* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */
5831 /* netmask update is performed unless FRI_NETMASKED is passed as atype, in */
5832 /* which case the operation fails. For all values of atype other than */
5833 /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */
5834 /* value. */
5835 /* ------------------------------------------------------------------------ */
5836 int fr_ifpfillv6addr(atype, sin, mask, inp, inpmask)
5837 int atype;
5838 struct sockaddr_in6 *sin, *mask;
5839 struct in_addr *inp, *inpmask;
5841 i6addr_t *src, *dst, *and, *dmask;
5843 src = (i6addr_t *)&sin->sin6_addr;
5844 and = (i6addr_t *)&mask->sin6_addr;
5845 dst = (i6addr_t *)inp;
5846 dmask = (i6addr_t *)inpmask;
5848 if (inpmask != NULL && atype != FRI_NETMASKED) {
5849 dmask->i6[0] = 0xffffffff;
5850 dmask->i6[1] = 0xffffffff;
5851 dmask->i6[2] = 0xffffffff;
5852 dmask->i6[3] = 0xffffffff;
5855 if (atype == FRI_NETWORK || atype == FRI_NETMASKED) {
5856 if (atype == FRI_NETMASKED) {
5857 if (inpmask == NULL)
5858 return -1;
5859 dmask->i6[0] = and->i6[0];
5860 dmask->i6[1] = and->i6[1];
5861 dmask->i6[2] = and->i6[2];
5862 dmask->i6[3] = and->i6[3];
5865 dst->i6[0] = src->i6[0] & and->i6[0];
5866 dst->i6[1] = src->i6[1] & and->i6[1];
5867 dst->i6[2] = src->i6[2] & and->i6[2];
5868 dst->i6[3] = src->i6[3] & and->i6[3];
5869 } else {
5870 dst->i6[0] = src->i6[0];
5871 dst->i6[1] = src->i6[1];
5872 dst->i6[2] = src->i6[2];
5873 dst->i6[3] = src->i6[3];
5875 return 0;
5877 #endif
5880 /* ------------------------------------------------------------------------ */
5881 /* Function: fr_matchtag */
5882 /* Returns: 0 == mismatch, 1 == match. */
5883 /* Parameters: tag1(I) - pointer to first tag to compare */
5884 /* tag2(I) - pointer to second tag to compare */
5885 /* */
5886 /* Returns true (non-zero) or false(0) if the two tag structures can be */
5887 /* considered to be a match or not match, respectively. The tag is 16 */
5888 /* bytes long (16 characters) but that is overlayed with 4 32bit ints so */
5889 /* compare the ints instead, for speed. tag1 is the master of the */
5890 /* comparison. This function should only be called with both tag1 and tag2 */
5891 /* as non-NULL pointers. */
5892 /* ------------------------------------------------------------------------ */
5893 int fr_matchtag(tag1, tag2)
5894 ipftag_t *tag1, *tag2;
5896 if (tag1 == tag2)
5897 return 1;
5899 if ((tag1->ipt_num[0] == 0) && (tag2->ipt_num[0] == 0))
5900 return 1;
5902 if ((tag1->ipt_num[0] == tag2->ipt_num[0]) &&
5903 (tag1->ipt_num[1] == tag2->ipt_num[1]) &&
5904 (tag1->ipt_num[2] == tag2->ipt_num[2]) &&
5905 (tag1->ipt_num[3] == tag2->ipt_num[3]))
5906 return 1;
5907 return 0;
5911 /* ------------------------------------------------------------------------ */
5912 /* Function: fr_coalesce */
5913 /* Returns: 1 == success, -1 == failure, 0 == no change */
5914 /* Parameters: fin(I) - pointer to packet information */
5915 /* */
5916 /* Attempt to get all of the packet data into a single, contiguous buffer. */
5917 /* If this call returns a failure then the buffers have also been freed. */
5918 /* ------------------------------------------------------------------------ */
5919 int fr_coalesce(fin)
5920 fr_info_t *fin;
5922 if ((fin->fin_flx & FI_COALESCE) != 0)
5923 return 1;
5926 * If the mbuf pointers indicate that there is no mbuf to work with,
5927 * return but do not indicate success or failure.
5929 if (fin->fin_m == NULL || fin->fin_mp == NULL)
5930 return 0;
5932 #if defined(_KERNEL)
5933 if (fr_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) {
5934 ATOMIC_INCL(fr_badcoalesces[fin->fin_out]);
5935 # ifdef MENTAT
5936 FREE_MB_T(*fin->fin_mp);
5937 # endif
5938 *fin->fin_mp = NULL;
5939 fin->fin_m = NULL;
5940 return -1;
5942 #else
5943 fin = fin; /* LINT */
5944 #endif
5945 return 1;
5950 * The following table lists all of the tunable variables that can be
5951 * accessed via SIOCIPFGET/SIOCIPFSET/SIOCIPFGETNEXt. The format of each row
5952 * in the table below is as follows:
5954 * pointer to value, name of value, minimum, maximum, size of the value's
5955 * container, value attribute flags
5957 * For convienience, IPFT_RDONLY means the value is read-only, IPFT_WRDISABLED
5958 * means the value can only be written to when IPFilter is loaded but disabled.
5959 * The obvious implication is if neither of these are set then the value can be
5960 * changed at any time without harm.
5962 ipftuneable_t ipf_tuneables[] = {
5963 /* filtering */
5964 { { &fr_flags }, "fr_flags", 0, 0xffffffff,
5965 sizeof(fr_flags), 0, NULL },
5966 { { &fr_active }, "fr_active", 0, 0,
5967 sizeof(fr_active), IPFT_RDONLY, NULL },
5968 { { &fr_control_forwarding }, "fr_control_forwarding", 0, 1,
5969 sizeof(fr_control_forwarding), 0, NULL },
5970 { { &fr_update_ipid }, "fr_update_ipid", 0, 1,
5971 sizeof(fr_update_ipid), 0, NULL },
5972 { { &fr_chksrc }, "fr_chksrc", 0, 1,
5973 sizeof(fr_chksrc), 0, NULL },
5974 { { &fr_minttl }, "fr_minttl", 0, 1,
5975 sizeof(fr_minttl), 0, NULL },
5976 { { &fr_icmpminfragmtu }, "fr_icmpminfragmtu", 0, 1,
5977 sizeof(fr_icmpminfragmtu), 0, NULL },
5978 { { &fr_pass }, "fr_pass", 0, 0xffffffff,
5979 sizeof(fr_pass), 0, NULL },
5980 /* state */
5981 { { &fr_tcpidletimeout }, "fr_tcpidletimeout", 1, 0x7fffffff,
5982 sizeof(fr_tcpidletimeout), IPFT_WRDISABLED, NULL },
5983 { { &fr_tcpclosewait }, "fr_tcpclosewait", 1, 0x7fffffff,
5984 sizeof(fr_tcpclosewait), IPFT_WRDISABLED, NULL },
5985 { { &fr_tcplastack }, "fr_tcplastack", 1, 0x7fffffff,
5986 sizeof(fr_tcplastack), IPFT_WRDISABLED, NULL },
5987 { { &fr_tcptimeout }, "fr_tcptimeout", 1, 0x7fffffff,
5988 sizeof(fr_tcptimeout), IPFT_WRDISABLED, NULL },
5989 { { &fr_tcpclosed }, "fr_tcpclosed", 1, 0x7fffffff,
5990 sizeof(fr_tcpclosed), IPFT_WRDISABLED, NULL },
5991 { { &fr_tcphalfclosed }, "fr_tcphalfclosed", 1, 0x7fffffff,
5992 sizeof(fr_tcphalfclosed), IPFT_WRDISABLED, NULL },
5993 { { &fr_tcptimewait }, "fr_tcptimewait", 1, 0x7fffffff,
5994 sizeof(fr_tcptimewait), IPFT_WRDISABLED, NULL },
5995 { { &fr_udptimeout }, "fr_udptimeout", 1, 0x7fffffff,
5996 sizeof(fr_udptimeout), IPFT_WRDISABLED, NULL },
5997 { { &fr_udpacktimeout }, "fr_udpacktimeout", 1, 0x7fffffff,
5998 sizeof(fr_udpacktimeout), IPFT_WRDISABLED, NULL },
5999 { { &fr_icmptimeout }, "fr_icmptimeout", 1, 0x7fffffff,
6000 sizeof(fr_icmptimeout), IPFT_WRDISABLED, NULL },
6001 { { &fr_icmpacktimeout }, "fr_icmpacktimeout", 1, 0x7fffffff,
6002 sizeof(fr_icmpacktimeout), IPFT_WRDISABLED, NULL },
6003 { { &fr_iptimeout }, "fr_iptimeout", 1, 0x7fffffff,
6004 sizeof(fr_iptimeout), IPFT_WRDISABLED, NULL },
6005 { { &fr_statemax }, "fr_statemax", 1, 0x7fffffff,
6006 sizeof(fr_statemax), 0, NULL },
6007 { { &fr_statesize }, "fr_statesize", 1, 0x7fffffff,
6008 sizeof(fr_statesize), IPFT_WRDISABLED, NULL },
6009 { { &fr_state_lock }, "fr_state_lock", 0, 1,
6010 sizeof(fr_state_lock), IPFT_RDONLY, NULL },
6011 { { &fr_state_maxbucket }, "fr_state_maxbucket", 1, 0x7fffffff,
6012 sizeof(fr_state_maxbucket), IPFT_WRDISABLED, NULL },
6013 { { &fr_state_maxbucket_reset }, "fr_state_maxbucket_reset", 0, 1,
6014 sizeof(fr_state_maxbucket_reset), IPFT_WRDISABLED, NULL },
6015 { { &ipstate_logging }, "ipstate_logging", 0, 1,
6016 sizeof(ipstate_logging), 0, NULL },
6017 /* nat */
6018 { { &fr_nat_lock }, "fr_nat_lock", 0, 1,
6019 sizeof(fr_nat_lock), IPFT_RDONLY, NULL },
6020 { { &ipf_nattable_sz }, "ipf_nattable_sz", 1, 0x7fffffff,
6021 sizeof(ipf_nattable_sz), IPFT_WRDISABLED, NULL },
6022 { { &ipf_nattable_max }, "ipf_nattable_max", 1, 0x7fffffff,
6023 sizeof(ipf_nattable_max), 0, NULL },
6024 { { &ipf_natrules_sz }, "ipf_natrules_sz", 1, 0x7fffffff,
6025 sizeof(ipf_natrules_sz), IPFT_WRDISABLED, NULL },
6026 { { &ipf_rdrrules_sz }, "ipf_rdrrules_sz", 1, 0x7fffffff,
6027 sizeof(ipf_rdrrules_sz), IPFT_WRDISABLED, NULL },
6028 { { &ipf_hostmap_sz }, "ipf_hostmap_sz", 1, 0x7fffffff,
6029 sizeof(ipf_hostmap_sz), IPFT_WRDISABLED, NULL },
6030 { { &fr_nat_maxbucket }, "fr_nat_maxbucket", 1, 0x7fffffff,
6031 sizeof(fr_nat_maxbucket), 0, NULL },
6032 { { &fr_nat_maxbucket_reset }, "fr_nat_maxbucket_reset", 0, 1,
6033 sizeof(fr_nat_maxbucket_reset), IPFT_WRDISABLED, NULL },
6034 { { &nat_logging }, "nat_logging", 0, 1,
6035 sizeof(nat_logging), 0, NULL },
6036 { { &fr_defnatage }, "fr_defnatage", 1, 0x7fffffff,
6037 sizeof(fr_defnatage), IPFT_WRDISABLED, NULL },
6038 { { &fr_defnatipage }, "fr_defnatipage", 1, 0x7fffffff,
6039 sizeof(fr_defnatipage), IPFT_WRDISABLED, NULL },
6040 { { &fr_defnaticmpage }, "fr_defnaticmpage", 1, 0x7fffffff,
6041 sizeof(fr_defnaticmpage), IPFT_WRDISABLED, NULL },
6042 { { &fr_nat_doflush }, "fr_nat_doflush", 0, 1,
6043 sizeof(fr_nat_doflush), 0, NULL },
6044 /* proxy */
6045 { { &ipf_proxy_debug }, "ipf_proxy_debug", 0, 10,
6046 sizeof(ipf_proxy_debug), 0, 0 },
6047 /* frag */
6048 { { &ipfr_size }, "ipfr_size", 1, 0x7fffffff,
6049 sizeof(ipfr_size), IPFT_WRDISABLED, NULL },
6050 { { &fr_ipfrttl }, "fr_ipfrttl", 1, 0x7fffffff,
6051 sizeof(fr_ipfrttl), IPFT_WRDISABLED, NULL },
6052 #ifdef IPFILTER_LOG
6053 /* log */
6054 { { &ipl_suppress }, "ipl_suppress", 0, 1,
6055 sizeof(ipl_suppress), 0, NULL },
6056 { { &ipl_logmax }, "ipl_logmax", 0, 0x7fffffff,
6057 sizeof(ipl_logmax), IPFT_WRDISABLED, NULL },
6058 { { &ipl_logall }, "ipl_logall", 0, 1,
6059 sizeof(ipl_logall), 0, NULL },
6060 { { &ipl_logsize }, "ipl_logsize", 0, 0x80000,
6061 sizeof(ipl_logsize), 0, NULL },
6062 #endif
6063 { { NULL }, NULL, 0, 0,
6064 0, 0, NULL }
6067 static ipftuneable_t *ipf_tunelist = NULL;
6070 /* ------------------------------------------------------------------------ */
6071 /* Function: fr_findtunebycookie */
6072 /* Returns: NULL = search failed, else pointer to tune struct */
6073 /* Parameters: cookie(I) - cookie value to search for amongst tuneables */
6074 /* next(O) - pointer to place to store the cookie for the */
6075 /* "next" tuneable, if it is desired. */
6076 /* */
6077 /* This function is used to walk through all of the existing tunables with */
6078 /* successive calls. It searches the known tunables for the one which has */
6079 /* a matching value for "cookie" - ie its address. When returning a match, */
6080 /* the next one to be found may be returned inside next. */
6081 /* ------------------------------------------------------------------------ */
6082 static ipftuneable_t *fr_findtunebycookie(cookie, next)
6083 void *cookie, **next;
6085 ipftuneable_t *ta, **tap;
6087 for (ta = ipf_tuneables; ta->ipft_name != NULL; ta++)
6088 if (ta == cookie) {
6089 if (next != NULL) {
6091 * If the next entry in the array has a name
6092 * present, then return a pointer to it for
6093 * where to go next, else return a pointer to
6094 * the dynaminc list as a key to search there
6095 * next. This facilitates a weak linking of
6096 * the two "lists" together.
6098 if ((ta + 1)->ipft_name != NULL)
6099 *next = ta + 1;
6100 else
6101 *next = &ipf_tunelist;
6103 return ta;
6106 for (tap = &ipf_tunelist; (ta = *tap) != NULL; tap = &ta->ipft_next)
6107 if (tap == cookie) {
6108 if (next != NULL)
6109 *next = &ta->ipft_next;
6110 return ta;
6113 if (next != NULL)
6114 *next = NULL;
6115 return NULL;
6119 /* ------------------------------------------------------------------------ */
6120 /* Function: fr_findtunebyname */
6121 /* Returns: NULL = search failed, else pointer to tune struct */
6122 /* Parameters: name(I) - name of the tuneable entry to find. */
6123 /* */
6124 /* Search the static array of tuneables and the list of dynamic tuneables */
6125 /* for an entry with a matching name. If we can find one, return a pointer */
6126 /* to the matching structure. */
6127 /* ------------------------------------------------------------------------ */
6128 static ipftuneable_t *fr_findtunebyname(name)
6129 const char *name;
6131 ipftuneable_t *ta;
6133 for (ta = ipf_tuneables; ta->ipft_name != NULL; ta++)
6134 if (!strcmp(ta->ipft_name, name)) {
6135 return ta;
6138 for (ta = ipf_tunelist; ta != NULL; ta = ta->ipft_next)
6139 if (!strcmp(ta->ipft_name, name)) {
6140 return ta;
6143 return NULL;
6147 /* ------------------------------------------------------------------------ */
6148 /* Function: fr_addipftune */
6149 /* Returns: int - 0 == success, else failure */
6150 /* Parameters: newtune - pointer to new tune struct to add to tuneables */
6151 /* */
6152 /* Appends the tune structure pointer to by "newtune" to the end of the */
6153 /* current list of "dynamic" tuneable parameters. Once added, the owner */
6154 /* of the object is not expected to ever change "ipft_next". */
6155 /* ------------------------------------------------------------------------ */
6156 int fr_addipftune(newtune)
6157 ipftuneable_t *newtune;
6159 ipftuneable_t *ta, **tap;
6161 ta = fr_findtunebyname(newtune->ipft_name);
6162 if (ta != NULL)
6163 return EEXIST;
6165 for (tap = &ipf_tunelist; *tap != NULL; tap = &(*tap)->ipft_next)
6168 newtune->ipft_next = NULL;
6169 *tap = newtune;
6170 return 0;
6174 /* ------------------------------------------------------------------------ */
6175 /* Function: fr_delipftune */
6176 /* Returns: int - 0 == success, else failure */
6177 /* Parameters: oldtune - pointer to tune struct to remove from the list of */
6178 /* current dynamic tuneables */
6179 /* */
6180 /* Search for the tune structure, by pointer, in the list of those that are */
6181 /* dynamically added at run time. If found, adjust the list so that this */
6182 /* structure is no longer part of it. */
6183 /* ------------------------------------------------------------------------ */
6184 int fr_delipftune(oldtune)
6185 ipftuneable_t *oldtune;
6187 ipftuneable_t *ta, **tap;
6189 for (tap = &ipf_tunelist; (ta = *tap) != NULL; tap = &ta->ipft_next)
6190 if (ta == oldtune) {
6191 *tap = oldtune->ipft_next;
6192 oldtune->ipft_next = NULL;
6193 return 0;
6196 return ESRCH;
6200 /* ------------------------------------------------------------------------ */
6201 /* Function: fr_ipftune */
6202 /* Returns: int - 0 == success, else failure */
6203 /* Parameters: cmd(I) - ioctl command number */
6204 /* data(I) - pointer to ioctl data structure */
6205 /* */
6206 /* Implement handling of SIOCIPFGETNEXT, SIOCIPFGET and SIOCIPFSET. These */
6207 /* three ioctls provide the means to access and control global variables */
6208 /* within IPFilter, allowing (for example) timeouts and table sizes to be */
6209 /* changed without rebooting, reloading or recompiling. The initialisation */
6210 /* and 'destruction' routines of the various components of ipfilter are all */
6211 /* each responsible for handling their own values being too big. */
6212 /* ------------------------------------------------------------------------ */
6213 int fr_ipftune(cmd, data)
6214 ioctlcmd_t cmd;
6215 void *data;
6217 ipftuneable_t *ta;
6218 ipftune_t tu;
6219 void *cookie;
6220 int error;
6222 error = fr_inobj(data, &tu, IPFOBJ_TUNEABLE);
6223 if (error != 0)
6224 return error;
6226 tu.ipft_name[sizeof(tu.ipft_name) - 1] = '\0';
6227 cookie = tu.ipft_cookie;
6228 ta = NULL;
6230 switch (cmd)
6232 case SIOCIPFGETNEXT :
6234 * If cookie is non-NULL, assume it to be a pointer to the last
6235 * entry we looked at, so find it (if possible) and return a
6236 * pointer to the next one after it. The last entry in the
6237 * the table is a NULL entry, so when we get to it, set cookie
6238 * to NULL and return that, indicating end of list, erstwhile
6239 * if we come in with cookie set to NULL, we are starting anew
6240 * at the front of the list.
6242 if (cookie != NULL) {
6243 ta = fr_findtunebycookie(cookie, &tu.ipft_cookie);
6244 } else {
6245 ta = ipf_tuneables;
6246 tu.ipft_cookie = ta + 1;
6248 if (ta != NULL) {
6250 * Entry found, but does the data pointed to by that
6251 * row fit in what we can return?
6253 if (ta->ipft_sz > sizeof(tu.ipft_un))
6254 return EINVAL;
6256 tu.ipft_vlong = 0;
6257 if (ta->ipft_sz == sizeof(u_long))
6258 tu.ipft_vlong = *ta->ipft_plong;
6259 else if (ta->ipft_sz == sizeof(u_int))
6260 tu.ipft_vint = *ta->ipft_pint;
6261 else if (ta->ipft_sz == sizeof(u_short))
6262 tu.ipft_vshort = *ta->ipft_pshort;
6263 else if (ta->ipft_sz == sizeof(u_char))
6264 tu.ipft_vchar = *ta->ipft_pchar;
6266 tu.ipft_sz = ta->ipft_sz;
6267 tu.ipft_min = ta->ipft_min;
6268 tu.ipft_max = ta->ipft_max;
6269 tu.ipft_flags = ta->ipft_flags;
6270 bcopy(ta->ipft_name, tu.ipft_name,
6271 MIN(sizeof(tu.ipft_name),
6272 strlen(ta->ipft_name) + 1));
6274 error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE);
6275 break;
6277 case SIOCIPFGET :
6278 case SIOCIPFSET :
6280 * Search by name or by cookie value for a particular entry
6281 * in the tuning paramter table.
6283 error = ESRCH;
6284 if (cookie != NULL) {
6285 ta = fr_findtunebycookie(cookie, NULL);
6286 if (ta != NULL)
6287 error = 0;
6288 } else if (tu.ipft_name[0] != '\0') {
6289 ta = fr_findtunebyname(tu.ipft_name);
6290 if (ta != NULL)
6291 error = 0;
6293 if (error != 0)
6294 break;
6296 if (cmd == (ioctlcmd_t)SIOCIPFGET) {
6298 * Fetch the tuning parameters for a particular value
6300 tu.ipft_vlong = 0;
6301 if (ta->ipft_sz == sizeof(u_long))
6302 tu.ipft_vlong = *ta->ipft_plong;
6303 else if (ta->ipft_sz == sizeof(u_int))
6304 tu.ipft_vint = *ta->ipft_pint;
6305 else if (ta->ipft_sz == sizeof(u_short))
6306 tu.ipft_vshort = *ta->ipft_pshort;
6307 else if (ta->ipft_sz == sizeof(u_char))
6308 tu.ipft_vchar = *ta->ipft_pchar;
6309 tu.ipft_cookie = ta;
6310 tu.ipft_sz = ta->ipft_sz;
6311 tu.ipft_min = ta->ipft_min;
6312 tu.ipft_max = ta->ipft_max;
6313 tu.ipft_flags = ta->ipft_flags;
6314 error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE);
6316 } else if (cmd == (ioctlcmd_t)SIOCIPFSET) {
6318 * Set an internal parameter. The hard part here is
6319 * getting the new value safely and correctly out of
6320 * the kernel (given we only know its size, not type.)
6322 u_long in;
6324 if (((ta->ipft_flags & IPFT_WRDISABLED) != 0) &&
6325 (fr_running > 0)) {
6326 error = EBUSY;
6327 break;
6330 in = tu.ipft_vlong;
6331 if (in < ta->ipft_min || in > ta->ipft_max) {
6332 error = EINVAL;
6333 break;
6336 if (ta->ipft_sz == sizeof(u_long)) {
6337 tu.ipft_vlong = *ta->ipft_plong;
6338 *ta->ipft_plong = in;
6339 } else if (ta->ipft_sz == sizeof(u_int)) {
6340 tu.ipft_vint = *ta->ipft_pint;
6341 *ta->ipft_pint = (u_int)(in & 0xffffffff);
6342 } else if (ta->ipft_sz == sizeof(u_short)) {
6343 tu.ipft_vshort = *ta->ipft_pshort;
6344 *ta->ipft_pshort = (u_short)(in & 0xffff);
6345 } else if (ta->ipft_sz == sizeof(u_char)) {
6346 tu.ipft_vchar = *ta->ipft_pchar;
6347 *ta->ipft_pchar = (u_char)(in & 0xff);
6349 error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE);
6351 break;
6353 default :
6354 error = EINVAL;
6355 break;
6358 return error;
6362 /* ------------------------------------------------------------------------ */
6363 /* Function: fr_initialise */
6364 /* Returns: int - 0 == success, < 0 == failure */
6365 /* Parameters: None. */
6366 /* */
6367 /* Call of the initialise functions for all the various subsystems inside */
6368 /* of IPFilter. If any of them should fail, return immeadiately a failure */
6369 /* BUT do not try to recover from the error here. */
6370 /* ------------------------------------------------------------------------ */
6371 int fr_initialise()
6373 int i;
6375 bzero(&frstats, sizeof(frstats));
6377 #ifdef IPFILTER_LOG
6378 i = fr_loginit();
6379 if (i < 0)
6380 return -10 + i;
6381 #endif
6382 i = fr_natinit();
6383 if (i < 0)
6384 return -20 + i;
6386 i = fr_stateinit();
6387 if (i < 0)
6388 return -30 + i;
6390 i = fr_authinit();
6391 if (i < 0)
6392 return -40 + i;
6394 i = fr_fraginit();
6395 if (i < 0)
6396 return -50 + i;
6398 i = appr_init();
6399 if (i < 0)
6400 return -60 + i;
6402 #ifdef IPFILTER_SYNC
6403 i = ipfsync_init();
6404 if (i < 0)
6405 return -70 + i;
6406 #endif
6407 #ifdef IPFILTER_SCAN
6408 i = ipsc_init();
6409 if (i < 0)
6410 return -80 + i;
6411 #endif
6412 #ifdef IPFILTER_LOOKUP
6413 i = ip_lookup_init();
6414 if (i < 0)
6415 return -90 + i;
6416 #endif
6417 #ifdef IPFILTER_COMPILED
6418 ipfrule_add();
6419 #endif
6420 return 0;
6424 /* ------------------------------------------------------------------------ */
6425 /* Function: fr_deinitialise */
6426 /* Returns: None. */
6427 /* Parameters: None. */
6428 /* */
6429 /* Call all the various subsystem cleanup routines to deallocate memory or */
6430 /* destroy locks or whatever they've done that they need to now undo. */
6431 /* The order here IS important as there are some cross references of */
6432 /* internal data structures. */
6433 /* ------------------------------------------------------------------------ */
6434 void fr_deinitialise()
6436 fr_fragunload();
6437 fr_authunload();
6438 fr_natunload();
6439 fr_stateunload();
6440 #ifdef IPFILTER_SCAN
6441 fr_scanunload();
6442 #endif
6443 appr_unload();
6445 #ifdef IPFILTER_COMPILED
6446 ipfrule_remove();
6447 #endif
6449 (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE);
6450 (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE);
6451 (void) frflush(IPL_LOGCOUNT, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE);
6452 (void) frflush(IPL_LOGCOUNT, 0, FR_INQUE|FR_OUTQUE);
6454 #ifdef IPFILTER_LOOKUP
6455 ip_lookup_unload();
6456 #endif
6458 #ifdef IPFILTER_LOG
6459 fr_logunload();
6460 #endif
6464 /* ------------------------------------------------------------------------ */
6465 /* Function: fr_zerostats */
6466 /* Returns: int - 0 = success, else failure */
6467 /* Parameters: data(O) - pointer to pointer for copying data back to */
6468 /* */
6469 /* Copies the current statistics out to userspace and then zero's the */
6470 /* current ones in the kernel. The lock is only held across the bzero() as */
6471 /* the copyout may result in paging (ie network activity.) */
6472 /* ------------------------------------------------------------------------ */
6473 int fr_zerostats(data)
6474 void *data;
6476 friostat_t fio;
6477 int error;
6479 fr_getstat(&fio);
6480 error = fr_outobj(data, &fio, IPFOBJ_IPFSTAT);
6481 if (error)
6482 return EFAULT;
6484 WRITE_ENTER(&ipf_mutex);
6485 bzero(&frstats, sizeof(frstats));
6486 RWLOCK_EXIT(&ipf_mutex);
6488 return 0;
6492 /* ------------------------------------------------------------------------ */
6493 /* Function: fr_resolvedest */
6494 /* Returns: Nil */
6495 /* Parameters: fdp(IO) - pointer to destination information to resolve */
6496 /* v(I) - IP protocol version to match */
6497 /* */
6498 /* Looks up an interface name in the frdest structure pointed to by fdp and */
6499 /* if a matching name can be found for the particular IP protocol version */
6500 /* then store the interface pointer in the frdest struct. If no match is */
6501 /* found, then set the interface pointer to be -1 as NULL is considered to */
6502 /* indicate there is no information at all in the structure. */
6503 /* ------------------------------------------------------------------------ */
6504 void fr_resolvedest(fdp, v)
6505 frdest_t *fdp;
6506 int v;
6508 void *ifp;
6510 ifp = NULL;
6511 v = v; /* LINT */
6513 if (*fdp->fd_ifname != '\0') {
6514 ifp = GETIFP(fdp->fd_ifname, v);
6515 if (ifp == NULL)
6516 ifp = (void *)-1;
6518 fdp->fd_ifp = ifp;
6522 /* ------------------------------------------------------------------------ */
6523 /* Function: fr_resolvenic */
6524 /* Returns: void* - NULL = wildcard name, -1 = failed to find NIC, else */
6525 /* pointer to interface structure for NIC */
6526 /* Parameters: name(I) - complete interface name */
6527 /* v(I) - IP protocol version */
6528 /* */
6529 /* Look for a network interface structure that firstly has a matching name */
6530 /* to that passed in and that is also being used for that IP protocol */
6531 /* version (necessary on some platforms where there are separate listings */
6532 /* for both IPv4 and IPv6 on the same physical NIC. */
6533 /* */
6534 /* One might wonder why name gets terminated with a \0 byte in here. The */
6535 /* reason is an interface name could get into the kernel structures of ipf */
6536 /* in any number of ways and so long as they all use the same sized array */
6537 /* to put the name in, it makes sense to ensure it gets null terminated */
6538 /* before it is used for its intended purpose - finding its match in the */
6539 /* kernel's list of configured interfaces. */
6540 /* */
6541 /* NOTE: This SHOULD ONLY be used with IPFilter structures that have an */
6542 /* array for the name that is LIFNAMSIZ bytes (at least) in length. */
6543 /* ------------------------------------------------------------------------ */
6544 void *fr_resolvenic(char *name, int v)
6546 void *nic;
6548 if (name[0] == '\0')
6549 return NULL;
6551 if ((name[1] == '\0') && ((name[0] == '-') || (name[0] == '*'))) {
6552 return NULL;
6555 name[LIFNAMSIZ - 1] = '\0';
6557 nic = GETIFP(name, v);
6558 if (nic == NULL)
6559 nic = (void *)-1;
6560 return nic;
6564 ipftoken_t *ipftokenhead = NULL, **ipftokentail = &ipftokenhead;
6567 /* ------------------------------------------------------------------------ */
6568 /* Function: ipf_expiretokens */
6569 /* Returns: None. */
6570 /* Parameters: None. */
6571 /* */
6572 /* This function is run every ipf tick to see if there are any tokens that */
6573 /* have been held for too long and need to be freed up. */
6574 /* ------------------------------------------------------------------------ */
6575 void ipf_expiretokens()
6577 ipftoken_t *it;
6579 WRITE_ENTER(&ipf_tokens);
6580 while ((it = ipftokenhead) != NULL) {
6581 if (it->ipt_die > fr_ticks)
6582 break;
6584 ipf_freetoken(it);
6586 RWLOCK_EXIT(&ipf_tokens);
6590 /* ------------------------------------------------------------------------ */
6591 /* Function: ipf_deltoken */
6592 /* Returns: int - 0 = success, else error */
6593 /* Parameters: type(I) - the token type to match */
6594 /* uid(I) - uid owning the token */
6595 /* ptr(I) - context pointer for the token */
6596 /* */
6597 /* This function looks for a a token in the current list that matches up */
6598 /* the fields (type, uid, ptr). If none is found, ESRCH is returned, else */
6599 /* call ipf_freetoken() to remove it from the list. */
6600 /* ------------------------------------------------------------------------ */
6601 int ipf_deltoken(type, uid, ptr)
6602 int type, uid;
6603 void *ptr;
6605 ipftoken_t *it;
6606 int error = ESRCH;
6608 WRITE_ENTER(&ipf_tokens);
6609 for (it = ipftokenhead; it != NULL; it = it->ipt_next)
6610 if (ptr == it->ipt_ctx && type == it->ipt_type &&
6611 uid == it->ipt_uid) {
6612 ipf_freetoken(it);
6613 error = 0;
6614 break;
6616 RWLOCK_EXIT(&ipf_tokens);
6618 return error;
6622 /* ------------------------------------------------------------------------ */
6623 /* Function: ipf_findtoken */
6624 /* Returns: ipftoken_t * - NULL if no memory, else pointer to token */
6625 /* Parameters: type(I) - the token type to match */
6626 /* uid(I) - uid owning the token */
6627 /* ptr(I) - context pointer for the token */
6628 /* */
6629 /* This function looks for a live token in the list of current tokens that */
6630 /* matches the tuple (type, uid, ptr). If one cannot be found then one is */
6631 /* allocated. If one is found then it is moved to the top of the list of */
6632 /* currently active tokens. */
6633 /* */
6634 /* NOTE: It is by design that this function returns holding a read lock on */
6635 /* ipf_tokens. Callers must make sure they release it! */
6636 /* ------------------------------------------------------------------------ */
6637 ipftoken_t *ipf_findtoken(type, uid, ptr)
6638 int type, uid;
6639 void *ptr;
6641 ipftoken_t *it, *new;
6643 KMALLOC(new, ipftoken_t *);
6645 WRITE_ENTER(&ipf_tokens);
6646 for (it = ipftokenhead; it != NULL; it = it->ipt_next) {
6647 if (ptr == it->ipt_ctx && type == it->ipt_type &&
6648 uid == it->ipt_uid)
6649 break;
6652 if (it == NULL) {
6653 it = new;
6654 new = NULL;
6655 if (it == NULL)
6656 return NULL;
6657 it->ipt_data = NULL;
6658 it->ipt_ctx = ptr;
6659 it->ipt_uid = uid;
6660 it->ipt_type = type;
6661 it->ipt_next = NULL;
6662 it->ipt_ref = 2;
6663 } else {
6664 if (new != NULL) {
6665 KFREE(new);
6666 new = NULL;
6669 ipf_unlinktoken(it);
6670 it->ipt_ref++;
6672 it->ipt_pnext = ipftokentail;
6673 *ipftokentail = it;
6674 ipftokentail = &it->ipt_next;
6675 it->ipt_next = NULL;
6677 it->ipt_die = fr_ticks + 2;
6679 RWLOCK_EXIT(&ipf_tokens);
6681 return it;
6685 /* ------------------------------------------------------------------------ */
6686 /* Function: ipf_unlinktoken */
6687 /* Returns: None. */
6688 /* Parameters: token(I) - pointer to token structure */
6689 /* Write Locks: ipf_tokens */
6690 /* */
6691 /* This function unlinks a token structure from the linked list of tokens */
6692 /* that "own" it. The head pointer never needs to be explicitly adjusted */
6693 /* but the tail does due to the linked list implementation. */
6694 /* ------------------------------------------------------------------------ */
6695 static void ipf_unlinktoken(token)
6696 ipftoken_t *token;
6699 if (ipftokentail == &token->ipt_next)
6700 ipftokentail = token->ipt_pnext;
6702 *token->ipt_pnext = token->ipt_next;
6703 if (token->ipt_next != NULL)
6704 token->ipt_next->ipt_pnext = token->ipt_pnext;
6709 /* ------------------------------------------------------------------------ */
6710 /* Function: ipf_dereftoken */
6711 /* Returns: None. */
6712 /* Parameters: token(I) - pointer to token structure */
6713 /* Write Locks: ipf_tokens */
6714 /* */
6715 /* Drop the reference count on the token structure and if it drops to zero, */
6716 /* call the dereference function for the token type because it is then */
6717 /* possible to free the token data structure. */
6718 /* ------------------------------------------------------------------------ */
6719 void ipf_dereftoken(token)
6720 ipftoken_t *token;
6722 void *data, **datap;
6724 token->ipt_ref--;
6725 if (token->ipt_ref > 0)
6726 return;
6728 data = token->ipt_data;
6729 datap = &data;
6731 if ((data != NULL) && (data != (void *)-1)) {
6732 switch (token->ipt_type)
6734 case IPFGENITER_IPF :
6735 (void) fr_derefrule((frentry_t **)datap);
6736 break;
6737 case IPFGENITER_IPNAT :
6738 WRITE_ENTER(&ipf_nat);
6739 fr_ipnatderef((ipnat_t **)datap);
6740 RWLOCK_EXIT(&ipf_nat);
6741 break;
6742 case IPFGENITER_NAT :
6743 fr_natderef((nat_t **)datap);
6744 break;
6745 case IPFGENITER_STATE :
6746 fr_statederef((ipstate_t **)datap);
6747 break;
6748 case IPFGENITER_FRAG :
6749 #ifdef USE_MUTEXES
6750 fr_fragderef((ipfr_t **)datap, &ipf_frag);
6751 #else
6752 fr_fragderef((ipfr_t **)datap);
6753 #endif
6754 break;
6755 case IPFGENITER_NATFRAG :
6756 #ifdef USE_MUTEXES
6757 fr_fragderef((ipfr_t **)datap, &ipf_natfrag);
6758 #else
6759 fr_fragderef((ipfr_t **)datap);
6760 #endif
6761 break;
6762 case IPFGENITER_HOSTMAP :
6763 WRITE_ENTER(&ipf_nat);
6764 fr_hostmapdel((hostmap_t **)datap);
6765 RWLOCK_EXIT(&ipf_nat);
6766 break;
6767 default :
6768 #ifdef IPFILTER_LOOKUP
6769 ip_lookup_iterderef(token->ipt_type, data);
6770 #endif
6771 break;
6775 KFREE(token);
6779 /* ------------------------------------------------------------------------ */
6780 /* Function: ipf_freetoken */
6781 /* Returns: None. */
6782 /* Parameters: token(I) - pointer to token structure */
6783 /* Write Locks: ipf_tokens */
6784 /* */
6785 /* This function unlinks a token from the linked list and does a dereference*/
6786 /* on it to encourage it to be freed. */
6787 /* ------------------------------------------------------------------------ */
6788 void ipf_freetoken(token)
6789 ipftoken_t *token;
6792 ipf_unlinktoken(token);
6794 ipf_dereftoken(token);
6798 /* ------------------------------------------------------------------------ */
6799 /* Function: ipf_getnextrule */
6800 /* Returns: int - 0 = success, else error */
6801 /* Parameters: t(I) - pointer to destination information to resolve */
6802 /* ptr(I) - pointer to ipfobj_t to copyin from user space */
6803 /* */
6804 /* This function's first job is to bring in the ipfruleiter_t structure via */
6805 /* the ipfobj_t structure to determine what should be the next rule to */
6806 /* return. Once the ipfruleiter_t has been brought in, it then tries to */
6807 /* find the 'next rule'. This may include searching rule group lists or */
6808 /* just be as simple as looking at the 'next' field in the rule structure. */
6809 /* When we have found the rule to return, increase its reference count and */
6810 /* if we used an existing rule to get here, decrease its reference count. */
6811 /* ------------------------------------------------------------------------ */
6812 int ipf_getnextrule(ipftoken_t *t, void *ptr)
6814 frentry_t *fr, *next, zero;
6815 int error, count, out;
6816 ipfruleiter_t it;
6817 frgroup_t *fg;
6818 char *dst;
6820 if (t == NULL || ptr == NULL)
6821 return EFAULT;
6822 error = fr_inobj(ptr, &it, IPFOBJ_IPFITER);
6823 if (error != 0)
6824 return error;
6825 if ((it.iri_inout < 0) || (it.iri_inout > 3))
6826 return EINVAL;
6827 if ((it.iri_active != 0) && (it.iri_active != 1))
6828 return EINVAL;
6829 if (it.iri_nrules == 0)
6830 return ENOSPC;
6831 if (it.iri_rule == NULL)
6832 return EFAULT;
6834 out = it.iri_inout & F_OUT;
6835 READ_ENTER(&ipf_mutex);
6838 * Retrieve "previous" entry from token and find the next entry.
6840 fr = t->ipt_data;
6841 if (fr == NULL) {
6842 if (*it.iri_group == '\0') {
6843 if ((it.iri_inout & F_ACIN) != 0) {
6844 if (it.iri_v == 4)
6845 next = ipacct[out][it.iri_active];
6846 else
6847 next = ipacct6[out][it.iri_active];
6848 } else {
6849 if (it.iri_v == 4)
6850 next = ipfilter[out][it.iri_active];
6851 else
6852 next = ipfilter6[out][it.iri_active];
6854 } else {
6855 fg = fr_findgroup(it.iri_group, IPL_LOGIPF,
6856 it.iri_active, NULL);
6857 if (fg != NULL)
6858 next = fg->fg_start;
6859 else
6860 next = NULL;
6862 } else {
6863 next = fr->fr_next;
6866 dst = (char *)it.iri_rule;
6868 * The ipfruleiter may ask for more than 1 rule at a time to be
6869 * copied out, so long as that many exist in the list to start with!
6871 for (count = it.iri_nrules; count > 0; count--) {
6873 * If we found an entry, add reference to it and update token.
6874 * Otherwise, zero out data to be returned and NULL out token.
6876 if (next != NULL) {
6877 MUTEX_ENTER(&next->fr_lock);
6878 next->fr_ref++;
6879 MUTEX_EXIT(&next->fr_lock);
6880 t->ipt_data = next;
6881 } else {
6882 bzero(&zero, sizeof(zero));
6883 next = &zero;
6884 t->ipt_data = NULL;
6888 * Now that we have ref, it's save to give up lock.
6890 RWLOCK_EXIT(&ipf_mutex);
6893 * Copy out data and clean up references and token as needed.
6895 error = COPYOUT(next, dst, sizeof(*next));
6896 if (error != 0)
6897 return EFAULT;
6898 if (t->ipt_data == NULL) {
6899 break;
6900 } else {
6901 if (fr != NULL)
6902 (void) fr_derefrule(&fr);
6903 if (next->fr_data != NULL) {
6904 dst += sizeof(*next);
6905 error = COPYOUT(next->fr_data, dst,
6906 next->fr_dsize);
6907 if (error != 0)
6908 error = EFAULT;
6909 else
6910 dst += next->fr_dsize;
6912 if (next->fr_next == NULL) {
6913 ipf_freetoken(t);
6914 break;
6918 if ((count == 1) || (error != 0))
6919 break;
6921 READ_ENTER(&ipf_mutex);
6922 fr = next;
6923 next = next->fr_next;
6925 return error;
6929 /* ------------------------------------------------------------------------ */
6930 /* Function: fr_frruleiter */
6931 /* Returns: int - 0 = success, else error */
6932 /* Parameters: data(I) - the token type to match */
6933 /* uid(I) - uid owning the token */
6934 /* ptr(I) - context pointer for the token */
6935 /* */
6936 /* This function serves as a stepping stone between fr_ipf_ioctl and */
6937 /* ipf_getnextrule. It's role is to find the right token in the kernel for */
6938 /* the process doing the ioctl and use that to ask for the next rule. */
6939 /* ------------------------------------------------------------------------ */
6940 static int ipf_frruleiter(data, uid, ctx)
6941 void *data, *ctx;
6942 int uid;
6944 ipftoken_t *token;
6945 int error;
6947 token = ipf_findtoken(IPFGENITER_IPF, uid, ctx);
6948 if (token != NULL) {
6949 error = ipf_getnextrule(token, data);
6950 WRITE_ENTER(&ipf_tokens);
6951 if (token->ipt_data == NULL)
6952 ipf_freetoken(token);
6953 else
6954 ipf_dereftoken(token);
6955 RWLOCK_EXIT(&ipf_tokens);
6956 } else {
6957 error = EFAULT;
6960 return error;
6964 /* ------------------------------------------------------------------------ */
6965 /* Function: fr_geniter */
6966 /* Returns: int - 0 = success, else error */
6967 /* Parameters: token(I) - pointer to ipftoken_t structure */
6968 /* itp(I) - */
6969 /* */
6970 /* ------------------------------------------------------------------------ */
6971 static int ipf_geniter(token, itp)
6972 ipftoken_t *token;
6973 ipfgeniter_t *itp;
6975 int error;
6977 switch (itp->igi_type)
6979 case IPFGENITER_FRAG :
6980 #ifdef USE_MUTEXES
6981 error = fr_nextfrag(token, itp,
6982 &ipfr_list, &ipfr_tail, &ipf_frag);
6983 #else
6984 error = fr_nextfrag(token, itp, &ipfr_list, &ipfr_tail);
6985 #endif
6986 break;
6987 default :
6988 error = EINVAL;
6989 break;
6992 return error;
6996 /* ------------------------------------------------------------------------ */
6997 /* Function: fr_genericiter */
6998 /* Returns: int - 0 = success, else error */
6999 /* Parameters: data(I) - the token type to match */
7000 /* uid(I) - uid owning the token */
7001 /* ptr(I) - context pointer for the token */
7002 /* */
7003 /* ------------------------------------------------------------------------ */
7004 int ipf_genericiter(data, uid, ctx)
7005 void *data, *ctx;
7006 int uid;
7008 ipftoken_t *token;
7009 ipfgeniter_t iter;
7010 int error;
7012 error = fr_inobj(data, &iter, IPFOBJ_GENITER);
7013 if (error != 0)
7014 return error;
7016 token = ipf_findtoken(iter.igi_type, uid, ctx);
7017 if (token != NULL) {
7018 token->ipt_subtype = iter.igi_type;
7019 error = ipf_geniter(token, &iter);
7020 WRITE_ENTER(&ipf_tokens);
7021 if (token->ipt_data == NULL)
7022 ipf_freetoken(token);
7023 else
7024 ipf_dereftoken(token);
7025 RWLOCK_EXIT(&ipf_tokens);
7026 } else
7027 error = EFAULT;
7028 RWLOCK_EXIT(&ipf_tokens);
7030 return error;
7034 /* ------------------------------------------------------------------------ */
7035 /* Function: fr_ipf_ioctl */
7036 /* Returns: int - 0 = success, else error */
7037 /* Parameters: data(I) - the token type to match */
7038 /* cmd(I) - the ioctl command number */
7039 /* mode(I) - mode flags for the ioctl */
7040 /* uid(I) - uid owning the token */
7041 /* ptr(I) - context pointer for the token */
7042 /* */
7043 /* This function handles all of the ioctl command that are actually isssued */
7044 /* to the /dev/ipl device. */
7045 /* ------------------------------------------------------------------------ */
7046 int fr_ipf_ioctl(data, cmd, mode, uid, ctx)
7047 void * data;
7048 ioctlcmd_t cmd;
7049 int mode, uid;
7050 void *ctx;
7052 friostat_t fio;
7053 int error, tmp;
7054 SPL_INT(s);
7056 switch (cmd)
7058 case SIOCFRENB :
7059 if (!(mode & FWRITE))
7060 error = EPERM;
7061 else {
7062 error = BCOPYIN(data, &tmp, sizeof(tmp));
7063 if (error != 0) {
7064 error = EFAULT;
7065 break;
7068 WRITE_ENTER(&ipf_global);
7069 if (tmp) {
7070 if (fr_running > 0)
7071 error = 0;
7072 else
7073 error = ipfattach();
7074 if (error == 0)
7075 fr_running = 1;
7076 else
7077 (void) ipfdetach();
7078 } else {
7079 error = ipfdetach();
7080 if (error == 0)
7081 fr_running = -1;
7083 RWLOCK_EXIT(&ipf_global);
7085 break;
7087 case SIOCIPFSET :
7088 if (!(mode & FWRITE)) {
7089 error = EPERM;
7090 break;
7092 /* FALLTHRU */
7093 case SIOCIPFGETNEXT :
7094 case SIOCIPFGET :
7095 error = fr_ipftune(cmd, (void *)data);
7096 break;
7098 case SIOCSETFF :
7099 if (!(mode & FWRITE))
7100 error = EPERM;
7101 else {
7102 error = BCOPYIN(data, &fr_flags, sizeof(fr_flags));
7103 if (error != 0)
7104 error = EFAULT;
7106 break;
7108 case SIOCGETFF :
7109 error = BCOPYOUT(&fr_flags, data, sizeof(fr_flags));
7110 if (error != 0)
7111 error = EFAULT;
7112 break;
7114 case SIOCFUNCL :
7115 error = fr_resolvefunc((void *)data);
7116 break;
7118 case SIOCINAFR :
7119 case SIOCRMAFR :
7120 case SIOCADAFR :
7121 case SIOCZRLST :
7122 if (!(mode & FWRITE))
7123 error = EPERM;
7124 else
7125 error = frrequest(IPL_LOGIPF, cmd, data, fr_active, 1);
7126 break;
7128 case SIOCINIFR :
7129 case SIOCRMIFR :
7130 case SIOCADIFR :
7131 if (!(mode & FWRITE))
7132 error = EPERM;
7133 else
7134 error = frrequest(IPL_LOGIPF, cmd, data,
7135 1 - fr_active, 1);
7136 break;
7138 case SIOCSWAPA :
7139 if (!(mode & FWRITE))
7140 error = EPERM;
7141 else {
7142 WRITE_ENTER(&ipf_mutex);
7143 bzero((char *)frcache, sizeof(frcache[0]) * 2);
7144 error = BCOPYOUT(&fr_active, data, sizeof(fr_active));
7145 if (error != 0)
7146 error = EFAULT;
7147 else
7148 fr_active = 1 - fr_active;
7149 RWLOCK_EXIT(&ipf_mutex);
7151 break;
7153 case SIOCGETFS :
7154 fr_getstat(&fio);
7155 error = fr_outobj((void *)data, &fio, IPFOBJ_IPFSTAT);
7156 break;
7158 case SIOCFRZST :
7159 if (!(mode & FWRITE))
7160 error = EPERM;
7161 else
7162 error = fr_zerostats(data);
7163 break;
7165 case SIOCIPFFL :
7166 if (!(mode & FWRITE))
7167 error = EPERM;
7168 else {
7169 error = BCOPYIN(data, &tmp, sizeof(tmp));
7170 if (!error) {
7171 tmp = frflush(IPL_LOGIPF, 4, tmp);
7172 error = BCOPYOUT(&tmp, data, sizeof(tmp));
7173 if (error != 0)
7174 error = EFAULT;
7175 } else
7176 error = EFAULT;
7178 break;
7180 #ifdef USE_INET6
7181 case SIOCIPFL6 :
7182 if (!(mode & FWRITE))
7183 error = EPERM;
7184 else {
7185 error = BCOPYIN(data, &tmp, sizeof(tmp));
7186 if (!error) {
7187 tmp = frflush(IPL_LOGIPF, 6, tmp);
7188 error = BCOPYOUT(&tmp, data, sizeof(tmp));
7189 if (error != 0)
7190 error = EFAULT;
7191 } else
7192 error = EFAULT;
7194 break;
7195 #endif
7197 case SIOCSTLCK :
7198 error = BCOPYIN(data, &tmp, sizeof(tmp));
7199 if (error == 0) {
7200 fr_state_lock = tmp;
7201 fr_nat_lock = tmp;
7202 fr_frag_lock = tmp;
7203 fr_auth_lock = tmp;
7204 } else
7205 error = EFAULT;
7206 break;
7208 #ifdef IPFILTER_LOG
7209 case SIOCIPFFB :
7210 if (!(mode & FWRITE))
7211 error = EPERM;
7212 else {
7213 tmp = ipflog_clear(IPL_LOGIPF);
7214 error = BCOPYOUT(&tmp, data, sizeof(tmp));
7215 if (error)
7216 error = EFAULT;
7218 break;
7219 #endif /* IPFILTER_LOG */
7221 case SIOCFRSYN :
7222 if (!(mode & FWRITE))
7223 error = EPERM;
7224 else {
7225 WRITE_ENTER(&ipf_global);
7226 #ifdef MENTAT
7227 error = ipfsync();
7228 #else
7229 frsync(NULL);
7230 error = 0;
7231 #endif
7232 RWLOCK_EXIT(&ipf_global);
7235 break;
7237 case SIOCGFRST :
7238 error = fr_outobj((void *)data, fr_fragstats(),
7239 IPFOBJ_FRAGSTAT);
7240 break;
7242 #ifdef IPFILTER_LOG
7243 case FIONREAD :
7244 tmp = (int)iplused[IPL_LOGIPF];
7246 error = BCOPYOUT(&tmp, data, sizeof(tmp));
7247 break;
7248 #endif
7250 case SIOCIPFITER :
7251 SPL_SCHED(s);
7252 error = ipf_frruleiter(data, uid, ctx);
7253 SPL_X(s);
7254 break;
7256 case SIOCGENITER :
7257 SPL_SCHED(s);
7258 error = ipf_genericiter(data, uid, ctx);
7259 SPL_X(s);
7260 break;
7262 case SIOCIPFDELTOK :
7263 SPL_SCHED(s);
7264 error = BCOPYIN(data, &tmp, sizeof(tmp));
7265 if (error == 0)
7266 error = ipf_deltoken(tmp, uid, ctx);
7267 SPL_X(s);
7268 break;
7270 default :
7271 error = EINVAL;
7272 break;
7275 return error;
7279 /* ------------------------------------------------------------------------ */
7280 /* Function: ipf_queueflush */
7281 /* Returns: int - number of entries flushed (0 = none) */
7282 /* Parameters: deletefn(I) - function to call to delete entry */
7283 /* ipfqs(I) - top of the list of ipf internal queues */
7284 /* userqs(I) - top of the list of user defined timeouts */
7285 /* */
7286 /* This fucntion gets called when the state/NAT hash tables fill up and we */
7287 /* need to try a bit harder to free up some space. The algorithm used is */
7288 /* to look for the oldest entries on each timeout queue and free them if */
7289 /* they are within the given window we are considering. Where the window */
7290 /* starts and the steps taken to increase its size depend upon how long ipf */
7291 /* has been running (fr_ticks.) Anything modified in the last 30 seconds */
7292 /* is not touched. */
7293 /* touched */
7294 /* die fr_ticks 30*1.5 1800*1.5 | 43200*1.5 */
7295 /* | | | | | | */
7296 /* future <--+----------+--------+-----------+-----+-----+-----------> past */
7297 /* now \_int=30s_/ \_int=1hr_/ \_int=12hr */
7298 /* */
7299 /* Points to note: */
7300 /* - tqe_die is the time, in the future, when entries die. */
7301 /* - tqe_die - fr_ticks is how long left the connection has to live in ipf */
7302 /* ticks. */
7303 /* - tqe_touched is when the entry was last used by NAT/state */
7304 /* - the closer tqe_touched is to fr_ticks, the further tqe_die will be for */
7305 /* any given timeout queue and vice versa. */
7306 /* - both tqe_die and tqe_touched increase over time */
7307 /* - timeout queues are sorted with the highest value of tqe_die at the */
7308 /* bottom and therefore the smallest values of each are at the top */
7309 /* */
7310 /* We start by setting up a maximum range to scan for things to move of */
7311 /* iend (newest) to istart (oldest) in chunks of "interval". If nothing is */
7312 /* found in that range, "interval" is adjusted (so long as it isn't 30) and */
7313 /* we start again with a new value for "iend" and "istart". The downside */
7314 /* of the current implementation is that it may return removing just 1 entry*/
7315 /* every time (pathological case) where it could remove more. */
7316 /* ------------------------------------------------------------------------ */
7317 int ipf_queueflush(deletefn, ipfqs, userqs)
7318 ipftq_delete_fn_t deletefn;
7319 ipftq_t *ipfqs, *userqs;
7321 u_long interval, istart, iend;
7322 ipftq_t *ifq, *ifqnext;
7323 ipftqent_t *tqe, *tqn;
7324 int removed;
7327 * NOTE: Use of "* 15 / 10" is required here because if "* 1.5" is
7328 * used then the operations are upgraded to floating point
7329 * and kernels don't like floating point...
7331 if (fr_ticks > IPF_TTLVAL(43200 * 15 / 10)) {
7332 istart = IPF_TTLVAL(86400 * 4);
7333 interval = IPF_TTLVAL(43200);
7334 } else if (fr_ticks > IPF_TTLVAL(1800 * 15 / 10)) {
7335 istart = IPF_TTLVAL(43200);
7336 interval = IPF_TTLVAL(1800);
7337 } else if (fr_ticks > IPF_TTLVAL(30 * 15 / 10)) {
7338 istart = IPF_TTLVAL(1800);
7339 interval = IPF_TTLVAL(30);
7340 } else {
7341 return 0;
7343 if (istart > fr_ticks) {
7344 if (fr_ticks - interval < interval)
7345 istart = interval;
7346 else
7347 istart = (fr_ticks / interval) * interval;
7350 iend = fr_ticks - interval;
7351 removed = 0;
7353 for (;;) {
7354 u_long try;
7356 try = fr_ticks - istart;
7358 for (ifq = ipfqs; ifq != NULL; ifq = ifq->ifq_next) {
7359 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) {
7360 if (try < tqe->tqe_touched)
7361 break;
7362 tqn = tqe->tqe_next;
7363 if ((*deletefn)(tqe->tqe_parent) == 0)
7364 removed++;
7368 for (ifq = userqs; ifq != NULL; ifq = ifqnext) {
7369 ifqnext = ifq->ifq_next;
7371 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) {
7372 if (try < tqe->tqe_touched)
7373 break;
7374 tqn = tqe->tqe_next;
7375 if ((*deletefn)(tqe->tqe_parent) == 0)
7376 removed++;
7380 if (try >= iend) {
7381 if (removed > 0)
7382 break;
7383 if (interval == IPF_TTLVAL(43200)) {
7384 interval = IPF_TTLVAL(1800);
7385 } else if (interval == IPF_TTLVAL(1800)) {
7386 interval = IPF_TTLVAL(30);
7387 } else {
7388 break;
7390 if (interval >= fr_ticks)
7391 break;
7393 iend = fr_ticks - interval;
7395 istart -= interval;
7398 return removed;