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