xref: /illumos-gate/usr/src/uts/common/inet/ipf/fil.c (revision 7663b816)
1 /*
2  * Copyright (C) 1993-2003 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  */
6 
7 /*
8  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
9  * Use is subject to license terms.
10  */
11 
12 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13 
14 #if defined(KERNEL) || defined(_KERNEL)
15 # undef KERNEL
16 # undef _KERNEL
17 # define        KERNEL	1
18 # define        _KERNEL	1
19 #endif
20 #include <sys/errno.h>
21 #include <sys/types.h>
22 #include <sys/param.h>
23 #include <sys/time.h>
24 #if defined(__NetBSD__)
25 # if (NetBSD >= 199905) && !defined(IPFILTER_LKM) && defined(_KERNEL)
26 #  include "opt_ipfilter_log.h"
27 # endif
28 #endif
29 #if defined(_KERNEL) && defined(__FreeBSD_version) && \
30     (__FreeBSD_version >= 220000)
31 # if (__FreeBSD_version >= 400000)
32 #  if !defined(IPFILTER_LKM)
33 #   include "opt_inet6.h"
34 #  endif
35 #  if (__FreeBSD_version == 400019)
36 #   define CSUM_DELAY_DATA
37 #  endif
38 # endif
39 # include <sys/filio.h>
40 #else
41 # include <sys/ioctl.h>
42 #endif
43 #include <sys/fcntl.h>
44 #if defined(_KERNEL)
45 # include <sys/systm.h>
46 # include <sys/file.h>
47 #else
48 # include <stdio.h>
49 # include <string.h>
50 # include <stdlib.h>
51 # include <sys/file.h>
52 # define _KERNEL
53 # ifdef __OpenBSD__
54 struct file;
55 # endif
56 # include <sys/uio.h>
57 # undef _KERNEL
58 #endif
59 #if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux)
60 # include <sys/mbuf.h>
61 #else
62 # include <sys/byteorder.h>
63 # if (SOLARIS2 < 5) && defined(sun)
64 #  include <sys/dditypes.h>
65 # endif
66 #endif
67 #ifdef __hpux
68 # define _NET_ROUTE_INCLUDED
69 #endif
70 #include <sys/protosw.h>
71 #include <sys/socket.h>
72 #include <net/if.h>
73 #ifdef sun
74 # include <net/af.h>
75 #endif
76 #include <net/route.h>
77 #include <netinet/in.h>
78 #include <netinet/in_systm.h>
79 #include <netinet/ip.h>
80 #include <netinet/ip_var.h>
81 #if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */
82 # include <sys/hashing.h>
83 # include <netinet/in_var.h>
84 #endif
85 #include <netinet/tcp.h>
86 #include <netinet/udp.h>
87 #include <netinet/ip_icmp.h>
88 #ifdef __hpux
89 # undef _NET_ROUTE_INCLUDED
90 #endif
91 #if SOLARIS2 >= 10
92 # include "ip_compat.h"
93 #else
94 # include "netinet/ip_compat.h"
95 #endif
96 #ifdef	USE_INET6
97 # include <netinet/icmp6.h>
98 # if !SOLARIS && defined(_KERNEL) && !defined(__osf__)
99 #  include <netinet6/in6_var.h>
100 # endif
101 #endif
102 #include <netinet/tcpip.h>
103 #if SOLARIS2 >= 10
104 #include "ip_fil.h"
105 #include "ip_nat.h"
106 #include "ip_frag.h"
107 #include "ip_state.h"
108 #include "ip_proxy.h"
109 #include "ip_auth.h"
110 #else
111 #include "netinet/ip_fil.h"
112 #include "netinet/ip_nat.h"
113 #include "netinet/ip_frag.h"
114 #include "netinet/ip_state.h"
115 #include "netinet/ip_proxy.h"
116 #include "netinet/ip_auth.h"
117 #endif
118 #ifdef IPFILTER_SCAN
119 # include "netinet/ip_scan.h"
120 #endif
121 #if SOLARIS2 >= 10
122 #include "ip_pool.h"
123 #include "ip_htable.h"
124 #else
125 #include "netinet/ip_pool.h"
126 #include "netinet/ip_htable.h"
127 #endif
128 #ifdef IPFILTER_BPF
129 # include <net/bpf.h>
130 #endif
131 #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
132 # include <sys/malloc.h>
133 # if defined(_KERNEL) && !defined(IPFILTER_LKM)
134 #  include "opt_ipfilter.h"
135 # endif
136 #endif
137 #if SOLARIS2 >= 10
138 #include "ipl.h"
139 #else
140 #include "netinet/ipl.h"
141 #endif
142 
143 #if !defined(lint)
144 static const char sccsid[] = "@(#)fil.c	1.36 6/5/96 (C) 1993-2000 Darren Reed";
145 static const char rcsid[] = "@(#)$Id: fil.c,v 2.197 2003/07/01 18:30:18 darrenr Exp $";
146 #endif
147 
148 #ifndef	_KERNEL
149 # include "ipf.h"
150 # include "ipt.h"
151 extern	int	opts;
152 
153 # define	FR_VERBOSE(verb_pr)			verbose verb_pr
154 # define	FR_DEBUG(verb_pr)			debug verb_pr
155 #else /* #ifndef _KERNEL */
156 # define	FR_VERBOSE(verb_pr)
157 # define	FR_DEBUG(verb_pr)
158 #endif /* _KERNEL */
159 
160 
161 struct	filterstats frstats[2] = { { 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 } };
162 struct	frentry	*ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } },
163 		*ipfilter6[2][2] = { { NULL, NULL }, { NULL, NULL } },
164 		*ipacct6[2][2] = { { NULL, NULL }, { NULL, NULL } },
165 		*ipacct[2][2] = { { NULL, NULL }, { NULL, NULL } },
166 		*ipnatrules[2][2] = { { NULL, NULL }, { NULL, NULL } };
167 struct	frgroup *ipfgroups[IPL_LOGSIZE][2];
168 int	fr_refcnt = 0;
169 /*
170  * For fr_running:
171  * 0 == loading, 1 = running, -1 = disabled, -2 = unloading
172  */
173 int	fr_running = 0;
174 int	fr_flags = IPF_LOGGING;
175 int	fr_active = 0;
176 int	fr_control_forwarding = 0;
177 #ifdef _KERNEL
178 int	fr_update_ipid = 0;
179 #else
180 int	fr_update_ipid = 1;
181 #endif
182 u_short	fr_ip_id = 0;
183 int	fr_chksrc = 0;
184 int	fr_minttl = 4;
185 #if defined(IPFILTER_DEFAULT_BLOCK)
186 int	fr_pass = FR_BLOCK|FR_NOMATCH;
187 #else
188 int	fr_pass = (IPF_DEFAULT_PASS)|FR_NOMATCH;
189 #endif
190 u_long	fr_frouteok[2] = {0, 0};
191 u_long	fr_userifqs = 0;
192 #ifdef  ICMP_UNREACH_FILTER_PROHIB
193 int	fr_unreach = ICMP_UNREACH_FILTER_PROHIB;
194 #else
195 int	fr_unreach = ICMP_UNREACH_FILTER;
196 #endif
197 u_char	ipf_iss_secret[32];
198 
199 char	ipfilter_version[] = IPL_VERSION;
200 
201 fr_info_t	frcache[2][8];
202 
203 static	INLINE int	fr_ipfcheck __P((fr_info_t *, frentry_t *, int));
204 static	int		fr_portcheck __P((frpcmp_t *, u_short *));
205 static	int		frflushlist __P((int, minor_t, int *, frentry_t **));
206 static	ipfunc_t	fr_findfunc __P((ipfunc_t));
207 static	frentry_t	*fr_firewall __P((fr_info_t *, u_32_t *));
208 static	int		fr_funcinit __P((frentry_t *fr));
209 static	INLINE void	frpr_esp __P((fr_info_t *));
210 static	INLINE void	frpr_gre __P((fr_info_t *));
211 static	INLINE void	frpr_udp __P((fr_info_t *));
212 static	INLINE void	frpr_tcp __P((fr_info_t *));
213 static	INLINE void	frpr_icmp __P((fr_info_t *));
214 static	INLINE void	frpr_ipv4hdr __P((fr_info_t *));
215 static	INLINE int	frpr_pullup __P((fr_info_t *, int));
216 static	INLINE void	frpr_short __P((fr_info_t *, int));
217 static	INLINE void	frpr_tcpcommon __P((fr_info_t *));
218 static	INLINE void	frpr_udpcommon __P((fr_info_t *));
219 static	INLINE int	fr_updateipid __P((fr_info_t *));
220 #ifdef	IPFILTER_LOOKUP
221 static	int		fr_grpmapinit __P((frentry_t *fr));
222 static	INLINE void	*fr_resolvelookup __P((u_int, u_int, lookupfunc_t *));
223 #endif
224 static	void		frsynclist __P((frentry_t *));
225 
226 
227 /*
228  * bit values for identifying presence of individual IP options
229  */
230 struct	optlist	ipopts[20] = {
231 	{ IPOPT_NOP,	0x000001 },
232 	{ IPOPT_RR,	0x000002 },
233 	{ IPOPT_ZSU,	0x000004 },
234 	{ IPOPT_MTUP,	0x000008 },
235 	{ IPOPT_MTUR,	0x000010 },
236 	{ IPOPT_ENCODE,	0x000020 },
237 	{ IPOPT_TS,	0x000040 },
238 	{ IPOPT_TR,	0x000080 },
239 	{ IPOPT_SECURITY, 0x000100 },
240 	{ IPOPT_LSRR,	0x000200 },
241 	{ IPOPT_E_SEC,	0x000400 },
242 	{ IPOPT_CIPSO,	0x000800 },
243 	{ IPOPT_SATID,	0x001000 },
244 	{ IPOPT_SSRR,	0x002000 },
245 	{ IPOPT_ADDEXT,	0x004000 },
246 	{ IPOPT_VISA,	0x008000 },
247 	{ IPOPT_IMITD,	0x010000 },
248 	{ IPOPT_EIP,	0x020000 },
249 	{ IPOPT_FINN,	0x040000 },
250 	{ 0,		0x000000 }
251 };
252 
253 #ifdef USE_INET6
254 struct optlist ip6exthdr[] = {
255 	{ IPPROTO_HOPOPTS,		0x000001 },
256 	{ IPPROTO_IPV6,			0x000002 },
257 	{ IPPROTO_ROUTING,		0x000004 },
258 	{ IPPROTO_FRAGMENT,		0x000008 },
259 	{ IPPROTO_ESP,			0x000010 },
260 	{ IPPROTO_AH,			0x000020 },
261 	{ IPPROTO_NONE,			0x000040 },
262 	{ IPPROTO_DSTOPTS,		0x000080 },
263 	{ 0,				0 }
264 };
265 #endif
266 
267 struct optlist tcpopts[] = {
268 	{ TCPOPT_NOP,			0x000001 },
269 	{ TCPOPT_MAXSEG,		0x000002 },
270 	{ TCPOPT_WINDOW,		0x000004 },
271 	{ TCPOPT_SACK_PERMITTED,	0x000008 },
272 	{ TCPOPT_SACK,			0x000010 },
273 	{ TCPOPT_TIMESTAMP,		0x000020 },
274 	{ 0,				0x000000 }
275 };
276 
277 /*
278  * bit values for identifying presence of individual IP security options
279  */
280 struct	optlist	secopt[8] = {
281 	{ IPSO_CLASS_RES4,	0x01 },
282 	{ IPSO_CLASS_TOPS,	0x02 },
283 	{ IPSO_CLASS_SECR,	0x04 },
284 	{ IPSO_CLASS_RES3,	0x08 },
285 	{ IPSO_CLASS_CONF,	0x10 },
286 	{ IPSO_CLASS_UNCL,	0x20 },
287 	{ IPSO_CLASS_RES2,	0x40 },
288 	{ IPSO_CLASS_RES1,	0x80 }
289 };
290 
291 
292 /*
293  * Table of functions available for use with call rules.
294  */
295 static ipfunc_resolve_t fr_availfuncs[] = {
296 #ifdef	IPFILTER_LOOKUP
297 	{ "fr_srcgrpmap", fr_srcgrpmap, fr_grpmapinit },
298 	{ "fr_dstgrpmap", fr_dstgrpmap, fr_grpmapinit },
299 #endif
300 	{ "", NULL }
301 };
302 
303 
304 /*
305  * The next section of code is a a collection of small routines that set
306  * fields in the fr_info_t structure passed based on properties of the
307  * current packet.  There are different routines for the same protocol
308  * for each of IPv4 and IPv6.  Adding a new protocol, for which there
309  * will "special" inspection for setup, is now more easily done by adding
310  * a new routine and expanding the frpr_ipinit*() function rather than by
311  * adding more code to a growing switch statement.
312  */
313 #ifdef USE_INET6
314 static	INLINE void	frpr_udp6 __P((fr_info_t *));
315 static	INLINE void	frpr_tcp6 __P((fr_info_t *));
316 static	INLINE void	frpr_icmp6 __P((fr_info_t *));
317 static	INLINE int	frpr_ipv6hdr __P((fr_info_t *));
318 static	INLINE void	frpr_short6 __P((fr_info_t *, int));
319 static	INLINE int	frpr_hopopts6 __P((fr_info_t *));
320 static	INLINE int	frpr_routing6 __P((fr_info_t *));
321 static	INLINE int	frpr_dstopts6 __P((fr_info_t *));
322 static	INLINE int	frpr_fragment6 __P((fr_info_t *));
323 
324 
325 /* ------------------------------------------------------------------------ */
326 /* Function:    frpr_short6                                                 */
327 /* Returns:     void                                                        */
328 /* Parameters:  fin(I) - pointer to packet information                      */
329 /*                                                                          */
330 /* IPv6 Only                                                                */
331 /* This is function enforces the 'is a packet too short to be legit' rule   */
332 /* for IPv6 and marks the packet with FI_BAD/FI_SHORT if so.                */
333 /* See function comment for frpr_short() for more details.                  */
334 /* ------------------------------------------------------------------------ */
335 static INLINE void frpr_short6(fin, min)
336 fr_info_t *fin;
337 int min;
338 {
339 	fr_ip_t *fi = &fin->fin_fi;
340 	int off;
341 
342 	off = fin->fin_off;
343 	if (off == 0) {
344 		if (fin->fin_dlen < min) {
345 			if (fin->fin_flx & FI_FRAG) {
346 				fin->fin_p = IPPROTO_FRAGMENT;
347 				fin->fin_flx |= FI_BAD;
348 			} else {
349 				fi->fi_flx |= FI_SHORT;
350 			}
351 		}
352 	} else if (off < min) {
353 		if (fin->fin_flx & FI_FRAG)
354 			fi->fi_flx |= FI_BAD;
355 		else
356 			fi->fi_flx |= FI_SHORT;
357 	}
358 }
359 
360 
361 /* ------------------------------------------------------------------------ */
362 /* Function:    frpr_ipv6hdr                                                */
363 /* Returns:     int                                                         */
364 /* Parameters:  fin(I) - pointer to packet information                      */
365 /*                                                                          */
366 /* IPv6 Only                                                                */
367 /* Copy values from the IPv6 header into the fr_info_t struct and call the  */
368 /* per-protocol analyzer if it exists.                                      */
369 /* ------------------------------------------------------------------------ */
370 static INLINE int frpr_ipv6hdr(fin)
371 fr_info_t *fin;
372 {
373 	int p, go = 1, i, hdrcount, coalesced;
374 	ip6_t *ip6 = (ip6_t *)fin->fin_ip;
375 	fr_ip_t *fi = &fin->fin_fi;
376 
377 	fin->fin_off = 0;
378 
379 	fi->fi_tos = 0;
380 	fi->fi_optmsk = 0;
381 	fi->fi_secmsk = 0;
382 	fi->fi_auth = 0;
383 
384 	coalesced = (fin->fin_flx & FI_COALESCE) ? 1 : 0;
385 	p = ip6->ip6_nxt;
386 	fi->fi_ttl = ip6->ip6_hlim;
387 	fi->fi_src.in6 = ip6->ip6_src;
388 	fi->fi_dst.in6 = ip6->ip6_dst;
389 	fin->fin_id = 0;
390 
391 	hdrcount = 0;
392 	while (go && !(fin->fin_flx & (FI_BAD|FI_SHORT))) {
393 		switch (p)
394 		{
395 		case IPPROTO_UDP :
396 			frpr_udp6(fin);
397 			go = 0;
398 			break;
399 
400 		case IPPROTO_TCP :
401 			frpr_tcp6(fin);
402 			go = 0;
403 			break;
404 
405 		case IPPROTO_ICMPV6 :
406 			frpr_icmp6(fin);
407 			go = 0;
408 			break;
409 
410 		case IPPROTO_GRE :
411 			frpr_gre(fin);
412 			go = 0;
413 			break;
414 
415 		case IPPROTO_HOPOPTS :
416 			/*
417 			 * Actually, hop by hop header is only allowed right
418 			 * after IPv6 header!
419 			 */
420 			if (hdrcount != 0)
421 				fin->fin_flx |= FI_BAD;
422 
423 			if (coalesced == 0) {
424 				coalesced = fr_coalesce(fin);
425 				if (coalesced != 1)
426 					return 0;
427 			}
428 			p = frpr_hopopts6(fin);
429 			break;
430 
431 		case IPPROTO_DSTOPTS :
432 			if (coalesced == 0) {
433 				coalesced = fr_coalesce(fin);
434 				if (coalesced != 1)
435 					return 0;
436 			}
437 			p = frpr_dstopts6(fin);
438 			break;
439 
440 		case IPPROTO_ROUTING :
441 			if (coalesced == 0) {
442 				coalesced = fr_coalesce(fin);
443 				if (coalesced != 1)
444 					return 0;
445 			}
446 			p = frpr_routing6(fin);
447 			break;
448 
449 		case IPPROTO_ESP :
450 			frpr_esp(fin);
451 			for (i = 0; ip6exthdr[i].ol_bit != 0; i++)
452 				if (ip6exthdr[i].ol_val == p) {
453 					fin->fin_optmsk |= ip6exthdr[i].ol_bit;
454 					break;
455 				}
456 			go = 0;
457 			break;
458 
459 		case IPPROTO_AH :
460 			frpr_short6(fin, 16);
461 			for (i = 0; ip6exthdr[i].ol_bit != 0; i++)
462 				if (ip6exthdr[i].ol_val == p) {
463 					fin->fin_optmsk |= ip6exthdr[i].ol_bit;
464 					break;
465 				}
466 			go = 0;
467 			break;
468 
469 		case IPPROTO_IPV6 :
470 			frpr_short6(fin, sizeof(ip6_t));
471 			for (i = 0; ip6exthdr[i].ol_bit != 0; i++)
472 				if (ip6exthdr[i].ol_val == p) {
473 					fin->fin_optmsk |= ip6exthdr[i].ol_bit;
474 					break;
475 				}
476 			go = 0;
477 			break;
478 
479 		case IPPROTO_FRAGMENT :
480 			p = frpr_fragment6(fin);
481 			if (fin->fin_off != 0)	/* Not the first frag */
482 				go = 0;
483 			break;
484 
485 		case IPPROTO_NONE :
486 			go = 0;
487 			break;
488 
489 		default :
490 			go = 0;
491 			break;
492 		}
493 		hdrcount++;
494 	}
495 
496 	fi->fi_p = p;
497 
498 	/* ext hdr error */
499 	if ((go && hdrcount && (fin->fin_flx & FI_FRAG)) ||
500 	    (fin->fin_flx & FI_BAD))
501 		return -1;
502 	return 0;
503 }
504 
505 
506 /* ------------------------------------------------------------------------ */
507 /* Function:    frpr_hopopts6                                               */
508 /* Returns:     int    - value of the next header or IPPROTO_NONE if error  */
509 /* Parameters:  fin(I) - pointer to packet information                      */
510 /*                                                                          */
511 /* IPv6 Only                                                                */
512 /* This is function checks pending hop by hop options extension header      */
513 /* ------------------------------------------------------------------------ */
514 static INLINE int frpr_hopopts6(fin)
515 fr_info_t *fin;
516 {
517 	struct ip6_ext *hdr;
518 	u_short shift;
519 	int i;
520 
521 	fin->fin_flx |= FI_V6EXTHDR;
522 
523 	/* 8 is default length of extension hdr */
524 	frpr_short6(fin, 8);
525 	if (fin->fin_flx & (FI_BAD|FI_SHORT))
526 		return IPPROTO_NONE;
527 
528 	if (frpr_pullup(fin, 8) == -1)
529 		return IPPROTO_NONE;
530 
531 	hdr = fin->fin_dp;
532 	shift = 8 + (hdr->ip6e_len << 3);
533 	if (shift > fin->fin_dlen) {	/* Nasty extension header length? */
534 		fin->fin_flx |= FI_BAD;
535 		return IPPROTO_NONE;
536 	}
537 
538 	for (i = 0; ip6exthdr[i].ol_bit != 0; i++)
539 		if (ip6exthdr[i].ol_val == IPPROTO_HOPOPTS) {
540 			fin->fin_optmsk |= ip6exthdr[i].ol_bit;
541 			break;
542 		}
543 
544 	fin->fin_dp = (char *)fin->fin_dp + shift;
545 	fin->fin_dlen -= shift;
546 
547 	return hdr->ip6e_nxt;
548 }
549 
550 
551 /* ------------------------------------------------------------------------ */
552 /* Function:    frpr_routing6                                               */
553 /* Returns:     int    - value of the next header or IPPROTO_NONE if error  */
554 /* Parameters:  fin(I) - pointer to packet information                      */
555 /*                                                                          */
556 /* IPv6 Only                                                                */
557 /* This is function checks pending routing extension header                 */
558 /* ------------------------------------------------------------------------ */
559 static INLINE int frpr_routing6(fin)
560 fr_info_t *fin;
561 {
562 	struct ip6_ext *hdr;
563 	u_short shift;
564 	int i;
565 
566 	fin->fin_flx |= FI_V6EXTHDR;
567 
568 	/* 8 is default length of extension hdr */
569 	frpr_short6(fin, 8);
570 	if (fin->fin_flx & (FI_BAD|FI_SHORT))
571 		return IPPROTO_NONE;
572 
573 	if (frpr_pullup(fin, 8) == -1)
574 		return IPPROTO_NONE;
575 	hdr = fin->fin_dp;
576 
577 	shift = 8 + (hdr->ip6e_len << 3);
578 	/*
579 	 * Nasty extension header length?
580 	 */
581 	if ((shift > fin->fin_dlen) || ((hdr->ip6e_len << 3) & 15)) {
582 		fin->fin_flx |= FI_BAD;
583 		return IPPROTO_NONE;
584 	}
585 
586 	for (i = 0; ip6exthdr[i].ol_bit != 0; i++)
587 		if (ip6exthdr[i].ol_val == IPPROTO_ROUTING) {
588 			fin->fin_optmsk |= ip6exthdr[i].ol_bit;
589 			break;
590 		}
591 
592 	fin->fin_dp = (char *)fin->fin_dp + shift;
593 	fin->fin_dlen -= shift;
594 
595 	return hdr->ip6e_nxt;
596 }
597 
598 
599 /* ------------------------------------------------------------------------ */
600 /* Function:    frpr_fragment6                                              */
601 /* Returns:     int    - value of the next header or IPPROTO_NONE if error  */
602 /* Parameters:  fin(I) - pointer to packet information                      */
603 /*                                                                          */
604 /* IPv6 Only                                                                */
605 /* Examine the IPv6 fragment header and extract fragment offset information.*/
606 /* ------------------------------------------------------------------------ */
607 static INLINE int frpr_fragment6(fin)
608 fr_info_t *fin;
609 {
610 	struct ip6_frag *frag;
611 	int i;
612 
613 	fin->fin_flx |= (FI_FRAG|FI_V6EXTHDR);
614 
615 	/*
616 	 * Only one frgament header is allowed per IPv6 packet but it need
617 	 * not be the first nor last (not possible in some cases.)
618 	 */
619 	for (i = 0; ip6exthdr[i].ol_bit != 0; i++)
620 		if (ip6exthdr[i].ol_val == IPPROTO_FRAGMENT)
621 			break;
622 
623 	if (fin->fin_optmsk & ip6exthdr[i].ol_bit) {
624 		fin->fin_flx |= FI_BAD;
625 		return IPPROTO_NONE;
626 	}
627 
628 	fin->fin_optmsk |= ip6exthdr[i].ol_bit;
629 
630 	/* 8 is default length of extension hdr */
631 	frpr_short6(fin, 8);
632 	if (fin->fin_flx & (FI_BAD|FI_SHORT))
633 		return IPPROTO_NONE;
634 
635 	if (frpr_pullup(fin, 8) == -1)
636 		return IPPROTO_NONE;
637 
638 	if ((int)(fin->fin_dlen - sizeof(*frag)) < 0) {
639 		fin->fin_flx |= FI_SHORT;
640 		return IPPROTO_NONE;
641 	}
642 
643 	frag = fin->fin_dp;
644 	fin->fin_id = frag->ip6f_ident;
645 	fin->fin_off = frag->ip6f_offlg & IP6F_OFF_MASK;
646 	fin->fin_off = ntohs(fin->fin_off);
647 	if (!(frag->ip6f_offlg & IP6F_MORE_FRAG)) {
648 		fin->fin_flx |= FI_FRAGTAIL;
649 	} else if (fin->fin_dlen % 8) {
650 		/*
651 		 * If the frag is not the last one and the payload length
652 		 * is not multiple of 8, it must be dropped.
653 		 */
654 		fin->fin_flx |= FI_BAD;
655 		return IPPROTO_NONE;
656 	}
657 
658 	fin->fin_dp = (char *)fin->fin_dp + sizeof(*frag);
659 	fin->fin_dlen -= sizeof(*frag);
660 
661 	/* length of hdrs(after frag hdr) + data */
662 	fin->fin_flen = fin->fin_dlen;
663 
664 	return frag->ip6f_nxt;
665 }
666 
667 
668 /* ------------------------------------------------------------------------ */
669 /* Function:    frpr_dstopts6                                               */
670 /* Returns:     int    - value of the next header or IPPROTO_NONE if error  */
671 /* Parameters:  fin(I) - pointer to packet information                      */
672 /*              nextheader(I) - stores next header value                    */
673 /*                                                                          */
674 /* IPv6 Only                                                                */
675 /* This is function checks pending destination options extension header     */
676 /* ------------------------------------------------------------------------ */
677 static INLINE int frpr_dstopts6(fin)
678 fr_info_t *fin;
679 {
680 	struct ip6_ext *hdr;
681 	u_short shift;
682 	int i;
683 
684 	/* 8 is default length of extension hdr */
685 	frpr_short6(fin, 8);
686 	if (fin->fin_flx & (FI_BAD|FI_SHORT))
687 		return IPPROTO_NONE;
688 
689 	if (frpr_pullup(fin, 8) == -1)
690 		return IPPROTO_NONE;
691 	hdr = fin->fin_dp;
692 
693 	shift = 8 + (hdr->ip6e_len << 3);
694 	if (shift > fin->fin_dlen) {	/* Nasty extension header length? */
695 		fin->fin_flx |= FI_BAD;
696 		return IPPROTO_NONE;
697 	}
698 
699 	for (i = 0; ip6exthdr[i].ol_bit != 0; i++)
700 		if (ip6exthdr[i].ol_val == IPPROTO_DSTOPTS)
701 			break;
702 	fin->fin_optmsk |= ip6exthdr[i].ol_bit;
703 	fin->fin_dp = (char *)fin->fin_dp + shift;
704 	fin->fin_dlen -= shift;
705 
706 	return hdr->ip6e_nxt;
707 }
708 
709 
710 /* ------------------------------------------------------------------------ */
711 /* Function:    frpr_icmp6                                                  */
712 /* Returns:     void                                                        */
713 /* Parameters:  fin(I) - pointer to packet information                      */
714 /*                                                                          */
715 /* IPv6 Only                                                                */
716 /* This routine is mainly concerned with determining the minimum valid size */
717 /* for an ICMPv6 packet.                                                    */
718 /* ------------------------------------------------------------------------ */
719 static INLINE void frpr_icmp6(fin)
720 fr_info_t *fin;
721 {
722 	int minicmpsz = sizeof(struct icmp6_hdr);
723 	struct icmp6_hdr *icmp6;
724 
725 	if (frpr_pullup(fin, ICMP6ERR_MINPKTLEN + 8 - sizeof(ip6_t)) == -1)
726 		return;
727 
728 	if (fin->fin_dlen > 1) {
729 		icmp6 = fin->fin_dp;
730 
731 		fin->fin_data[0] = *(u_short *)icmp6;
732 
733 		switch (icmp6->icmp6_type)
734 		{
735 		case ICMP6_ECHO_REPLY :
736 		case ICMP6_ECHO_REQUEST :
737 			minicmpsz = ICMP6ERR_MINPKTLEN - sizeof(ip6_t);
738 			break;
739 		case ICMP6_DST_UNREACH :
740 		case ICMP6_PACKET_TOO_BIG :
741 		case ICMP6_TIME_EXCEEDED :
742 		case ICMP6_PARAM_PROB :
743 			if ((fin->fin_m != NULL) &&
744 			    (M_LEN(fin->fin_m) < fin->fin_plen)) {
745 				if (fr_coalesce(fin) != 1)
746 					return;
747 			}
748 			fin->fin_flx |= FI_ICMPERR;
749 			minicmpsz = ICMP6ERR_IPICMPHLEN - sizeof(ip6_t);
750 			break;
751 		default :
752 			break;
753 		}
754 	}
755 
756 	frpr_short6(fin, minicmpsz);
757 
758 	fin->fin_flen -= fin->fin_dlen - minicmpsz;
759 }
760 
761 
762 /* ------------------------------------------------------------------------ */
763 /* Function:    frpr_udp6                                                   */
764 /* Returns:     void                                                        */
765 /* Parameters:  fin(I) - pointer to packet information                      */
766 /*                                                                          */
767 /* IPv6 Only                                                                */
768 /* Analyse the packet for IPv6/UDP properties.                              */
769 /* ------------------------------------------------------------------------ */
770 static INLINE void frpr_udp6(fin)
771 fr_info_t *fin;
772 {
773 
774 	fr_checkv6sum(fin);
775 
776 	frpr_short6(fin, sizeof(struct udphdr));
777 
778 	fin->fin_flen -= fin->fin_dlen - sizeof(struct udphdr);
779 
780 	frpr_udpcommon(fin);
781 }
782 
783 
784 /* ------------------------------------------------------------------------ */
785 /* Function:    frpr_tcp6                                                   */
786 /* Returns:     void                                                        */
787 /* Parameters:  fin(I) - pointer to packet information                      */
788 /*                                                                          */
789 /* IPv6 Only                                                                */
790 /* Analyse the packet for IPv6/TCP properties.                              */
791 /* ------------------------------------------------------------------------ */
792 static INLINE void frpr_tcp6(fin)
793 fr_info_t *fin;
794 {
795 
796 	fr_checkv6sum(fin);
797 
798 	frpr_short6(fin, sizeof(struct tcphdr));
799 
800 	fin->fin_flen -= fin->fin_dlen - sizeof(struct tcphdr);
801 
802 	frpr_tcpcommon(fin);
803 }
804 #endif	/* USE_INET6 */
805 
806 
807 /* ------------------------------------------------------------------------ */
808 /* Function:    frpr_pullup                                                 */
809 /* Returns:     int     - 0 == pullup succeeded, -1 == failure              */
810 /* Parameters:  fin(I)  - pointer to packet information                     */
811 /*              plen(I) - length (excluding L3 header) to pullup            */
812 /*                                                                          */
813 /* Short inline function to cut down on code duplication to perform a call  */
814 /* to fr_pullup to ensure there is the required amount of data,             */
815 /* consecutively in the packet buffer.                                      */
816 /* ------------------------------------------------------------------------ */
817 static INLINE int frpr_pullup(fin, plen)
818 fr_info_t *fin;
819 int plen;
820 {
821 #if defined(_KERNEL) && !defined(__sgi)
822 	if (fin->fin_m != NULL) {
823 		if (fin->fin_dp != NULL)
824 			plen += (char *)fin->fin_dp -
825 				((char *)fin->fin_ip + fin->fin_hlen);
826 		plen += fin->fin_hlen;
827 		if (M_LEN(fin->fin_m) < plen) {
828 			if (fr_pullup(fin->fin_m, fin, plen) == NULL)
829 				return -1;
830 		}
831 	}
832 #endif
833 	return 0;
834 }
835 
836 
837 /* ------------------------------------------------------------------------ */
838 /* Function:    frpr_short                                                  */
839 /* Returns:     void                                                        */
840 /* Parameters:  fin(I)    - pointer to packet information                   */
841 /*              min(I)    - minimum size of packet                          */
842 /*                                                                          */
843 /* Check if a packet is "short" as defined by min.                          */
844 /* ------------------------------------------------------------------------ */
845 /*ARGSUSED*/
846 static INLINE void frpr_short(fin, min)
847 fr_info_t *fin;
848 int min;
849 {
850 	fr_ip_t *fi = &fin->fin_fi;
851 	int off;
852 
853 	off = fin->fin_off;
854 	if (off == 0) {
855 		if (fin->fin_plen < fin->fin_hlen + min)
856 			fi->fi_flx |= FI_SHORT;
857 	} else if (off < min) {
858 		fi->fi_flx |= FI_SHORT;
859 	}
860 }
861 
862 
863 /* ------------------------------------------------------------------------ */
864 /* Function:    frpr_icmp                                                   */
865 /* Returns:     void                                                        */
866 /* Parameters:  fin(I) - pointer to packet information                      */
867 /*                                                                          */
868 /* IPv4 Only                                                                */
869 /* Do a sanity check on the packet for ICMP (v4).  In nearly all cases,     */
870 /* except extrememly bad packets, both type and code will be present.       */
871 /* The expected minimum size of an ICMP packet is very much dependant on    */
872 /* the type of it.                                                          */
873 /*                                                                          */
874 /* XXX - other ICMP sanity checks?                                          */
875 /* ------------------------------------------------------------------------ */
876 static INLINE void frpr_icmp(fin)
877 fr_info_t *fin;
878 {
879 	int minicmpsz = sizeof(struct icmp);
880 	icmphdr_t *icmp;
881 
882 	fr_checkv4sum(fin);
883 
884 	if (!fin->fin_off && (fin->fin_dlen > 1)) {
885 		icmp = fin->fin_dp;
886 
887 		fin->fin_data[0] = *(u_short *)icmp;
888 
889 		switch (icmp->icmp_type)
890 		{
891 		case ICMP_ECHOREPLY :
892 		case ICMP_ECHO :
893 		/* Router discovery messaes - RFC 1256 */
894 		case ICMP_ROUTERADVERT :
895 		case ICMP_ROUTERSOLICIT :
896 			minicmpsz = ICMP_MINLEN;
897 			break;
898 		/*
899 		 * type(1) + code(1) + cksum(2) + id(2) seq(2) +
900 		 * 3 * timestamp(3 * 4)
901 		 */
902 		case ICMP_TSTAMP :
903 		case ICMP_TSTAMPREPLY :
904 			minicmpsz = 20;
905 			break;
906 		/*
907 		 * type(1) + code(1) + cksum(2) + id(2) seq(2) +
908 		 * mask(4)
909 		 */
910 		case ICMP_MASKREQ :
911 		case ICMP_MASKREPLY :
912 			minicmpsz = 12;
913 			break;
914 		/*
915 		 * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+)
916 		 */
917 		case ICMP_UNREACH :
918 		case ICMP_SOURCEQUENCH :
919 		case ICMP_REDIRECT :
920 		case ICMP_TIMXCEED :
921 		case ICMP_PARAMPROB :
922 			if ((fin->fin_m != NULL) &&
923 			    (M_LEN(fin->fin_m) < fin->fin_plen)) {
924 				if (fr_coalesce(fin) != 1)
925 					return;
926 			}
927 			fin->fin_flx |= FI_ICMPERR;
928 			break;
929 		default :
930 			break;
931 		}
932 	}
933 
934 	frpr_short(fin, minicmpsz);
935 }
936 
937 
938 /* ------------------------------------------------------------------------ */
939 /* Function:    frpr_tcpcommon                                              */
940 /* Returns:     void                                                        */
941 /* Parameters:  fin(I) - pointer to packet information                      */
942 /*                                                                          */
943 /* TCP header sanity checking.  Look for bad combinations of TCP flags,     */
944 /* and make some checks with how they interact with other fields.           */
945 /* If compiled with IPFILTER_CKSUM, check to see if the TCP checksum is     */
946 /* valid and mark the packet as bad if not.                                 */
947 /* ------------------------------------------------------------------------ */
948 static INLINE void frpr_tcpcommon(fin)
949 fr_info_t *fin;
950 {
951 	int flags, tlen;
952 	tcphdr_t *tcp;
953 	fr_ip_t *fi;
954 	u_short off;
955 
956 	fi = &fin->fin_fi;
957 	fi->fi_flx |= FI_TCPUDP;
958 	off = fin->fin_off;
959 	tcp = fin->fin_dp;
960 	tlen = TCP_OFF(tcp) << 2;
961 
962 #if defined(_KERNEL) && !defined(__sgi)
963 	if ((off == 0) && (fin->fin_m != NULL) &&
964 	    (M_LEN(fin->fin_m) < tlen + fin->fin_hlen)) {
965 		if (fr_pullup(fin->fin_m, fin, tlen + fin->fin_hlen) == NULL)
966 			return;
967 	}
968 #endif
969 
970 	/*
971 	 * Use of the TCP data offset *must* result in a value that is at
972 	 * least the same size as the TCP header.
973 	 */
974 	if (tlen < sizeof(tcphdr_t))
975 		fin->fin_flx |= FI_BAD;
976 	if (!(fi->fi_flx & FI_SHORT) && !off) {
977 		flags = tcp->th_flags;
978 		fin->fin_tcpf = tcp->th_flags;
979 
980 		/*
981 		 * If the urgent flag is set, then the urgent pointer must
982 		 * also be set and vice versa.  Good TCP packets do not have
983 		 * just one of these set.
984 		 */
985 		if (((flags & TH_URG) != 0 && (tcp->th_urp == 0)) ||
986 		    ((flags & TH_URG) == 0 && (tcp->th_urp != 0))) {
987 			fin->fin_flx |= FI_BAD;
988 		} else if (((flags & (TH_SYN|TH_FIN)) != 0) &&
989 			   ((flags & TH_RST) != 0)) {
990 			fin->fin_flx |= FI_BAD;
991 		} else if (!(flags & TH_ACK)) {
992 			/*
993 			 * If the ack bit isn't set, then either the SYN or
994 			 * RST bit must be set.  If the SYN bit is set, then
995 			 * we expect the ACK field to be 0.  If the ACK is
996 			 * not set and if URG, PSH or FIN are set, consdier
997 			 * that to indicate a bad TCP packet.
998 			 */
999 			if ((flags == TH_SYN) && (tcp->th_ack != 0)) {
1000 				fin->fin_flx |= FI_BAD;
1001 			} else if (!(flags & (TH_RST|TH_SYN))) {
1002 				fin->fin_flx |= FI_BAD;
1003 			} else if ((flags & (TH_URG|TH_PUSH|TH_FIN)) != 0) {
1004 				fin->fin_flx |= FI_BAD;
1005 			}
1006 		}
1007 	}
1008 
1009 	if (!off && (fin->fin_dlen > 3)) {
1010 		fin->fin_sport = ntohs(tcp->th_sport);
1011 		fin->fin_dport = ntohs(tcp->th_dport);
1012 	}
1013 
1014 #if 0
1015 	/*
1016 	 * At this point, it's not exactly clear what is to be gained by
1017 	 * marking up which TCP options are and are not present.  The one we
1018 	 * are most interested in is the TCP window scale.  This is only in
1019 	 * a SYN packet [RFC1323] so we don't need this here...?
1020 	 * Now if we were to analyse the header for passive fingerprinting,
1021 	 * then that might add some weight to adding this...
1022 	 */
1023 	s = (u_char *)(tcp + 1);
1024 	hlen = TCP_OFF(tcp) << 2;
1025 	if (hlen == sizeof(tcphdr_t))
1026 		return;
1027 	off = IP_HL(ip) << 2;
1028 # ifdef _KERNEL
1029 	if (fin->fin_mp != NULL) {
1030 		mb_t *m = *fin->fin_mp;
1031 
1032 		if (off + hlen > M_LEN(m))
1033 			return;
1034 	}
1035 # endif
1036 	for (hlen -= (int)sizeof(*tcp); hlen > 0; ) {
1037 		opt = *s;
1038 		if (opt == '\0')
1039 			break;
1040 		else if (opt == TCPOPT_NOP)
1041 			ol = 1;
1042 		else {
1043 			if (hlen < 2)
1044 				break;
1045 			ol = (int)*(s + 1);
1046 			if (ol < 2 || ol > hlen)
1047 				break;
1048 		}
1049 
1050 		for (i = 9, mv = 4; mv >= 0; ) {
1051 			op = ipopts + i;
1052 			if (opt == (u_char)op->ol_val) {
1053 				optmsk |= op->ol_bit;
1054 				break;
1055 			}
1056 		}
1057 		hlen -= ol;
1058 		s += ol;
1059 	}
1060 #endif /* 0 */
1061 }
1062 
1063 
1064 
1065 /* ------------------------------------------------------------------------ */
1066 /* Function:    frpr_udpcommon                                              */
1067 /* Returns:     void                                                        */
1068 /* Parameters:  fin(I) - pointer to packet information                      */
1069 /*                                                                          */
1070 /* Extract the UDP source and destination ports, if present.  If compiled   */
1071 /* with IPFILTER_CKSUM, check to see if the UDP checksum is valid.          */
1072 /* ------------------------------------------------------------------------ */
1073 static INLINE void frpr_udpcommon(fin)
1074 fr_info_t *fin;
1075 {
1076 	udphdr_t *udp;
1077 	fr_ip_t *fi;
1078 
1079 	fi = &fin->fin_fi;
1080 	fi->fi_flx |= FI_TCPUDP;
1081 	udp = fin->fin_dp;
1082 
1083 	if (!fin->fin_off && (fin->fin_dlen > 3)) {
1084 		fin->fin_sport = ntohs(udp->uh_sport);
1085 		fin->fin_dport = ntohs(udp->uh_dport);
1086 	}
1087 }
1088 
1089 
1090 /* ------------------------------------------------------------------------ */
1091 /* Function:    frpr_tcp                                                    */
1092 /* Returns:     void                                                        */
1093 /* Parameters:  fin(I) - pointer to packet information                      */
1094 /*                                                                          */
1095 /* IPv4 Only                                                                */
1096 /* Analyse the packet for IPv4/TCP properties.                              */
1097 /* ------------------------------------------------------------------------ */
1098 static INLINE void frpr_tcp(fin)
1099 fr_info_t *fin;
1100 {
1101 
1102 	fr_checkv4sum(fin);
1103 
1104 	frpr_short(fin, sizeof(struct tcphdr));
1105 
1106 	frpr_tcpcommon(fin);
1107 }
1108 
1109 
1110 /* ------------------------------------------------------------------------ */
1111 /* Function:    frpr_udp                                                    */
1112 /* Returns:     void                                                        */
1113 /* Parameters:  fin(I) - pointer to packet information                      */
1114 /*                                                                          */
1115 /* IPv4 Only                                                                */
1116 /* Analyse the packet for IPv4/UDP properties.                              */
1117 /* ------------------------------------------------------------------------ */
1118 static INLINE void frpr_udp(fin)
1119 fr_info_t *fin;
1120 {
1121 
1122 	fr_checkv4sum(fin);
1123 
1124 	frpr_short(fin, sizeof(struct udphdr));
1125 
1126 	frpr_udpcommon(fin);
1127 }
1128 
1129 
1130 /* ------------------------------------------------------------------------ */
1131 /* Function:    frpr_esp                                                    */
1132 /* Returns:     void                                                        */
1133 /* Parameters:  fin(I) - pointer to packet information                      */
1134 /*                                                                          */
1135 /* Analyse the packet for ESP properties.                                   */
1136 /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits)  */
1137 /* even though the newer ESP packets must also have a sequence number that  */
1138 /* is 32bits as well, it is not possible(?) to determine the version from a */
1139 /* simple packet header.                                                    */
1140 /* ------------------------------------------------------------------------ */
1141 static INLINE void frpr_esp(fin)
1142 fr_info_t *fin;
1143 {
1144 	if (frpr_pullup(fin, 8) == -1)
1145 		return;
1146 
1147 	if (fin->fin_v == 4)
1148 		frpr_short(fin, 8);
1149 #ifdef USE_INET6
1150 	else if (fin->fin_v == 6)
1151 		frpr_short6(fin, sizeof(grehdr_t));
1152 #endif
1153 }
1154 
1155 
1156 /* ------------------------------------------------------------------------ */
1157 /* Function:    frpr_gre                                                    */
1158 /* Returns:     void                                                        */
1159 /* Parameters:  fin(I) - pointer to packet information                      */
1160 /*                                                                          */
1161 /* Analyse the packet for GRE properties.                                   */
1162 /* ------------------------------------------------------------------------ */
1163 static INLINE void frpr_gre(fin)
1164 fr_info_t *fin;
1165 {
1166 	if (frpr_pullup(fin, sizeof(grehdr_t)) == -1)
1167 		return;
1168 
1169 	if (fin->fin_v == 4)
1170 		frpr_short(fin, sizeof(grehdr_t));
1171 #ifdef USE_INET6
1172 	else if (fin->fin_v == 6)
1173 		frpr_short6(fin, sizeof(grehdr_t));
1174 #endif
1175 }
1176 
1177 
1178 /* ------------------------------------------------------------------------ */
1179 /* Function:    frpr_ipv4hdr                                                */
1180 /* Returns:     void                                                        */
1181 /* Parameters:  fin(I) - pointer to packet information                      */
1182 /*                                                                          */
1183 /* IPv4 Only                                                                */
1184 /* Analyze the IPv4 header and set fields in the fr_info_t structure.       */
1185 /* Check all options present and flag their presence if any exist.          */
1186 /* ------------------------------------------------------------------------ */
1187 static INLINE void frpr_ipv4hdr(fin)
1188 fr_info_t *fin;
1189 {
1190 	u_short optmsk = 0, secmsk = 0, auth = 0;
1191 	int hlen, ol, mv, p, i;
1192 	struct optlist *op;
1193 	u_char *s, opt;
1194 	u_short off;
1195 	fr_ip_t *fi;
1196 	ip_t *ip;
1197 
1198 	fi = &fin->fin_fi;
1199 	hlen = fin->fin_hlen;
1200 
1201 	ip = fin->fin_ip;
1202 	p = ip->ip_p;
1203 	fi->fi_p = p;
1204 	fi->fi_tos = ip->ip_tos;
1205 	fin->fin_id = ip->ip_id;
1206 	off = ip->ip_off;
1207 
1208 	/* Get both TTL and protocol */
1209 	fi->fi_p = ip->ip_p;
1210 	fi->fi_ttl = ip->ip_ttl;
1211 #if 0
1212 	(*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4));
1213 #endif
1214 
1215 	/* Zero out bits not used in IPv6 address */
1216 	fi->fi_src.i6[1] = 0;
1217 	fi->fi_src.i6[2] = 0;
1218 	fi->fi_src.i6[3] = 0;
1219 	fi->fi_dst.i6[1] = 0;
1220 	fi->fi_dst.i6[2] = 0;
1221 	fi->fi_dst.i6[3] = 0;
1222 
1223 	fi->fi_saddr = ip->ip_src.s_addr;
1224 	fi->fi_daddr = ip->ip_dst.s_addr;
1225 
1226 	/*
1227 	 * set packet attribute flags based on the offset and
1228 	 * calculate the byte offset that it represents.
1229 	 */
1230 	if ((off & IP_MF) != 0) {
1231 		fi->fi_flx |= FI_FRAG;
1232 		if (fin->fin_dlen == 0)
1233 			fi->fi_flx |= FI_BAD;
1234 	}
1235 
1236 	off &= IP_MF|IP_OFFMASK;
1237 	if (off != 0) {
1238 		fi->fi_flx |= FI_FRAG;
1239 		if ((off & IP_MF) == 0)
1240 			fi->fi_flx |= FI_FRAGTAIL;
1241 		off &= IP_OFFMASK;
1242 		if (off != 0) {
1243 			off <<= 3;
1244 			if (off + fin->fin_dlen > 0xffff)
1245 				fi->fi_flx |= FI_BAD;
1246 		}
1247 	}
1248 	fin->fin_off = off;
1249 
1250 	/*
1251 	 * Call per-protocol setup and checking
1252 	 */
1253 	switch (p)
1254 	{
1255 	case IPPROTO_UDP :
1256 		frpr_udp(fin);
1257 		break;
1258 	case IPPROTO_TCP :
1259 		frpr_tcp(fin);
1260 		break;
1261 	case IPPROTO_ICMP :
1262 		frpr_icmp(fin);
1263 		break;
1264 	}
1265 
1266 	ip = fin->fin_ip;
1267 	if (ip == NULL)
1268 		return;
1269 
1270 	/*
1271 	 * If it is a standard IP header (no options), set the flag fields
1272 	 * which relate to options to 0.
1273 	 */
1274 	if (hlen == sizeof(*ip)) {
1275 		fi->fi_optmsk = 0;
1276 		fi->fi_secmsk = 0;
1277 		fi->fi_auth = 0;
1278 		return;
1279 	}
1280 
1281 	/*
1282 	 * So the IP header has some IP options attached.  Walk the entire
1283 	 * list of options present with this packet and set flags to indicate
1284 	 * which ones are here and which ones are not.  For the somewhat out
1285 	 * of date and obscure security classification options, set a flag to
1286 	 * represent which classification is present.
1287 	 */
1288 	fi->fi_flx |= FI_OPTIONS;
1289 
1290 	for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) {
1291 		opt = *s;
1292 		if (opt == '\0')
1293 			break;
1294 		else if (opt == IPOPT_NOP)
1295 			ol = 1;
1296 		else {
1297 			if (hlen < 2)
1298 				break;
1299 			ol = (int)*(s + 1);
1300 			if (ol < 2 || ol > hlen)
1301 				break;
1302 		}
1303 		for (i = 9, mv = 4; mv >= 0; ) {
1304 			op = ipopts + i;
1305 			if ((opt == (u_char)op->ol_val) && (ol > 4)) {
1306 				optmsk |= op->ol_bit;
1307 				if (opt == IPOPT_SECURITY) {
1308 					struct optlist *sp;
1309 					u_char	sec;
1310 					int j, m;
1311 
1312 					sec = *(s + 2);	/* classification */
1313 					for (j = 3, m = 2; m >= 0; ) {
1314 						sp = secopt + j;
1315 						if (sec == sp->ol_val) {
1316 							secmsk |= sp->ol_bit;
1317 							auth = *(s + 3);
1318 							auth *= 256;
1319 							auth += *(s + 4);
1320 							break;
1321 						}
1322 						if (sec < sp->ol_val)
1323 							j -= m;
1324 						else
1325 							j += m;
1326 						m--;
1327 					}
1328 				}
1329 				break;
1330 			}
1331 			if (opt < op->ol_val)
1332 				i -= mv;
1333 			else
1334 				i += mv;
1335 			mv--;
1336 		}
1337 		hlen -= ol;
1338 		s += ol;
1339 	}
1340 
1341 	/*
1342 	 *
1343 	 */
1344 	if (auth && !(auth & 0x0100))
1345 		auth &= 0xff00;
1346 	fi->fi_optmsk = optmsk;
1347 	fi->fi_secmsk = secmsk;
1348 	fi->fi_auth = auth;
1349 }
1350 
1351 
1352 /* ------------------------------------------------------------------------ */
1353 /* Function:    fr_makefrip                                                 */
1354 /* Returns:     void                                                        */
1355 /* Parameters:  hlen(I) - length of IP packet header                        */
1356 /*              ip(I)   - pointer to the IP header                          */
1357 /*              fin(IO)  - pointer to packet information                    */
1358 /*                                                                          */
1359 /* Compact the IP header into a structure which contains just the info.     */
1360 /* which is useful for comparing IP headers with and store this information */
1361 /* in the fr_info_t structure pointer to by fin.  At present, it is assumed */
1362 /* this function will be called with either an IPv4 or IPv6 packet.         */
1363 /* ------------------------------------------------------------------------ */
1364 int	fr_makefrip(hlen, ip, fin)
1365 int hlen;
1366 ip_t *ip;
1367 fr_info_t *fin;
1368 {
1369 	int v;
1370 
1371 	fin->fin_nat = NULL;
1372 	fin->fin_state = NULL;
1373 	fin->fin_depth = 0;
1374 	fin->fin_hlen = (u_short)hlen;
1375 	fin->fin_ip = ip;
1376 	fin->fin_rule = 0xffffffff;
1377 	fin->fin_group[0] = -1;
1378 	fin->fin_group[1] = '\0';
1379 	fin->fin_dlen = fin->fin_plen - hlen;
1380 	fin->fin_dp = (char *)ip + hlen;
1381 
1382 	v = fin->fin_v;
1383 	if (v == 4)
1384 		frpr_ipv4hdr(fin);
1385 #ifdef	USE_INET6
1386 	else if (v == 6)
1387 		if (frpr_ipv6hdr(fin) == -1)
1388 			return -1;
1389 #endif
1390 	if (fin->fin_ip == NULL)
1391 		return -1;
1392 	return 0;
1393 }
1394 
1395 
1396 /* ------------------------------------------------------------------------ */
1397 /* Function:    fr_portcheck                                                */
1398 /* Returns:     int - 1 == port matched, 0 == port match failed             */
1399 /* Parameters:  frp(I) - pointer to port check `expression'                 */
1400 /*              pop(I) - pointer to port number to evaluate                 */
1401 /*                                                                          */
1402 /* Perform a comparison of a port number against some other(s), using a     */
1403 /* structure with compare information stored in it.                         */
1404 /* ------------------------------------------------------------------------ */
1405 static INLINE int fr_portcheck(frp, pop)
1406 frpcmp_t *frp;
1407 u_short *pop;
1408 {
1409 	u_short tup, po;
1410 	int err = 1;
1411 
1412 	tup = *pop;
1413 	po = frp->frp_port;
1414 
1415 	/*
1416 	 * Do opposite test to that required and continue if that succeeds.
1417 	 */
1418 	switch (frp->frp_cmp)
1419 	{
1420 	case FR_EQUAL :
1421 		if (tup != po) /* EQUAL */
1422 			err = 0;
1423 		break;
1424 	case FR_NEQUAL :
1425 		if (tup == po) /* NOTEQUAL */
1426 			err = 0;
1427 		break;
1428 	case FR_LESST :
1429 		if (tup >= po) /* LESSTHAN */
1430 			err = 0;
1431 		break;
1432 	case FR_GREATERT :
1433 		if (tup <= po) /* GREATERTHAN */
1434 			err = 0;
1435 		break;
1436 	case FR_LESSTE :
1437 		if (tup > po) /* LT or EQ */
1438 			err = 0;
1439 		break;
1440 	case FR_GREATERTE :
1441 		if (tup < po) /* GT or EQ */
1442 			err = 0;
1443 		break;
1444 	case FR_OUTRANGE :
1445 		if (tup >= po && tup <= frp->frp_top) /* Out of range */
1446 			err = 0;
1447 		break;
1448 	case FR_INRANGE :
1449 		if (tup <= po || tup >= frp->frp_top) /* In range */
1450 			err = 0;
1451 		break;
1452 	case FR_INCRANGE :
1453 		if (tup < po || tup > frp->frp_top) /* Inclusive range */
1454 			err = 0;
1455 		break;
1456 	default :
1457 		break;
1458 	}
1459 	return err;
1460 }
1461 
1462 
1463 /* ------------------------------------------------------------------------ */
1464 /* Function:    fr_tcpudpchk                                                */
1465 /* Returns:     int - 1 == protocol matched, 0 == check failed              */
1466 /* Parameters:  fin(I) - pointer to packet information                      */
1467 /*              ft(I)  - pointer to structure with comparison data          */
1468 /*                                                                          */
1469 /* Compares the current pcket (assuming it is TCP/UDP) information with a   */
1470 /* structure containing information that we want to match against.          */
1471 /* ------------------------------------------------------------------------ */
1472 int fr_tcpudpchk(fin, ft)
1473 fr_info_t *fin;
1474 frtuc_t *ft;
1475 {
1476 	int err = 1;
1477 
1478 	/*
1479 	 * Both ports should *always* be in the first fragment.
1480 	 * So far, I cannot find any cases where they can not be.
1481 	 *
1482 	 * compare destination ports
1483 	 */
1484 	if (ft->ftu_dcmp)
1485 		err = fr_portcheck(&ft->ftu_dst, &fin->fin_dport);
1486 
1487 	/*
1488 	 * compare source ports
1489 	 */
1490 	if (err && ft->ftu_scmp)
1491 		err = fr_portcheck(&ft->ftu_src, &fin->fin_sport);
1492 
1493 	/*
1494 	 * If we don't have all the TCP/UDP header, then how can we
1495 	 * expect to do any sort of match on it ?  If we were looking for
1496 	 * TCP flags, then NO match.  If not, then match (which should
1497 	 * satisfy the "short" class too).
1498 	 */
1499 	if (err && (fin->fin_p == IPPROTO_TCP)) {
1500 		if (fin->fin_flx & FI_SHORT)
1501 			return !(ft->ftu_tcpf | ft->ftu_tcpfm);
1502 		/*
1503 		 * Match the flags ?  If not, abort this match.
1504 		 */
1505 		if (ft->ftu_tcpfm &&
1506 		    ft->ftu_tcpf != (fin->fin_tcpf & ft->ftu_tcpfm)) {
1507 			FR_DEBUG(("f. %#x & %#x != %#x\n", fin->fin_tcpf,
1508 				 ft->ftu_tcpfm, ft->ftu_tcpf));
1509 			err = 0;
1510 		}
1511 	}
1512 	return err;
1513 }
1514 
1515 
1516 /* ------------------------------------------------------------------------ */
1517 /* Function:    fr_ipfcheck                                                 */
1518 /* Returns:     int - 0 == match, 1 == no match                             */
1519 /* Parameters:  fin(I)     - pointer to packet information                  */
1520 /*              fr(I)      - pointer to filter rule                         */
1521 /*              portcmp(I) - flag indicating whether to attempt matching on */
1522 /*                           TCP/UDP port data.                             */
1523 /*                                                                          */
1524 /* Check to see if a packet matches an IPFilter rule.  Checks of addresses, */
1525 /* port numbers, etc, for "standard" IPFilter rules are all orchestrated in */
1526 /* this function.                                                           */
1527 /* ------------------------------------------------------------------------ */
1528 static INLINE int fr_ipfcheck(fin, fr, portcmp)
1529 fr_info_t *fin;
1530 frentry_t *fr;
1531 int portcmp;
1532 {
1533 	u_32_t	*ld, *lm, *lip;
1534 	fripf_t *fri;
1535 	fr_ip_t *fi;
1536 	int i;
1537 
1538 	fi = &fin->fin_fi;
1539 	fri = fr->fr_ipf;
1540 	lip = (u_32_t *)fi;
1541 	lm = (u_32_t *)&fri->fri_mip;
1542 	ld = (u_32_t *)&fri->fri_ip;
1543 
1544 	/*
1545 	 * first 32 bits to check coversion:
1546 	 * IP version, TOS, TTL, protocol
1547 	 */
1548 	i = ((*lip & *lm) != *ld);
1549 	FR_DEBUG(("0. %#08x & %#08x != %#08x\n",
1550 		   *lip, *lm, *ld));
1551 	if (i)
1552 		return 1;
1553 
1554 	/*
1555 	 * Next 32 bits is a constructed bitmask indicating which IP options
1556 	 * are present (if any) in this packet.
1557 	 */
1558 	lip++, lm++, ld++;
1559 	i |= ((*lip & *lm) != *ld);
1560 	FR_DEBUG(("1. %#08x & %#08x != %#08x\n",
1561 		   *lip, *lm, *ld));
1562 	if (i)
1563 		return 1;
1564 
1565 	lip++, lm++, ld++;
1566 	/*
1567 	 * Unrolled loops (4 each, for 32 bits) for address checks.
1568 	 */
1569 	/*
1570 	 * Check the source address.
1571 	 */
1572 #ifdef	IPFILTER_LOOKUP
1573 	if (fr->fr_satype == FRI_LOOKUP) {
1574 		i = (*fr->fr_srcfunc)(fr->fr_srcptr, fi->fi_v, lip);
1575 		if (i == -1)
1576 			return 1;
1577 		lip += 3;
1578 		lm += 3;
1579 		ld += 3;
1580 	} else {
1581 #endif
1582 		i = ((*lip & *lm) != *ld);
1583 		FR_DEBUG(("2a. %#08x & %#08x != %#08x\n",
1584 			   *lip, *lm, *ld));
1585 		if (fi->fi_v == 6) {
1586 			lip++, lm++, ld++;
1587 			i |= ((*lip & *lm) != *ld);
1588 			FR_DEBUG(("2b. %#08x & %#08x != %#08x\n",
1589 				   *lip, *lm, *ld));
1590 			lip++, lm++, ld++;
1591 			i |= ((*lip & *lm) != *ld);
1592 			FR_DEBUG(("2c. %#08x & %#08x != %#08x\n",
1593 				   *lip, *lm, *ld));
1594 			lip++, lm++, ld++;
1595 			i |= ((*lip & *lm) != *ld);
1596 			FR_DEBUG(("2d. %#08x & %#08x != %#08x\n",
1597 				   *lip, *lm, *ld));
1598 		} else {
1599 			lip += 3;
1600 			lm += 3;
1601 			ld += 3;
1602 		}
1603 #ifdef	IPFILTER_LOOKUP
1604 	}
1605 #endif
1606 	i ^= (fr->fr_flags & FR_NOTSRCIP) >> 6;
1607 	if (i)
1608 		return 1;
1609 
1610 	/*
1611 	 * Check the destination address.
1612 	 */
1613 	lip++, lm++, ld++;
1614 #ifdef	IPFILTER_LOOKUP
1615 	if (fr->fr_datype == FRI_LOOKUP) {
1616 		i = (*fr->fr_dstfunc)(fr->fr_dstptr, fi->fi_v, lip);
1617 		if (i == -1)
1618 			return 1;
1619 		lip += 3;
1620 		lm += 3;
1621 		ld += 3;
1622 	} else {
1623 #endif
1624 		i = ((*lip & *lm) != *ld);
1625 		FR_DEBUG(("3a. %#08x & %#08x != %#08x\n",
1626 			   *lip, *lm, *ld));
1627 		if (fi->fi_v == 6) {
1628 			lip++, lm++, ld++;
1629 			i |= ((*lip & *lm) != *ld);
1630 			FR_DEBUG(("3b. %#08x & %#08x != %#08x\n",
1631 				   *lip, *lm, *ld));
1632 			lip++, lm++, ld++;
1633 			i |= ((*lip & *lm) != *ld);
1634 			FR_DEBUG(("3c. %#08x & %#08x != %#08x\n",
1635 				   *lip, *lm, *ld));
1636 			lip++, lm++, ld++;
1637 			i |= ((*lip & *lm) != *ld);
1638 			FR_DEBUG(("3d. %#08x & %#08x != %#08x\n",
1639 				   *lip, *lm, *ld));
1640 		} else {
1641 			lip += 3;
1642 			lm += 3;
1643 			ld += 3;
1644 		}
1645 #ifdef	IPFILTER_LOOKUP
1646 	}
1647 #endif
1648 	i ^= (fr->fr_flags & FR_NOTDSTIP) >> 7;
1649 	if (i)
1650 		return 1;
1651 	/*
1652 	 * IP addresses matched.  The next 32bits contains:
1653 	 * mast of old IP header security & authentication bits.
1654 	 */
1655 	lip++, lm++, ld++;
1656 	i |= ((*lip & *lm) != *ld);
1657 	FR_DEBUG(("4. %#08x & %#08x != %#08x\n",
1658 		   *lip, *lm, *ld));
1659 
1660 	/*
1661 	 * Next we have 32 bits of packet flags.
1662 	 */
1663 	lip++, lm++, ld++;
1664 	i |= ((*lip & *lm) != *ld);
1665 	FR_DEBUG(("5. %#08x & %#08x != %#08x\n",
1666 		   *lip, *lm, *ld));
1667 
1668 	if (i == 0) {
1669 		/*
1670 		 * If a fragment, then only the first has what we're
1671 		 * looking for here...
1672 		 */
1673 		if (portcmp) {
1674 			if (!fr_tcpudpchk(fin, &fr->fr_tuc))
1675 				i = 1;
1676 		} else {
1677 			if (fr->fr_dcmp || fr->fr_scmp ||
1678 			    fr->fr_tcpf || fr->fr_tcpfm)
1679 				i = 1;
1680 			if (fr->fr_icmpm || fr->fr_icmp) {
1681 				if (((fi->fi_p != IPPROTO_ICMP) &&
1682 				     (fi->fi_p != IPPROTO_ICMPV6)) ||
1683 				    fin->fin_off || (fin->fin_dlen < 2))
1684 					i = 1;
1685 				else if ((fin->fin_data[0] & fr->fr_icmpm) !=
1686 					 fr->fr_icmp) {
1687 					FR_DEBUG(("i. %#x & %#x != %#x\n",
1688 						 fin->fin_data[0],
1689 						 fr->fr_icmpm, fr->fr_icmp));
1690 					i = 1;
1691 				}
1692 			}
1693 		}
1694 	}
1695 	return i;
1696 }
1697 
1698 
1699 /* ------------------------------------------------------------------------ */
1700 /* Function:    fr_scanlist                                                 */
1701 /* Returns:     int - result flags of scanning filter list                  */
1702 /* Parameters:  fin(I) - pointer to packet information                      */
1703 /*              pass(I) - default result to return for filtering            */
1704 /*                                                                          */
1705 /* Check the input/output list of rules for a match to the current packet.  */
1706 /* If a match is found, the value of fr_flags from the rule becomes the     */
1707 /* return value and fin->fin_fr points to the matched rule.                 */
1708 /*                                                                          */
1709 /* This function may be called recusively upto 16 times (limit inbuilt.)    */
1710 /* When unwinding, it should finish up with fin_depth as 0.                 */
1711 /*                                                                          */
1712 /* Could be per interface, but this gets real nasty when you don't have,    */
1713 /* or can't easily change, the kernel source code to .                      */
1714 /* ------------------------------------------------------------------------ */
1715 int fr_scanlist(fin, pass)
1716 fr_info_t *fin;
1717 u_32_t pass;
1718 {
1719 	int rulen, portcmp, off, logged, skip;
1720 	struct frentry *fr, *fnext;
1721 	u_32_t passt;
1722 
1723 	/*
1724 	 * Do not allow nesting deeper than 16 levels.
1725 	 */
1726 	if (fin->fin_depth >= 16)
1727 		return pass;
1728 
1729 	fr = fin->fin_fr;
1730 
1731 	/*
1732 	 * If there are no rules in this list, return now.
1733 	 */
1734 	if (fr == NULL)
1735 		return pass;
1736 
1737 	skip = 0;
1738 	logged = 0;
1739 	portcmp = 0;
1740 	fin->fin_depth++;
1741 	fin->fin_fr = NULL;
1742 	off = fin->fin_off;
1743 
1744 	if ((fin->fin_flx & FI_TCPUDP) && (fin->fin_dlen > 3) && !off)
1745 		portcmp = 1;
1746 
1747 	for (rulen = 0; fr; fr = fnext, rulen++) {
1748 		fnext = fr->fr_next;
1749 		if (skip != 0) {
1750 			FR_VERBOSE(("%d (%#x)\n", skip, fr->fr_flags));
1751 			skip--;
1752 			continue;
1753 		}
1754 
1755 		/*
1756 		 * In all checks below, a null (zero) value in the
1757 		 * filter struture is taken to mean a wildcard.
1758 		 *
1759 		 * check that we are working for the right interface
1760 		 */
1761 #ifdef	_KERNEL
1762 		if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp)
1763 			continue;
1764 #else
1765 		if (opts & (OPT_VERBOSE|OPT_DEBUG))
1766 			printf("\n");
1767 		FR_VERBOSE(("%c", FR_ISSKIP(pass) ? 's' :
1768 				  FR_ISPASS(pass) ? 'p' :
1769 				  FR_ISACCOUNT(pass) ? 'A' :
1770 				  FR_ISAUTH(pass) ? 'a' :
1771 				  (pass & FR_NOMATCH) ? 'n' :'b'));
1772 		if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp)
1773 			continue;
1774 		FR_VERBOSE((":i"));
1775 #endif
1776 
1777 		switch (fr->fr_type)
1778 		{
1779 		case FR_T_IPF :
1780 		case FR_T_IPF|FR_T_BUILTIN :
1781 			if (fr_ipfcheck(fin, fr, portcmp))
1782 				continue;
1783 			break;
1784 #if defined(IPFILTER_BPF) && defined(_KERNEL)
1785 		case FR_T_BPFOPC :
1786 		case FR_T_BPFOPC|FR_T_BUILTIN :
1787 		    {
1788 			u_char *mc;
1789 			int wlen;
1790 
1791 			if (*fin->fin_mp == NULL)
1792 				continue;
1793 			if (fin->fin_v != fr->fr_v)
1794 				continue;
1795 			mc = (u_char *)fin->fin_m;
1796 			wlen = fin->fin_dlen + fin->fin_hlen;
1797 			if (!bpf_filter(fr->fr_data, mc, wlen, 0))
1798 				continue;
1799 			break;
1800 		    }
1801 #endif
1802 		case FR_T_CALLFUNC|FR_T_BUILTIN :
1803 		    {
1804 			frentry_t *f;
1805 
1806 			f = (*fr->fr_func)(fin, &pass);
1807 			if (f != NULL)
1808 				fr = f;
1809 			else
1810 				continue;
1811 			break;
1812 		    }
1813 		default :
1814 			break;
1815 		}
1816 
1817 		if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) {
1818 			if (fin->fin_nattag == NULL)
1819 				continue;
1820 			if (fr_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0)
1821 				continue;
1822 		}
1823 		FR_VERBOSE(("=%s.%d *", fr->fr_group, rulen));
1824 
1825 		passt = fr->fr_flags;
1826 
1827 		/*
1828 		 * If the rule is a "call now" rule, then call the function
1829 		 * in the rule, if it exists and use the results from that.
1830 		 * If the function pointer is bad, just make like we ignore
1831 		 * it, except for increasing the hit counter.
1832 		 */
1833 		if ((passt & FR_CALLNOW) != 0) {
1834 			ATOMIC_INCL(fr->fr_hits);
1835 			if ((fr->fr_func != NULL) &&
1836 			    (fr->fr_func != (ipfunc_t)-1)) {
1837 				frentry_t *frs;
1838 
1839 				frs = fin->fin_fr;
1840 				fin->fin_fr = fr;
1841 				fr = (*fr->fr_func)(fin, &passt);
1842 				if (fr == NULL) {
1843 					fin->fin_fr = frs;
1844 					continue;
1845 				}
1846 				passt = fr->fr_flags;
1847 				fin->fin_fr = fr;
1848 			}
1849 		} else {
1850 			fin->fin_fr = fr;
1851 		}
1852 
1853 #ifdef  IPFILTER_LOG
1854 		/*
1855 		 * Just log this packet...
1856 		 */
1857 		if ((passt & FR_LOGMASK) == FR_LOG) {
1858 			if (ipflog(fin, passt) == -1) {
1859 				if (passt & FR_LOGORBLOCK) {
1860 					passt &= ~FR_CMDMASK;
1861 					passt |= FR_BLOCK|FR_QUICK;
1862 				}
1863 				ATOMIC_INCL(frstats[fin->fin_out].fr_skip);
1864 			}
1865 			ATOMIC_INCL(frstats[fin->fin_out].fr_pkl);
1866 			logged = 1;
1867 		}
1868 #endif /* IPFILTER_LOG */
1869 		fr->fr_bytes += (U_QUAD_T)fin->fin_plen;
1870 		if (FR_ISSKIP(passt))
1871 			skip = fr->fr_arg;
1872 		else if ((passt & FR_LOGMASK) != FR_LOG)
1873 			pass = passt;
1874 		if (passt & (FR_RETICMP|FR_FAKEICMP))
1875 			fin->fin_icode = fr->fr_icode;
1876 		FR_DEBUG(("pass %#x\n", pass));
1877 		ATOMIC_INCL(fr->fr_hits);
1878 		fin->fin_rule = rulen;
1879 		(void) strncpy(fin->fin_group, fr->fr_group, FR_GROUPLEN);
1880 		if (fr->fr_grp != NULL) {
1881 			fin->fin_fr = *fr->fr_grp;
1882 			pass = fr_scanlist(fin, pass);
1883 			if (fin->fin_fr == NULL) {
1884 				fin->fin_rule = rulen;
1885 				(void) strncpy(fin->fin_group, fr->fr_group,
1886 					       FR_GROUPLEN);
1887 				fin->fin_fr = fr;
1888 			}
1889 			if (fin->fin_flx & FI_DONTCACHE)
1890 				logged = 1;
1891 		}
1892 		if (pass & FR_QUICK)
1893 			break;
1894 	}
1895 	if (logged)
1896 		fin->fin_flx |= FI_DONTCACHE;
1897 	fin->fin_depth--;
1898 	return pass;
1899 }
1900 
1901 
1902 /* ------------------------------------------------------------------------ */
1903 /* Function:    fr_acctpkt                                                  */
1904 /* Returns:     frentry_t* - always returns NULL                            */
1905 /* Parameters:  fin(I) - pointer to packet information                      */
1906 /*              passp(IO) - pointer to current/new filter decision (unused) */
1907 /*                                                                          */
1908 /* Checks a packet against accounting rules, if there are any for the given */
1909 /* IP protocol version.                                                     */
1910 /*                                                                          */
1911 /* N.B.: this function returns NULL to match the prototype used by other    */
1912 /* functions called from the IPFilter "mainline" in fr_check().             */
1913 /* ------------------------------------------------------------------------ */
1914 frentry_t *fr_acctpkt(fin, passp)
1915 fr_info_t *fin;
1916 u_32_t *passp;
1917 {
1918 	char group[FR_GROUPLEN];
1919 	frentry_t *fr, *frsave;
1920 	u_32_t pass, rulen;
1921 
1922 	passp = passp;
1923 #ifdef	USE_INET6
1924 	if (fin->fin_v == 6)
1925 		fr = ipacct6[fin->fin_out][fr_active];
1926 	else
1927 #endif
1928 		fr = ipacct[fin->fin_out][fr_active];
1929 
1930 	if (fr != NULL) {
1931 		frsave = fin->fin_fr;
1932 		bcopy(fin->fin_group, group, FR_GROUPLEN);
1933 		rulen = fin->fin_rule;
1934 		fin->fin_fr = fr;
1935 		pass = fr_scanlist(fin, FR_NOMATCH);
1936 		if (FR_ISACCOUNT(pass)) {
1937 			ATOMIC_INCL(frstats[0].fr_acct);
1938 		}
1939 		fin->fin_fr = frsave;
1940 		bcopy(group, fin->fin_group, FR_GROUPLEN);
1941 		fin->fin_rule = rulen;
1942 	}
1943 	return NULL;
1944 }
1945 
1946 
1947 /* ------------------------------------------------------------------------ */
1948 /* Function:    fr_firewall                                                 */
1949 /* Returns:     frentry_t* - returns pointer to matched rule, if no matches */
1950 /*                           were found, returns NULL.                      */
1951 /* Parameters:  fin(I) - pointer to packet information                      */
1952 /*              passp(IO) - pointer to current/new filter decision (unused) */
1953 /*                                                                          */
1954 /* Applies an appropriate set of firewall rules to the packet, to see if    */
1955 /* there are any matches.  The first check is to see if a match can be seen */
1956 /* in the cache.  If not, then search an appropriate list of rules.  Once a */
1957 /* matching rule is found, take any appropriate actions as defined by the   */
1958 /* rule - except logging.                                                   */
1959 /* ------------------------------------------------------------------------ */
1960 static frentry_t *fr_firewall(fin, passp)
1961 fr_info_t *fin;
1962 u_32_t *passp;
1963 {
1964 	frentry_t *fr;
1965 	fr_info_t *fc;
1966 	u_32_t pass;
1967 	int out;
1968 
1969 	out = fin->fin_out;
1970 	pass = *passp;
1971 
1972 	/*
1973 	 * If a packet is found in the auth table, then skip checking
1974 	 * the access lists for permission but we do need to consider
1975 	 * the result as if it were from the ACL's.
1976 	 */
1977 	fc = &frcache[out][CACHE_HASH(fin)];
1978 	if (!bcmp((char *)fin, (char *)fc, FI_CSIZE)) {
1979 		/*
1980 		 * copy cached data so we can unlock the mutex
1981 		 * earlier.
1982 		 */
1983 		bcopy((char *)fc, (char *)fin, FI_COPYSIZE);
1984 		ATOMIC_INCL(frstats[out].fr_chit);
1985 		if ((fr = fin->fin_fr) != NULL) {
1986 			ATOMIC_INCL(fr->fr_hits);
1987 			pass = fr->fr_flags;
1988 		}
1989 	} else {
1990 #ifdef	USE_INET6
1991 		if (fin->fin_v == 6)
1992 			fin->fin_fr = ipfilter6[out][fr_active];
1993 		else
1994 #endif
1995 			fin->fin_fr = ipfilter[out][fr_active];
1996 		if (fin->fin_fr != NULL)
1997 			pass = fr_scanlist(fin, fr_pass);
1998 		if (((pass & FR_KEEPSTATE) == 0) &&
1999 		    ((fin->fin_flx & FI_DONTCACHE) == 0))
2000 			bcopy((char *)fin, (char *)fc, FI_COPYSIZE);
2001 		if ((pass & FR_NOMATCH)) {
2002 			ATOMIC_INCL(frstats[out].fr_nom);
2003 		}
2004 		fr = fin->fin_fr;
2005 	}
2006 
2007 	/*
2008 	 * Apply packets per second rate-limiting to a rule as required.
2009 	 */
2010 	if ((fr != NULL) && (fr->fr_pps != 0) &&
2011 	    !ppsratecheck(&fr->fr_lastpkt, &fr->fr_curpps, fr->fr_pps)) {
2012 		pass &= ~(FR_CMDMASK|FR_DUP|FR_RETICMP|FR_RETRST);
2013 		pass |= FR_BLOCK;
2014 		ATOMIC_INCL(frstats[out].fr_ppshit);
2015 	}
2016 
2017 	/*
2018 	 * If we fail to add a packet to the authorization queue, then we
2019 	 * drop the packet later.  However, if it was added then pretend
2020 	 * we've dropped it already.
2021 	 */
2022 	if (FR_ISAUTH(pass)) {
2023 		if (fr_newauth(fin->fin_m, fin) != 0) {
2024 #ifdef	_KERNEL
2025 			fin->fin_m = *fin->fin_mp = NULL;
2026 #else
2027 			;
2028 #endif
2029 			fin->fin_error = 0;
2030 		} else
2031 			fin->fin_error = ENOSPC;
2032 	}
2033 
2034 	if ((fr != NULL) && (fr->fr_func != NULL) &&
2035 	    (fr->fr_func != (ipfunc_t)-1) && !(pass & FR_CALLNOW))
2036 		(void) (*fr->fr_func)(fin, &pass);
2037 
2038 	/*
2039 	 * If a rule is a pre-auth rule, check again in the list of rules
2040 	 * loaded for authenticated use.  It does not particulary matter
2041 	 * if this search fails because a "preauth" result, from a rule,
2042 	 * is treated as "not a pass", hence the packet is blocked.
2043 	 */
2044 	if (FR_ISPREAUTH(pass)) {
2045 		if ((fin->fin_fr = ipauth) != NULL)
2046 			pass = fr_scanlist(fin, fr_pass);
2047 	}
2048 
2049 	/*
2050 	 * If the rule has "keep frag" and the packet is actually a fragment,
2051 	 * then create a fragment state entry.
2052 	 */
2053 	if ((pass & (FR_KEEPFRAG|FR_KEEPSTATE)) == FR_KEEPFRAG) {
2054 		if (fin->fin_flx & FI_FRAG) {
2055 			if (fr_newfrag(fin, pass) == -1) {
2056 				ATOMIC_INCL(frstats[out].fr_bnfr);
2057 			} else {
2058 				ATOMIC_INCL(frstats[out].fr_nfr);
2059 			}
2060 		} else {
2061 			ATOMIC_INCL(frstats[out].fr_cfr);
2062 		}
2063 	}
2064 
2065 	/*
2066 	 * Finally, if we've asked to track state for this packet, set it up.
2067 	 */
2068 	if (pass & FR_KEEPSTATE) {
2069 		if (fr_addstate(fin, NULL, 0) != NULL) {
2070 			ATOMIC_INCL(frstats[out].fr_ads);
2071 		} else {
2072 			ATOMIC_INCL(frstats[out].fr_bads);
2073 		}
2074 	}
2075 
2076 	fr = fin->fin_fr;
2077 
2078 	if (passp != NULL)
2079 		*passp = pass;
2080 
2081 	return fr;
2082 }
2083 
2084 
2085 /* ------------------------------------------------------------------------ */
2086 /* Function:    fr_check                                                    */
2087 /* Returns:     int -  0 == packet allowed through,                         */
2088 /*              User space:                                                 */
2089 /*                    -1 == packet blocked                                  */
2090 /*                     1 == packet not matched                              */
2091 /*                    -2 == requires authantication                         */
2092 /*              Kernel:                                                     */
2093 /*                   > 0 == filter error # for packet                       */
2094 /* Parameters: ip(I)   - pointer to start of IPv4/6 packet                  */
2095 /*             hlen(I) - length of header                                   */
2096 /*             ifp(I)  - pointer to interface this packet is on             */
2097 /*             out(I)  - 0 == packet going in, 1 == packet going out        */
2098 /*             mp(IO)  - pointer to caller's buffer pointer that holds this */
2099 /*                       IP packet.                                         */
2100 /* Solaris & HP-UX ONLY :                                                   */
2101 /*             qif(I)  - pointer to STREAMS queue information for this      */
2102 /*                       interface & direction.                             */
2103 /*                                                                          */
2104 /* fr_check() is the master function for all IPFilter packet processing.    */
2105 /* It orchestrates: Network Address Translation (NAT), checking for packet  */
2106 /* authorisation (or pre-authorisation), presence of related state info.,   */
2107 /* generating log entries, IP packet accounting, routing of packets as      */
2108 /* directed by firewall rules and of course whether or not to allow the     */
2109 /* packet to be further processed by the kernel.                            */
2110 /*                                                                          */
2111 /* For packets blocked, the contents of "mp" will be NULL'd and the buffer  */
2112 /* freed.  Packets passed may be returned with the pointer pointed to by    */
2113 /* by "mp" changed to a new buffer.                                         */
2114 /* ------------------------------------------------------------------------ */
2115 int fr_check(ip, hlen, ifp, out
2116 #if defined(_KERNEL) && defined(MENTAT)
2117 , qif, mp)
2118 qif_t *qif;
2119 #else
2120 , mp)
2121 #endif
2122 mb_t **mp;
2123 ip_t *ip;
2124 int hlen;
2125 void *ifp;
2126 int out;
2127 {
2128 	/*
2129 	 * The above really sucks, but short of writing a diff
2130 	 */
2131 	fr_info_t frinfo;
2132 	fr_info_t *fin = &frinfo;
2133 	int v = IP_V(ip), len, p;
2134 	frentry_t *fr = NULL;
2135 	mb_t *mc = NULL;
2136 	u_32_t pass;
2137 	mb_t *m;
2138 #ifdef USE_INET6
2139 	ip6_t *ip6;
2140 #endif
2141 
2142 	/*
2143 	 * The first part of fr_check() deals with making sure that what goes
2144 	 * into the filtering engine makes some sense.  Information about the
2145 	 * the packet is distilled, collected into a fr_info_t structure and
2146 	 * the an attempt to ensure the buffer the packet is in is big enough
2147 	 * to hold all the required packet headers.
2148 	 */
2149 #ifdef	_KERNEL
2150 # ifdef __sgi
2151 	char hbuf[MAX_IPV4HDR];
2152 # endif
2153 # ifdef MENTAT
2154 	if ((uintptr_t)ip & 0x3)
2155 		return 2;
2156 # endif
2157 	READ_ENTER(&ipf_global);
2158 
2159 	if (fr_running <= 0) {
2160 		RWLOCK_EXIT(&ipf_global);
2161 		return 0;
2162 	}
2163 
2164 	bzero((char *)fin, sizeof(*fin));
2165 
2166 # ifdef MENTAT
2167 	if (qif->qf_flags & QF_GROUP)
2168 		fin->fin_flx |= FI_MBCAST;
2169 	m = qif->qf_m;
2170 	fin->fin_qfm = m;
2171 	fin->fin_qif = qif;
2172 # else /* MENTAT */
2173 
2174 	m = *mp;
2175 #  if defined(M_MCAST)
2176 	if ((m->m_flags & M_MCAST) != 0)
2177 		fin->fin_flx |= FI_MBCAST|FI_MULTICAST;
2178 #  endif
2179 #  if defined(M_BCAST)
2180 	if ((m->m_flags & M_BCAST) != 0)
2181 		fin->fin_flx |= FI_MBCAST|FI_BROADCAST;
2182 #  endif
2183 #  ifdef M_CANFASTFWD
2184 	/*
2185 	 * XXX For now, IP Filter and fast-forwarding of cached flows
2186 	 * XXX are mutually exclusive.  Eventually, IP Filter should
2187 	 * XXX get a "can-fast-forward" filter rule.
2188 	 */
2189 	m->m_flags &= ~M_CANFASTFWD;
2190 #  endif /* M_CANFASTFWD */
2191 #  ifdef CSUM_DELAY_DATA
2192 	/*
2193 	 * disable delayed checksums.
2194 	 */
2195 	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
2196 		in_delayed_cksum(m);
2197 		m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
2198 	}
2199 #  endif /* CSUM_DELAY_DATA */
2200 # endif /* MENTAT */
2201 #else /* _KERNEL */
2202 	READ_ENTER(&ipf_global);
2203 
2204 	bzero((char *)fin, sizeof(*fin));
2205 	m = *mp;
2206 #endif /* _KERNEL */
2207 
2208 #ifdef	USE_INET6
2209 	if (v == 6) {
2210 		len = ntohs(((ip6_t*)ip)->ip6_plen);
2211 		/*
2212 		 * Jumbo grams are quite likely too big for internal buffer
2213 		 * structures to handle comfortably, for now, so just drop
2214 		 * them for now.
2215 		 */
2216 		if (len == 0) {
2217 			pass = FR_BLOCK|FR_NOMATCH;
2218 			goto filtered;
2219 		}
2220 		len += sizeof(ip6_t);
2221 		p = ((ip6_t *)ip)->ip6_nxt;
2222 	} else
2223 #endif
2224 	{
2225 		p = ip->ip_p;
2226 		len = ip->ip_len;
2227 	}
2228 
2229 	fin->fin_v = v;
2230 	fin->fin_m = m;
2231 	fin->fin_mp = mp;
2232 	fin->fin_out = out;
2233 	fin->fin_ifp = ifp;
2234 	fin->fin_plen = len;
2235 	fin->fin_hlen = (u_short )hlen;
2236 	fin->fin_dp = (char *)ip + hlen;
2237 
2238 	if (p == IPPROTO_TCP || p == IPPROTO_UDP ||
2239 	    (v == 4 && p == IPPROTO_ICMP)
2240 #ifdef USE_INET6
2241 	    || (v == 6 && p == IPPROTO_ICMPV6)
2242 #endif
2243 	    ) {
2244 #if defined(_KERNEL)
2245 		int plen = 0, up = 0;
2246 
2247 		if ((v == 6) || (ip->ip_off & IP_OFFMASK) == 0)
2248 			switch(p)
2249 			{
2250 			case IPPROTO_TCP:
2251 				plen = sizeof(tcphdr_t);
2252 				break;
2253 			case IPPROTO_UDP:
2254 				plen = sizeof(udphdr_t);
2255 				break;
2256 			/* 96 - enough for complete ICMP error IP header */
2257 			case IPPROTO_ICMP:
2258 				plen = ICMPERR_MAXPKTLEN - sizeof(ip_t);
2259 				break;
2260 # ifdef USE_INET6
2261 			case IPPROTO_ICMPV6 :
2262 				/*
2263 				 * XXX does not take intermediate header
2264 				 * into account.
2265 				 */
2266 				plen = ICMP6ERR_MINPKTLEN + 8 - sizeof(ip6_t);
2267 				break;
2268 # endif
2269 			}
2270 
2271 		up = MIN(hlen + plen, len);
2272 		if (up > M_LEN(m)) {
2273 # ifdef __sgi
2274 	/* Under IRIX, avoid m_pullup as it makes ping <hostname> panic */
2275 			if ((up > sizeof(hbuf)) || (m_length(m) < up)) {
2276 				ATOMIC_INCL(frstats[out].fr_pull[1]);
2277 				pass = FR_BLOCK|FR_NOMATCH;
2278 				goto filtered;
2279 			}
2280 			m_copydata(m, 0, up, hbuf);
2281 			ATOMIC_INCL(frstats[out].fr_pull[0]);
2282 			ip = (ip_t *)hbuf;
2283 # else /* __ sgi */
2284 			/*
2285 			 * Having determined that we need to pullup some data,
2286 			 * try to bring as much of the packet up into a single
2287 			 * buffer with the first pullup.  This hopefully means
2288 			 * less need for doing futher pullups.  Not needed for
2289 			 * Solaris because fr_precheck() does it anyway.
2290 			 *
2291 			 * The main potential for trouble here is if MLEN/MHLEN
2292 			 * become quite small, lets say < 64 bytes...but if
2293 			 * that did happen, BSD networking as a whole would be
2294 			 * slow/inefficient.
2295 			 */
2296 #  ifdef MHLEN
2297 			/*
2298 			 * Assume that M_PKTHDR is set and just work with what
2299 			 * is left rather than check..  Should not make any
2300 			 * real difference, anyway.
2301 			 */
2302 			if ((MHLEN > up) && (len > up))
2303 				up = MIN(len, MHLEN);
2304 #  else
2305 #   ifdef MLEN
2306 			if ((MLEN > up) && (len > up))
2307 				up = MIN(len, MLEN);
2308 #   endif /* MLEN */
2309 #  endif /* MHLEN */
2310 			fin->fin_ip = ip;
2311 			ip = fr_pullup(m, fin, up);
2312 			if (ip == NULL)
2313 				goto finished;
2314 # endif /* __sgi */
2315 		}
2316 #else
2317 		/*EMPTY*/
2318 #endif /* _KERNEL */
2319 	}
2320 
2321 	fin->fin_error = fr_unreach;
2322 	if (fr_makefrip(hlen, ip, fin) == -1) {
2323 		READ_ENTER(&ipf_mutex);
2324 		pass = FR_BLOCK;
2325 		goto filtered;
2326 	}
2327 	ip = fin->fin_ip;
2328 
2329 	if (v == 6) {
2330 		ATOMIC_INCL(frstats[out].fr_ipv6);
2331 	}
2332 
2333 	/*
2334 	 * For at least IPv6 packets, if a m_pullup() fails then this pointer
2335 	 * becomes NULL and so we have no packet to free.
2336 	 */
2337 	if (*fin->fin_mp == NULL)
2338 		goto finished;
2339 
2340 	if (!out) {
2341 		if (v == 4) {
2342 #ifdef _KERNEL
2343 			if (fr_chksrc && !fr_verifysrc(fin)) {
2344 				ATOMIC_INCL(frstats[0].fr_badsrc);
2345 				fin->fin_flx |= FI_BADSRC;
2346 			}
2347 #endif
2348 			if (ip->ip_ttl < fr_minttl) {
2349 				ATOMIC_INCL(frstats[0].fr_badttl);
2350 				fin->fin_flx |= FI_LOWTTL;
2351 			}
2352 		}
2353 #ifdef USE_INET6
2354 		else  if (v == 6) {
2355 			ip6 = (ip6_t *)ip;
2356 #ifdef _KERNEL
2357 			if (fr_chksrc && !fr_verifysrc(fin)) {
2358 				ATOMIC_INCL(frstats[0].fr_badsrc);
2359 				fin->fin_flx |= FI_BADSRC;
2360 			}
2361 #endif
2362 			if (ip6->ip6_hlim < fr_minttl) {
2363 				ATOMIC_INCL(frstats[0].fr_badttl);
2364 				fin->fin_flx |= FI_LOWTTL;
2365 			}
2366 		}
2367 #endif
2368 	}
2369 
2370 	if (fin->fin_flx & FI_SHORT) {
2371 		ATOMIC_INCL(frstats[out].fr_short);
2372 	}
2373 
2374 	pass = fr_pass;
2375 
2376 	READ_ENTER(&ipf_mutex);
2377 
2378 	/*
2379 	 * Check auth now.  This, combined with the check below to see if apass
2380 	 * is 0 is to ensure that we don't count the packet twice, which can
2381 	 * otherwise occur when we reprocess it.  As it is, we only count it
2382 	 * after it has no auth. table matchup.  This also stops NAT from
2383 	 * occuring until after the packet has been auth'd.
2384 	 */
2385 	fr = fr_checkauth(fin, &pass);
2386 	if (!out)
2387 		(void) fr_checknatin(fin, &pass);
2388 	if (!out)
2389 		(void) fr_acctpkt(fin, NULL);
2390 
2391 	if (fr == NULL)
2392 		if ((fin->fin_flx & (FI_FRAG|FI_BAD)) == FI_FRAG)
2393 			fr = fr_knownfrag(fin, &pass);
2394 	if (fr == NULL)
2395 		fr = fr_checkstate(fin, &pass);
2396 
2397 	if ((pass & FR_NOMATCH) || (fr == NULL))
2398 		fr = fr_firewall(fin, &pass);
2399 
2400 	fin->fin_fr = fr;
2401 
2402 	/*
2403 	 * Only count/translate packets which will be passed on, out the
2404 	 * interface.
2405 	 */
2406 	if (out && FR_ISPASS(pass)) {
2407 		(void) fr_acctpkt(fin, NULL);
2408 		(void) fr_checknatout(fin, &pass);
2409 
2410 		if ((fr_update_ipid != 0) && (v == 4)) {
2411 			if (fr_updateipid(fin) == -1) {
2412 				ATOMIC_INCL(frstats[1].fr_ipud);
2413 				pass &= ~FR_CMDMASK;
2414 				pass |= FR_BLOCK;
2415 				FREE_MB_T(*mp);
2416 				m = *mp = NULL;
2417 			} else {
2418 				ATOMIC_INCL(frstats[0].fr_ipud);
2419 			}
2420 		}
2421 	}
2422 
2423 #ifdef	IPFILTER_LOG
2424 	if ((fr_flags & FF_LOGGING) || (pass & FR_LOGMASK))
2425 		(void) fr_dolog(fin, &pass);
2426 #endif
2427 
2428 	if (fin->fin_state != NULL)
2429 		fr_statederef(fin, (ipstate_t **)&fin->fin_state);
2430 
2431 	if (fin->fin_nat != NULL)
2432 		fr_natderef((nat_t **)&fin->fin_nat);
2433 
2434 	/*
2435 	 * Only allow FR_DUP to work if a rule matched - it makes no sense to
2436 	 * set FR_DUP as a "default" as there are no instructions about where
2437 	 * to send the packet.
2438 	 */
2439 	if ((fr != NULL) && (pass & FR_DUP)) {
2440 		mc = M_DUPLICATE(m);
2441 	}
2442 
2443 	if (pass & (FR_RETRST|FR_RETICMP)) {
2444 		/*
2445 		 * Should we return an ICMP packet to indicate error
2446 		 * status passing through the packet filter ?
2447 		 * WARNING: ICMP error packets AND TCP RST packets should
2448 		 * ONLY be sent in repsonse to incoming packets.  Sending them
2449 		 * in response to outbound packets can result in a panic on
2450 		 * some operating systems.
2451 		 */
2452 		if (!out) {
2453 			if (pass & FR_RETICMP) {
2454 				int dst;
2455 
2456 				if ((pass & FR_RETMASK) == FR_FAKEICMP)
2457 					dst = 1;
2458 				else
2459 					dst = 0;
2460 				(void) fr_send_icmp_err(ICMP_UNREACH, fin, dst);
2461 				ATOMIC_INCL(frstats[0].fr_ret);
2462 			} else if (((pass & FR_RETMASK) == FR_RETRST) &&
2463 				   !(fin->fin_flx & FI_SHORT)) {
2464 				if (fr_send_reset(fin) == 0) {
2465 					ATOMIC_INCL(frstats[1].fr_ret);
2466 				}
2467 			}
2468 		} else {
2469 			if (pass & FR_RETRST)
2470 				fin->fin_error = ECONNRESET;
2471 		}
2472 	}
2473 
2474 	/*
2475 	 * If we didn't drop off the bottom of the list of rules (and thus
2476 	 * the 'current' rule fr is not NULL), then we may have some extra
2477 	 * instructions about what to do with a packet.
2478 	 * Once we're finished return to our caller, freeing the packet if
2479 	 * we are dropping it (* BSD ONLY *).
2480 	 */
2481 #if defined(USE_INET6) || (defined(__sgi) && defined(_KERNEL))
2482 filtered:
2483 #endif
2484 	if (FR_ISPASS(pass)) {
2485 		ATOMIC_INCL(frstats[out].fr_pass);
2486 	} else if (FR_ISBLOCK(pass)) {
2487 		ATOMIC_INCL(frstats[out].fr_block);
2488 	}
2489 
2490 	if (fr != NULL) {
2491 		frdest_t *fdp;
2492 
2493 		fdp = &fr->fr_tifs[fin->fin_rev];
2494 
2495 		if (!out && (pass & FR_FASTROUTE)) {
2496 			/*
2497 			 * For fastroute rule, no destioation interface defined
2498 			 * so pass NULL as the frdest_t parameter
2499 			 */
2500 			(void) fr_fastroute(m, mp, fin, NULL);
2501 			m = *mp = NULL;
2502 		} else if ((fdp->fd_ifp != NULL) &&
2503 			   (fdp->fd_ifp != (struct ifnet *)-1)) {
2504 			/* this is for to rules: */
2505 			(void) fr_fastroute(m, mp, fin, fdp);
2506 			m = *mp = NULL;
2507 		}
2508 
2509 		/*
2510 		 * Generate a duplicated packet.
2511 		 */
2512 		if (mc != NULL)
2513 			(void) fr_fastroute(mc, &mc, fin, &fr->fr_dif);
2514 	}
2515 
2516 	/*
2517 	 * This late because the likes of fr_fastroute() use fin_fr.
2518 	 */
2519 	RWLOCK_EXIT(&ipf_mutex);
2520 
2521 	if (!FR_ISPASS(pass)) {
2522 		if (m != NULL) {
2523 			FREE_MB_T(*mp);
2524 			m = *mp = NULL;
2525 		}
2526 	}
2527 #if defined(_KERNEL) && defined(__sgi)
2528 	else {
2529 		if ((fin->fin_flx & FI_NATED) && up && (m != NULL)) {
2530 			COPYBACK(m, 0, up, hbuf);
2531 		}
2532 	}
2533 #endif
2534 finished:
2535 	RWLOCK_EXIT(&ipf_global);
2536 #ifdef _KERNEL
2537 	return (FR_ISPASS(pass)) ? 0 : fin->fin_error;
2538 #else /* _KERNEL */
2539 	if ((pass & FR_NOMATCH) != 0)
2540 		return 1;
2541 
2542 	if ((pass & FR_RETMASK) != 0)
2543 		switch (pass & FR_RETMASK)
2544 		{
2545 		case FR_RETRST :
2546 			return 3;
2547 		case FR_RETICMP :
2548 			return 4;
2549 		case FR_FAKEICMP :
2550 			return 5;
2551 		}
2552 
2553 	switch (pass & FR_CMDMASK)
2554 	{
2555 	case FR_PASS :
2556 		return 0;
2557 	case FR_BLOCK :
2558 		return -1;
2559 	case FR_AUTH :
2560 		return -2;
2561 	case FR_ACCOUNT :
2562 		return -3;
2563 	case FR_PREAUTH :
2564 		return -4;
2565 	}
2566 	return 2;
2567 #endif /* _KERNEL */
2568 }
2569 
2570 
2571 #ifdef	IPFILTER_LOG
2572 /* ------------------------------------------------------------------------ */
2573 /* Function:    fr_dolog                                                    */
2574 /* Returns:     frentry_t* - returns contents of fin_fr (no change made)    */
2575 /* Parameters:  fin(I) - pointer to packet information                      */
2576 /*              passp(IO) - pointer to current/new filter decision (unused) */
2577 /*                                                                          */
2578 /* Checks flags set to see how a packet should be logged, if it is to be    */
2579 /* logged.  Adjust statistics based on its success or not.                  */
2580 /* ------------------------------------------------------------------------ */
2581 frentry_t *fr_dolog(fin, passp)
2582 fr_info_t *fin;
2583 u_32_t *passp;
2584 {
2585 	u_32_t pass;
2586 	int out;
2587 
2588 	out = fin->fin_out;
2589 	pass = *passp;
2590 
2591 	if ((fr_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) {
2592 		pass |= FF_LOGNOMATCH;
2593 		ATOMIC_INCL(frstats[out].fr_npkl);
2594 		goto logit;
2595 	} else if (((pass & FR_LOGMASK) == FR_LOGP) ||
2596 	    (FR_ISPASS(pass) && (fr_flags & FF_LOGPASS))) {
2597 		if ((pass & FR_LOGMASK) != FR_LOGP)
2598 			pass |= FF_LOGPASS;
2599 		ATOMIC_INCL(frstats[out].fr_ppkl);
2600 		goto logit;
2601 	} else if (((pass & FR_LOGMASK) == FR_LOGB) ||
2602 		   (FR_ISBLOCK(pass) && (fr_flags & FF_LOGBLOCK))) {
2603 		if ((pass & FR_LOGMASK) != FR_LOGB)
2604 			pass |= FF_LOGBLOCK;
2605 		ATOMIC_INCL(frstats[out].fr_bpkl);
2606 logit:
2607 		if (ipflog(fin, pass) == -1) {
2608 			ATOMIC_INCL(frstats[out].fr_skip);
2609 
2610 			/*
2611 			 * If the "or-block" option has been used then
2612 			 * block the packet if we failed to log it.
2613 			 */
2614 			if ((pass & FR_LOGORBLOCK) &&
2615 			    FR_ISPASS(pass)) {
2616 				pass &= ~FR_CMDMASK;
2617 				pass |= FR_BLOCK;
2618 			}
2619 		}
2620 		*passp = pass;
2621 	}
2622 
2623 	return fin->fin_fr;
2624 }
2625 #endif /* IPFILTER_LOG */
2626 
2627 
2628 /* ------------------------------------------------------------------------ */
2629 /* Function:    ipf_cksum                                                   */
2630 /* Returns:     u_short - IP header checksum                                */
2631 /* Parameters:  addr(I) - pointer to start of buffer to checksum            */
2632 /*              len(I)  - length of buffer in bytes                         */
2633 /*                                                                          */
2634 /* Calculate the two's complement 16 bit checksum of the buffer passed.     */
2635 /*                                                                          */
2636 /* N.B.: addr should be 16bit aligned.                                      */
2637 /* ------------------------------------------------------------------------ */
2638 u_short ipf_cksum(addr, len)
2639 u_short *addr;
2640 int len;
2641 {
2642 	u_32_t sum = 0;
2643 
2644 	for (sum = 0; len > 1; len -= 2)
2645 		sum += *addr++;
2646 
2647 	/* mop up an odd byte, if necessary */
2648 	if (len == 1)
2649 		sum += *(u_char *)addr;
2650 
2651 	/*
2652 	 * add back carry outs from top 16 bits to low 16 bits
2653 	 */
2654 	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
2655 	sum += (sum >> 16);			/* add carry */
2656 	return (u_short)(~sum);
2657 }
2658 
2659 
2660 /* ------------------------------------------------------------------------ */
2661 /* Function:    fr_cksum                                                    */
2662 /* Returns:     u_short - layer 4 checksum                                  */
2663 /* Parameters:  m(I  )   - pointer to buffer holding packet                 */
2664 /*              ip(I)    - pointer to IP header                             */
2665 /*              l4hdr(I) - pointer to layer 4 header                        */
2666 /*                                                                          */
2667 /* Calculates the TCP checksum for the packet held in "m", using the data   */
2668 /* in the IP header "ip" to seed it.                                        */
2669 /*                                                                          */
2670 /* NB: This function assumes we've pullup'd enough for all of the IP header */
2671 /* and the TCP header.  We also assume that data blocks aren't allocated in */
2672 /* odd sizes.                                                               */
2673 /* ------------------------------------------------------------------------ */
2674 u_short fr_cksum(m, ip, l4proto, l4hdr)
2675 mb_t *m;
2676 ip_t *ip;
2677 int l4proto;
2678 void *l4hdr;
2679 {
2680 	u_short *sp, slen, sumsave, l4hlen, *csump;
2681 	u_int sum, sum2;
2682 	int hlen;
2683 #ifdef	USE_INET6
2684 	ip6_t *ip6;
2685 #endif
2686 
2687 	csump = NULL;
2688 	sumsave = 0;
2689 	l4hlen = 0;
2690 	sp = NULL;
2691 	slen = 0;
2692 	hlen = 0;
2693 	sum = 0;
2694 
2695 	/*
2696 	 * Add up IP Header portion
2697 	 */
2698 #ifdef	USE_INET6
2699 	if (IP_V(ip) == 4) {
2700 #endif
2701 		hlen = IP_HL(ip) << 2;
2702 		slen = ip->ip_len - hlen;
2703 		sum = htons((u_short)ip->ip_p);
2704 		sum += htons(slen);
2705 		sp = (u_short *)&ip->ip_src;
2706 		sum += *sp++;	/* ip_src */
2707 		sum += *sp++;
2708 		sum += *sp++;	/* ip_dst */
2709 		sum += *sp++;
2710 #ifdef	USE_INET6
2711 	} else if (IP_V(ip) == 6) {
2712 		ip6 = (ip6_t *)ip;
2713 		hlen = sizeof(*ip6);
2714 		slen = ntohs(ip6->ip6_plen);
2715 		sum = htons((u_short)ip6->ip6_nxt);
2716 		sum += htons(slen);
2717 		sp = (u_short *)&ip6->ip6_src;
2718 		sum += *sp++;	/* ip6_src */
2719 		sum += *sp++;
2720 		sum += *sp++;
2721 		sum += *sp++;
2722 		sum += *sp++;
2723 		sum += *sp++;
2724 		sum += *sp++;
2725 		sum += *sp++;
2726 		sum += *sp++;	/* ip6_dst */
2727 		sum += *sp++;
2728 		sum += *sp++;
2729 		sum += *sp++;
2730 		sum += *sp++;
2731 		sum += *sp++;
2732 		sum += *sp++;
2733 		sum += *sp++;
2734 	}
2735 #endif
2736 
2737 	switch (l4proto)
2738 	{
2739 	case IPPROTO_UDP :
2740 		csump = &((udphdr_t *)l4hdr)->uh_sum;
2741 		l4hlen = sizeof(udphdr_t);
2742 		break;
2743 
2744 	case IPPROTO_TCP :
2745 		csump = &((tcphdr_t *)l4hdr)->th_sum;
2746 		l4hlen = sizeof(tcphdr_t);
2747 		break;
2748 	case IPPROTO_ICMP :
2749 		csump = &((icmphdr_t *)l4hdr)->icmp_cksum;
2750 		l4hlen = 4;
2751 		break;
2752 	default :
2753 		break;
2754 	}
2755 
2756 	if (csump != NULL) {
2757 		sumsave = *csump;
2758 		*csump = 0;
2759 	}
2760 
2761 	l4hlen = l4hlen;	/* LINT */
2762 
2763 #ifdef	_KERNEL
2764 # ifdef MENTAT
2765 	{
2766 	void *rp = m->b_rptr;
2767 
2768 	if ((unsigned char *)ip > m->b_rptr && (unsigned char *)ip < m->b_wptr)
2769 		m->b_rptr = (u_char *)ip;
2770 	sum2 = ip_cksum(m, hlen, sum);	/* hlen == offset */
2771 	m->b_rptr = rp;
2772 	}
2773 	sum2 = (sum2 & 0xffff) + (sum2 >> 16);
2774 	sum2 = ~sum2 & 0xffff;
2775 # else /* MENTAT */
2776 #  if defined(BSD) || defined(sun)
2777 #   if BSD >= 199103
2778 	m->m_data += hlen;
2779 #   else
2780 	m->m_off += hlen;
2781 #   endif
2782 	m->m_len -= hlen;
2783 	sum2 = in_cksum(m, slen);
2784 	m->m_len += hlen;
2785 #   if BSD >= 199103
2786 	m->m_data -= hlen;
2787 #   else
2788 	m->m_off -= hlen;
2789 #   endif
2790 	/*
2791 	 * Both sum and sum2 are partial sums, so combine them together.
2792 	 */
2793 	sum += ~sum2 & 0xffff;
2794 	while (sum > 0xffff)
2795 		sum = (sum & 0xffff) + (sum >> 16);
2796 	sum2 = ~sum & 0xffff;
2797 #  else /* defined(BSD) || defined(sun) */
2798 {
2799 	union {
2800 		u_char	c[2];
2801 		u_short	s;
2802 	} bytes;
2803 	u_short len = ip->ip_len;
2804 #   if defined(__sgi)
2805 	int add;
2806 #   endif
2807 
2808 	/*
2809 	 * Add up IP Header portion
2810 	 */
2811 	sp = (u_short *)&ip->ip_src;
2812 	len -= (IP_HL(ip) << 2);
2813 	sum = ntohs((u_short)ip->ip_p);
2814 	sum += htons(len);
2815 	sum += *sp++;	/* ip_src */
2816 	sum += *sp++;
2817 	sum += *sp++;	/* ip_dst */
2818 	sum += *sp++;
2819 
2820 	if (sp != (u_short *)l4hdr)
2821 		sp = (u_short *)l4hdr;
2822 
2823 	switch (ip->ip_p)
2824 	{
2825 	case IPPROTO_UDP :
2826 		sum += *sp++;	/* sport */
2827 		sum += *sp++;	/* dport */
2828 		sum += *sp++;	/* udp length */
2829 		sum += *sp++;	/* checksum */
2830 		break;
2831 
2832 	case IPPROTO_TCP :
2833 		sum += *sp++;	/* sport */
2834 		sum += *sp++;	/* dport */
2835 		sum += *sp++;	/* seq */
2836 		sum += *sp++;
2837 		sum += *sp++;	/* ack */
2838 		sum += *sp++;
2839 		sum += *sp++;	/* off */
2840 		sum += *sp++;	/* win */
2841 		sum += *sp++;	/* checksum */
2842 		sum += *sp++;	/* urp */
2843 		break;
2844 	}
2845 
2846 #   ifdef	__sgi
2847 	/*
2848 	 * In case we had to copy the IP & TCP header out of mbufs,
2849 	 * skip over the mbuf bits which are the header
2850 	 */
2851 	if ((caddr_t)ip != mtod(m, caddr_t)) {
2852 		hlen = (caddr_t)sp - (caddr_t)ip;
2853 		while (hlen) {
2854 			add = MIN(hlen, m->m_len);
2855 			sp = (u_short *)(mtod(m, caddr_t) + add);
2856 			hlen -= add;
2857 			if (add == m->m_len) {
2858 				m = m->m_next;
2859 				if (!hlen) {
2860 					if (!m)
2861 						break;
2862 					sp = mtod(m, u_short *);
2863 				}
2864 				PANIC((!m),("fr_cksum(1): not enough data"));
2865 			}
2866 		}
2867 	}
2868 #   endif
2869 
2870 	len -= l4hlen;
2871 	if (len <= 0)
2872 		goto nodata;
2873 
2874 	while (len > 1) {
2875 		if (((caddr_t)sp - mtod(m, caddr_t)) >= m->m_len) {
2876 			m = m->m_next;
2877 			PANIC((!m),("fr_cksum(2): not enough data"));
2878 			sp = mtod(m, u_short *);
2879 		}
2880 		if (((caddr_t)(sp + 1) - mtod(m, caddr_t)) > m->m_len) {
2881 			bytes.c[0] = *(u_char *)sp;
2882 			m = m->m_next;
2883 			PANIC((!m),("fr_cksum(3): not enough data"));
2884 			sp = mtod(m, u_short *);
2885 			bytes.c[1] = *(u_char *)sp;
2886 			sum += bytes.s;
2887 			sp = (u_short *)((u_char *)sp + 1);
2888 		}
2889 		if ((u_long)sp & 1) {
2890 			bcopy((char *)sp++, (char *)&bytes.s, sizeof(bytes.s));
2891 			sum += bytes.s;
2892 		} else
2893 			sum += *sp++;
2894 		len -= 2;
2895 	}
2896 
2897 	if (len != 0)
2898 		sum += ntohs(*(u_char *)sp << 8);
2899 nodata:
2900 	while (sum > 0xffff)
2901 		sum = (sum & 0xffff) + (sum >> 16);
2902 	sum2 = (u_short)(~sum & 0xffff);
2903 }
2904 #  endif /*  defined(BSD) || defined(sun) */
2905 # endif /* MENTAT */
2906 #else /* _KERNEL */
2907 	for (; slen > 1; slen -= 2)
2908 	        sum += *sp++;
2909 	if (slen)
2910 		sum += ntohs(*(u_char *)sp << 8);
2911 	while (sum > 0xffff)
2912 		sum = (sum & 0xffff) + (sum >> 16);
2913 	sum2 = (u_short)(~sum & 0xffff);
2914 #endif /* _KERNEL */
2915 	if (csump != NULL)
2916 		*csump = sumsave;
2917 	return sum2;
2918 }
2919 
2920 
2921 #if defined(_KERNEL) && ( ((BSD < 199103) && !defined(MENTAT)) || \
2922     defined(__sgi) )
2923 /*
2924  * Copyright (c) 1982, 1986, 1988, 1991, 1993
2925  *	The Regents of the University of California.  All rights reserved.
2926  *
2927  * Redistribution and use in source and binary forms, with or without
2928  * modification, are permitted provided that the following conditions
2929  * are met:
2930  * 1. Redistributions of source code must retain the above copyright
2931  *    notice, this list of conditions and the following disclaimer.
2932  * 2. Redistributions in binary form must reproduce the above copyright
2933  *    notice, this list of conditions and the following disclaimer in the
2934  *    documentation and/or other materials provided with the distribution.
2935  * 3. Neither the name of the University nor the names of its contributors
2936  *    may be used to endorse or promote products derived from this software
2937  *    without specific prior written permission.
2938  *
2939  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2940  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2941  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2942  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2943  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2944  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2945  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2946  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2947  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2948  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2949  * SUCH DAMAGE.
2950  *
2951  *	@(#)uipc_mbuf.c	8.2 (Berkeley) 1/4/94
2952  * $Id: fil.c,v 2.197 2003/07/01 18:30:18 darrenr Exp $
2953  */
2954 /*
2955  * Copy data from an mbuf chain starting "off" bytes from the beginning,
2956  * continuing for "len" bytes, into the indicated buffer.
2957  */
2958 void
2959 m_copydata(m, off, len, cp)
2960 	mb_t *m;
2961 	int off;
2962 	int len;
2963 	caddr_t cp;
2964 {
2965 	unsigned count;
2966 
2967 	if (off < 0 || len < 0)
2968 		panic("m_copydata");
2969 	while (off > 0) {
2970 		if (m == 0)
2971 			panic("m_copydata");
2972 		if (off < m->m_len)
2973 			break;
2974 		off -= m->m_len;
2975 		m = m->m_next;
2976 	}
2977 	while (len > 0) {
2978 		if (m == 0)
2979 			panic("m_copydata");
2980 		count = MIN(m->m_len - off, len);
2981 		bcopy(mtod(m, caddr_t) + off, cp, count);
2982 		len -= count;
2983 		cp += count;
2984 		off = 0;
2985 		m = m->m_next;
2986 	}
2987 }
2988 
2989 
2990 /*
2991  * Copy data from a buffer back into the indicated mbuf chain,
2992  * starting "off" bytes from the beginning, extending the mbuf
2993  * chain if necessary.
2994  */
2995 void
2996 m_copyback(m0, off, len, cp)
2997 	struct	mbuf *m0;
2998 	int off;
2999 	int len;
3000 	caddr_t cp;
3001 {
3002 	int mlen;
3003 	struct mbuf *m = m0, *n;
3004 	int totlen = 0;
3005 
3006 	if (m0 == 0)
3007 		return;
3008 	while (off > (mlen = m->m_len)) {
3009 		off -= mlen;
3010 		totlen += mlen;
3011 		if (m->m_next == 0) {
3012 			n = m_getclr(M_DONTWAIT, m->m_type);
3013 			if (n == 0)
3014 				goto out;
3015 			n->m_len = min(MLEN, len + off);
3016 			m->m_next = n;
3017 		}
3018 		m = m->m_next;
3019 	}
3020 	while (len > 0) {
3021 		mlen = min (m->m_len - off, len);
3022 		bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen);
3023 		cp += mlen;
3024 		len -= mlen;
3025 		mlen += off;
3026 		off = 0;
3027 		totlen += mlen;
3028 		if (len == 0)
3029 			break;
3030 		if (m->m_next == 0) {
3031 			n = m_get(M_DONTWAIT, m->m_type);
3032 			if (n == 0)
3033 				break;
3034 			n->m_len = min(MLEN, len);
3035 			m->m_next = n;
3036 		}
3037 		m = m->m_next;
3038 	}
3039 out:
3040 #if 0
3041 	if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
3042 		m->m_pkthdr.len = totlen;
3043 #endif
3044 	return;
3045 }
3046 #endif /* (_KERNEL) && ( ((BSD < 199103) && !MENTAT) || __sgi) */
3047 
3048 
3049 /* ------------------------------------------------------------------------ */
3050 /* Function:    fr_findgroup                                                */
3051 /* Returns:     frgroup_t * - NULL = group not found, else pointer to group */
3052 /* Parameters:  group(I) - group name to search for                         */
3053 /*              unit(I)  - device to which this group belongs               */
3054 /*              set(I)   - which set of rules (inactive/inactive) this is   */
3055 /*              fgpp(O)  - pointer to place to store pointer to the pointer */
3056 /*                         to where to add the next (last) group or where   */
3057 /*                         to delete group from.                            */
3058 /*                                                                          */
3059 /* Search amongst the defined groups for a particular group number.         */
3060 /* ------------------------------------------------------------------------ */
3061 frgroup_t *fr_findgroup(group, unit, set, fgpp)
3062 char *group;
3063 minor_t unit;
3064 int set;
3065 frgroup_t ***fgpp;
3066 {
3067 	frgroup_t *fg, **fgp;
3068 
3069 	/*
3070 	 * Which list of groups to search in is dependant on which list of
3071 	 * rules are being operated on.
3072 	 */
3073 	fgp = &ipfgroups[unit][set];
3074 
3075 	while ((fg = *fgp) != NULL) {
3076 		if (strncmp(group, fg->fg_name, FR_GROUPLEN) == 0)
3077 			break;
3078 		else
3079 			fgp = &fg->fg_next;
3080 	}
3081 	if (fgpp != NULL)
3082 		*fgpp = fgp;
3083 	return fg;
3084 }
3085 
3086 
3087 /* ------------------------------------------------------------------------ */
3088 /* Function:    fr_addgroup                                                 */
3089 /* Returns:     frgroup_t * - NULL == did not create group,                 */
3090 /*                            != NULL == pointer to the group               */
3091 /* Parameters:  num(I)   - group number to add                              */
3092 /*              head(I)  - rule pointer that is using this as the head      */
3093 /*              flags(I) - rule flags which describe the type of rule it is */
3094 /*              unit(I)  - device to which this group will belong to        */
3095 /*              set(I)   - which set of rules (inactive/inactive) this is   */
3096 /* Write Locks: ipf_mutex                                                   */
3097 /*                                                                          */
3098 /* Add a new group head, or if it already exists, increase the reference    */
3099 /* count to it.                                                             */
3100 /* ------------------------------------------------------------------------ */
3101 frgroup_t *fr_addgroup(group, head, flags, unit, set)
3102 char *group;
3103 void *head;
3104 u_32_t flags;
3105 minor_t unit;
3106 int set;
3107 {
3108 	frgroup_t *fg, **fgp;
3109 	u_32_t gflags;
3110 
3111 	if (group == NULL)
3112 		return NULL;
3113 
3114 	if (unit == IPL_LOGIPF && *group == '\0')
3115 		return NULL;
3116 
3117 	fgp = NULL;
3118 	gflags = flags & FR_INOUT;
3119 
3120 	fg = fr_findgroup(group, unit, set, &fgp);
3121 	if (fg != NULL) {
3122 		if (fg->fg_flags == 0)
3123 			fg->fg_flags = gflags;
3124 		else if (gflags != fg->fg_flags)
3125 			return NULL;
3126 		fg->fg_ref++;
3127 		return fg;
3128 	}
3129 	KMALLOC(fg, frgroup_t *);
3130 	if (fg != NULL) {
3131 		fg->fg_head = head;
3132 		fg->fg_start = NULL;
3133 		fg->fg_next = *fgp;
3134 		bcopy(group, fg->fg_name, FR_GROUPLEN);
3135 		fg->fg_flags = gflags;
3136 		fg->fg_ref = 1;
3137 		*fgp = fg;
3138 	}
3139 	return fg;
3140 }
3141 
3142 
3143 /* ------------------------------------------------------------------------ */
3144 /* Function:    fr_delgroup                                                 */
3145 /* Returns:     Nil                                                         */
3146 /* Parameters:  group(I) - group name to delete                             */
3147 /*              unit(I)  - device to which this group belongs               */
3148 /*              set(I)   - which set of rules (inactive/inactive) this is   */
3149 /* Write Locks: ipf_mutex                                                   */
3150 /*                                                                          */
3151 /* Attempt to delete a group head.                                          */
3152 /* Only do this when its reference count reaches 0.                         */
3153 /* ------------------------------------------------------------------------ */
3154 void fr_delgroup(group, unit, set)
3155 char *group;
3156 minor_t unit;
3157 int set;
3158 {
3159 	frgroup_t *fg, **fgp;
3160 
3161 	fg = fr_findgroup(group, unit, set, &fgp);
3162 	if (fg == NULL)
3163 		return;
3164 
3165 	fg->fg_ref--;
3166 	if (fg->fg_ref == 0) {
3167 		*fgp = fg->fg_next;
3168 		KFREE(fg);
3169 	}
3170 }
3171 
3172 
3173 /* ------------------------------------------------------------------------ */
3174 /* Function:    fr_getrulen                                                 */
3175 /* Returns:     frentry_t * - NULL == not found, else pointer to rule n     */
3176 /* Parameters:  unit(I)  - device for which to count the rule's number      */
3177 /*              flags(I) - which set of rules to find the rule in           */
3178 /*              group(I) - group name                                       */
3179 /*              n(I)     - rule number to find                              */
3180 /*                                                                          */
3181 /* Find rule # n in group # g and return a pointer to it.  Return NULl if   */
3182 /* group # g doesn't exist or there are less than n rules in the group.     */
3183 /* ------------------------------------------------------------------------ */
3184 frentry_t *fr_getrulen(unit, group, n)
3185 int unit;
3186 char *group;
3187 u_32_t n;
3188 {
3189 	frentry_t *fr;
3190 	frgroup_t *fg;
3191 
3192 	fg = fr_findgroup(group, unit, fr_active, NULL);
3193 	if (fg == NULL)
3194 		return NULL;
3195 	for (fr = fg->fg_head; fr && n; fr = fr->fr_next, n--)
3196 		;
3197 	if (n != 0)
3198 		return NULL;
3199 	return fr;
3200 }
3201 
3202 
3203 /* ------------------------------------------------------------------------ */
3204 /* Function:    fr_rulen                                                    */
3205 /* Returns:     int - >= 0 - rule number, -1 == search failed               */
3206 /* Parameters:  unit(I) - device for which to count the rule's number       */
3207 /*              fr(I)   - pointer to rule to match                          */
3208 /*                                                                          */
3209 /* Return the number for a rule on a specific filtering device.             */
3210 /* ------------------------------------------------------------------------ */
3211 int fr_rulen(unit, fr)
3212 int unit;
3213 frentry_t *fr;
3214 {
3215 	frentry_t *fh;
3216 	frgroup_t *fg;
3217 	u_32_t n = 0;
3218 
3219 	if (fr == NULL)
3220 		return -1;
3221 	fg = fr_findgroup(fr->fr_group, unit, fr_active, NULL);
3222 	if (fg == NULL)
3223 		return -1;
3224 	for (fh = fg->fg_head; fh; n++, fh = fh->fr_next)
3225 		if (fh == fr)
3226 			break;
3227 	if (fh == NULL)
3228 		return -1;
3229 	return n;
3230 }
3231 
3232 
3233 /* ------------------------------------------------------------------------ */
3234 /* Function:    frflushlist                                                 */
3235 /* Returns:     int - >= 0 - number of flushed rules                        */
3236 /* Parameters:  set(I)   - which set of rules (inactive/inactive) this is   */
3237 /*              unit(I)  - device for which to flush rules                  */
3238 /*              flags(I) - which set of rules to flush                      */
3239 /*              nfreedp(O) - pointer to int where flush count is stored     */
3240 /*              listp(I)   - pointer to list to flush pointer               */
3241 /* Write Locks: ipf_mutex                                                   */
3242 /*                                                                          */
3243 /* Recursively flush rules from the list, descending groups as they are     */
3244 /* encountered.  if a rule is the head of a group and it has lost all its   */
3245 /* group members, then also delete the group reference.  nfreedp is needed  */
3246 /* to store the accumulating count of rules removed, whereas the returned   */
3247 /* value is just the number removed from the current list.  The latter is   */
3248 /* needed to correctly adjust reference counts on rules that define groups. */
3249 /*                                                                          */
3250 /* NOTE: Rules not loaded from user space cannot be flushed.                */
3251 /* ------------------------------------------------------------------------ */
3252 static int frflushlist(set, unit, nfreedp, listp)
3253 int set;
3254 minor_t unit;
3255 int *nfreedp;
3256 frentry_t **listp;
3257 {
3258 	int freed = 0, i;
3259 	frentry_t *fp;
3260 
3261 	while ((fp = *listp) != NULL) {
3262 		if ((fp->fr_type & FR_T_BUILTIN) ||
3263 		    !(fp->fr_flags & FR_COPIED)) {
3264 			listp = &fp->fr_next;
3265 			continue;
3266 		}
3267 		*listp = fp->fr_next;
3268 		if (fp->fr_grp != NULL) {
3269 			i = frflushlist(set, unit, nfreedp, fp->fr_grp);
3270 			fp->fr_ref -= i;
3271 		}
3272 
3273 		if (fp->fr_grhead != NULL) {
3274 			fr_delgroup(fp->fr_grhead, unit, set);
3275 			*fp->fr_grhead = '\0';
3276 		}
3277 
3278 		ASSERT(fp->fr_ref > 0);
3279 		fp->fr_next = NULL;
3280 		if (fr_derefrule(&fp) == 0)
3281 			freed++;
3282 	}
3283 	*nfreedp += freed;
3284 	return freed;
3285 }
3286 
3287 
3288 /* ------------------------------------------------------------------------ */
3289 /* Function:    frflush                                                     */
3290 /* Returns:     int - >= 0 - number of flushed rules                        */
3291 /* Parameters:  unit(I)  - device for which to flush rules                  */
3292 /*              proto(I) - which proto rules ipv4(4), ipv6(6)               */
3293 /*                         ipv4 and ipv6(0) to flush                        */
3294 /*              flags(I) - which set of rules to flush                      */
3295 /*                                                                          */
3296 /* Calls flushlist() for all filter rules (accounting, firewall - both IPv4 */
3297 /* and IPv6) as defined by the value of flags.                              */
3298 /* ------------------------------------------------------------------------ */
3299 int frflush(unit, proto, flags)
3300 minor_t unit;
3301 int proto, flags;
3302 {
3303 	int flushed = 0, set;
3304 
3305 	WRITE_ENTER(&ipf_mutex);
3306 	bzero((char *)frcache, sizeof(frcache));
3307 
3308 	set = fr_active;
3309 	if ((flags & FR_INACTIVE) == FR_INACTIVE)
3310 		set = 1 - set;
3311 
3312 	if (flags & FR_OUTQUE) {
3313 		if (proto == 0 || proto == 6) {
3314 			(void) frflushlist(set, unit,
3315 			    &flushed, &ipfilter6[1][set]);
3316 			(void) frflushlist(set, unit,
3317 			    &flushed, &ipacct6[1][set]);
3318 		}
3319 		if (proto == 0 || proto == 4) {
3320 			(void) frflushlist(set, unit,
3321 			    &flushed, &ipfilter[1][set]);
3322 			(void) frflushlist(set, unit,
3323 			    &flushed, &ipacct[1][set]);
3324 		}
3325 	}
3326 	if (flags & FR_INQUE) {
3327 		if (proto == 0 || proto == 6) {
3328 			(void) frflushlist(set, unit,
3329 			    &flushed, &ipfilter6[0][set]);
3330 			(void) frflushlist(set, unit,
3331 			    &flushed, &ipacct6[0][set]);
3332 		}
3333 		if (proto == 0 || proto == 4) {
3334 			(void) frflushlist(set, unit,
3335 			    &flushed, &ipfilter[0][set]);
3336 			(void) frflushlist(set, unit,
3337 			    &flushed, &ipacct[0][set]);
3338 		}
3339 	}
3340 	RWLOCK_EXIT(&ipf_mutex);
3341 
3342 	if (unit == IPL_LOGIPF) {
3343 		int tmp;
3344 
3345 		tmp = frflush(IPL_LOGCOUNT, proto, flags);
3346 		if (tmp >= 0)
3347 			flushed += tmp;
3348 	}
3349 	return flushed;
3350 }
3351 
3352 
3353 /* ------------------------------------------------------------------------ */
3354 /* Function:    memstr                                                      */
3355 /* Returns:     char *  - NULL if failed, != NULL pointer to matching bytes */
3356 /* Parameters:  src(I)  - pointer to byte sequence to match                 */
3357 /*              dst(I)  - pointer to byte sequence to search                */
3358 /*              slen(I) - match length                                      */
3359 /*              dlen(I) - length available to search in                     */
3360 /*                                                                          */
3361 /* Search dst for a sequence of bytes matching those at src and extend for  */
3362 /* slen bytes.                                                              */
3363 /* ------------------------------------------------------------------------ */
3364 char *memstr(src, dst, slen, dlen)
3365 char *src, *dst;
3366 int slen, dlen;
3367 {
3368 	char *s = NULL;
3369 
3370 	while (dlen >= slen) {
3371 		if (bcmp(src, dst, slen) == 0) {
3372 			s = dst;
3373 			break;
3374 		}
3375 		dst++;
3376 		dlen--;
3377 	}
3378 	return s;
3379 }
3380 /* ------------------------------------------------------------------------ */
3381 /* Function:    fr_fixskip                                                  */
3382 /* Returns:     Nil                                                         */
3383 /* Parameters:  listp(IO)    - pointer to start of list with skip rule      */
3384 /*              rp(I)        - rule added/removed with skip in it.          */
3385 /*              addremove(I) - adjustment (-1/+1) to make to skip count,    */
3386 /*                             depending on whether a rule was just added   */
3387 /*                             or removed.                                  */
3388 /*                                                                          */
3389 /* Adjust all the rules in a list which would have skip'd past the position */
3390 /* where we are inserting to skip to the right place given the change.      */
3391 /* ------------------------------------------------------------------------ */
3392 void fr_fixskip(listp, rp, addremove)
3393 frentry_t **listp, *rp;
3394 int addremove;
3395 {
3396 	int rules, rn;
3397 	frentry_t *fp;
3398 
3399 	rules = 0;
3400 	for (fp = *listp; (fp != NULL) && (fp != rp); fp = fp->fr_next)
3401 		rules++;
3402 
3403 	if (!fp)
3404 		return;
3405 
3406 	for (rn = 0, fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++)
3407 		if (FR_ISSKIP(fp->fr_flags) && (rn + fp->fr_arg >= rules))
3408 			fp->fr_arg += addremove;
3409 }
3410 
3411 
3412 #ifdef	_KERNEL
3413 /* ------------------------------------------------------------------------ */
3414 /* Function:    count4bits                                                  */
3415 /* Returns:     int - >= 0 - number of consecutive bits in input            */
3416 /* Parameters:  ip(I) - 32bit IP address                                    */
3417 /*                                                                          */
3418 /* IPv4 ONLY                                                                */
3419 /* count consecutive 1's in bit mask.  If the mask generated by counting    */
3420 /* consecutive 1's is different to that passed, return -1, else return #    */
3421 /* of bits.                                                                 */
3422 /* ------------------------------------------------------------------------ */
3423 int	count4bits(ip)
3424 u_32_t	ip;
3425 {
3426 	u_32_t	ipn;
3427 	int	cnt = 0, i, j;
3428 
3429 	ip = ipn = ntohl(ip);
3430 	for (i = 32; i; i--, ipn *= 2)
3431 		if (ipn & 0x80000000)
3432 			cnt++;
3433 		else
3434 			break;
3435 	ipn = 0;
3436 	for (i = 32, j = cnt; i; i--, j--) {
3437 		ipn *= 2;
3438 		if (j > 0)
3439 			ipn++;
3440 	}
3441 	if (ipn == ip)
3442 		return cnt;
3443 	return -1;
3444 }
3445 
3446 #ifdef USE_INET6
3447 /* ------------------------------------------------------------------------ */
3448 /* Function:    count6bits                                                  */
3449 /* Returns:     int - >= 0 - number of consecutive bits in input            */
3450 /* Parameters:  msk(I) - pointer to start of IPv6 bitmask                   */
3451 /*                                                                          */
3452 /* IPv6 ONLY                                                                */
3453 /* count consecutive 1's in bit mask.                                       */
3454 /* ------------------------------------------------------------------------ */
3455 int count6bits(msk)
3456 u_32_t *msk;
3457 {
3458 	int i = 0, k;
3459 	u_32_t j;
3460 
3461 	for (k = 3; k >= 0; k--)
3462 		if (msk[k] == 0xffffffff)
3463 			i += 32;
3464 		else {
3465 			for (j = msk[k]; j; j <<= 1)
3466 				if (j & 0x80000000)
3467 					i++;
3468 		}
3469 	return i;
3470 }
3471 #endif
3472 #endif /* _KERNEL */
3473 
3474 
3475 /* ------------------------------------------------------------------------ */
3476 /* Function:    frsynclist                                                  */
3477 /* Returns:     void                                                        */
3478 /* Parameters:  Nil                                                         */
3479 /* Write Locks: ipf_mutex                                                   */
3480 /*                                                                          */
3481 /* Walk through a list of filter rules and resolve any interface names into */
3482 /* pointers.  Where dynamic addresses are used, also update the IP address  */
3483 /* used in the rule.                                                        */
3484 /* ------------------------------------------------------------------------ */
3485 static void frsynclist(fr)
3486 frentry_t *fr;
3487 {
3488 	frdest_t *fdp;
3489 	int v, i;
3490 
3491 	for (; fr; fr = fr->fr_next) {
3492 		v = fr->fr_v;
3493 
3494 		/*
3495 		 * Lookup all the interface names that are part of the rule.
3496 		 */
3497 		for (i = 0; i < 4; i++) {
3498 			if ((fr->fr_ifnames[i][1] == '\0') &&
3499 			    ((fr->fr_ifnames[i][0] == '-') ||
3500 			     (fr->fr_ifnames[i][0] == '*'))) {
3501 				fr->fr_ifas[i] = NULL;
3502 			} else if (fr->fr_ifnames[i][0] != '\0') {
3503 				fr->fr_ifas[i] = GETIFP(fr->fr_ifnames[i], v);
3504 				if (fr->fr_ifas[i] == (void *)NULL)
3505 					fr->fr_ifas[i] = (void *)-1;
3506 			}
3507 		}
3508 
3509 		if (fr->fr_type == FR_T_IPF) {
3510 			if (fr->fr_satype != FRI_NORMAL &&
3511 			    fr->fr_satype != FRI_LOOKUP) {
3512 				(void)fr_ifpaddr(v, fr->fr_satype,
3513 						 fr->fr_ifas[fr->fr_sifpidx],
3514 						 &fr->fr_src, &fr->fr_smsk);
3515 			}
3516 			if (fr->fr_datype != FRI_NORMAL &&
3517 			    fr->fr_datype != FRI_LOOKUP) {
3518 				(void)fr_ifpaddr(v, fr->fr_datype,
3519 						 fr->fr_ifas[fr->fr_difpidx],
3520 						 &fr->fr_dst, &fr->fr_dmsk);
3521 			}
3522 		}
3523 
3524 		fdp = &fr->fr_tifs[0];
3525 		if (fdp->fd_ifname[0] != '\0') {
3526 			fdp->fd_ifp = GETIFP(fdp->fd_ifname, v);
3527 			if (fdp->fd_ifp == NULL)
3528 				fdp->fd_ifp = (void *)-1;
3529 		}
3530 
3531 		fdp = &fr->fr_tifs[1];
3532 		if (fdp->fd_ifname[0] != '\0') {
3533 			fdp->fd_ifp = GETIFP(fdp->fd_ifname, v);
3534 			if (fdp->fd_ifp == NULL)
3535 				fdp->fd_ifp = (void *)-1;
3536 		}
3537 
3538 		fdp = &fr->fr_dif;
3539 		if (fdp->fd_ifname[0] != '\0') {
3540 			fr->fr_flags &= ~FR_DUP;
3541 			fdp->fd_ifp = GETIFP(fdp->fd_ifname, v);
3542 			if (fdp->fd_ifp == NULL)
3543 				fdp->fd_ifp = (void *)-1;
3544 			else
3545 				fr->fr_flags |= FR_DUP;
3546 		}
3547 
3548 #ifdef	IPFILTER_LOOKUP
3549 		if (fr->fr_type == FR_T_IPF && fr->fr_satype == FRI_LOOKUP &&
3550 		    fr->fr_srcptr == NULL) {
3551 			fr->fr_srcptr = fr_resolvelookup(fr->fr_srctype,
3552 							 fr->fr_srcnum,
3553 							 &fr->fr_srcfunc);
3554 		}
3555 		if (fr->fr_type == FR_T_IPF && fr->fr_datype == FRI_LOOKUP &&
3556 		    fr->fr_dstptr == NULL) {
3557 			fr->fr_dstptr = fr_resolvelookup(fr->fr_dsttype,
3558 							 fr->fr_dstnum,
3559 							 &fr->fr_dstfunc);
3560 		}
3561 #endif
3562 	}
3563 }
3564 
3565 
3566 #ifdef	_KERNEL
3567 /* ------------------------------------------------------------------------ */
3568 /* Function:    frsync                                                      */
3569 /* Returns:     void                                                        */
3570 /* Parameters:  Nil                                                         */
3571 /*                                                                          */
3572 /* frsync() is called when we suspect that the interface list or            */
3573 /* information about interfaces (like IP#) has changed.  Go through all     */
3574 /* filter rules, NAT entries and the state table and check if anything      */
3575 /* needs to be changed/updated.                                             */
3576 /* ------------------------------------------------------------------------ */
3577 void frsync()
3578 {
3579 	int i;
3580 # ifndef MENTAT
3581 	struct ifnet *ifp;
3582 
3583 #  if defined(__OpenBSD__) || ((NetBSD >= 199511) && (NetBSD < 1991011)) || \
3584      (defined(__FreeBSD_version) && (__FreeBSD_version >= 300000))
3585 #   if (NetBSD >= 199905) || defined(__OpenBSD__)
3586 	for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
3587 #   elif defined(__FreeBSD_version) && (__FreeBSD_version >= 500043)
3588 	IFNET_RLOCK();
3589 	TAILQ_FOREACH(ifp, &ifnet, if_link);
3590 #   else
3591 	for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next)
3592 #   endif
3593 #  else
3594 	for (ifp = ifnet; ifp; ifp = ifp->if_next)
3595 #  endif
3596 	{
3597 		fr_natsync(ifp);
3598 		fr_statesync(ifp);
3599 	}
3600 #  if defined(__FreeBSD_version) && (__FreeBSD_version >= 500043)
3601 	IFNET_RUNLOCK();
3602 #  endif
3603 # endif
3604 
3605 	WRITE_ENTER(&ipf_mutex);
3606 	frsynclist(ipacct[0][fr_active]);
3607 	frsynclist(ipacct[1][fr_active]);
3608 	frsynclist(ipfilter[0][fr_active]);
3609 	frsynclist(ipfilter[1][fr_active]);
3610 	frsynclist(ipacct6[0][fr_active]);
3611 	frsynclist(ipacct6[1][fr_active]);
3612 	frsynclist(ipfilter6[0][fr_active]);
3613 	frsynclist(ipfilter6[1][fr_active]);
3614 
3615 	for (i = 0; i < IPL_LOGSIZE; i++) {
3616 		frgroup_t *g;
3617 
3618 		for (g = ipfgroups[i][0]; g != NULL; g = g->fg_next)
3619 			frsynclist(g->fg_start);
3620 		for (g = ipfgroups[i][1]; g != NULL; g = g->fg_next)
3621 			frsynclist(g->fg_start);
3622 	}
3623 	RWLOCK_EXIT(&ipf_mutex);
3624 }
3625 
3626 
3627 /*
3628  * In the functions below, bcopy() is called because the pointer being
3629  * copied _from_ in this instance is a pointer to a char buf (which could
3630  * end up being unaligned) and on the kernel's local stack.
3631  */
3632 /* ------------------------------------------------------------------------ */
3633 /* Function:    copyinptr                                                   */
3634 /* Returns:     int - 0 = success, else failure                             */
3635 /* Parameters:  src(I)  - pointer to the source address                     */
3636 /*              dst(I)  - destination address                               */
3637 /*              size(I) - number of bytes to copy                           */
3638 /*                                                                          */
3639 /* Copy a block of data in from user space, given a pointer to the pointer  */
3640 /* to start copying from (src) and a pointer to where to store it (dst).    */
3641 /* NB: src - pointer to user space pointer, dst - kernel space pointer      */
3642 /* ------------------------------------------------------------------------ */
3643 int copyinptr(src, dst, size)
3644 void *src, *dst;
3645 size_t size;
3646 {
3647 	caddr_t ca;
3648 	int err;
3649 
3650 #if SOLARIS
3651 	err = copyin(src, (caddr_t)&ca, sizeof(ca));
3652 	if (err != 0)
3653 		return err;
3654 #else
3655 	bcopy(src, (caddr_t)&ca, sizeof(ca));
3656 #endif
3657 	err = copyin(ca, dst, size);
3658 	return err;
3659 }
3660 
3661 
3662 /* ------------------------------------------------------------------------ */
3663 /* Function:    copyoutptr                                                  */
3664 /* Returns:     int - 0 = success, else failure                             */
3665 /* Parameters:  src(I)  - pointer to the source address                     */
3666 /*              dst(I)  - destination address                               */
3667 /*              size(I) - number of bytes to copy                           */
3668 /*                                                                          */
3669 /* Copy a block of data out to user space, given a pointer to the pointer   */
3670 /* to start copying from (src) and a pointer to where to store it (dst).    */
3671 /* NB: src - kernel space pointer, dst - pointer to user space pointer.     */
3672 /* ------------------------------------------------------------------------ */
3673 int copyoutptr(src, dst, size)
3674 void *src, *dst;
3675 size_t size;
3676 {
3677 	caddr_t ca;
3678 	int err;
3679 
3680 #if SOLARIS
3681 	err = copyin(dst, (caddr_t)&ca, sizeof(ca));
3682 	if (err != 0)
3683 		return err;
3684 #else
3685 	bcopy(dst, (caddr_t)&ca, sizeof(ca));
3686 #endif
3687 	err = copyout(src, ca, size);
3688 	return err;
3689 }
3690 
3691 #else /* _KERNEL */
3692 
3693 
3694 /*
3695  * See above for description, except that all addressing is in user space.
3696  */
3697 int copyoutptr(src, dst, size)
3698 void *src, *dst;
3699 size_t size;
3700 {
3701 	caddr_t ca;
3702 
3703 	bcopy(dst, (char *)&ca, sizeof(ca));
3704 	bcopy(src, ca, size);
3705 	return 0;
3706 }
3707 
3708 
3709 /*
3710  * See above for description, except that all addressing is in user space.
3711  */
3712 int copyinptr(src, dst, size)
3713 void *src, *dst;
3714 size_t size;
3715 {
3716 	caddr_t ca;
3717 
3718 	bcopy(src, (char *)&ca, sizeof(ca));
3719 	bcopy(ca, dst, size);
3720 	return 0;
3721 }
3722 
3723 
3724 /*
3725  * return the first IP Address associated with an interface
3726  */
3727 int fr_ifpaddr(v, flags, ifptr, inp, inpmask)
3728 int v, flags;
3729 void *ifptr;
3730 struct in_addr *inp, *inpmask;
3731 {
3732 	return 0;
3733 }
3734 
3735 #endif
3736 
3737 
3738 /* ------------------------------------------------------------------------ */
3739 /* Function:    fr_lock                                                     */
3740 /* Returns:     int - 0 == success, else error                              */
3741 /* Parameters:  data(I)  - pointer to lock value to set                     */
3742 /*              lockp(O) - pointer to location to store old lock value      */
3743 /*                                                                          */
3744 /* Get the new value for the lock integer, set it and return the old value  */
3745 /* in *lockp.                                                               */
3746 /* ------------------------------------------------------------------------ */
3747 int fr_lock(data, lockp)
3748 caddr_t data;
3749 int *lockp;
3750 {
3751 	int arg, error;
3752 
3753 	error = COPYIN(data, (caddr_t)&arg, sizeof(arg));
3754 	if (!error) {
3755 		error = COPYOUT((caddr_t)lockp, data, sizeof(*lockp));
3756 		if (!error)
3757 			*lockp = arg;
3758 	}
3759 	return error;
3760 }
3761 
3762 
3763 /* ------------------------------------------------------------------------ */
3764 /* Function:    fr_getstat                                                  */
3765 /* Returns:     Nil                                                         */
3766 /* Parameters:  fiop(I)  - pointer to ipfilter stats structure              */
3767 /*                                                                          */
3768 /* Stores a copy of current pointers, counters, etc, in the friostat        */
3769 /* structure.                                                               */
3770 /* ------------------------------------------------------------------------ */
3771 void fr_getstat(fiop)
3772 friostat_t *fiop;
3773 {
3774 	int i, j;
3775 
3776 	bcopy((char *)frstats, (char *)fiop->f_st, sizeof(filterstats_t) * 2);
3777 	fiop->f_locks[IPL_LOGSTATE] = fr_state_lock;
3778 	fiop->f_locks[IPL_LOGNAT] = fr_nat_lock;
3779 	fiop->f_locks[IPL_LOGIPF] = fr_frag_lock;
3780 	fiop->f_locks[IPL_LOGAUTH] = fr_auth_lock;
3781 
3782 	for (i = 0; i < 2; i++)
3783 		for (j = 0; j < 2; j++) {
3784 			fiop->f_ipf[i][j] = ipfilter[i][j];
3785 			fiop->f_acct[i][j] = ipacct[i][j];
3786 			fiop->f_ipf6[i][j] = ipfilter6[i][j];
3787 			fiop->f_acct6[i][j] = ipacct6[i][j];
3788 		}
3789 
3790 	fiop->f_ticks = fr_ticks;
3791 	fiop->f_active = fr_active;
3792 	fiop->f_froute[0] = fr_frouteok[0];
3793 	fiop->f_froute[1] = fr_frouteok[1];
3794 
3795 	fiop->f_running = fr_running;
3796 	for (i = 0; i < IPL_LOGSIZE; i++) {
3797 		fiop->f_groups[i][0] = ipfgroups[i][0];
3798 		fiop->f_groups[i][1] = ipfgroups[i][1];
3799 	}
3800 #ifdef  IPFILTER_LOG
3801 	fiop->f_logging = 1;
3802 #else
3803 	fiop->f_logging = 0;
3804 #endif
3805 	fiop->f_defpass = fr_pass;
3806 	(void) strncpy(fiop->f_version, ipfilter_version, sizeof(fiop->f_version));
3807 }
3808 
3809 
3810 #ifdef	USE_INET6
3811 int icmptoicmp6types[ICMP_MAXTYPE+1] = {
3812 	ICMP6_ECHO_REPLY,	/* 0: ICMP_ECHOREPLY */
3813 	-1,			/* 1: UNUSED */
3814 	-1,			/* 2: UNUSED */
3815 	ICMP6_DST_UNREACH,	/* 3: ICMP_UNREACH */
3816 	-1,			/* 4: ICMP_SOURCEQUENCH */
3817 	ND_REDIRECT,		/* 5: ICMP_REDIRECT */
3818 	-1,			/* 6: UNUSED */
3819 	-1,			/* 7: UNUSED */
3820 	ICMP6_ECHO_REQUEST,	/* 8: ICMP_ECHO */
3821 	-1,			/* 9: UNUSED */
3822 	-1,			/* 10: UNUSED */
3823 	ICMP6_TIME_EXCEEDED,	/* 11: ICMP_TIMXCEED */
3824 	ICMP6_PARAM_PROB,	/* 12: ICMP_PARAMPROB */
3825 	-1,			/* 13: ICMP_TSTAMP */
3826 	-1,			/* 14: ICMP_TSTAMPREPLY */
3827 	-1,			/* 15: ICMP_IREQ */
3828 	-1,			/* 16: ICMP_IREQREPLY */
3829 	-1,			/* 17: ICMP_MASKREQ */
3830 	-1,			/* 18: ICMP_MASKREPLY */
3831 };
3832 
3833 
3834 int	icmptoicmp6unreach[ICMP_MAX_UNREACH] = {
3835 	ICMP6_DST_UNREACH_ADDR,		/* 0: ICMP_UNREACH_NET */
3836 	ICMP6_DST_UNREACH_ADDR,		/* 1: ICMP_UNREACH_HOST */
3837 	-1,				/* 2: ICMP_UNREACH_PROTOCOL */
3838 	ICMP6_DST_UNREACH_NOPORT,	/* 3: ICMP_UNREACH_PORT */
3839 	-1,				/* 4: ICMP_UNREACH_NEEDFRAG */
3840 	ICMP6_DST_UNREACH_NOTNEIGHBOR,	/* 5: ICMP_UNREACH_SRCFAIL */
3841 	ICMP6_DST_UNREACH_ADDR,		/* 6: ICMP_UNREACH_NET_UNKNOWN */
3842 	ICMP6_DST_UNREACH_ADDR,		/* 7: ICMP_UNREACH_HOST_UNKNOWN */
3843 	-1,				/* 8: ICMP_UNREACH_ISOLATED */
3844 	ICMP6_DST_UNREACH_ADMIN,	/* 9: ICMP_UNREACH_NET_PROHIB */
3845 	ICMP6_DST_UNREACH_ADMIN,	/* 10: ICMP_UNREACH_HOST_PROHIB */
3846 	-1,				/* 11: ICMP_UNREACH_TOSNET */
3847 	-1,				/* 12: ICMP_UNREACH_TOSHOST */
3848 	ICMP6_DST_UNREACH_ADMIN,	/* 13: ICMP_UNREACH_ADMIN_PROHIBIT */
3849 };
3850 int	icmpreplytype6[ICMP6_MAXTYPE + 1];
3851 #endif
3852 
3853 int	icmpreplytype4[ICMP_MAXTYPE + 1];
3854 
3855 
3856 /* ------------------------------------------------------------------------ */
3857 /* Function:    fr_matchicmpqueryreply                                      */
3858 /* Returns:     int - 1 if "icmp" is a valid reply to "ic" else 0.          */
3859 /* Parameters:  v(I)    - IP protocol version (4 or 6)                      */
3860 /*              ic(I)   - ICMP information                                  */
3861 /*              icmp(I) - ICMP packet header                                */
3862 /*              rev(I)  - direction (0 = forward/1 = reverse) of packet     */
3863 /*                                                                          */
3864 /* Check if the ICMP packet defined by the header pointed to by icmp is a   */
3865 /* reply to one as described by what's in ic.  If it is a match, return 1,  */
3866 /* else return 0 for no match.                                              */
3867 /* ------------------------------------------------------------------------ */
3868 int fr_matchicmpqueryreply(v, ic, icmp, rev)
3869 int v;
3870 icmpinfo_t *ic;
3871 icmphdr_t *icmp;
3872 int rev;
3873 {
3874 	int ictype;
3875 
3876 	ictype = ic->ici_type;
3877 
3878 	if (v == 4) {
3879 		/*
3880 		 * If we matched its type on the way in, then when going out
3881 		 * it will still be the same type.
3882 		 */
3883 		if ((!rev && (icmp->icmp_type == ictype)) ||
3884 		    (rev && (icmpreplytype4[ictype] == icmp->icmp_type))) {
3885 			if (icmp->icmp_type != ICMP_ECHOREPLY)
3886 				return 1;
3887 			if ((icmp->icmp_id == ic->ici_id) &&
3888 			    (icmp->icmp_seq == ic->ici_seq))
3889 				return 1;
3890 		}
3891 	}
3892 #ifdef	USE_INET6
3893 	else if (v == 6) {
3894 		if ((!rev && (icmp->icmp_type == ictype)) ||
3895 		    (rev && (icmpreplytype6[ictype] == icmp->icmp_type))) {
3896 			if (icmp->icmp_type != ICMP6_ECHO_REPLY)
3897 				return 1;
3898 			if ((icmp->icmp_id == ic->ici_id) &&
3899 			    (icmp->icmp_seq == ic->ici_seq))
3900 				return 1;
3901 		}
3902 	}
3903 #endif
3904 	return 0;
3905 }
3906 
3907 
3908 #ifdef	IPFILTER_LOOKUP
3909 /* ------------------------------------------------------------------------ */
3910 /* Function:    fr_resolvelookup                                            */
3911 /* Returns:     void * - NULL = failure, else success.                      */
3912 /* Parameters:  type(I)     - type of lookup these parameters are for.      */
3913 /*              number(I)   - table number to use when searching            */
3914 /*              funcptr(IO) - pointer to pointer for storing IP address     */
3915 /*                           searching function.                            */
3916 /*                                                                          */
3917 /* Search for the "table" number passed in amongst those configured for     */
3918 /* that particular type.  If the type is recognised then the function to    */
3919 /* call to do the IP address search will be change, regardless of whether   */
3920 /* or not the "table" number exists.                                        */
3921 /* ------------------------------------------------------------------------ */
3922 static void *fr_resolvelookup(type, number, funcptr)
3923 u_int type, number;
3924 lookupfunc_t *funcptr;
3925 {
3926 	char name[FR_GROUPLEN];
3927 	iphtable_t *iph;
3928 	ip_pool_t *ipo;
3929 	void *ptr;
3930 
3931 	(void) sprintf(name, "%u", number);
3932 
3933 	READ_ENTER(&ip_poolrw);
3934 
3935 	switch (type)
3936 	{
3937 	case IPLT_POOL :
3938 # if (defined(__osf__) && defined(_KERNEL))
3939 		ptr = NULL;
3940 		*funcptr = NULL;
3941 # else
3942 		ipo = ip_pool_find(IPL_LOGIPF, name);
3943 		ptr = ipo;
3944 		if (ipo != NULL) {
3945 			ATOMIC_INC32(ipo->ipo_ref);
3946 		}
3947 		*funcptr = ip_pool_search;
3948 # endif
3949 		break;
3950 	case IPLT_HASH :
3951 		iph = fr_findhtable(IPL_LOGIPF, name);
3952 		ptr = iph;
3953 		if (iph != NULL) {
3954 			ATOMIC_INC32(iph->iph_ref);
3955 		}
3956 		*funcptr = fr_iphmfindip;
3957 		break;
3958 	default:
3959 		ptr = NULL;
3960 		*funcptr = NULL;
3961 		break;
3962 	}
3963 	RWLOCK_EXIT(&ip_poolrw);
3964 
3965 	return ptr;
3966 }
3967 #endif
3968 
3969 
3970 /* ------------------------------------------------------------------------ */
3971 /* Function:    frrequest                                                   */
3972 /* Returns:     int - 0 == success, > 0 == errno value                      */
3973 /* Parameters:  unit(I)     - device for which this is for                  */
3974 /*              req(I)      - ioctl command (SIOC*)                         */
3975 /*              data(I)     - pointr to ioctl data                          */
3976 /*              set(I)      - 1 or 0 (filter set)                           */
3977 /*              makecopy(I) - flag indicating whether data points to a rule */
3978 /*                            in kernel space & hence doesn't need copying. */
3979 /*                                                                          */
3980 /* This function handles all the requests which operate on the list of      */
3981 /* filter rules.  This includes adding, deleting, insertion.  It is also    */
3982 /* responsible for creating groups when a "head" rule is loaded.  Interface */
3983 /* names are resolved here and other sanity checks are made on the content  */
3984 /* of the rule structure being loaded.  If a rule has user defined timeouts */
3985 /* then make sure they are created and initialised before exiting.          */
3986 /* ------------------------------------------------------------------------ */
3987 int frrequest(unit, req, data, set, makecopy)
3988 int unit;
3989 #if defined(__NetBSD__) || defined(__OpenBSD__) || \
3990  (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300003)
3991 u_long req;
3992 #else
3993 int req;
3994 #endif
3995 int set, makecopy;
3996 caddr_t data;
3997 {
3998 	frentry_t frd, *fp, *f, **fprev, **ftail;
3999 	int error = 0, in, v;
4000 	u_int *p, *pp;
4001 	frgroup_t *fg;
4002 	char *group;
4003 	void *ptr;
4004 
4005 	fg = NULL;
4006 	fp = &frd;
4007 	if (makecopy != 0) {
4008 		error = fr_inobj(data, fp, IPFOBJ_FRENTRY);
4009 		if (error)
4010 			return EFAULT;
4011 		if ((fp->fr_flags & FR_T_BUILTIN) != 0)
4012 			return EINVAL;
4013 		fp->fr_ref = 0;
4014 		fp->fr_flags |= FR_COPIED;
4015 	} else {
4016 		fp = (frentry_t *)data;
4017 		if ((fp->fr_type & FR_T_BUILTIN) == 0)
4018 			return EINVAL;
4019 		fp->fr_flags &= ~FR_COPIED;
4020 	}
4021 
4022 	if (((fp->fr_dsize == 0) && (fp->fr_data != NULL)) ||
4023 	    ((fp->fr_dsize != 0) && (fp->fr_data == NULL)))
4024 		return EINVAL;
4025 
4026 	v = fp->fr_v;
4027 
4028 	/*
4029 	 * Only filter rules for IPv4 or IPv6 are accepted.
4030 	 */
4031 	if (v == 4)
4032 		/*EMPTY*/;
4033 #ifdef	USE_INET6
4034 	else if (v == 6)
4035 		/*EMPTY*/;
4036 #endif
4037 	else {
4038 		return EINVAL;
4039 	}
4040 
4041 	/*
4042 	 * If the rule is being loaded from user space, i.e. we had to copy it
4043 	 * into kernel space, then do not trust the function pointer in the
4044 	 * rule.
4045 	 */
4046 	if ((makecopy == 1) && (fp->fr_func != NULL)) {
4047 		if (fr_findfunc(fp->fr_func) == NULL)
4048 			return ESRCH;
4049 		error = fr_funcinit(fp);
4050 		if (error != 0)
4051 			return error;
4052 	}
4053 
4054 	ptr = NULL;
4055 	/*
4056 	 * Check that the group number does exist and that its use (in/out)
4057 	 * matches what the rule is.
4058 	 */
4059 	group = fp->fr_group;
4060 	if (!strcmp(group, "0"))
4061 		*group = '\0';
4062 
4063 	if (FR_ISACCOUNT(fp->fr_flags))
4064 		unit = IPL_LOGCOUNT;
4065 
4066 	if ((req != (int)SIOCZRLST) && (*group != '\0')) {
4067 		fg = fr_findgroup(group, unit, set, NULL);
4068 		if (fg == NULL)
4069 			return ESRCH;
4070 		if (fg->fg_flags == 0)
4071 			fg->fg_flags = fp->fr_flags & FR_INOUT;
4072 		else if (fg->fg_flags != (fp->fr_flags & FR_INOUT))
4073 			return ESRCH;
4074 	}
4075 
4076 	in = (fp->fr_flags & FR_INQUE) ? 0 : 1;
4077 
4078 	/*
4079 	 * Work out which rule list this change is being applied to.
4080 	 */
4081 	ftail = NULL;
4082 	fprev = NULL;
4083 	if (unit == IPL_LOGAUTH)
4084 		fprev = &ipauth;
4085 	else if (v == 4) {
4086 		if (FR_ISACCOUNT(fp->fr_flags))
4087 			fprev = &ipacct[in][set];
4088 		else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0)
4089 			fprev = &ipfilter[in][set];
4090 	} else if (v == 6) {
4091 		if (FR_ISACCOUNT(fp->fr_flags))
4092 			fprev = &ipacct6[in][set];
4093 		else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0)
4094 			fprev = &ipfilter6[in][set];
4095 	}
4096 	if (fprev == NULL)
4097 		return ESRCH;
4098 
4099 	if (*group != '\0') {
4100 		if (!fg && !(fg = fr_findgroup(group, unit, set, NULL)))
4101 			return ESRCH;
4102 		fprev = &fg->fg_start;
4103 	}
4104 
4105 	for (f = *fprev; f != NULL; fprev = &f->fr_next)
4106 		if (fp->fr_collect <= f->fr_collect)
4107 			break;
4108 	ftail = fprev;
4109 
4110 	/*
4111 	 * Copy in extra data for the rule.
4112 	 */
4113 	if (fp->fr_dsize != 0) {
4114 		if (makecopy != 0) {
4115 			KMALLOCS(ptr, void *, fp->fr_dsize);
4116 			if (!ptr)
4117 				return ENOMEM;
4118 			error = COPYIN(fp->fr_data, ptr, fp->fr_dsize);
4119 		} else {
4120 			ptr = fp->fr_data;
4121 			error = 0;
4122 		}
4123 		if (error != 0) {
4124 			KFREES(ptr, fp->fr_dsize);
4125 			return ENOMEM;
4126 		}
4127 		fp->fr_data = ptr;
4128 	} else
4129 		fp->fr_data = NULL;
4130 
4131 	/*
4132 	 * Perform per-rule type sanity checks of their members.
4133 	 */
4134 	switch (fp->fr_type & ~FR_T_BUILTIN)
4135 	{
4136 #if defined(IPFILTER_BPF) && defined(_KERNEL)
4137 	case FR_T_BPFOPC :
4138 		if (fp->fr_dsize == 0)
4139 			return EINVAL;
4140 		if (!bpf_validate(ptr, fp->fr_dsize/sizeof(struct bpf_insn))) {
4141 			if (makecopy && fp->fr_data != NULL) {
4142 				KFREES(fp->fr_data, fp->fr_dsize);
4143 			}
4144 			return EINVAL;
4145 		}
4146 		break;
4147 #endif
4148 	case FR_T_IPF :
4149 		if (fp->fr_dsize == 0)
4150 			return EINVAL;
4151 		switch (fp->fr_satype)
4152 		{
4153 		case FRI_BROADCAST :
4154 		case FRI_DYNAMIC :
4155 		case FRI_NETWORK :
4156 		case FRI_NETMASKED :
4157 		case FRI_PEERADDR :
4158 			if (fp->fr_sifpidx < 0 || fp->fr_sifpidx > 3) {
4159 				if (makecopy && fp->fr_data != NULL) {
4160 					KFREES(fp->fr_data, fp->fr_dsize);
4161 				}
4162 				return EINVAL;
4163 			}
4164 			break;
4165 #ifdef	IPFILTER_LOOKUP
4166 		case FRI_LOOKUP :
4167 			fp->fr_srcptr = fr_resolvelookup(fp->fr_srctype,
4168 							 fp->fr_srcnum,
4169 							 &fp->fr_srcfunc);
4170 			break;
4171 #endif
4172 		default :
4173 			break;
4174 		}
4175 
4176 		switch (fp->fr_datype)
4177 		{
4178 		case FRI_BROADCAST :
4179 		case FRI_DYNAMIC :
4180 		case FRI_NETWORK :
4181 		case FRI_NETMASKED :
4182 		case FRI_PEERADDR :
4183 			if (fp->fr_difpidx < 0 || fp->fr_difpidx > 3) {
4184 				if (makecopy && fp->fr_data != NULL) {
4185 					KFREES(fp->fr_data, fp->fr_dsize);
4186 				}
4187 				return EINVAL;
4188 			}
4189 			break;
4190 #ifdef	IPFILTER_LOOKUP
4191 		case FRI_LOOKUP :
4192 			fp->fr_dstptr = fr_resolvelookup(fp->fr_dsttype,
4193 							 fp->fr_dstnum,
4194 							 &fp->fr_dstfunc);
4195 			break;
4196 #endif
4197 		default :
4198 
4199 			break;
4200 		}
4201 		break;
4202 	case FR_T_NONE :
4203 		break;
4204 	case FR_T_CALLFUNC :
4205 		break;
4206 	case FR_T_COMPIPF :
4207 		break;
4208 	default :
4209 		if (makecopy && fp->fr_data != NULL) {
4210 			KFREES(fp->fr_data, fp->fr_dsize);
4211 		}
4212 		return EINVAL;
4213 	}
4214 
4215 	/*
4216 	 * Lookup all the interface names that are part of the rule.
4217 	 */
4218 	frsynclist(fp);
4219 	fp->fr_statecnt = 0;
4220 
4221 	/*
4222 	 * Look for an existing matching filter rule, but don't include the
4223 	 * next or interface pointer in the comparison (fr_next, fr_ifa).
4224 	 * This elminates rules which are indentical being loaded.  Checksum
4225 	 * the constant part of the filter rule to make comparisons quicker
4226 	 * (this meaning no pointers are included).
4227 	 */
4228 	for (fp->fr_cksum = 0, p = (u_int *)&fp->fr_func, pp = &fp->fr_cksum;
4229 	     p < pp; p++)
4230 		fp->fr_cksum += *p;
4231 	pp = (u_int *)(fp->fr_caddr + fp->fr_dsize);
4232 	for (p = (u_int *)fp->fr_data; p < pp; p++)
4233 		fp->fr_cksum += *p;
4234 
4235 	WRITE_ENTER(&ipf_mutex);
4236 	bzero((char *)frcache, sizeof(frcache));
4237 
4238 	for (; (f = *ftail) != NULL; ftail = &f->fr_next)
4239 		if ((fp->fr_cksum == f->fr_cksum) &&
4240 		    (f->fr_dsize == fp->fr_dsize) &&
4241 		    !bcmp((char *)&f->fr_dsize,
4242 			  (char *)&fp->fr_dsize, FR_CMPSIZ) &&
4243 		    (!ptr || !f->fr_data ||
4244 		     !bcmp((char *)ptr, (char *)f->fr_data, f->fr_dsize)))
4245 			break;
4246 
4247 	/*
4248 	 * If zero'ing statistics, copy current to caller and zero.
4249 	 */
4250 	if (req == (int)SIOCZRLST) {
4251 		if (f == NULL)
4252 			error = ESRCH;
4253 		else {
4254 			error = fr_outobj(data, f, IPFOBJ_FRENTRY);
4255 			if (error == 0) {
4256 				if (f->fr_dsize != 0 && f->fr_data != NULL)
4257 					error = COPYOUT(f->fr_data, ptr,
4258 							f->fr_dsize);
4259 				if (error == 0) {
4260 					f->fr_hits = 0;
4261 					f->fr_bytes = 0;
4262 				}
4263 			}
4264 		}
4265 
4266 		if (ptr != NULL && makecopy != 0) {
4267 			KFREES(ptr, fp->fr_dsize);
4268 		}
4269 		RWLOCK_EXIT(&ipf_mutex);
4270 		return error;
4271 	}
4272 
4273 	if (!f) {
4274 		if (req == (int)SIOCINAFR || req == (int)SIOCINIFR) {
4275 			ftail = fprev;
4276 			if (fp->fr_hits != 0) {
4277 				while (--fp->fr_hits && (f = *ftail))
4278 					ftail = &f->fr_next;
4279 			}
4280 			f = NULL;
4281 			ptr = NULL;
4282 			error = 0;
4283 		}
4284 	}
4285 
4286 	/*
4287 	 * Request to remove a rule.
4288 	 */
4289 	if (req == (int)SIOCRMAFR || req == (int)SIOCRMIFR) {
4290 		if (!f)
4291 			error = ESRCH;
4292 		else {
4293 			/*
4294 			 * Do not allow activity from user space to interfere
4295 			 * with rules not loaded that way.
4296 			 */
4297 			if ((makecopy == 1) && !(f->fr_flags & FR_COPIED)) {
4298 				error = EPERM;
4299 				goto done;
4300 			}
4301 
4302 			/*
4303 			 * Return EBUSY if the rule is being reference by
4304 			 * something else (eg state information.
4305 			 */
4306 			if (f->fr_ref > 1) {
4307 				error = EBUSY;
4308 				goto done;
4309 			}
4310 #ifdef	IPFILTER_SCAN
4311 			if (f->fr_isctag[0] != '\0' &&
4312 			    (f->fr_isc != (struct ipscan *)-1))
4313 				isc_detachfr(f);
4314 #endif
4315 			if ((fg != NULL) && (fg->fg_head != NULL))
4316 				fg->fg_head->fr_ref--;
4317 			if (unit == IPL_LOGAUTH) {
4318 				error = fr_preauthcmd(req, f, ftail);
4319 				goto done;
4320 			}
4321 			if (*f->fr_grhead != '\0')
4322 				fr_delgroup(f->fr_grhead, unit, set);
4323 			fr_fixskip(fprev, f, -1);
4324 			*ftail = f->fr_next;
4325 			f->fr_next = NULL;
4326 			(void)fr_derefrule(&f);
4327 		}
4328 	} else {
4329 		/*
4330 		 * Not removing, so we must be adding/inserting a rule.
4331 		 */
4332 		if (f)
4333 			error = EEXIST;
4334 		else {
4335 			if (unit == IPL_LOGAUTH) {
4336 				error = fr_preauthcmd(req, fp, ftail);
4337 				goto done;
4338 			}
4339 			if (makecopy) {
4340 				KMALLOC(f, frentry_t *);
4341 			} else
4342 				f = fp;
4343 			if (f != NULL) {
4344 				if (fg != NULL && fg->fg_head!= NULL )
4345 					fg->fg_head->fr_ref++;
4346 				if (fp != f)
4347 					bcopy((char *)fp, (char *)f,
4348 					      sizeof(*f));
4349 				MUTEX_NUKE(&f->fr_lock);
4350 				MUTEX_INIT(&f->fr_lock, "filter rule lock");
4351 #ifdef	IPFILTER_SCAN
4352 				if (f->fr_isctag[0] != '\0' && isc_attachfr(f))
4353 					f->fr_isc = (struct ipscan *)-1;
4354 #endif
4355 				f->fr_hits = 0;
4356 				if (makecopy != 0)
4357 					f->fr_ref = 1;
4358 				f->fr_next = *ftail;
4359 				*ftail = f;
4360 				if (req == (int)SIOCINIFR ||
4361 				    req == (int)SIOCINAFR)
4362 					fr_fixskip(fprev, f, 1);
4363 				f->fr_grp = NULL;
4364 				group = f->fr_grhead;
4365 				if (*group != '\0') {
4366 					fg = fr_addgroup(group, f, f->fr_flags,
4367 							 unit, set);
4368 					if (fg != NULL)
4369 						f->fr_grp = &fg->fg_start;
4370 				}
4371 			} else
4372 				error = ENOMEM;
4373 		}
4374 	}
4375 done:
4376 	RWLOCK_EXIT(&ipf_mutex);
4377 	if ((ptr != NULL) && (error != 0) && (makecopy != 0)) {
4378 		KFREES(ptr, fp->fr_dsize);
4379 	}
4380 	return (error);
4381 }
4382 
4383 
4384 /* ------------------------------------------------------------------------ */
4385 /* Function:    fr_funcinit                                                 */
4386 /* Returns:     int - 0 == success, else ESRCH: cannot resolve rule details */
4387 /* Parameters:  fr(I) - pointer to filter rule                              */
4388 /*                                                                          */
4389 /* If a rule is a call rule, then check if the function it points to needs  */
4390 /* an init function to be called now the rule has been loaded.              */
4391 /* ------------------------------------------------------------------------ */
4392 static int fr_funcinit(fr)
4393 frentry_t *fr;
4394 {
4395 	ipfunc_resolve_t *ft;
4396 	int err;
4397 
4398 	err = ESRCH;
4399 
4400 	for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4401 		if (ft->ipfu_addr == fr->fr_func) {
4402 			err = 0;
4403 			if (ft->ipfu_init != NULL)
4404 				err = (*ft->ipfu_init)(fr);
4405 			break;
4406 		}
4407 	return err;
4408 }
4409 
4410 
4411 /* ------------------------------------------------------------------------ */
4412 /* Function:    fr_findfunc                                                 */
4413 /* Returns:     ipfunc_t - pointer to function if found, else NULL          */
4414 /* Parameters:  funcptr(I) - function pointer to lookup                     */
4415 /*                                                                          */
4416 /* Look for a function in the table of known functions.                     */
4417 /* ------------------------------------------------------------------------ */
4418 static ipfunc_t fr_findfunc(funcptr)
4419 ipfunc_t funcptr;
4420 {
4421 	ipfunc_resolve_t *ft;
4422 
4423 	for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4424 		if (ft->ipfu_addr == funcptr)
4425 			return funcptr;
4426 	return NULL;
4427 }
4428 
4429 
4430 /* ------------------------------------------------------------------------ */
4431 /* Function:    fr_resolvefunc                                              */
4432 /* Returns:     int - 0 == success, else error                              */
4433 /* Parameters:  data(IO) - ioctl data pointer to ipfunc_resolve_t struct    */
4434 /*                                                                          */
4435 /* Copy in a ipfunc_resolve_t structure and then fill in the missing field. */
4436 /* This will either be the function name (if the pointer is set) or the     */
4437 /* function pointer if the name is set.  When found, fill in the details so */
4438 /* it can be copied back to user space.                                     */
4439 /* ------------------------------------------------------------------------ */
4440 int fr_resolvefunc(data)
4441 void *data;
4442 {
4443 	ipfunc_resolve_t res, *ft;
4444 
4445 	if (COPYIN(data, &res, sizeof(res)) != 0)
4446 		return EFAULT;
4447 
4448 	if (res.ipfu_addr == NULL && res.ipfu_name[0] != '\0') {
4449 		for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4450 			if (strncmp(res.ipfu_name, ft->ipfu_name,
4451 				    sizeof(res.ipfu_name)) == 0) {
4452 				res.ipfu_addr = ft->ipfu_addr;
4453 				res.ipfu_init = ft->ipfu_init;
4454 				if (COPYOUT(&res, data, sizeof(res)) != 0)
4455 					return EFAULT;
4456 				return 0;
4457 			}
4458 	}
4459 	if (res.ipfu_addr != NULL && res.ipfu_name[0] == '\0') {
4460 		for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4461 			if (ft->ipfu_addr == res.ipfu_addr) {
4462 				(void) strncpy(res.ipfu_name, ft->ipfu_name,
4463 					sizeof(res.ipfu_name));
4464 				res.ipfu_init = ft->ipfu_init;
4465 				if (COPYOUT(&res, data, sizeof(res)) != 0)
4466 					return EFAULT;
4467 				return 0;
4468 			}
4469 	}
4470 	return ESRCH;
4471 }
4472 
4473 
4474 #if !defined(_KERNEL) || (!defined(NetBSD) && !defined(OpenBSD)) || \
4475     ((defined(NetBSD) && (__NetBSD_Version__ < 105000000)) && \
4476      (defined(OpenBSD) && (OpenBSD < 200006)))
4477 /*
4478  * From: NetBSD
4479  * ppsratecheck(): packets (or events) per second limitation.
4480  */
4481 int
4482 ppsratecheck(lasttime, curpps, maxpps)
4483 	struct timeval *lasttime;
4484 	int *curpps;
4485 	int maxpps;	/* maximum pps allowed */
4486 {
4487 	struct timeval tv, delta;
4488 	int rv;
4489 
4490 	GETKTIME(&tv);
4491 
4492 	delta.tv_sec = tv.tv_sec - lasttime->tv_sec;
4493 	delta.tv_usec = tv.tv_usec - lasttime->tv_usec;
4494 	if (delta.tv_usec < 0) {
4495 		delta.tv_sec--;
4496 		delta.tv_usec += 1000000;
4497 	}
4498 
4499 	/*
4500 	 * check for 0,0 is so that the message will be seen at least once.
4501 	 * if more than one second have passed since the last update of
4502 	 * lasttime, reset the counter.
4503 	 *
4504 	 * we do increment *curpps even in *curpps < maxpps case, as some may
4505 	 * try to use *curpps for stat purposes as well.
4506 	 */
4507 	if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) ||
4508 	    delta.tv_sec >= 1) {
4509 		*lasttime = tv;
4510 		*curpps = 0;
4511 		rv = 1;
4512 	} else if (maxpps < 0)
4513 		rv = 1;
4514 	else if (*curpps < maxpps)
4515 		rv = 1;
4516 	else
4517 		rv = 0;
4518 	*curpps = *curpps + 1;
4519 
4520 	return (rv);
4521 }
4522 #endif
4523 
4524 
4525 /* ------------------------------------------------------------------------ */
4526 /* Function:    fr_derefrule                                                */
4527 /* Returns:     int   - 0 == rule freed up, else rule not freed             */
4528 /* Parameters:  fr(I) - pointer to filter rule                              */
4529 /*                                                                          */
4530 /* Decrement the reference counter to a rule by one.  If it reaches zero,   */
4531 /* free it and any associated storage space being used by it.               */
4532 /* ------------------------------------------------------------------------ */
4533 int fr_derefrule(frp)
4534 frentry_t **frp;
4535 {
4536 	frentry_t *fr;
4537 
4538 	fr = *frp;
4539 
4540 	MUTEX_ENTER(&fr->fr_lock);
4541 	fr->fr_ref--;
4542 	if (fr->fr_ref == 0) {
4543 		MUTEX_EXIT(&fr->fr_lock);
4544 		MUTEX_DESTROY(&fr->fr_lock);
4545 
4546 #ifdef IPFILTER_LOOKUP
4547 		if (fr->fr_type == FR_T_IPF && fr->fr_satype == FRI_LOOKUP)
4548 			ip_lookup_deref(fr->fr_srctype, fr->fr_srcptr);
4549 		if (fr->fr_type == FR_T_IPF && fr->fr_datype == FRI_LOOKUP)
4550 			ip_lookup_deref(fr->fr_dsttype, fr->fr_dstptr);
4551 #endif
4552 
4553 		if (fr->fr_dsize) {
4554 			KFREES(fr->fr_data, fr->fr_dsize);
4555 		}
4556 		if ((fr->fr_flags & FR_COPIED) != 0) {
4557 			KFREE(fr);
4558 			return 0;
4559 		}
4560 		return 1;
4561 	} else {
4562 		MUTEX_EXIT(&fr->fr_lock);
4563 	}
4564 	*frp = NULL;
4565 	return -1;
4566 }
4567 
4568 
4569 #ifdef	IPFILTER_LOOKUP
4570 /* ------------------------------------------------------------------------ */
4571 /* Function:    fr_grpmapinit                                               */
4572 /* Returns:     int - 0 == success, else ESRCH because table entry not found*/
4573 /* Parameters:  fr(I) - pointer to rule to find hash table for              */
4574 /*                                                                          */
4575 /* Looks for group hash table fr_arg and stores a pointer to it in fr_ptr.  */
4576 /* fr_ptr is later used by fr_srcgrpmap and fr_dstgrpmap.                   */
4577 /* ------------------------------------------------------------------------ */
4578 static int fr_grpmapinit(fr)
4579 frentry_t *fr;
4580 {
4581 	char name[FR_GROUPLEN];
4582 	iphtable_t *iph;
4583 
4584 	(void) sprintf(name, "%d", fr->fr_arg);
4585 	iph = fr_findhtable(IPL_LOGIPF, name);
4586 	if (iph == NULL)
4587 		return ESRCH;
4588 	if ((iph->iph_flags & FR_INOUT) != (fr->fr_flags & FR_INOUT))
4589 		return ESRCH;
4590 	fr->fr_ptr = iph;
4591 	return 0;
4592 }
4593 
4594 
4595 /* ------------------------------------------------------------------------ */
4596 /* Function:    fr_srcgrpmap                                                */
4597 /* Returns:     frentry_t * - pointer to "new last matching" rule or NULL   */
4598 /* Parameters:  fin(I)    - pointer to packet information                   */
4599 /*              passp(IO) - pointer to current/new filter decision (unused) */
4600 /*                                                                          */
4601 /* Look for a rule group head in a hash table, using the source address as  */
4602 /* the key, and descend into that group and continue matching rules against */
4603 /* the packet.                                                              */
4604 /* ------------------------------------------------------------------------ */
4605 frentry_t *fr_srcgrpmap(fin, passp)
4606 fr_info_t *fin;
4607 u_32_t *passp;
4608 {
4609 	frgroup_t *fg;
4610 	void *rval;
4611 
4612 	rval = fr_iphmfindgroup(fin->fin_fr->fr_ptr, fin->fin_v, &fin->fin_src);
4613 	if (rval == NULL)
4614 		return NULL;
4615 
4616 	fg = rval;
4617 	fin->fin_fr = fg->fg_start;
4618 	(void) fr_scanlist(fin, *passp);
4619 	return fin->fin_fr;
4620 }
4621 
4622 
4623 /* ------------------------------------------------------------------------ */
4624 /* Function:    fr_dstgrpmap                                                */
4625 /* Returns:     frentry_t * - pointer to "new last matching" rule or NULL   */
4626 /* Parameters:  fin(I)    - pointer to packet information                   */
4627 /*              passp(IO) - pointer to current/new filter decision (unused) */
4628 /*                                                                          */
4629 /* Look for a rule group head in a hash table, using the destination        */
4630 /* address as the key, and descend into that group and continue matching    */
4631 /* rules against  the packet.                                               */
4632 /* ------------------------------------------------------------------------ */
4633 frentry_t *fr_dstgrpmap(fin, passp)
4634 fr_info_t *fin;
4635 u_32_t *passp;
4636 {
4637 	frgroup_t *fg;
4638 	void *rval;
4639 
4640 	rval = fr_iphmfindgroup(fin->fin_fr->fr_ptr, fin->fin_v, &fin->fin_dst);
4641 	if (rval == NULL)
4642 		return NULL;
4643 
4644 	fg = rval;
4645 	fin->fin_fr = fg->fg_start;
4646 	(void) fr_scanlist(fin, *passp);
4647 	return fin->fin_fr;
4648 }
4649 #endif /* IPFILTER_LOOKUP */
4650 
4651 
4652 /* ------------------------------------------------------------------------ */
4653 /* Function:    fr_addtimeoutqueue                                          */
4654 /* Returns:     struct ifqtq * - NULL if malloc fails, else pointer to      */
4655 /*                               timeout queue with given interval.         */
4656 /* Parameters:  parent(I)  - pointer to pointer to parent node of this list */
4657 /*                           of interface queues.                           */
4658 /*              seconds(I) - timeout value in seconds for this queue.       */
4659 /*                                                                          */
4660 /* This routine first looks for a timeout queue that matches the interval   */
4661 /* being requested.  If it finds one, increments the reference counter and  */
4662 /* returns a pointer to it.  If none are found, it allocates a new one and  */
4663 /* inserts it at the top of the list.                                       */
4664 /* ------------------------------------------------------------------------ */
4665 ipftq_t *fr_addtimeoutqueue(parent, seconds)
4666 ipftq_t **parent;
4667 u_int seconds;
4668 {
4669 	u_int period;
4670 	ipftq_t *ifq;
4671 
4672 	period = seconds * IPF_HZ_DIVIDE;
4673 	MUTEX_ENTER(&ipf_timeoutlock);
4674 	for (ifq = *parent; ifq != NULL; ifq = ifq->ifq_next)
4675 		if (ifq->ifq_ttl == period)
4676 			break;
4677 
4678 	if (ifq != NULL) {
4679 		MUTEX_ENTER(&ifq->ifq_lock);
4680 		ifq->ifq_ref++;
4681 		MUTEX_EXIT(&ifq->ifq_lock);
4682 		MUTEX_EXIT(&ipf_timeoutlock);
4683 		return ifq;
4684 	}
4685 
4686 	KMALLOC(ifq, ipftq_t *);
4687 	if (ifq != NULL) {
4688 		ifq->ifq_ttl = period;
4689 		ifq->ifq_head = NULL;
4690 		ifq->ifq_tail = &ifq->ifq_head;
4691 		ifq->ifq_next = *parent;
4692 		ifq->ifq_pnext = parent;
4693 		ifq->ifq_ref = 1;
4694 		ifq->ifq_flags = IFQF_USER;
4695 		*parent = ifq;
4696 		fr_userifqs++;
4697 		MUTEX_NUKE(&ifq->ifq_lock);
4698 		MUTEX_INIT(&ifq->ifq_lock, "ipftq mutex");
4699 	}
4700 	MUTEX_EXIT(&ipf_timeoutlock);
4701 	return ifq;
4702 }
4703 
4704 
4705 /* ------------------------------------------------------------------------ */
4706 /* Function:    fr_deletetimeoutqueue                                       */
4707 /* Returns:     Nil                                                         */
4708 /* Parameters:  difp(I) - timeout queue which is losing a reference.        */
4709 /*                                                                          */
4710 /* This routine must be called when we're discarding a pointer to a timeout */
4711 /* queue object.  It takes care of the reference counter and free's it when */
4712 /* it reaches 0.                                                            */
4713 /* ------------------------------------------------------------------------ */
4714 void fr_deletetimeoutqueue(ifq)
4715 ipftq_t *ifq;
4716 {
4717 	MUTEX_ENTER(&ipf_timeoutlock);
4718 	MUTEX_ENTER(&ifq->ifq_lock);
4719 
4720 	ifq->ifq_ref--;
4721 
4722 	if ((ifq->ifq_ref == 0) && (ifq->ifq_head == NULL)) {
4723 		/*
4724 		 * Remove from its position in the list.
4725 		 */
4726 		*ifq->ifq_pnext = ifq->ifq_next;
4727 		if (ifq->ifq_next != NULL)
4728 			ifq->ifq_next->ifq_pnext = ifq->ifq_pnext;
4729 
4730 		MUTEX_EXIT(&ifq->ifq_lock);	/* Tru64 */
4731 		MUTEX_DESTROY(&ifq->ifq_lock);
4732 		KFREE(ifq);
4733 		fr_userifqs--;
4734 	} else {
4735 		MUTEX_EXIT(&ifq->ifq_lock);
4736 	}
4737 	MUTEX_EXIT(&ipf_timeoutlock);
4738 }
4739 
4740 
4741 /* ------------------------------------------------------------------------ */
4742 /* Function:    fr_queuefront                                               */
4743 /* Returns:     Nil                                                         */
4744 /* Parameters:  tq(I)  - pointer to timeout queue information               */
4745 /*                                                                          */
4746 /* Move a queue entry to the front of the queue, if it isn't already there. */
4747 /* ------------------------------------------------------------------------ */
4748 void fr_queuefront(tqe)
4749 ipftqent_t *tqe;
4750 {
4751 	ipftq_t *ifq;
4752 
4753 	ifq = tqe->tqe_ifq;
4754 
4755 	if (ifq->ifq_head != tqe) {
4756 		MUTEX_ENTER(&ifq->ifq_lock);
4757 		*tqe->tqe_pnext = tqe->tqe_next;
4758 		if (tqe->tqe_next)
4759 			tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
4760 		else
4761 			ifq->ifq_tail = tqe->tqe_pnext;
4762 
4763 		tqe->tqe_next = ifq->ifq_head;
4764 		ifq->ifq_head->tqe_pnext = &tqe->tqe_next;
4765 		ifq->ifq_head = tqe;
4766 		tqe->tqe_pnext = &ifq->ifq_head;
4767 		MUTEX_EXIT(&ifq->ifq_lock);
4768 	}
4769 }
4770 
4771 
4772 /* ------------------------------------------------------------------------ */
4773 /* Function:    fr_queueback                                                */
4774 /* Returns:     Nil                                                         */
4775 /* Parameters:  tq(I)  - pointer to timeout queue information               */
4776 /*                                                                          */
4777 /* Move a queue entry to the back of the queue, if it isn't already there.  */
4778 /* ------------------------------------------------------------------------ */
4779 void fr_queueback(tqe)
4780 ipftqent_t *tqe;
4781 {
4782 	ipftq_t *ifq;
4783 
4784 	ifq = tqe->tqe_ifq;
4785 	if (ifq == NULL)
4786 		return;
4787 	if (ifq->ifq_tail != NULL && *ifq->ifq_tail != tqe) {
4788 		MUTEX_ENTER(&ifq->ifq_lock);
4789 		*tqe->tqe_pnext = tqe->tqe_next;
4790 		tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
4791 
4792 		tqe->tqe_next = NULL;
4793 		tqe->tqe_die = fr_ticks + ifq->ifq_ttl;
4794 		tqe->tqe_pnext = ifq->ifq_tail;
4795 		*ifq->ifq_tail = tqe;
4796 		ifq->ifq_tail = &tqe->tqe_next;
4797 		tqe->tqe_die = fr_ticks + ifq->ifq_ttl;
4798 		MUTEX_EXIT(&ifq->ifq_lock);
4799 	}
4800 }
4801 
4802 
4803 /* ------------------------------------------------------------------------ */
4804 /* Function:    fr_movequeue                                                */
4805 /* Returns:     Nil                                                         */
4806 /* Parameters:  tq(I)   - pointer to timeout queue information              */
4807 /*              oifp(I) - old timeout queue entry was on                    */
4808 /*              nifp(I) - new timeout queue to put entry on                 */
4809 /*                                                                          */
4810 /* Move a queue entry from one timeout queue to another timeout queue.      */
4811 /* If it notices that the current entry is already last and does not need   */
4812 /* to move queue, the return.                                               */
4813 /* ------------------------------------------------------------------------ */
4814 void fr_movequeue(tqe, oifq, nifq)
4815 ipftqent_t *tqe;
4816 ipftq_t *oifq, *nifq;
4817 {
4818 	/*
4819 	 * Is the operation here going to be a no-op ?
4820 	 */
4821 	if (oifq == nifq && *oifq->ifq_tail == tqe)
4822 		return;
4823 
4824 	/*
4825 	 * Remove from the old queue
4826 	 */
4827 	MUTEX_ENTER(&oifq->ifq_lock);
4828 	*tqe->tqe_pnext = tqe->tqe_next;
4829 	if (tqe->tqe_next)
4830 		tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
4831 	else
4832 		oifq->ifq_tail = tqe->tqe_pnext;
4833 	tqe->tqe_next = NULL;
4834 
4835 	/*
4836 	 * If we're moving from one queue to another, release the lock on the
4837 	 * old queue and get a lock on the new queue.  For user defined queues,
4838 	 * if we're moving off it, call delete in case it can now be freed.
4839 	 */
4840 	if (oifq != nifq) {
4841 		tqe->tqe_ifq = NULL;
4842 		MUTEX_EXIT(&oifq->ifq_lock);
4843 		if ((oifq->ifq_flags & IFQF_USER) != 0)
4844 			fr_deletetimeoutqueue(oifq);
4845 
4846 		MUTEX_ENTER(&nifq->ifq_lock);
4847 		tqe->tqe_ifq = nifq;
4848 		nifq->ifq_ref++;
4849 	}
4850 
4851 	/*
4852 	 * Add to the bottom of the new queue
4853 	 */
4854 	tqe->tqe_die = fr_ticks + nifq->ifq_ttl;
4855 	tqe->tqe_pnext = nifq->ifq_tail;
4856 	*nifq->ifq_tail = tqe;
4857 	nifq->ifq_tail = &tqe->tqe_next;
4858 	MUTEX_EXIT(&nifq->ifq_lock);
4859 }
4860 
4861 
4862 /* ------------------------------------------------------------------------ */
4863 /* Function:    fr_updateipid                                               */
4864 /* Returns:     int - 0 == success, -1 == error (packet should be droppped) */
4865 /* Parameters:  fin(I) - pointer to packet information                      */
4866 /*                                                                          */
4867 /* When we are doing NAT, change the IP of every packet to represent a      */
4868 /* single sequence of packets coming from the host, hiding any host         */
4869 /* specific sequencing that might otherwise be revealed.  If the packet is  */
4870 /* a fragment, then store the 'new' IPid in the fragment cache and look up  */
4871 /* the fragment cache for non-leading fragments.  If a non-leading fragment */
4872 /* has no match in the cache, return an error.                              */
4873 /* ------------------------------------------------------------------------ */
4874 static INLINE int fr_updateipid(fin)
4875 fr_info_t *fin;
4876 {
4877 	u_short id, ido, sums;
4878 	u_32_t sumd, sum;
4879 	ip_t *ip;
4880 
4881 	if (fin->fin_off != 0) {
4882 		sum = fr_ipid_knownfrag(fin);
4883 		if (sum == 0xffffffff)
4884 			return -1;
4885 		sum &= 0xffff;
4886 		id = (u_short)sum;
4887 	} else {
4888 		id = fr_nextipid(fin);
4889 		if (fin->fin_off == 0 && (fin->fin_flx & FI_FRAG) != 0)
4890 			(void) fr_ipid_newfrag(fin, (u_32_t)id);
4891 	}
4892 
4893 	ip = fin->fin_ip;
4894 	ido = ntohs(ip->ip_id);
4895 	if (id == ido)
4896 		return 0;
4897 	ip->ip_id = htons(id);
4898 	CALC_SUMD(ido, id, sumd);	/* DESTRUCTIVE MACRO! id,ido change */
4899 	sum = (~ntohs(ip->ip_sum)) & 0xffff;
4900 	sum += sumd;
4901 	sum = (sum >> 16) + (sum & 0xffff);
4902 	sum = (sum >> 16) + (sum & 0xffff);
4903 	sums = ~(u_short)sum;
4904 	ip->ip_sum = htons(sums);
4905 	return 0;
4906 }
4907 
4908 
4909 #ifdef	NEED_FRGETIFNAME
4910 /* ------------------------------------------------------------------------ */
4911 /* Function:    fr_getifname                                                */
4912 /* Returns:     char * - pointer to interface name                          */
4913 /* Parameters:  ifp(I) - pointer to network interface                       */
4914 /*                                                                          */
4915 /* Constructs an interface name in the buffer passed.                       */
4916 /* ------------------------------------------------------------------------ */
4917 char *fr_getifname(ifp, buffer)
4918 struct ifnet *ifp;
4919 char *buffer;
4920 {
4921 	static char namebuf[LIFNAMSIZ+1];
4922 	int unit, space;
4923 	char temp[20];
4924 	char *s;
4925 
4926 	if (buffer == NULL)
4927 		buffer = namebuf;
4928 	(void) strncpy(buffer, ifp->if_name, LIFNAMSIZ);
4929 	namebuf[LIFNAMSIZ] = '\0';
4930 	for (s = buffer; *s; s++)
4931 		;
4932 	unit = ifp->if_unit;
4933 	space = LIFNAMSIZ - (s - buffer);
4934 	if (space > 0) {
4935 		(void) sprintf(temp, "%d", unit);
4936 		(void) strncpy(s, temp, space);
4937 	}
4938 	return buffer;
4939 }
4940 #endif
4941 
4942 
4943 /* ------------------------------------------------------------------------ */
4944 /* Function:    fr_ioctlswitch                                              */
4945 /* Returns:     int     - -1 continue processing, else ioctl return value   */
4946 /* Parameters:  unit(I) - device unit opened                                */
4947 /*              data(I) - pointer to ioctl data                             */
4948 /*              cmd(I)  - ioctl command                                     */
4949 /*              mode(I) - mode value                                        */
4950 /*                                                                          */
4951 /* Based on the value of unit, call the appropriate ioctl handler or return */
4952 /* EIO if ipfilter is not running.   Also checks if write perms are req'd   */
4953 /* for the device in order to execute the ioctl.                            */
4954 /* ------------------------------------------------------------------------ */
4955 INLINE int fr_ioctlswitch(unit, data, cmd, mode)
4956 int unit, mode;
4957 # if defined(__NetBSD__) || defined(__OpenBSD__)
4958 u_long cmd;
4959 #else
4960 int cmd;
4961 #endif
4962 void *data;
4963 {
4964 	int error = 0;
4965 
4966 	switch (unit)
4967 	{
4968 	case IPL_LOGIPF :
4969 		error = -1;
4970 		break;
4971 	case IPL_LOGNAT :
4972 		if (fr_running > 0)
4973 			error = fr_nat_ioctl(data, cmd, mode);
4974 		else
4975 			error = EIO;
4976 		break;
4977 	case IPL_LOGSTATE :
4978 		if (fr_running > 0)
4979 			error = fr_state_ioctl(data, cmd, mode);
4980 		else
4981 			error = EIO;
4982 		break;
4983 	case IPL_LOGAUTH :
4984 		if (fr_running > 0) {
4985 			if ((cmd == SIOCADAFR) || (cmd == SIOCRMAFR)) {
4986 				if (!(mode & FWRITE)) {
4987 					error = EPERM;
4988 				} else {
4989 					error = frrequest(unit, cmd, data,
4990 							  fr_active, 1);
4991 				}
4992 			} else {
4993 				error = fr_auth_ioctl(data, cmd, mode);
4994 			}
4995 		} else
4996 			error = EIO;
4997 		break;
4998 	case IPL_LOGSYNC :
4999 #ifdef IPFILTER_SYNC
5000 		if (fr_running > 0)
5001 			error = fr_sync_ioctl(data, cmd, mode);
5002 		else
5003 #endif
5004 			error = EIO;
5005 		break;
5006 	case IPL_LOGSCAN :
5007 #ifdef IPFILTER_SCAN
5008 		if (fr_running > 0)
5009 			error = fr_scan_ioctl(data, cmd, mode);
5010 		else
5011 #endif
5012 			error = EIO;
5013 		break;
5014 	case IPL_LOGLOOKUP :
5015 #ifdef IPFILTER_LOOKUP
5016 		if (fr_running > 0)
5017 			error = ip_lookup_ioctl(data, cmd, mode);
5018 		else
5019 #endif
5020 			error = EIO;
5021 		break;
5022 	default :
5023 		error = EIO;
5024 		break;
5025 	}
5026 
5027 	return error;
5028 }
5029 
5030 
5031 /*
5032  * This array defines the expected size of objects coming into the kernel
5033  * for the various recognised object types.
5034  */
5035 static	int	fr_objbytes[] = {
5036 	0,			/* frentry */
5037 	sizeof(struct friostat),
5038 	sizeof(struct fr_info),
5039 	sizeof(struct fr_authstat),
5040 	sizeof(struct ipfrstat),
5041 	sizeof(struct ipnat),
5042 	sizeof(struct natstat),
5043 	sizeof(struct ipstate_save),
5044 	sizeof(struct nat_save),
5045 	sizeof(struct natlookup),
5046 	0,			/* ipstate */
5047 	sizeof(struct ips_stat),
5048 	sizeof(struct frauth),
5049 	sizeof(struct ipftune)
5050 };
5051 
5052 
5053 /* ------------------------------------------------------------------------ */
5054 /* Function:    fr_inobj                                                    */
5055 /* Returns:     int     - 0 = success, else failure                         */
5056 /* Parameters:  data(I) - pointer to ioctl data                             */
5057 /*              ptr(I)  - pointer to store real data in                     */
5058 /*              type(I) - type of structure being moved                     */
5059 /*                                                                          */
5060 /* Copy in the contents of what the ipfobj_t points to.  In future, we      */
5061 /* add things to check for version numbers, sizes, etc, to make it backward */
5062 /* compatible at the ABI for user land.                                     */
5063 /* ------------------------------------------------------------------------ */
5064 int fr_inobj(data, ptr, type)
5065 void *data;
5066 void *ptr;
5067 int type;
5068 {
5069 	ipfobj_t obj;
5070 	int error = 0;
5071 
5072 	if ((type < 0) ||
5073 	    (type > ((sizeof(fr_objbytes)/sizeof(fr_objbytes[0])) - 1)))
5074 		return EINVAL;
5075 
5076 	(void) BCOPYIN((caddr_t)data, (caddr_t)&obj, sizeof(obj));
5077 
5078 	if (obj.ipfo_type != type)
5079 		return EINVAL;
5080 
5081 #ifndef	IPFILTER_COMPAT
5082 	if (obj.ipfo_rev != IPFILTER_VERSION)
5083 		return EINVAL;
5084 	if ((fr_objbytes[type] != 0) && (obj.ipfo_size != fr_objbytes[type]))
5085 		return EINVAL;
5086 #else
5087 	if (obj.ipfo_rev != IPFILTER_VERSION)
5088 		/* XXX compatibility hook here */
5089 		return EINVAL;
5090 
5091 	if ((fr_objbytes[type] != 0) && (obj.ipfo_size != fr_objbytes[type]))
5092 		/* XXX compatibility hook here */
5093 		return EINVAL;
5094 #endif
5095 
5096 	error = COPYIN((caddr_t)obj.ipfo_ptr, (caddr_t)ptr, obj.ipfo_size);
5097 
5098 	return error;
5099 }
5100 
5101 
5102 /* ------------------------------------------------------------------------ */
5103 /* Function:    fr_outobj                                                   */
5104 /* Returns:     int     - 0 = success, else failure                         */
5105 /* Parameters:  data(I) - pointer to ioctl data                             */
5106 /*              ptr(I)  - pointer to store real data in                     */
5107 /*              type(I) - type of structure being moved                     */
5108 /*                                                                          */
5109 /* Copy out the contents of what ptr is to where ipfobj points to.  In      */
5110 /* future, we add things to check for version numbers, sizes, etc, to make  */
5111 /* it backward  compatible at the ABI for user land.                        */
5112 /* ------------------------------------------------------------------------ */
5113 int fr_outobj(data, ptr, type)
5114 void *data;
5115 void *ptr;
5116 int type;
5117 {
5118 	ipfobj_t obj;
5119 	int error;
5120 
5121 	(void) BCOPYIN((caddr_t)data, (caddr_t)&obj, sizeof(obj));
5122 
5123 	if (obj.ipfo_type != type)
5124 		return EINVAL;
5125 
5126 #ifndef	IPFILTER_COMPAT
5127 	if (obj.ipfo_rev != IPFILTER_VERSION)
5128 		return EINVAL;
5129 	if ((fr_objbytes[type] != 0) && (obj.ipfo_size != fr_objbytes[type]))
5130 		return EINVAL;
5131 #else
5132 	if (obj.ipfo_rev != IPFILTER_VERSION)
5133 		/* XXX compatibility hook here */
5134 		return EINVAL;
5135 	if ((fr_objbytes[type] != 0) && (obj.ipfo_size != fr_objbytes[type]))
5136 		/* XXX compatibility hook here */
5137 		return EINVAL;
5138 #endif
5139 
5140 	error = COPYOUT((caddr_t)ptr, (caddr_t)obj.ipfo_ptr, obj.ipfo_size);
5141 	return error;
5142 }
5143 
5144 
5145 /* ------------------------------------------------------------------------ */
5146 /* Function:    fr_checkl4sum                                               */
5147 /* Returns:     int     - 0 = good, -1 = bad, 1 = cannot check              */
5148 /* Parameters:  fin(I) - pointer to packet information                      */
5149 /*                                                                          */
5150 /* If possible, calculate the layer 4 checksum for the packet.  If this is  */
5151 /* not possible, return without indicating a failure or success but in a    */
5152 /* way that is ditinguishable.                                              */
5153 /* ------------------------------------------------------------------------ */
5154 int fr_checkl4sum(fin)
5155 fr_info_t *fin;
5156 {
5157 	u_short sum, hdrsum, *csump;
5158 	udphdr_t *udp;
5159 	int dosum;
5160 
5161 	if ((fin->fin_flx & FI_NOCKSUM) != 0)
5162 		return 0;
5163 
5164 	/*
5165 	 * If the TCP packet isn't a fragment, isn't too short and otherwise
5166 	 * isn't already considered "bad", then validate the checksum.  If
5167 	 * this check fails then considered the packet to be "bad".
5168 	 */
5169 	if ((fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) != 0)
5170 		return 1;
5171 
5172 	csump = NULL;
5173 	hdrsum = 0;
5174 	dosum = 0;
5175 	sum = 0;
5176 
5177 #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID)
5178 	if (dohwcksum && ((*fin->fin_mp)->b_ick_flag == ICK_VALID)) {
5179 		hdrsum = 0;
5180 		sum = 0;
5181 	} else {
5182 #endif
5183 		switch (fin->fin_p)
5184 		{
5185 		case IPPROTO_TCP :
5186 			csump = &((tcphdr_t *)fin->fin_dp)->th_sum;
5187 			dosum = 1;
5188 			break;
5189 
5190 		case IPPROTO_UDP :
5191 			udp = fin->fin_dp;
5192 			if (udp->uh_sum != 0) {
5193 				csump = &udp->uh_sum;
5194 				dosum = 1;
5195 			}
5196 			break;
5197 
5198 		case IPPROTO_ICMP :
5199 			csump = &((struct icmp *)fin->fin_dp)->icmp_cksum;
5200 			dosum = 1;
5201 			break;
5202 
5203 		default :
5204 			return 1;
5205 			/*NOTREACHED*/
5206 		}
5207 
5208 		if (csump != NULL)
5209 			hdrsum = *csump;
5210 
5211 		if (dosum)
5212 			sum = fr_cksum(fin->fin_m, fin->fin_ip,
5213 				       fin->fin_p, fin->fin_dp);
5214 #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID)
5215 	}
5216 #endif
5217 #if !defined(_KERNEL)
5218 	FR_DEBUG(("checkl4sum: %hx == %hx\n", sum, hdrsum));
5219 #endif
5220 	if (hdrsum == sum)
5221 		return 0;
5222 	return -1;
5223 }
5224 
5225 
5226 /* ------------------------------------------------------------------------ */
5227 /* Function:    fr_ifpfillv4addr                                            */
5228 /* Returns:     int     - 0 = address update, -1 = address not updated      */
5229 /* Parameters:  atype(I)   - type of network address update to perform      */
5230 /*              sin(I)     - pointer to source of address information       */
5231 /*              mask(I)    - pointer to source of netmask information       */
5232 /*              inp(I)     - pointer to destination address store           */
5233 /*              inpmask(I) - pointer to destination netmask store           */
5234 /*                                                                          */
5235 /* Given a type of network address update (atype) to perform, copy          */
5236 /* information from sin/mask into inp/inpmask.  If ipnmask is NULL then no  */
5237 /* netmask update is performed unless FRI_NETMASKED is passed as atype, in  */
5238 /* which case the operation fails.  For all values of atype other than      */
5239 /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s  */
5240 /* value.                                                                   */
5241 /* ------------------------------------------------------------------------ */
5242 int fr_ifpfillv4addr(atype, sin, mask, inp, inpmask)
5243 int atype;
5244 struct sockaddr_in *sin, *mask;
5245 struct in_addr *inp, *inpmask;
5246 {
5247 	if (inpmask != NULL && atype != FRI_NETMASKED)
5248 		inpmask->s_addr = 0xffffffff;
5249 
5250 	if (atype == FRI_NETWORK || atype == FRI_NETMASKED) {
5251 		if (atype == FRI_NETMASKED) {
5252 			if (inpmask == NULL)
5253 				return -1;
5254 			inpmask->s_addr = mask->sin_addr.s_addr;
5255 		}
5256 		inp->s_addr = sin->sin_addr.s_addr & mask->sin_addr.s_addr;
5257 	} else {
5258 		inp->s_addr = sin->sin_addr.s_addr;
5259 	}
5260 	return 0;
5261 }
5262 
5263 
5264 #ifdef	USE_INET6
5265 /* ------------------------------------------------------------------------ */
5266 /* Function:    fr_ifpfillv6addr                                            */
5267 /* Returns:     int     - 0 = address update, -1 = address not updated      */
5268 /* Parameters:  atype(I)   - type of network address update to perform      */
5269 /*              sin(I)     - pointer to source of address information       */
5270 /*              mask(I)    - pointer to source of netmask information       */
5271 /*              inp(I)     - pointer to destination address store           */
5272 /*              inpmask(I) - pointer to destination netmask store           */
5273 /*                                                                          */
5274 /* Given a type of network address update (atype) to perform, copy          */
5275 /* information from sin/mask into inp/inpmask.  If ipnmask is NULL then no  */
5276 /* netmask update is performed unless FRI_NETMASKED is passed as atype, in  */
5277 /* which case the operation fails.  For all values of atype other than      */
5278 /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s  */
5279 /* value.                                                                   */
5280 /* ------------------------------------------------------------------------ */
5281 int fr_ifpfillv6addr(atype, sin, mask, inp, inpmask)
5282 int atype;
5283 struct sockaddr_in6 *sin, *mask;
5284 struct in_addr *inp, *inpmask;
5285 {
5286 	i6addr_t *src, *dst, *and, *dmask;
5287 
5288 	src = (i6addr_t *)&sin->sin6_addr;
5289 	and = (i6addr_t *)&mask->sin6_addr;
5290 	dst = (i6addr_t *)inp;
5291 	dmask = (i6addr_t *)inpmask;
5292 
5293 	if (inpmask != NULL && atype != FRI_NETMASKED) {
5294 		dmask->i6[0] = 0xffffffff;
5295 		dmask->i6[1] = 0xffffffff;
5296 		dmask->i6[2] = 0xffffffff;
5297 		dmask->i6[3] = 0xffffffff;
5298 	}
5299 
5300 	if (atype == FRI_NETWORK || atype == FRI_NETMASKED) {
5301 		if (atype == FRI_NETMASKED) {
5302 			if (inpmask == NULL)
5303 				return -1;
5304 			dmask->i6[0] = and->i6[0];
5305 			dmask->i6[1] = and->i6[1];
5306 			dmask->i6[2] = and->i6[2];
5307 			dmask->i6[3] = and->i6[3];
5308 		}
5309 
5310 		dst->i6[0] = src->i6[0] & and->i6[0];
5311 		dst->i6[1] = src->i6[1] & and->i6[1];
5312 		dst->i6[2] = src->i6[2] & and->i6[2];
5313 		dst->i6[3] = src->i6[3] & and->i6[3];
5314 	} else {
5315 		dst->i6[0] = src->i6[0];
5316 		dst->i6[1] = src->i6[1];
5317 		dst->i6[2] = src->i6[2];
5318 		dst->i6[3] = src->i6[3];
5319 	}
5320 	return 0;
5321 }
5322 #endif
5323 
5324 
5325 /* ------------------------------------------------------------------------ */
5326 /* Function:    fr_coalesce                                                 */
5327 /* Returns:     1 == success,  -1 == failure, 0 == no change                */
5328 /* Parameters:  fin(I) - pointer to packet information                      */
5329 /*                                                                          */
5330 /* Attempt to get all of the packet data into a single, contiguous buffer.  */
5331 /* If this call returns a failure then the buffers have also been freed.    */
5332 /* ------------------------------------------------------------------------ */
5333 int fr_coalesce(fin)
5334 fr_info_t *fin;
5335 {
5336 	if ((fin->fin_flx & FI_COALESCE) != 0)
5337 		return 1;
5338 
5339 	/*
5340 	 * If the mbuf pointers indicate that there is no mbuf to work with,
5341 	 * return but do not indicate success or failure.
5342 	 */
5343 	if (fin->fin_m == NULL || fin->fin_mp == NULL)
5344 		return 0;
5345 
5346 #if !defined(__sgi) && defined(_KERNEL)
5347 	if (fr_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) {
5348 # ifdef MENTAT
5349 		FREE_MB_T(*fin->fin_mp);
5350 # endif
5351 		fin->fin_m = NULL;
5352 		*fin->fin_mp = NULL;
5353 		return -1;
5354 	}
5355 #endif
5356 	return 1;
5357 }
5358 
5359 
5360 /* ------------------------------------------------------------------------ */
5361 /* Function:    fr_matchtag                                                 */
5362 /* Returns:     0 == mismatch, 1 == match.                                  */
5363 /* Parameters:  tag1(I) - pointer to first tag to compare                   */
5364 /*              tag2(I) - pointer to second tag to compare                  */
5365 /*                                                                          */
5366 /* Returns true (non-zero) or false(0) if the two tag structures can be     */
5367 /* considered to be a match or not match, respectively.  The tag is 16      */
5368 /* bytes long (16 characters) but that is overlayed with 4 32bit ints so    */
5369 /* compare the ints instead, for speed. tag1 is the master of the           */
5370 /* comparison.  This function should only be called with both tag1 and tag2 */
5371 /* as non-NULL pointers.                                                    */
5372 /* ------------------------------------------------------------------------ */
5373 int fr_matchtag(tag1, tag2)
5374 ipftag_t *tag1, *tag2;
5375 {
5376 	if (tag1 == tag2)
5377 		return 1;
5378 
5379 	if ((tag1->ipt_num[0] == 0) && (tag2->ipt_num[0] == 0))
5380 		return 1;
5381 
5382 	if ((tag1->ipt_num[0] == tag2->ipt_num[0]) &&
5383 	    (tag1->ipt_num[1] == tag2->ipt_num[1]) &&
5384 	    (tag1->ipt_num[2] == tag2->ipt_num[2]) &&
5385 	    (tag1->ipt_num[3] == tag2->ipt_num[3]))
5386 		return 1;
5387 	return 0;
5388 }
5389 
5390 
5391 /* ------------------------------------------------------------------------ */
5392 /* Function:    fr_pullup                                                   */
5393 /* Returns:     NULL == pullup failed, else pointer to protocol header      */
5394 /* Parameters:  m(I)   - pointer to buffer where data packet starts         */
5395 /*              fin(I) - pointer to packet information                      */
5396 /*              len(I) - number of bytes to pullup                          */
5397 /*                                                                          */
5398 /* Attempt to move at least len bytes (from the start of the buffer) into a */
5399 /* single buffer for ease of access.  Operating system native functions are */
5400 /* used to manage buffers - if necessary.  If the entire packet ends up in  */
5401 /* a single buffer, set the FI_COALESCE flag even though fr_coalesce() has  */
5402 /* not been called.  Both fin_ip and fin_dp are updated before exiting _IF_ */
5403 /* and ONLY if the pullup succeeds.                                         */
5404 /* ------------------------------------------------------------------------ */
5405 #if defined(_KERNEL) && !defined(__sgi)
5406 void *fr_pullup(min, fin, len)
5407 mb_t *min;
5408 fr_info_t *fin;
5409 int len;
5410 {
5411 # ifdef MENTAT
5412 	qif_t *qf = fin->fin_qif;
5413 # endif
5414 	int out = fin->fin_out, dpoff, ipoff;
5415 	mb_t *m = min;
5416 	char *ip;
5417 
5418 	if (m == NULL)
5419 		return NULL;
5420 
5421 	if ((fin->fin_flx & FI_COALESCE) != 0)
5422 		return MTOD(m, void *);
5423 
5424 	ipoff = (char *)fin->fin_ip - MTOD(m, char *);
5425 	if (fin->fin_dp != NULL)
5426 		dpoff = (char *)fin->fin_dp - (char *)fin->fin_ip;
5427 	else
5428 		dpoff = 0;
5429 
5430 	if (M_LEN(m) < len) {
5431 # ifdef MENTAT
5432 		int inc = 0;
5433 
5434 		if (ipoff > 0) {
5435 			if ((ipoff & 3) != 0) {
5436 				inc = 4 - (ipoff & 3);
5437 				if (m->b_rptr - inc >= m->b_datap->db_base)
5438 					m->b_rptr -= inc;
5439 				else
5440 					inc = 0;
5441 			}
5442 		}
5443 		if (!pullupmsg(m, len + ipoff + inc)) {
5444 			ATOMIC_INCL(frstats[out].fr_pull[1]);
5445 			return NULL;
5446 		}
5447 		m->b_rptr += inc;
5448 		ATOMIC_INCL(frstats[out].fr_pull[0]);
5449 		qf->qf_data = MTOD(m, char *) + ipoff;
5450 # else
5451 		m = m_pullup(m, len);
5452 		*fin->fin_mp = m;
5453 		if (m == NULL) {
5454 			ATOMIC_INCL(frstats[out].fr_pull[1]);
5455 			return NULL;
5456 		}
5457 		ATOMIC_INCL(frstats[out].fr_pull[0]);
5458 # endif /* MENTAT */
5459 	}
5460 	ip = MTOD(m, char *) + ipoff;
5461 	fin->fin_ip = (ip_t *)ip;
5462 	if (fin->fin_dp != NULL)
5463 		fin->fin_dp = (char *)fin->fin_ip + dpoff;
5464 
5465 	if (len == fin->fin_plen)
5466 		fin->fin_flx |= FI_COALESCE;
5467 	return ip;
5468 }
5469 #endif /* _KERNEL && !__sgi */
5470 
5471 
5472 /*
5473  * The following table lists all of the tunable variables that can be
5474  * accessed via SIOCIPFGET/SIOCIPFSET/SIOCIPFGETNEXt.  The format of each row
5475  * in the table below is as follows:
5476  *
5477  * pointer to value, name of value, minimum, maximum, size of the value's
5478  *     container, value attribute flags
5479  *
5480  * For convienience, IPFT_RDONLY means the value is read-only, IPFT_WRDISABLED
5481  * means the value can only be written to when IPFilter is loaded but disabled.
5482  * The obvious implication is if neither of these are set then the value can be
5483  * changed at any time without harm.
5484  */
5485 ipftuneable_t ipf_tuneables[] = {
5486 	/* filtering */
5487 	{ { &fr_flags },	"fr_flags",		0,	0xffffffff,
5488 			sizeof(fr_flags),		0 },
5489 	{ { &fr_active },	"fr_active",		0,	0,
5490 			sizeof(fr_active),		IPFT_RDONLY },
5491 	{ { &fr_control_forwarding },	"fr_control_forwarding",	0, 1,
5492 			sizeof(fr_control_forwarding),	0 },
5493 	{ { &fr_update_ipid },	"fr_update_ipid",	0,	1,
5494 			sizeof(fr_update_ipid),		0 },
5495 	{ { &fr_chksrc },	"fr_chksrc",		0,	1,
5496 			sizeof(fr_chksrc),		0 },
5497 	{ { &fr_pass },		"fr_pass",		0,	0xffffffff,
5498 			sizeof(fr_pass),		0 },
5499 	{ { &fr_unreach },	"fr_unreach",		0,	0xff,
5500 			sizeof(fr_unreach),		0 },
5501 	/* state */
5502 	{ { &fr_tcpidletimeout }, "fr_tcpidletimeout",	1,	0x7fffffff,
5503 			sizeof(fr_tcpidletimeout),	IPFT_WRDISABLED },
5504 	{ { &fr_tcpclosewait },	"fr_tcpclosewait",	1,	0x7fffffff,
5505 			sizeof(fr_tcpclosewait),	IPFT_WRDISABLED },
5506 	{ { &fr_tcplastack },	"fr_tcplastack",	1,	0x7fffffff,
5507 			sizeof(fr_tcplastack),		IPFT_WRDISABLED },
5508 	{ { &fr_tcptimeout },	"fr_tcptimeout",	1,	0x7fffffff,
5509 			sizeof(fr_tcptimeout),		IPFT_WRDISABLED },
5510 	{ { &fr_tcpclosed },	"fr_tcpclosed",		1,	0x7fffffff,
5511 			sizeof(fr_tcpclosed),		IPFT_WRDISABLED },
5512 	{ { &fr_tcphalfclosed }, "fr_tcphalfclosed",	1,	0x7fffffff,
5513 			sizeof(fr_tcphalfclosed),	IPFT_WRDISABLED },
5514 	{ { &fr_udptimeout },	"fr_udptimeout",	1,	0x7fffffff,
5515 			sizeof(fr_udptimeout),		IPFT_WRDISABLED },
5516 	{ { &fr_udpacktimeout }, "fr_udpacktimeout",	1,	0x7fffffff,
5517 			sizeof(fr_udpacktimeout),	IPFT_WRDISABLED },
5518 	{ { &fr_icmptimeout },	"fr_icmptimeout",	1,	0x7fffffff,
5519 			sizeof(fr_icmptimeout),		IPFT_WRDISABLED },
5520 	{ { &fr_icmpacktimeout }, "fr_icmpacktimeout",	1,	0x7fffffff,
5521 			sizeof(fr_icmpacktimeout),	IPFT_WRDISABLED },
5522 	{ { &fr_statemax },	"fr_statemax",		1,	0x7fffffff,
5523 			sizeof(fr_statemax),		IPFT_WRDISABLED },
5524 	{ { &fr_statesize },	"fr_statesize",		1,	0x7fffffff,
5525 			sizeof(fr_statesize),		IPFT_WRDISABLED },
5526 	{ { &fr_state_lock },	"fr_state_lock",	0,	1,
5527 			sizeof(fr_state_lock),		IPFT_RDONLY },
5528 	{ { &fr_state_maxbucket }, "fr_state_maxbucket", 1,	0x7fffffff,
5529 			sizeof(fr_state_maxbucket),	IPFT_WRDISABLED },
5530 	{ { &fr_state_maxbucket_reset }, "fr_state_maxbucket_reset",	0, 1,
5531 			sizeof(fr_state_maxbucket_reset), IPFT_WRDISABLED },
5532 	{ { &ipstate_logging },	"ipstate_logging",	0,	1,
5533 			sizeof(ipstate_logging),	0 },
5534 	/* nat */
5535 	{ { &fr_nat_lock },		"fr_nat_lock",		0,	1,
5536 			sizeof(fr_nat_lock),		IPFT_RDONLY },
5537 	{ { &ipf_nattable_sz },	"ipf_nattable_sz",	1,	0x7fffffff,
5538 			sizeof(ipf_nattable_sz),	IPFT_WRDISABLED },
5539 	{ { &ipf_natrules_sz },	"ipf_natrules_sz",	1,	0x7fffffff,
5540 			sizeof(ipf_natrules_sz),	IPFT_WRDISABLED },
5541 	{ { &ipf_rdrrules_sz },	"ipf_rdrrules_sz",	1,	0x7fffffff,
5542 			sizeof(ipf_rdrrules_sz),	IPFT_WRDISABLED },
5543 	{ { &ipf_hostmap_sz },	"ipf_hostmap_sz",	1,	0x7fffffff,
5544 			sizeof(ipf_hostmap_sz),		IPFT_WRDISABLED },
5545 	{ { &fr_nat_maxbucket }, "fr_nat_maxbucket",	1,	0x7fffffff,
5546 			sizeof(fr_nat_maxbucket),	IPFT_WRDISABLED },
5547 	{ { &fr_nat_maxbucket_reset },	"fr_nat_maxbucket_reset",	0, 1,
5548 			sizeof(fr_nat_maxbucket_reset),	IPFT_WRDISABLED },
5549 	{ { &nat_logging },		"nat_logging",		0,	1,
5550 			sizeof(nat_logging),		0 },
5551 	{ { &fr_defnatage },	"fr_defnatage",		1,	0x7fffffff,
5552 			sizeof(fr_defnatage),		IPFT_WRDISABLED },
5553 	{ { &fr_defnaticmpage }, "fr_defnaticmpage",	1,	0x7fffffff,
5554 			sizeof(fr_defnaticmpage),	IPFT_WRDISABLED },
5555 	/* frag */
5556 	{ { &ipfr_size },	"ipfr_size",		1,	0x7fffffff,
5557 			sizeof(ipfr_size),		IPFT_WRDISABLED },
5558 	{ { &fr_ipfrttl },	"fr_ipfrttl",		1,	0x7fffffff,
5559 			sizeof(fr_ipfrttl),		IPFT_WRDISABLED },
5560 #ifdef IPFILTER_LOG
5561 	/* log */
5562 	{ { &ipl_suppress },	"ipl_suppress",		0,	1,
5563 			sizeof(ipl_suppress),		0 },
5564 	{ { &ipl_buffer_sz },	"ipl_buffer_sz",	0,	0,
5565 			sizeof(ipl_buffer_sz),		IPFT_RDONLY },
5566 	{ { &ipl_logmax },	"ipl_logmax",		0,	0x7fffffff,
5567 			sizeof(ipl_logmax),		IPFT_WRDISABLED },
5568 	{ { &ipl_logall },	"ipl_logall",		0,	1,
5569 			sizeof(ipl_logall),		0 },
5570 #endif
5571 	{ { NULL },		NULL,			0,	0 }
5572 };
5573 
5574 
5575 /* ------------------------------------------------------------------------ */
5576 /* Function:    fr_ipftune                                                  */
5577 /* Returns:     int - 0 == success, else failure                            */
5578 /* Parameters:  cmd(I)  - ioctl command number                              */
5579 /*              data(I) - pointer to ioctl data structure                   */
5580 /*                                                                          */
5581 /* Implement handling of SIOCIPFGETNEXT, SIOCIPFGET and SIOCIPFSET.  These  */
5582 /* three ioctls provide the means to access and control global variables    */
5583 /* within IPFilter, allowing (for example) timeouts and table sizes to be   */
5584 /* changed without rebooting, reloading or recompiling.  The initialisation */
5585 /* and 'destruction' routines of the various components of ipfilter are all */
5586 /* each responsible for handling their own values being too big.            */
5587 /* ------------------------------------------------------------------------ */
5588 int fr_ipftune(cmd, data)
5589 #if defined(__NetBSD__) || defined(__OpenBSD__) || \
5590  (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300003)
5591 u_long cmd;
5592 #else
5593 int cmd;
5594 #endif
5595 char *data;
5596 {
5597 	ipftuneable_t *ta;
5598 	char namebuf[80];
5599 	ipftune_t tu;
5600 	void *cookie;
5601 	int error;
5602 
5603 	error = fr_inobj(data, &tu, IPFOBJ_TUNEABLE);
5604 	if (error != 0)
5605 		return error;
5606 
5607 	tu.ipft_name[sizeof(tu.ipft_name) - 1] = '\0';
5608 	ta = ipf_tuneables;
5609 	cookie = tu.ipft_cookie;
5610 	namebuf[sizeof(namebuf) - 1] = '\0';
5611 
5612 	switch (cmd)
5613 	{
5614 	case SIOCIPFGETNEXT :
5615 		/*
5616 		 * If cookie is non-NULL, assume it to be a pointer to the last
5617 		 * entry we looked at, so find it (if possible) and return a
5618 		 * pointer to the next one after it.  The last entry in the
5619 		 * the table is a NULL entry, so when we get to it, set cookie
5620 		 * to NULL and return that, indicating end of list, erstwhile
5621 		 * if we come in with cookie set to NULL, we are starting anew
5622 		 * at the front of the list.
5623 		 */
5624 		if (cookie != NULL) {
5625 			for (; ta->ipft_name != NULL; ta++)
5626 				if (ta == cookie) {
5627 					ta++;
5628 					break;
5629 				}
5630 			if (ta->ipft_name == NULL)
5631 				ta = NULL;
5632 		}
5633 		cookie = ta;
5634 		tu.ipft_cookie = cookie;
5635 		if (ta != NULL) {
5636 			/*
5637 			 * Entry found, but does the data pointed to by that
5638 			 * row fit in what we can return?
5639 			 */
5640 			if (ta->ipft_sz > sizeof(tu.ipft_un))
5641 				return EINVAL;
5642 
5643 			tu.ipft_vlong = 0;
5644 			if (ta->ipft_sz == sizeof(u_long))
5645 				tu.ipft_vlong = *ta->ipft_plong;
5646 			else if (ta->ipft_sz == sizeof(u_int))
5647 				tu.ipft_vint = *ta->ipft_pint;
5648 			else if (ta->ipft_sz == sizeof(u_short))
5649 				tu.ipft_vshort = *ta->ipft_pshort;
5650 			else if (ta->ipft_sz == sizeof(u_char))
5651 				tu.ipft_vchar = *ta->ipft_pchar;
5652 
5653 			tu.ipft_sz = ta->ipft_sz;
5654 			tu.ipft_min = ta->ipft_min;
5655 			tu.ipft_max = ta->ipft_max;
5656 			tu.ipft_flags = ta->ipft_flags;
5657 			bcopy(ta->ipft_name, tu.ipft_name,
5658 			      MIN(sizeof(tu.ipft_name),
5659 				  strlen(ta->ipft_name) + 1));
5660 		}
5661 		error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE);
5662 		return error;
5663 		/*NOTREACHED*/
5664 
5665 	case SIOCIPFGET :
5666 	case SIOCIPFSET :
5667 		/*
5668 		 * Search by name or by cookie value for a particular entry
5669 		 * in the tuning paramter table.
5670 		 */
5671 		if (cookie != NULL) {
5672 			for (; ta->ipft_name != NULL; ta++)
5673 				if (ta == cookie)
5674 					break;
5675 		} else if (tu.ipft_name[0] != '\0') {
5676 			for (; ta->ipft_name != NULL; ta++)
5677 				if (!strncmp(ta->ipft_name, tu.ipft_name,
5678 					     MIN(sizeof(tu.ipft_name),
5679 						 strlen(ta->ipft_name) + 1)))
5680 					break;
5681 		} else
5682 			return ESRCH;
5683 		if (ta == NULL)
5684 			return ESRCH;
5685 
5686 		switch (cmd)
5687 		{
5688 		case SIOCIPFGET :
5689 			/*
5690 			 * Fetch the tuning parameters for a particular value
5691 			 */
5692 			tu.ipft_cookie = ta;
5693 			tu.ipft_vlong = 0;
5694 			if (ta->ipft_sz == sizeof(u_long))
5695 				tu.ipft_vlong = *ta->ipft_plong;
5696 			else if (ta->ipft_sz == sizeof(u_int))
5697 				tu.ipft_vint = *ta->ipft_pint;
5698 			else if (ta->ipft_sz == sizeof(u_short))
5699 				tu.ipft_vshort = *ta->ipft_pshort;
5700 			else if (ta->ipft_sz == sizeof(u_char))
5701 				tu.ipft_vchar = *ta->ipft_pchar;
5702 			tu.ipft_sz = ta->ipft_sz;
5703 			tu.ipft_min = ta->ipft_min;
5704 			tu.ipft_max = ta->ipft_max;
5705 			tu.ipft_flags = ta->ipft_flags;
5706 			error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE);
5707 			return error;
5708 			/*NOTREACHED*/
5709 		case SIOCIPFSET :
5710 			/*
5711 			 * Set an internal parameter.  The hard part here is
5712 			 * getting the new value safely and correctly out of
5713 			 * the kernel (given we only know its size, not type.)
5714 			 */
5715 		    {
5716 			u_long in;
5717 
5718 			if (((ta->ipft_flags & IPFT_WRDISABLED) != 0) &&
5719 			    (fr_running > 0))
5720 				return EBUSY;
5721 
5722 			in = tu.ipft_vlong;
5723 			if (in < ta->ipft_min || in > ta->ipft_max)
5724 				return EINVAL;
5725 
5726 			if (ta->ipft_sz == sizeof(u_long)) {
5727 				tu.ipft_vlong = *ta->ipft_plong;
5728 				*ta->ipft_plong = in;
5729 			} else if (ta->ipft_sz == sizeof(u_int)) {
5730 				tu.ipft_vint = *ta->ipft_pint;
5731 				*ta->ipft_pint = (u_int)(in & 0xffffffff);
5732 			} else if (ta->ipft_sz == sizeof(u_short)) {
5733 				tu.ipft_vshort = *ta->ipft_pshort;
5734 				*ta->ipft_pshort = (u_short)(in & 0xffff);
5735 			} else if (ta->ipft_sz == sizeof(u_char)) {
5736 				tu.ipft_vchar = *ta->ipft_pchar;
5737 				*ta->ipft_pchar = (u_char)(in & 0xff);
5738 			}
5739 			error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE);
5740 			return error;
5741 		    }
5742 			/*NOTREACHED*/
5743 		}
5744 	default :
5745 		break;
5746 	}
5747 
5748 	return EINVAL;
5749 }
5750