fil.c revision 153084
1/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/fil.c 153084 2005-12-04 10:06:06Z ru $	*/
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 153084 2005-12-04 10:06:06Z ru $";
140/* static 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 (defined(OpenBSD) && 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 defined(OpenBSD) && 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	ftail = fprev;
4002	for (f = *ftail; (f = *ftail) != NULL; ftail = &f->fr_next) {
4003		if (fp->fr_collect <= f->fr_collect) {
4004			ftail = fprev;
4005			f = NULL;
4006			break;
4007		}
4008		fprev = ftail;
4009	}
4010
4011	/*
4012	 * Copy in extra data for the rule.
4013	 */
4014	if (fp->fr_dsize != 0) {
4015		if (makecopy != 0) {
4016			KMALLOCS(ptr, void *, fp->fr_dsize);
4017			if (!ptr)
4018				return ENOMEM;
4019			error = COPYIN(uptr, ptr, fp->fr_dsize);
4020		} else {
4021			ptr = uptr;
4022			error = 0;
4023		}
4024		if (error != 0) {
4025			KFREES(ptr, fp->fr_dsize);
4026			return ENOMEM;
4027		}
4028		fp->fr_data = ptr;
4029	} else
4030		fp->fr_data = NULL;
4031
4032	/*
4033	 * Perform per-rule type sanity checks of their members.
4034	 */
4035	switch (fp->fr_type & ~FR_T_BUILTIN)
4036	{
4037#if defined(IPFILTER_BPF)
4038	case FR_T_BPFOPC :
4039		if (fp->fr_dsize == 0)
4040			return EINVAL;
4041		if (!bpf_validate(ptr, fp->fr_dsize/sizeof(struct bpf_insn))) {
4042			if (makecopy && fp->fr_data != NULL) {
4043				KFREES(fp->fr_data, fp->fr_dsize);
4044			}
4045			return EINVAL;
4046		}
4047		break;
4048#endif
4049	case FR_T_IPF :
4050		if (fp->fr_dsize != sizeof(fripf_t))
4051			return EINVAL;
4052
4053		/*
4054		 * Allowing a rule with both "keep state" and "with oow" is
4055		 * pointless because adding a state entry to the table will
4056		 * fail with the out of window (oow) flag set.
4057		 */
4058		if ((fp->fr_flags & FR_KEEPSTATE) && (fp->fr_flx & FI_OOW))
4059			return EINVAL;
4060
4061		switch (fp->fr_satype)
4062		{
4063		case FRI_BROADCAST :
4064		case FRI_DYNAMIC :
4065		case FRI_NETWORK :
4066		case FRI_NETMASKED :
4067		case FRI_PEERADDR :
4068			if (fp->fr_sifpidx < 0 || fp->fr_sifpidx > 3) {
4069				if (makecopy && fp->fr_data != NULL) {
4070					KFREES(fp->fr_data, fp->fr_dsize);
4071				}
4072				return EINVAL;
4073			}
4074			break;
4075#ifdef	IPFILTER_LOOKUP
4076		case FRI_LOOKUP :
4077			fp->fr_srcptr = fr_resolvelookup(fp->fr_srctype,
4078							 fp->fr_srcnum,
4079							 &fp->fr_srcfunc);
4080			break;
4081#endif
4082		default :
4083			break;
4084		}
4085
4086		switch (fp->fr_datype)
4087		{
4088		case FRI_BROADCAST :
4089		case FRI_DYNAMIC :
4090		case FRI_NETWORK :
4091		case FRI_NETMASKED :
4092		case FRI_PEERADDR :
4093			if (fp->fr_difpidx < 0 || fp->fr_difpidx > 3) {
4094				if (makecopy && fp->fr_data != NULL) {
4095					KFREES(fp->fr_data, fp->fr_dsize);
4096				}
4097				return EINVAL;
4098			}
4099			break;
4100#ifdef	IPFILTER_LOOKUP
4101		case FRI_LOOKUP :
4102			fp->fr_dstptr = fr_resolvelookup(fp->fr_dsttype,
4103							 fp->fr_dstnum,
4104							 &fp->fr_dstfunc);
4105			break;
4106#endif
4107		default :
4108
4109			break;
4110		}
4111		break;
4112	case FR_T_NONE :
4113		break;
4114	case FR_T_CALLFUNC :
4115		break;
4116	case FR_T_COMPIPF :
4117		break;
4118	default :
4119		if (makecopy && fp->fr_data != NULL) {
4120			KFREES(fp->fr_data, fp->fr_dsize);
4121		}
4122		return EINVAL;
4123	}
4124
4125	/*
4126	 * Lookup all the interface names that are part of the rule.
4127	 */
4128	frsynclist(fp, NULL);
4129	fp->fr_statecnt = 0;
4130
4131	/*
4132	 * Look for an existing matching filter rule, but don't include the
4133	 * next or interface pointer in the comparison (fr_next, fr_ifa).
4134	 * This elminates rules which are indentical being loaded.  Checksum
4135	 * the constant part of the filter rule to make comparisons quicker
4136	 * (this meaning no pointers are included).
4137	 */
4138	for (fp->fr_cksum = 0, p = (u_int *)&fp->fr_func, pp = &fp->fr_cksum;
4139	     p < pp; p++)
4140		fp->fr_cksum += *p;
4141	pp = (u_int *)(fp->fr_caddr + fp->fr_dsize);
4142	for (p = (u_int *)fp->fr_data; p < pp; p++)
4143		fp->fr_cksum += *p;
4144
4145	WRITE_ENTER(&ipf_mutex);
4146	bzero((char *)frcache, sizeof(frcache));
4147
4148	for (; (f = *ftail) != NULL; ftail = &f->fr_next) {
4149		if ((fp->fr_cksum != f->fr_cksum) ||
4150		    (f->fr_dsize != fp->fr_dsize))
4151			continue;
4152		if (bcmp((char *)&f->fr_func, (char *)&fp->fr_func, FR_CMPSIZ))
4153			continue;
4154		if ((!ptr && !f->fr_data) ||
4155		    (ptr && f->fr_data &&
4156		     !bcmp((char *)ptr, (char *)f->fr_data, f->fr_dsize)))
4157			break;
4158	}
4159
4160	/*
4161	 * If zero'ing statistics, copy current to caller and zero.
4162	 */
4163	if (req == (ioctlcmd_t)SIOCZRLST) {
4164		if (f == NULL)
4165			error = ESRCH;
4166		else {
4167			/*
4168			 * Copy and reduce lock because of impending copyout.
4169			 * Well we should, but if we do then the atomicity of
4170			 * this call and the correctness of fr_hits and
4171			 * fr_bytes cannot be guaranteed.  As it is, this code
4172			 * only resets them to 0 if they are successfully
4173			 * copied out into user space.
4174			 */
4175			bcopy((char *)f, (char *)fp, sizeof(*f));
4176			/* MUTEX_DOWNGRADE(&ipf_mutex); */
4177
4178			/*
4179			 * When we copy this rule back out, set the data
4180			 * pointer to be what it was in user space.
4181			 */
4182			fp->fr_data = uptr;
4183			error = fr_outobj(data, fp, IPFOBJ_FRENTRY);
4184
4185			if (error == 0) {
4186				if ((f->fr_dsize != 0) && (uptr != NULL))
4187					error = COPYOUT(f->fr_data, uptr,
4188							f->fr_dsize);
4189				if (error == 0) {
4190					f->fr_hits = 0;
4191					f->fr_bytes = 0;
4192				}
4193			}
4194		}
4195
4196		if ((ptr != NULL) && (makecopy != 0)) {
4197			KFREES(ptr, fp->fr_dsize);
4198		}
4199		RWLOCK_EXIT(&ipf_mutex);
4200		return error;
4201	}
4202
4203	if (!f) {
4204		/*
4205		 * At the end of this, ftail must point to the place where the
4206		 * new rule is to be saved/inserted/added.
4207		 * For SIOCAD*FR, this should be the last rule in the group of
4208		 * rules that have equal fr_collect fields.
4209		 * For SIOCIN*FR, ...
4210		 */
4211		if (req == (ioctlcmd_t)SIOCADAFR ||
4212		    req == (ioctlcmd_t)SIOCADIFR) {
4213
4214			for (ftail = fprev; (f = *ftail) != NULL; ) {
4215				if (f->fr_collect > fp->fr_collect)
4216					break;
4217				ftail = &f->fr_next;
4218			}
4219			f = NULL;
4220			ptr = NULL;
4221			error = 0;
4222		} else if (req == (ioctlcmd_t)SIOCINAFR ||
4223			   req == (ioctlcmd_t)SIOCINIFR) {
4224			while ((f = *fprev) != NULL) {
4225				if (f->fr_collect >= fp->fr_collect)
4226					break;
4227				fprev = &f->fr_next;
4228			}
4229			ftail = fprev;
4230			if (fp->fr_hits != 0) {
4231				while (fp->fr_hits && (f = *ftail)) {
4232					if (f->fr_collect != fp->fr_collect)
4233						break;
4234					fprev = ftail;
4235					ftail = &f->fr_next;
4236					fp->fr_hits--;
4237				}
4238			}
4239			f = NULL;
4240			ptr = NULL;
4241			error = 0;
4242		}
4243	}
4244
4245	/*
4246	 * Request to remove a rule.
4247	 */
4248	if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) {
4249		if (!f)
4250			error = ESRCH;
4251		else {
4252			/*
4253			 * Do not allow activity from user space to interfere
4254			 * with rules not loaded that way.
4255			 */
4256			if ((makecopy == 1) && !(f->fr_flags & FR_COPIED)) {
4257				error = EPERM;
4258				goto done;
4259			}
4260
4261			/*
4262			 * Return EBUSY if the rule is being reference by
4263			 * something else (eg state information.
4264			 */
4265			if (f->fr_ref > 1) {
4266				error = EBUSY;
4267				goto done;
4268			}
4269#ifdef	IPFILTER_SCAN
4270			if (f->fr_isctag[0] != '\0' &&
4271			    (f->fr_isc != (struct ipscan *)-1))
4272				ipsc_detachfr(f);
4273#endif
4274			if ((fg != NULL) && (fg->fg_head != NULL))
4275				fg->fg_head->fr_ref--;
4276			if (unit == IPL_LOGAUTH) {
4277				error = fr_preauthcmd(req, f, ftail);
4278				goto done;
4279			}
4280			if (*f->fr_grhead != '\0')
4281				fr_delgroup(f->fr_grhead, unit, set);
4282			fr_fixskip(ftail, f, -1);
4283			*ftail = f->fr_next;
4284			f->fr_next = NULL;
4285			(void)fr_derefrule(&f);
4286		}
4287	} else {
4288		/*
4289		 * Not removing, so we must be adding/inserting a rule.
4290		 */
4291		if (f)
4292			error = EEXIST;
4293		else {
4294			if (unit == IPL_LOGAUTH) {
4295				error = fr_preauthcmd(req, fp, ftail);
4296				goto done;
4297			}
4298			if (makecopy) {
4299				KMALLOC(f, frentry_t *);
4300			} else
4301				f = fp;
4302			if (f != NULL) {
4303				if (fg != NULL && fg->fg_head!= NULL )
4304					fg->fg_head->fr_ref++;
4305				if (fp != f)
4306					bcopy((char *)fp, (char *)f,
4307					      sizeof(*f));
4308				MUTEX_NUKE(&f->fr_lock);
4309				MUTEX_INIT(&f->fr_lock, "filter rule lock");
4310#ifdef	IPFILTER_SCAN
4311				if (f->fr_isctag[0] != '\0' &&
4312				    ipsc_attachfr(f))
4313					f->fr_isc = (struct ipscan *)-1;
4314#endif
4315				f->fr_hits = 0;
4316				if (makecopy != 0)
4317					f->fr_ref = 1;
4318				f->fr_next = *ftail;
4319				*ftail = f;
4320				if (req == (ioctlcmd_t)SIOCINIFR ||
4321				    req == (ioctlcmd_t)SIOCINAFR)
4322					fr_fixskip(ftail, f, 1);
4323				f->fr_grp = NULL;
4324				group = f->fr_grhead;
4325				if (*group != '\0') {
4326					fg = fr_addgroup(group, f, f->fr_flags,
4327							 unit, set);
4328					if (fg != NULL)
4329						f->fr_grp = &fg->fg_start;
4330				}
4331			} else
4332				error = ENOMEM;
4333		}
4334	}
4335done:
4336	RWLOCK_EXIT(&ipf_mutex);
4337	if ((ptr != NULL) && (error != 0) && (makecopy != 0)) {
4338		KFREES(ptr, fp->fr_dsize);
4339	}
4340	return (error);
4341}
4342
4343
4344/* ------------------------------------------------------------------------ */
4345/* Function:    fr_funcinit                                                 */
4346/* Returns:     int - 0 == success, else ESRCH: cannot resolve rule details */
4347/* Parameters:  fr(I) - pointer to filter rule                              */
4348/*                                                                          */
4349/* If a rule is a call rule, then check if the function it points to needs  */
4350/* an init function to be called now the rule has been loaded.              */
4351/* ------------------------------------------------------------------------ */
4352static int fr_funcinit(fr)
4353frentry_t *fr;
4354{
4355	ipfunc_resolve_t *ft;
4356	int err;
4357
4358	err = ESRCH;
4359
4360	for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4361		if (ft->ipfu_addr == fr->fr_func) {
4362			err = 0;
4363			if (ft->ipfu_init != NULL)
4364				err = (*ft->ipfu_init)(fr);
4365			break;
4366		}
4367	return err;
4368}
4369
4370
4371/* ------------------------------------------------------------------------ */
4372/* Function:    fr_findfunc                                                 */
4373/* Returns:     ipfunc_t - pointer to function if found, else NULL          */
4374/* Parameters:  funcptr(I) - function pointer to lookup                     */
4375/*                                                                          */
4376/* Look for a function in the table of known functions.                     */
4377/* ------------------------------------------------------------------------ */
4378static ipfunc_t fr_findfunc(funcptr)
4379ipfunc_t funcptr;
4380{
4381	ipfunc_resolve_t *ft;
4382
4383	for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4384		if (ft->ipfu_addr == funcptr)
4385			return funcptr;
4386	return NULL;
4387}
4388
4389
4390/* ------------------------------------------------------------------------ */
4391/* Function:    fr_resolvefunc                                              */
4392/* Returns:     int - 0 == success, else error                              */
4393/* Parameters:  data(IO) - ioctl data pointer to ipfunc_resolve_t struct    */
4394/*                                                                          */
4395/* Copy in a ipfunc_resolve_t structure and then fill in the missing field. */
4396/* This will either be the function name (if the pointer is set) or the     */
4397/* function pointer if the name is set.  When found, fill in the other one  */
4398/* so that the entire, complete, structure can be copied back to user space.*/
4399/* ------------------------------------------------------------------------ */
4400int fr_resolvefunc(data)
4401void *data;
4402{
4403	ipfunc_resolve_t res, *ft;
4404
4405	BCOPYIN(data, &res, sizeof(res));
4406
4407	if (res.ipfu_addr == NULL && res.ipfu_name[0] != '\0') {
4408		for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4409			if (strncmp(res.ipfu_name, ft->ipfu_name,
4410				    sizeof(res.ipfu_name)) == 0) {
4411				res.ipfu_addr = ft->ipfu_addr;
4412				res.ipfu_init = ft->ipfu_init;
4413				if (COPYOUT(&res, data, sizeof(res)) != 0)
4414					return EFAULT;
4415				return 0;
4416			}
4417	}
4418	if (res.ipfu_addr != NULL && res.ipfu_name[0] == '\0') {
4419		for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++)
4420			if (ft->ipfu_addr == res.ipfu_addr) {
4421				(void) strncpy(res.ipfu_name, ft->ipfu_name,
4422					       sizeof(res.ipfu_name));
4423				res.ipfu_init = ft->ipfu_init;
4424				if (COPYOUT(&res, data, sizeof(res)) != 0)
4425					return EFAULT;
4426				return 0;
4427			}
4428	}
4429	return ESRCH;
4430}
4431
4432
4433#if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__FreeBSD__)) || \
4434    (defined(__FreeBSD__) && (__FreeBSD_version < 490000)) || \
4435    (defined(__NetBSD__) && (__NetBSD_Version__ < 105000000)) || \
4436    (defined(__OpenBSD__) && (OpenBSD < 200006))
4437/*
4438 * From: NetBSD
4439 * ppsratecheck(): packets (or events) per second limitation.
4440 */
4441int
4442ppsratecheck(lasttime, curpps, maxpps)
4443	struct timeval *lasttime;
4444	int *curpps;
4445	int maxpps;	/* maximum pps allowed */
4446{
4447	struct timeval tv, delta;
4448	int rv;
4449
4450	GETKTIME(&tv);
4451
4452	delta.tv_sec = tv.tv_sec - lasttime->tv_sec;
4453	delta.tv_usec = tv.tv_usec - lasttime->tv_usec;
4454	if (delta.tv_usec < 0) {
4455		delta.tv_sec--;
4456		delta.tv_usec += 1000000;
4457	}
4458
4459	/*
4460	 * check for 0,0 is so that the message will be seen at least once.
4461	 * if more than one second have passed since the last update of
4462	 * lasttime, reset the counter.
4463	 *
4464	 * we do increment *curpps even in *curpps < maxpps case, as some may
4465	 * try to use *curpps for stat purposes as well.
4466	 */
4467	if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) ||
4468	    delta.tv_sec >= 1) {
4469		*lasttime = tv;
4470		*curpps = 0;
4471		rv = 1;
4472	} else if (maxpps < 0)
4473		rv = 1;
4474	else if (*curpps < maxpps)
4475		rv = 1;
4476	else
4477		rv = 0;
4478	*curpps = *curpps + 1;
4479
4480	return (rv);
4481}
4482#endif
4483
4484
4485/* ------------------------------------------------------------------------ */
4486/* Function:    fr_derefrule                                                */
4487/* Returns:     int   - 0 == rule freed up, else rule not freed             */
4488/* Parameters:  fr(I) - pointer to filter rule                              */
4489/*                                                                          */
4490/* Decrement the reference counter to a rule by one.  If it reaches zero,   */
4491/* free it and any associated storage space being used by it.               */
4492/* ------------------------------------------------------------------------ */
4493int fr_derefrule(frp)
4494frentry_t **frp;
4495{
4496	frentry_t *fr;
4497
4498	fr = *frp;
4499
4500	MUTEX_ENTER(&fr->fr_lock);
4501	fr->fr_ref--;
4502	if (fr->fr_ref == 0) {
4503		MUTEX_EXIT(&fr->fr_lock);
4504		MUTEX_DESTROY(&fr->fr_lock);
4505
4506#ifdef IPFILTER_LOOKUP
4507		if (fr->fr_type == FR_T_IPF && fr->fr_satype == FRI_LOOKUP)
4508			ip_lookup_deref(fr->fr_srctype, fr->fr_srcptr);
4509		if (fr->fr_type == FR_T_IPF && fr->fr_datype == FRI_LOOKUP)
4510			ip_lookup_deref(fr->fr_dsttype, fr->fr_dstptr);
4511#endif
4512
4513		if (fr->fr_dsize) {
4514			KFREES(fr->fr_data, fr->fr_dsize);
4515		}
4516		if ((fr->fr_flags & FR_COPIED) != 0) {
4517			KFREE(fr);
4518			return 0;
4519		}
4520		return 1;
4521	} else {
4522		MUTEX_EXIT(&fr->fr_lock);
4523	}
4524	*frp = NULL;
4525	return -1;
4526}
4527
4528
4529#ifdef	IPFILTER_LOOKUP
4530/* ------------------------------------------------------------------------ */
4531/* Function:    fr_grpmapinit                                               */
4532/* Returns:     int - 0 == success, else ESRCH because table entry not found*/
4533/* Parameters:  fr(I) - pointer to rule to find hash table for              */
4534/*                                                                          */
4535/* Looks for group hash table fr_arg and stores a pointer to it in fr_ptr.  */
4536/* fr_ptr is later used by fr_srcgrpmap and fr_dstgrpmap.                   */
4537/* ------------------------------------------------------------------------ */
4538static int fr_grpmapinit(fr)
4539frentry_t *fr;
4540{
4541	char name[FR_GROUPLEN];
4542	iphtable_t *iph;
4543
4544#if defined(SNPRINTF) && defined(_KERNEL)
4545	SNPRINTF(name, sizeof(name), "%d", fr->fr_arg);
4546#else
4547	(void) sprintf(name, "%d", fr->fr_arg);
4548#endif
4549	iph = fr_findhtable(IPL_LOGIPF, name);
4550	if (iph == NULL)
4551		return ESRCH;
4552	if ((iph->iph_flags & FR_INOUT) != (fr->fr_flags & FR_INOUT))
4553		return ESRCH;
4554	fr->fr_ptr = iph;
4555	return 0;
4556}
4557
4558
4559/* ------------------------------------------------------------------------ */
4560/* Function:    fr_srcgrpmap                                                */
4561/* Returns:     frentry_t * - pointer to "new last matching" rule or NULL   */
4562/* Parameters:  fin(I)    - pointer to packet information                   */
4563/*              passp(IO) - pointer to current/new filter decision (unused) */
4564/*                                                                          */
4565/* Look for a rule group head in a hash table, using the source address as  */
4566/* the key, and descend into that group and continue matching rules against */
4567/* the packet.                                                              */
4568/* ------------------------------------------------------------------------ */
4569frentry_t *fr_srcgrpmap(fin, passp)
4570fr_info_t *fin;
4571u_32_t *passp;
4572{
4573	frgroup_t *fg;
4574	void *rval;
4575
4576	rval = fr_iphmfindgroup(fin->fin_fr->fr_ptr, &fin->fin_src);
4577	if (rval == NULL)
4578		return NULL;
4579
4580	fg = rval;
4581	fin->fin_fr = fg->fg_start;
4582	(void) fr_scanlist(fin, *passp);
4583	return fin->fin_fr;
4584}
4585
4586
4587/* ------------------------------------------------------------------------ */
4588/* Function:    fr_dstgrpmap                                                */
4589/* Returns:     frentry_t * - pointer to "new last matching" rule or NULL   */
4590/* Parameters:  fin(I)    - pointer to packet information                   */
4591/*              passp(IO) - pointer to current/new filter decision (unused) */
4592/*                                                                          */
4593/* Look for a rule group head in a hash table, using the destination        */
4594/* address as the key, and descend into that group and continue matching    */
4595/* rules against  the packet.                                               */
4596/* ------------------------------------------------------------------------ */
4597frentry_t *fr_dstgrpmap(fin, passp)
4598fr_info_t *fin;
4599u_32_t *passp;
4600{
4601	frgroup_t *fg;
4602	void *rval;
4603
4604	rval = fr_iphmfindgroup(fin->fin_fr->fr_ptr, &fin->fin_dst);
4605	if (rval == NULL)
4606		return NULL;
4607
4608	fg = rval;
4609	fin->fin_fr = fg->fg_start;
4610	(void) fr_scanlist(fin, *passp);
4611	return fin->fin_fr;
4612}
4613#endif /* IPFILTER_LOOKUP */
4614
4615/*
4616 * Queue functions
4617 * ===============
4618 * These functions manage objects on queues for efficient timeouts.  There are
4619 * a number of system defined queues as well as user defined timeouts.  It is
4620 * expected that a lock is held in the domain in which the queue belongs
4621 * (i.e. either state or NAT) when calling any of these functions that prevents
4622 * fr_freetimeoutqueue() from being called at the same time as any other.
4623 */
4624
4625
4626/* ------------------------------------------------------------------------ */
4627/* Function:    fr_addtimeoutqueue                                          */
4628/* Returns:     struct ifqtq * - NULL if malloc fails, else pointer to      */
4629/*                               timeout queue with given interval.         */
4630/* Parameters:  parent(I)  - pointer to pointer to parent node of this list */
4631/*                           of interface queues.                           */
4632/*              seconds(I) - timeout value in seconds for this queue.       */
4633/*                                                                          */
4634/* This routine first looks for a timeout queue that matches the interval   */
4635/* being requested.  If it finds one, increments the reference counter and  */
4636/* returns a pointer to it.  If none are found, it allocates a new one and  */
4637/* inserts it at the top of the list.                                       */
4638/*                                                                          */
4639/* Locking.                                                                 */
4640/* It is assumed that the caller of this function has an appropriate lock   */
4641/* held (exclusively) in the domain that encompases 'parent'.               */
4642/* ------------------------------------------------------------------------ */
4643ipftq_t *fr_addtimeoutqueue(parent, seconds)
4644ipftq_t **parent;
4645u_int seconds;
4646{
4647	ipftq_t *ifq;
4648	u_int period;
4649
4650	period = seconds * IPF_HZ_DIVIDE;
4651
4652	MUTEX_ENTER(&ipf_timeoutlock);
4653	for (ifq = *parent; ifq != NULL; ifq = ifq->ifq_next) {
4654		if (ifq->ifq_ttl == period) {
4655			/*
4656			 * Reset the delete flag, if set, so the structure
4657			 * gets reused rather than freed and reallocated.
4658			 */
4659			MUTEX_ENTER(&ifq->ifq_lock);
4660			ifq->ifq_flags &= ~IFQF_DELETE;
4661			ifq->ifq_ref++;
4662			MUTEX_EXIT(&ifq->ifq_lock);
4663			MUTEX_EXIT(&ipf_timeoutlock);
4664
4665			return ifq;
4666		}
4667	}
4668
4669	KMALLOC(ifq, ipftq_t *);
4670	if (ifq != NULL) {
4671		ifq->ifq_ttl = period;
4672		ifq->ifq_head = NULL;
4673		ifq->ifq_tail = &ifq->ifq_head;
4674		ifq->ifq_next = *parent;
4675		ifq->ifq_pnext = parent;
4676		ifq->ifq_ref = 1;
4677		ifq->ifq_flags = IFQF_USER;
4678		*parent = ifq;
4679		fr_userifqs++;
4680		MUTEX_NUKE(&ifq->ifq_lock);
4681		MUTEX_INIT(&ifq->ifq_lock, "ipftq mutex");
4682	}
4683	MUTEX_EXIT(&ipf_timeoutlock);
4684	return ifq;
4685}
4686
4687
4688/* ------------------------------------------------------------------------ */
4689/* Function:    fr_deletetimeoutqueue                                       */
4690/* Returns:     int    - new reference count value of the timeout queue     */
4691/* Parameters:  ifq(I) - timeout queue which is losing a reference.         */
4692/* Locks:       ifq->ifq_lock                                               */
4693/*                                                                          */
4694/* This routine must be called when we're discarding a pointer to a timeout */
4695/* queue object, taking care of the reference counter.                      */
4696/*                                                                          */
4697/* Now that this just sets a DELETE flag, it requires the expire code to    */
4698/* check the list of user defined timeout queues and call the free function */
4699/* below (currently commented out) to stop memory leaking.  It is done this */
4700/* way because the locking may not be sufficient to safely do a free when   */
4701/* this function is called.                                                 */
4702/* ------------------------------------------------------------------------ */
4703int fr_deletetimeoutqueue(ifq)
4704ipftq_t *ifq;
4705{
4706
4707	ifq->ifq_ref--;
4708	if ((ifq->ifq_ref == 0) && ((ifq->ifq_flags & IFQF_USER) != 0)) {
4709		ifq->ifq_flags |= IFQF_DELETE;
4710	}
4711
4712	return ifq->ifq_ref;
4713}
4714
4715
4716/* ------------------------------------------------------------------------ */
4717/* Function:    fr_freetimeoutqueue                                         */
4718/* Parameters:  ifq(I) - timeout queue which is losing a reference.         */
4719/* Returns:     Nil                                                         */
4720/*                                                                          */
4721/* Locking:                                                                 */
4722/* It is assumed that the caller of this function has an appropriate lock   */
4723/* held (exclusively) in the domain that encompases the callers "domain".   */
4724/* The ifq_lock for this structure should not be held.                      */
4725/*                                                                          */
4726/* Remove a user definde timeout queue from the list of queues it is in and */
4727/* tidy up after this is done.                                              */
4728/* ------------------------------------------------------------------------ */
4729void fr_freetimeoutqueue(ifq)
4730ipftq_t *ifq;
4731{
4732
4733
4734	if (((ifq->ifq_flags & IFQF_DELETE) == 0) || (ifq->ifq_ref != 0) ||
4735	    ((ifq->ifq_flags & IFQF_USER) == 0)) {
4736		printf("fr_freetimeoutqueue(%lx) flags 0x%x ttl %d ref %d\n",
4737		       (u_long)ifq, ifq->ifq_flags, ifq->ifq_ttl,
4738		       ifq->ifq_ref);
4739		return;
4740	}
4741
4742	/*
4743	 * Remove from its position in the list.
4744	 */
4745	*ifq->ifq_pnext = ifq->ifq_next;
4746	if (ifq->ifq_next != NULL)
4747		ifq->ifq_next->ifq_pnext = ifq->ifq_pnext;
4748
4749	MUTEX_DESTROY(&ifq->ifq_lock);
4750	fr_userifqs--;
4751	KFREE(ifq);
4752}
4753
4754
4755/* ------------------------------------------------------------------------ */
4756/* Function:    fr_deletequeueentry                                         */
4757/* Returns:     Nil                                                         */
4758/* Parameters:  tqe(I) - timeout queue entry to delete                      */
4759/*              ifq(I) - timeout queue to remove entry from                 */
4760/*                                                                          */
4761/* Remove a tail queue entry from its queue and make it an orphan.          */
4762/* fr_deletetimeoutqueue is called to make sure the reference count on the  */
4763/* queue is correct.  We can't, however, call fr_freetimeoutqueue because   */
4764/* the correct lock(s) may not be held that would make it safe to do so.    */
4765/* ------------------------------------------------------------------------ */
4766void fr_deletequeueentry(tqe)
4767ipftqent_t *tqe;
4768{
4769	ipftq_t *ifq;
4770
4771	ifq = tqe->tqe_ifq;
4772	if (ifq == NULL)
4773		return;
4774
4775	MUTEX_ENTER(&ifq->ifq_lock);
4776
4777	if (tqe->tqe_pnext != NULL) {
4778		*tqe->tqe_pnext = tqe->tqe_next;
4779		if (tqe->tqe_next != NULL)
4780			tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
4781		else    /* we must be the tail anyway */
4782			ifq->ifq_tail = tqe->tqe_pnext;
4783
4784		tqe->tqe_pnext = NULL;
4785		tqe->tqe_ifq = NULL;
4786	}
4787
4788	(void) fr_deletetimeoutqueue(ifq);
4789
4790	MUTEX_EXIT(&ifq->ifq_lock);
4791}
4792
4793
4794/* ------------------------------------------------------------------------ */
4795/* Function:    fr_queuefront                                               */
4796/* Returns:     Nil                                                         */
4797/* Parameters:  tqe(I) - pointer to timeout queue entry                     */
4798/*                                                                          */
4799/* Move a queue entry to the front of the queue, if it isn't already there. */
4800/* ------------------------------------------------------------------------ */
4801void fr_queuefront(tqe)
4802ipftqent_t *tqe;
4803{
4804	ipftq_t *ifq;
4805
4806	ifq = tqe->tqe_ifq;
4807	if (ifq == NULL)
4808		return;
4809
4810	MUTEX_ENTER(&ifq->ifq_lock);
4811	if (ifq->ifq_head != tqe) {
4812		*tqe->tqe_pnext = tqe->tqe_next;
4813		if (tqe->tqe_next)
4814			tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
4815		else
4816			ifq->ifq_tail = tqe->tqe_pnext;
4817
4818		tqe->tqe_next = ifq->ifq_head;
4819		ifq->ifq_head->tqe_pnext = &tqe->tqe_next;
4820		ifq->ifq_head = tqe;
4821		tqe->tqe_pnext = &ifq->ifq_head;
4822	}
4823	MUTEX_EXIT(&ifq->ifq_lock);
4824}
4825
4826
4827/* ------------------------------------------------------------------------ */
4828/* Function:    fr_queueback                                                */
4829/* Returns:     Nil                                                         */
4830/* Parameters:  tqe(I) - pointer to timeout queue entry                     */
4831/*                                                                          */
4832/* Move a queue entry to the back of the queue, if it isn't already there.  */
4833/* ------------------------------------------------------------------------ */
4834void fr_queueback(tqe)
4835ipftqent_t *tqe;
4836{
4837	ipftq_t *ifq;
4838
4839	ifq = tqe->tqe_ifq;
4840	if (ifq == NULL)
4841		return;
4842	tqe->tqe_die = fr_ticks + ifq->ifq_ttl;
4843
4844	MUTEX_ENTER(&ifq->ifq_lock);
4845	if (tqe->tqe_next == NULL) {		/* at the end already ? */
4846		MUTEX_EXIT(&ifq->ifq_lock);
4847		return;
4848	}
4849
4850	/*
4851	 * Remove from list
4852	 */
4853	*tqe->tqe_pnext = tqe->tqe_next;
4854	tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
4855
4856	/*
4857	 * Make it the last entry.
4858	 */
4859	tqe->tqe_next = NULL;
4860	tqe->tqe_pnext = ifq->ifq_tail;
4861	*ifq->ifq_tail = tqe;
4862	ifq->ifq_tail = &tqe->tqe_next;
4863	MUTEX_EXIT(&ifq->ifq_lock);
4864}
4865
4866
4867/* ------------------------------------------------------------------------ */
4868/* Function:    fr_queueappend                                              */
4869/* Returns:     Nil                                                         */
4870/* Parameters:  tqe(I)    - pointer to timeout queue entry                  */
4871/*              ifq(I)    - pointer to timeout queue                        */
4872/*              parent(I) - owing object pointer                            */
4873/*                                                                          */
4874/* Add a new item to this queue and put it on the very end.                 */
4875/* ------------------------------------------------------------------------ */
4876void fr_queueappend(tqe, ifq, parent)
4877ipftqent_t *tqe;
4878ipftq_t *ifq;
4879void *parent;
4880{
4881
4882	MUTEX_ENTER(&ifq->ifq_lock);
4883	tqe->tqe_parent = parent;
4884	tqe->tqe_pnext = ifq->ifq_tail;
4885	*ifq->ifq_tail = tqe;
4886	ifq->ifq_tail = &tqe->tqe_next;
4887	tqe->tqe_next = NULL;
4888	tqe->tqe_ifq = ifq;
4889	tqe->tqe_die = fr_ticks + ifq->ifq_ttl;
4890	ifq->ifq_ref++;
4891	MUTEX_EXIT(&ifq->ifq_lock);
4892}
4893
4894
4895/* ------------------------------------------------------------------------ */
4896/* Function:    fr_movequeue                                                */
4897/* Returns:     Nil                                                         */
4898/* Parameters:  tq(I)   - pointer to timeout queue information              */
4899/*              oifp(I) - old timeout queue entry was on                    */
4900/*              nifp(I) - new timeout queue to put entry on                 */
4901/*                                                                          */
4902/* Move a queue entry from one timeout queue to another timeout queue.      */
4903/* If it notices that the current entry is already last and does not need   */
4904/* to move queue, the return.                                               */
4905/* ------------------------------------------------------------------------ */
4906void fr_movequeue(tqe, oifq, nifq)
4907ipftqent_t *tqe;
4908ipftq_t *oifq, *nifq;
4909{
4910	/*
4911	 * Is the operation here going to be a no-op ?
4912	 */
4913	MUTEX_ENTER(&oifq->ifq_lock);
4914	if (oifq == nifq && *oifq->ifq_tail == tqe) {
4915		MUTEX_EXIT(&oifq->ifq_lock);
4916		return;
4917	}
4918
4919	/*
4920	 * Remove from the old queue
4921	 */
4922	*tqe->tqe_pnext = tqe->tqe_next;
4923	if (tqe->tqe_next)
4924		tqe->tqe_next->tqe_pnext = tqe->tqe_pnext;
4925	else
4926		oifq->ifq_tail = tqe->tqe_pnext;
4927	tqe->tqe_next = NULL;
4928
4929	/*
4930	 * If we're moving from one queue to another, release the lock on the
4931	 * old queue and get a lock on the new queue.  For user defined queues,
4932	 * if we're moving off it, call delete in case it can now be freed.
4933	 */
4934	if (oifq != nifq) {
4935		tqe->tqe_ifq = NULL;
4936
4937		(void) fr_deletetimeoutqueue(oifq);
4938
4939		MUTEX_EXIT(&oifq->ifq_lock);
4940
4941		MUTEX_ENTER(&nifq->ifq_lock);
4942
4943		tqe->tqe_ifq = nifq;
4944		nifq->ifq_ref++;
4945	}
4946
4947	/*
4948	 * Add to the bottom of the new queue
4949	 */
4950	tqe->tqe_die = fr_ticks + nifq->ifq_ttl;
4951	tqe->tqe_pnext = nifq->ifq_tail;
4952	*nifq->ifq_tail = tqe;
4953	nifq->ifq_tail = &tqe->tqe_next;
4954	MUTEX_EXIT(&nifq->ifq_lock);
4955}
4956
4957
4958/* ------------------------------------------------------------------------ */
4959/* Function:    fr_updateipid                                               */
4960/* Returns:     int - 0 == success, -1 == error (packet should be droppped) */
4961/* Parameters:  fin(I) - pointer to packet information                      */
4962/*                                                                          */
4963/* When we are doing NAT, change the IP of every packet to represent a      */
4964/* single sequence of packets coming from the host, hiding any host         */
4965/* specific sequencing that might otherwise be revealed.  If the packet is  */
4966/* a fragment, then store the 'new' IPid in the fragment cache and look up  */
4967/* the fragment cache for non-leading fragments.  If a non-leading fragment */
4968/* has no match in the cache, return an error.                              */
4969/* ------------------------------------------------------------------------ */
4970static INLINE int fr_updateipid(fin)
4971fr_info_t *fin;
4972{
4973	u_short id, ido, sums;
4974	u_32_t sumd, sum;
4975	ip_t *ip;
4976
4977	if (fin->fin_off != 0) {
4978		sum = fr_ipid_knownfrag(fin);
4979		if (sum == 0xffffffff)
4980			return -1;
4981		sum &= 0xffff;
4982		id = (u_short)sum;
4983	} else {
4984		id = fr_nextipid(fin);
4985		if (fin->fin_off == 0 && (fin->fin_flx & FI_FRAG) != 0)
4986			(void) fr_ipid_newfrag(fin, (u_32_t)id);
4987	}
4988
4989	ip = fin->fin_ip;
4990	ido = ntohs(ip->ip_id);
4991	if (id == ido)
4992		return 0;
4993	ip->ip_id = htons(id);
4994	CALC_SUMD(ido, id, sumd);	/* DESTRUCTIVE MACRO! id,ido change */
4995	sum = (~ntohs(ip->ip_sum)) & 0xffff;
4996	sum += sumd;
4997	sum = (sum >> 16) + (sum & 0xffff);
4998	sum = (sum >> 16) + (sum & 0xffff);
4999	sums = ~(u_short)sum;
5000	ip->ip_sum = htons(sums);
5001	return 0;
5002}
5003
5004
5005#ifdef	NEED_FRGETIFNAME
5006/* ------------------------------------------------------------------------ */
5007/* Function:    fr_getifname                                                */
5008/* Returns:     char *    - pointer to interface name                       */
5009/* Parameters:  ifp(I)    - pointer to network interface                    */
5010/*              buffer(O) - pointer to where to store interface name        */
5011/*                                                                          */
5012/* Constructs an interface name in the buffer passed.  The buffer passed is */
5013/* expected to be at least LIFNAMSIZ in bytes big.  If buffer is passed in  */
5014/* as a NULL pointer then return a pointer to a static array.               */
5015/* ------------------------------------------------------------------------ */
5016char *fr_getifname(ifp, buffer)
5017struct ifnet *ifp;
5018char *buffer;
5019{
5020	static char namebuf[LIFNAMSIZ];
5021# if defined(MENTAT) || defined(__FreeBSD__) || defined(__osf__) || \
5022     defined(__sgi) || defined(linux) || \
5023     (defined(sun) && !defined(__SVR4) && !defined(__svr4__))
5024	int unit, space;
5025	char temp[20];
5026	char *s;
5027# endif
5028
5029	if (buffer == NULL)
5030		buffer = namebuf;
5031	(void) strncpy(buffer, ifp->if_name, LIFNAMSIZ);
5032	buffer[LIFNAMSIZ - 1] = '\0';
5033# if defined(MENTAT) || defined(__FreeBSD__) || defined(__osf__) || \
5034     defined(__sgi) || \
5035     (defined(sun) && !defined(__SVR4) && !defined(__svr4__))
5036	for (s = buffer; *s; s++)
5037		;
5038	unit = ifp->if_unit;
5039	space = LIFNAMSIZ - (s - buffer);
5040	if (space > 0) {
5041#  if defined(SNPRINTF) && defined(_KERNEL)
5042		SNPRINTF(temp, sizeof(temp), "%d", unit);
5043#  else
5044		(void) sprintf(temp, "%d", unit);
5045#  endif
5046		(void) strncpy(s, temp, space);
5047	}
5048# endif
5049	return buffer;
5050}
5051#endif
5052
5053
5054/* ------------------------------------------------------------------------ */
5055/* Function:    fr_ioctlswitch                                              */
5056/* Returns:     int     - -1 continue processing, else ioctl return value   */
5057/* Parameters:  unit(I) - device unit opened                                */
5058/*              data(I) - pointer to ioctl data                             */
5059/*              cmd(I)  - ioctl command                                     */
5060/*              mode(I) - mode value                                        */
5061/*                                                                          */
5062/* Based on the value of unit, call the appropriate ioctl handler or return */
5063/* EIO if ipfilter is not running.   Also checks if write perms are req'd   */
5064/* for the device in order to execute the ioctl.                            */
5065/* ------------------------------------------------------------------------ */
5066int fr_ioctlswitch(unit, data, cmd, mode)
5067int unit, mode;
5068ioctlcmd_t cmd;
5069void *data;
5070{
5071	int error = 0;
5072
5073	switch (unit)
5074	{
5075	case IPL_LOGIPF :
5076		error = -1;
5077		break;
5078	case IPL_LOGNAT :
5079		if (fr_running > 0)
5080			error = fr_nat_ioctl(data, cmd, mode);
5081		else
5082			error = EIO;
5083		break;
5084	case IPL_LOGSTATE :
5085		if (fr_running > 0)
5086			error = fr_state_ioctl(data, cmd, mode);
5087		else
5088			error = EIO;
5089		break;
5090	case IPL_LOGAUTH :
5091		if (fr_running > 0) {
5092			if ((cmd == (ioctlcmd_t)SIOCADAFR) ||
5093			    (cmd == (ioctlcmd_t)SIOCRMAFR)) {
5094				if (!(mode & FWRITE)) {
5095					error = EPERM;
5096				} else {
5097					error = frrequest(unit, cmd, data,
5098							  fr_active, 1);
5099				}
5100			} else {
5101				error = fr_auth_ioctl(data, cmd, mode);
5102			}
5103		} else
5104			error = EIO;
5105		break;
5106	case IPL_LOGSYNC :
5107#ifdef IPFILTER_SYNC
5108		if (fr_running > 0)
5109			error = fr_sync_ioctl(data, cmd, mode);
5110		else
5111#endif
5112			error = EIO;
5113		break;
5114	case IPL_LOGSCAN :
5115#ifdef IPFILTER_SCAN
5116		if (fr_running > 0)
5117			error = fr_scan_ioctl(data, cmd, mode);
5118		else
5119#endif
5120			error = EIO;
5121		break;
5122	case IPL_LOGLOOKUP :
5123#ifdef IPFILTER_LOOKUP
5124		if (fr_running > 0)
5125			error = ip_lookup_ioctl(data, cmd, mode);
5126		else
5127#endif
5128			error = EIO;
5129		break;
5130	default :
5131		error = EIO;
5132		break;
5133	}
5134
5135	return error;
5136}
5137
5138
5139/*
5140 * This array defines the expected size of objects coming into the kernel
5141 * for the various recognised object types.
5142 */
5143#define	NUM_OBJ_TYPES	14
5144
5145static	int	fr_objbytes[NUM_OBJ_TYPES][2] = {
5146	{ 1,	sizeof(struct frentry) },		/* frentry */
5147	{ 0,	sizeof(struct friostat) },
5148	{ 0,	sizeof(struct fr_info) },
5149	{ 0,	sizeof(struct fr_authstat) },
5150	{ 0,	sizeof(struct ipfrstat) },
5151	{ 0,	sizeof(struct ipnat) },
5152	{ 0,	sizeof(struct natstat) },
5153	{ 0,	sizeof(struct ipstate_save) },
5154	{ 1,	sizeof(struct nat_save) },		/* nat_save */
5155	{ 0,	sizeof(struct natlookup) },
5156	{ 1,	sizeof(struct ipstate) },		/* ipstate */
5157	{ 0,	sizeof(struct ips_stat) },
5158	{ 0,	sizeof(struct frauth) },
5159	{ 0,	sizeof(struct ipftune) }
5160};
5161
5162
5163/* ------------------------------------------------------------------------ */
5164/* Function:    fr_inobj                                                    */
5165/* Returns:     int     - 0 = success, else failure                         */
5166/* Parameters:  data(I) - pointer to ioctl data                             */
5167/*              ptr(I)  - pointer to store real data in                     */
5168/*              type(I) - type of structure being moved                     */
5169/*                                                                          */
5170/* Copy in the contents of what the ipfobj_t points to.  In future, we      */
5171/* add things to check for version numbers, sizes, etc, to make it backward */
5172/* compatible at the ABI for user land.                                     */
5173/* ------------------------------------------------------------------------ */
5174int fr_inobj(data, ptr, type)
5175void *data;
5176void *ptr;
5177int type;
5178{
5179	ipfobj_t obj;
5180	int error = 0;
5181
5182	if ((type < 0) || (type > NUM_OBJ_TYPES-1))
5183		return EINVAL;
5184
5185	BCOPYIN((caddr_t)data, (caddr_t)&obj, sizeof(obj));
5186
5187	if (obj.ipfo_type != type)
5188		return EINVAL;
5189
5190#ifndef	IPFILTER_COMPAT
5191	if ((fr_objbytes[type][0] & 1) != 0) {
5192		if (obj.ipfo_size < fr_objbytes[type][1])
5193			return EINVAL;
5194	} else if (obj.ipfo_size != fr_objbytes[type][1])
5195		return EINVAL;
5196#else
5197	if (obj.ipfo_rev != IPFILTER_VERSION)
5198		/* XXX compatibility hook here */
5199		;
5200	if ((fr_objbytes[type][0] & 1) != 0) {
5201		if (obj.ipfo_size < fr_objbytes[type][1])
5202			/* XXX compatibility hook here */
5203			return EINVAL;
5204	} else if (obj.ipfo_size != fr_objbytes[type][1])
5205		/* XXX compatibility hook here */
5206		return EINVAL;
5207#endif
5208
5209	if ((fr_objbytes[type][0] & 1) != 0) {
5210		error = COPYIN((caddr_t)obj.ipfo_ptr, (caddr_t)ptr,
5211				fr_objbytes[type][1]);
5212	} else {
5213		error = COPYIN((caddr_t)obj.ipfo_ptr, (caddr_t)ptr,
5214				obj.ipfo_size);
5215	}
5216	return error;
5217}
5218
5219
5220/* ------------------------------------------------------------------------ */
5221/* Function:    fr_inobjsz                                                  */
5222/* Returns:     int     - 0 = success, else failure                         */
5223/* Parameters:  data(I) - pointer to ioctl data                             */
5224/*              ptr(I)  - pointer to store real data in                     */
5225/*              type(I) - type of structure being moved                     */
5226/*              sz(I)   - size of data to copy                              */
5227/*                                                                          */
5228/* As per fr_inobj, except the size of the object to copy in is passed in   */
5229/* but it must not be smaller than the size defined for the type and the    */
5230/* type must allow for varied sized objects.  The extra requirement here is */
5231/* that sz must match the size of the object being passed in - this is not  */
5232/* not possible nor required in fr_inobj().                                 */
5233/* ------------------------------------------------------------------------ */
5234int fr_inobjsz(data, ptr, type, sz)
5235void *data;
5236void *ptr;
5237int type, sz;
5238{
5239	ipfobj_t obj;
5240	int error;
5241
5242	if ((type < 0) || (type > NUM_OBJ_TYPES-1))
5243		return EINVAL;
5244	if (((fr_objbytes[type][0] & 1) == 0) || (sz < fr_objbytes[type][1]))
5245		return EINVAL;
5246
5247	BCOPYIN((caddr_t)data, (caddr_t)&obj, sizeof(obj));
5248
5249	if (obj.ipfo_type != type)
5250		return EINVAL;
5251
5252#ifndef	IPFILTER_COMPAT
5253	if (obj.ipfo_size != sz)
5254		return EINVAL;
5255#else
5256	if (obj.ipfo_rev != IPFILTER_VERSION)
5257		/* XXX compatibility hook here */
5258		;
5259	if (obj.ipfo_size != sz)
5260		/* XXX compatibility hook here */
5261		return EINVAL;
5262#endif
5263
5264	error = COPYIN((caddr_t)obj.ipfo_ptr, (caddr_t)ptr, sz);
5265	return error;
5266}
5267
5268
5269/* ------------------------------------------------------------------------ */
5270/* Function:    fr_outobjsz                                                 */
5271/* Returns:     int     - 0 = success, else failure                         */
5272/* Parameters:  data(I) - pointer to ioctl data                             */
5273/*              ptr(I)  - pointer to store real data in                     */
5274/*              type(I) - type of structure being moved                     */
5275/*              sz(I)   - size of data to copy                              */
5276/*                                                                          */
5277/* As per fr_outobj, except the size of the object to copy out is passed in */
5278/* but it must not be smaller than the size defined for the type and the    */
5279/* type must allow for varied sized objects.  The extra requirement here is */
5280/* that sz must match the size of the object being passed in - this is not  */
5281/* not possible nor required in fr_outobj().                                */
5282/* ------------------------------------------------------------------------ */
5283int fr_outobjsz(data, ptr, type, sz)
5284void *data;
5285void *ptr;
5286int type, sz;
5287{
5288	ipfobj_t obj;
5289	int error;
5290
5291	if ((type < 0) || (type > NUM_OBJ_TYPES-1) ||
5292	    ((fr_objbytes[type][0] & 1) == 0) ||
5293	    (sz < fr_objbytes[type][1]))
5294		return EINVAL;
5295
5296	BCOPYIN((caddr_t)data, (caddr_t)&obj, sizeof(obj));
5297
5298	if (obj.ipfo_type != type)
5299		return EINVAL;
5300
5301#ifndef	IPFILTER_COMPAT
5302	if (obj.ipfo_size != sz)
5303		return EINVAL;
5304#else
5305	if (obj.ipfo_rev != IPFILTER_VERSION)
5306		/* XXX compatibility hook here */
5307		;
5308	if (obj.ipfo_size != sz)
5309		/* XXX compatibility hook here */
5310		return EINVAL;
5311#endif
5312
5313	error = COPYOUT((caddr_t)ptr, (caddr_t)obj.ipfo_ptr, sz);
5314	return error;
5315}
5316
5317
5318/* ------------------------------------------------------------------------ */
5319/* Function:    fr_outobj                                                   */
5320/* Returns:     int     - 0 = success, else failure                         */
5321/* Parameters:  data(I) - pointer to ioctl data                             */
5322/*              ptr(I)  - pointer to store real data in                     */
5323/*              type(I) - type of structure being moved                     */
5324/*                                                                          */
5325/* Copy out the contents of what ptr is to where ipfobj points to.  In      */
5326/* future, we add things to check for version numbers, sizes, etc, to make  */
5327/* it backward  compatible at the ABI for user land.                        */
5328/* ------------------------------------------------------------------------ */
5329int fr_outobj(data, ptr, type)
5330void *data;
5331void *ptr;
5332int type;
5333{
5334	ipfobj_t obj;
5335	int error;
5336
5337	if ((type < 0) || (type > NUM_OBJ_TYPES-1))
5338		return EINVAL;
5339
5340	BCOPYIN((caddr_t)data, (caddr_t)&obj, sizeof(obj));
5341
5342	if (obj.ipfo_type != type)
5343		return EINVAL;
5344
5345#ifndef	IPFILTER_COMPAT
5346	if ((fr_objbytes[type][0] & 1) != 0) {
5347		if (obj.ipfo_size < fr_objbytes[type][1])
5348			return EINVAL;
5349	} else if (obj.ipfo_size != fr_objbytes[type][1])
5350		return EINVAL;
5351#else
5352	if (obj.ipfo_rev != IPFILTER_VERSION)
5353		/* XXX compatibility hook here */
5354		;
5355	if ((fr_objbytes[type][0] & 1) != 0) {
5356		if (obj.ipfo_size < fr_objbytes[type][1])
5357			/* XXX compatibility hook here */
5358			return EINVAL;
5359	} else if (obj.ipfo_size != fr_objbytes[type][1])
5360		/* XXX compatibility hook here */
5361		return EINVAL;
5362#endif
5363
5364	error = COPYOUT((caddr_t)ptr, (caddr_t)obj.ipfo_ptr, obj.ipfo_size);
5365	return error;
5366}
5367
5368
5369/* ------------------------------------------------------------------------ */
5370/* Function:    fr_checkl4sum                                               */
5371/* Returns:     int     - 0 = good, -1 = bad, 1 = cannot check              */
5372/* Parameters:  fin(I) - pointer to packet information                      */
5373/*                                                                          */
5374/* If possible, calculate the layer 4 checksum for the packet.  If this is  */
5375/* not possible, return without indicating a failure or success but in a    */
5376/* way that is ditinguishable.                                              */
5377/* ------------------------------------------------------------------------ */
5378int fr_checkl4sum(fin)
5379fr_info_t *fin;
5380{
5381	u_short sum, hdrsum, *csump;
5382	udphdr_t *udp;
5383	int dosum;
5384
5385	if ((fin->fin_flx & FI_NOCKSUM) != 0)
5386		return 0;
5387
5388	/*
5389	 * If the TCP packet isn't a fragment, isn't too short and otherwise
5390	 * isn't already considered "bad", then validate the checksum.  If
5391	 * this check fails then considered the packet to be "bad".
5392	 */
5393	if ((fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) != 0)
5394		return 1;
5395
5396	csump = NULL;
5397	hdrsum = 0;
5398	dosum = 0;
5399	sum = 0;
5400
5401#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID)
5402	if (dohwcksum && ((*fin->fin_mp)->b_ick_flag == ICK_VALID)) {
5403		hdrsum = 0;
5404		sum = 0;
5405	} else {
5406#endif
5407		switch (fin->fin_p)
5408		{
5409		case IPPROTO_TCP :
5410			csump = &((tcphdr_t *)fin->fin_dp)->th_sum;
5411			dosum = 1;
5412			break;
5413
5414		case IPPROTO_UDP :
5415			udp = fin->fin_dp;
5416			if (udp->uh_sum != 0) {
5417				csump = &udp->uh_sum;
5418				dosum = 1;
5419			}
5420			break;
5421
5422		case IPPROTO_ICMP :
5423			csump = &((struct icmp *)fin->fin_dp)->icmp_cksum;
5424			dosum = 1;
5425			break;
5426
5427		default :
5428			return 1;
5429			/*NOTREACHED*/
5430		}
5431
5432		if (csump != NULL)
5433			hdrsum = *csump;
5434
5435		if (dosum)
5436			sum = fr_cksum(fin->fin_m, fin->fin_ip,
5437				       fin->fin_p, fin->fin_dp);
5438#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID)
5439	}
5440#endif
5441#if !defined(_KERNEL)
5442	if (sum == hdrsum) {
5443		FR_DEBUG(("checkl4sum: %hx == %hx\n", sum, hdrsum));
5444	} else {
5445		FR_DEBUG(("checkl4sum: %hx != %hx\n", sum, hdrsum));
5446	}
5447#endif
5448	if (hdrsum == sum)
5449		return 0;
5450	return -1;
5451}
5452
5453
5454/* ------------------------------------------------------------------------ */
5455/* Function:    fr_ifpfillv4addr                                            */
5456/* Returns:     int     - 0 = address update, -1 = address not updated      */
5457/* Parameters:  atype(I)   - type of network address update to perform      */
5458/*              sin(I)     - pointer to source of address information       */
5459/*              mask(I)    - pointer to source of netmask information       */
5460/*              inp(I)     - pointer to destination address store           */
5461/*              inpmask(I) - pointer to destination netmask store           */
5462/*                                                                          */
5463/* Given a type of network address update (atype) to perform, copy          */
5464/* information from sin/mask into inp/inpmask.  If ipnmask is NULL then no  */
5465/* netmask update is performed unless FRI_NETMASKED is passed as atype, in  */
5466/* which case the operation fails.  For all values of atype other than      */
5467/* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s  */
5468/* value.                                                                   */
5469/* ------------------------------------------------------------------------ */
5470int fr_ifpfillv4addr(atype, sin, mask, inp, inpmask)
5471int atype;
5472struct sockaddr_in *sin, *mask;
5473struct in_addr *inp, *inpmask;
5474{
5475	if (inpmask != NULL && atype != FRI_NETMASKED)
5476		inpmask->s_addr = 0xffffffff;
5477
5478	if (atype == FRI_NETWORK || atype == FRI_NETMASKED) {
5479		if (atype == FRI_NETMASKED) {
5480			if (inpmask == NULL)
5481				return -1;
5482			inpmask->s_addr = mask->sin_addr.s_addr;
5483		}
5484		inp->s_addr = sin->sin_addr.s_addr & mask->sin_addr.s_addr;
5485	} else {
5486		inp->s_addr = sin->sin_addr.s_addr;
5487	}
5488	return 0;
5489}
5490
5491
5492#ifdef	USE_INET6
5493/* ------------------------------------------------------------------------ */
5494/* Function:    fr_ifpfillv6addr                                            */
5495/* Returns:     int     - 0 = address update, -1 = address not updated      */
5496/* Parameters:  atype(I)   - type of network address update to perform      */
5497/*              sin(I)     - pointer to source of address information       */
5498/*              mask(I)    - pointer to source of netmask information       */
5499/*              inp(I)     - pointer to destination address store           */
5500/*              inpmask(I) - pointer to destination netmask store           */
5501/*                                                                          */
5502/* Given a type of network address update (atype) to perform, copy          */
5503/* information from sin/mask into inp/inpmask.  If ipnmask is NULL then no  */
5504/* netmask update is performed unless FRI_NETMASKED is passed as atype, in  */
5505/* which case the operation fails.  For all values of atype other than      */
5506/* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s  */
5507/* value.                                                                   */
5508/* ------------------------------------------------------------------------ */
5509int fr_ifpfillv6addr(atype, sin, mask, inp, inpmask)
5510int atype;
5511struct sockaddr_in6 *sin, *mask;
5512struct in_addr *inp, *inpmask;
5513{
5514	i6addr_t *src, *dst, *and, *dmask;
5515
5516	src = (i6addr_t *)&sin->sin6_addr;
5517	and = (i6addr_t *)&mask->sin6_addr;
5518	dst = (i6addr_t *)inp;
5519	dmask = (i6addr_t *)inpmask;
5520
5521	if (inpmask != NULL && atype != FRI_NETMASKED) {
5522		dmask->i6[0] = 0xffffffff;
5523		dmask->i6[1] = 0xffffffff;
5524		dmask->i6[2] = 0xffffffff;
5525		dmask->i6[3] = 0xffffffff;
5526	}
5527
5528	if (atype == FRI_NETWORK || atype == FRI_NETMASKED) {
5529		if (atype == FRI_NETMASKED) {
5530			if (inpmask == NULL)
5531				return -1;
5532			dmask->i6[0] = and->i6[0];
5533			dmask->i6[1] = and->i6[1];
5534			dmask->i6[2] = and->i6[2];
5535			dmask->i6[3] = and->i6[3];
5536		}
5537
5538		dst->i6[0] = src->i6[0] & and->i6[0];
5539		dst->i6[1] = src->i6[1] & and->i6[1];
5540		dst->i6[2] = src->i6[2] & and->i6[2];
5541		dst->i6[3] = src->i6[3] & and->i6[3];
5542	} else {
5543		dst->i6[0] = src->i6[0];
5544		dst->i6[1] = src->i6[1];
5545		dst->i6[2] = src->i6[2];
5546		dst->i6[3] = src->i6[3];
5547	}
5548	return 0;
5549}
5550#endif
5551
5552
5553/* ------------------------------------------------------------------------ */
5554/* Function:    fr_matchtag                                                 */
5555/* Returns:     0 == mismatch, 1 == match.                                  */
5556/* Parameters:  tag1(I) - pointer to first tag to compare                   */
5557/*              tag2(I) - pointer to second tag to compare                  */
5558/*                                                                          */
5559/* Returns true (non-zero) or false(0) if the two tag structures can be     */
5560/* considered to be a match or not match, respectively.  The tag is 16      */
5561/* bytes long (16 characters) but that is overlayed with 4 32bit ints so    */
5562/* compare the ints instead, for speed. tag1 is the master of the           */
5563/* comparison.  This function should only be called with both tag1 and tag2 */
5564/* as non-NULL pointers.                                                    */
5565/* ------------------------------------------------------------------------ */
5566int fr_matchtag(tag1, tag2)
5567ipftag_t *tag1, *tag2;
5568{
5569	if (tag1 == tag2)
5570		return 1;
5571
5572	if ((tag1->ipt_num[0] == 0) && (tag2->ipt_num[0] == 0))
5573		return 1;
5574
5575	if ((tag1->ipt_num[0] == tag2->ipt_num[0]) &&
5576	    (tag1->ipt_num[1] == tag2->ipt_num[1]) &&
5577	    (tag1->ipt_num[2] == tag2->ipt_num[2]) &&
5578	    (tag1->ipt_num[3] == tag2->ipt_num[3]))
5579		return 1;
5580	return 0;
5581}
5582
5583
5584/* ------------------------------------------------------------------------ */
5585/* Function:    fr_coalesce                                                 */
5586/* Returns:     1 == success, -1 == failure, 0 == no change                 */
5587/* Parameters:  fin(I) - pointer to packet information                      */
5588/*                                                                          */
5589/* Attempt to get all of the packet data into a single, contiguous buffer.  */
5590/* If this call returns a failure then the buffers have also been freed.    */
5591/* ------------------------------------------------------------------------ */
5592int fr_coalesce(fin)
5593fr_info_t *fin;
5594{
5595	if ((fin->fin_flx & FI_COALESCE) != 0)
5596		return 1;
5597
5598	/*
5599	 * If the mbuf pointers indicate that there is no mbuf to work with,
5600	 * return but do not indicate success or failure.
5601	 */
5602	if (fin->fin_m == NULL || fin->fin_mp == NULL)
5603		return 0;
5604
5605#if defined(_KERNEL)
5606	if (fr_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) {
5607		ATOMIC_INCL(fr_badcoalesces[fin->fin_out]);
5608# ifdef MENTAT
5609		FREE_MB_T(*fin->fin_mp);
5610# endif
5611		*fin->fin_mp = NULL;
5612		fin->fin_m = NULL;
5613		return -1;
5614	}
5615#else
5616	fin = fin;	/* LINT */
5617#endif
5618	return 1;
5619}
5620
5621
5622/*
5623 * The following table lists all of the tunable variables that can be
5624 * accessed via SIOCIPFGET/SIOCIPFSET/SIOCIPFGETNEXt.  The format of each row
5625 * in the table below is as follows:
5626 *
5627 * pointer to value, name of value, minimum, maximum, size of the value's
5628 *     container, value attribute flags
5629 *
5630 * For convienience, IPFT_RDONLY means the value is read-only, IPFT_WRDISABLED
5631 * means the value can only be written to when IPFilter is loaded but disabled.
5632 * The obvious implication is if neither of these are set then the value can be
5633 * changed at any time without harm.
5634 */
5635ipftuneable_t ipf_tuneables[] = {
5636	/* filtering */
5637	{ { &fr_flags },	"fr_flags",		0,	0xffffffff,
5638			sizeof(fr_flags),		0 },
5639	{ { &fr_active },	"fr_active",		0,	0,
5640			sizeof(fr_active),		IPFT_RDONLY },
5641	{ { &fr_control_forwarding },	"fr_control_forwarding",	0, 1,
5642			sizeof(fr_control_forwarding),	0 },
5643	{ { &fr_update_ipid },	"fr_update_ipid",	0,	1,
5644			sizeof(fr_update_ipid),		0 },
5645	{ { &fr_chksrc },	"fr_chksrc",		0,	1,
5646			sizeof(fr_chksrc),		0 },
5647	{ { &fr_pass },		"fr_pass",		0,	0xffffffff,
5648			sizeof(fr_pass),		0 },
5649	/* state */
5650	{ { &fr_tcpidletimeout }, "fr_tcpidletimeout",	1,	0x7fffffff,
5651			sizeof(fr_tcpidletimeout),	IPFT_WRDISABLED },
5652	{ { &fr_tcpclosewait },	"fr_tcpclosewait",	1,	0x7fffffff,
5653			sizeof(fr_tcpclosewait),	IPFT_WRDISABLED },
5654	{ { &fr_tcplastack },	"fr_tcplastack",	1,	0x7fffffff,
5655			sizeof(fr_tcplastack),		IPFT_WRDISABLED },
5656	{ { &fr_tcptimeout },	"fr_tcptimeout",	1,	0x7fffffff,
5657			sizeof(fr_tcptimeout),		IPFT_WRDISABLED },
5658	{ { &fr_tcpclosed },	"fr_tcpclosed",		1,	0x7fffffff,
5659			sizeof(fr_tcpclosed),		IPFT_WRDISABLED },
5660	{ { &fr_tcphalfclosed }, "fr_tcphalfclosed",	1,	0x7fffffff,
5661			sizeof(fr_tcphalfclosed),	IPFT_WRDISABLED },
5662	{ { &fr_udptimeout },	"fr_udptimeout",	1,	0x7fffffff,
5663			sizeof(fr_udptimeout),		IPFT_WRDISABLED },
5664	{ { &fr_udpacktimeout }, "fr_udpacktimeout",	1,	0x7fffffff,
5665			sizeof(fr_udpacktimeout),	IPFT_WRDISABLED },
5666	{ { &fr_icmptimeout },	"fr_icmptimeout",	1,	0x7fffffff,
5667			sizeof(fr_icmptimeout),		IPFT_WRDISABLED },
5668	{ { &fr_icmpacktimeout }, "fr_icmpacktimeout",	1,	0x7fffffff,
5669			sizeof(fr_icmpacktimeout),	IPFT_WRDISABLED },
5670	{ { &fr_iptimeout }, "fr_iptimeout",		1,	0x7fffffff,
5671			sizeof(fr_iptimeout),		IPFT_WRDISABLED },
5672	{ { &fr_statemax },	"fr_statemax",		1,	0x7fffffff,
5673			sizeof(fr_statemax),		0 },
5674	{ { &fr_statesize },	"fr_statesize",		1,	0x7fffffff,
5675			sizeof(fr_statesize),		IPFT_WRDISABLED },
5676	{ { &fr_state_lock },	"fr_state_lock",	0,	1,
5677			sizeof(fr_state_lock),		IPFT_RDONLY },
5678	{ { &fr_state_maxbucket }, "fr_state_maxbucket", 1,	0x7fffffff,
5679			sizeof(fr_state_maxbucket),	IPFT_WRDISABLED },
5680	{ { &fr_state_maxbucket_reset }, "fr_state_maxbucket_reset",	0, 1,
5681			sizeof(fr_state_maxbucket_reset), IPFT_WRDISABLED },
5682	{ { &ipstate_logging },	"ipstate_logging",	0,	1,
5683			sizeof(ipstate_logging),	0 },
5684	/* nat */
5685	{ { &fr_nat_lock },		"fr_nat_lock",		0,	1,
5686			sizeof(fr_nat_lock),		IPFT_RDONLY },
5687	{ { &ipf_nattable_sz },	"ipf_nattable_sz",	1,	0x7fffffff,
5688			sizeof(ipf_nattable_sz),	IPFT_WRDISABLED },
5689	{ { &ipf_nattable_max }, "ipf_nattable_max",	1,	0x7fffffff,
5690			sizeof(ipf_nattable_max),	0 },
5691	{ { &ipf_natrules_sz },	"ipf_natrules_sz",	1,	0x7fffffff,
5692			sizeof(ipf_natrules_sz),	IPFT_WRDISABLED },
5693	{ { &ipf_rdrrules_sz },	"ipf_rdrrules_sz",	1,	0x7fffffff,
5694			sizeof(ipf_rdrrules_sz),	IPFT_WRDISABLED },
5695	{ { &ipf_hostmap_sz },	"ipf_hostmap_sz",	1,	0x7fffffff,
5696			sizeof(ipf_hostmap_sz),		IPFT_WRDISABLED },
5697	{ { &fr_nat_maxbucket }, "fr_nat_maxbucket",	1,	0x7fffffff,
5698			sizeof(fr_nat_maxbucket),	IPFT_WRDISABLED },
5699	{ { &fr_nat_maxbucket_reset },	"fr_nat_maxbucket_reset",	0, 1,
5700			sizeof(fr_nat_maxbucket_reset),	IPFT_WRDISABLED },
5701	{ { &nat_logging },		"nat_logging",		0,	1,
5702			sizeof(nat_logging),		0 },
5703	{ { &fr_defnatage },	"fr_defnatage",		1,	0x7fffffff,
5704			sizeof(fr_defnatage),		IPFT_WRDISABLED },
5705	{ { &fr_defnatipage },	"fr_defnatipage",	1,	0x7fffffff,
5706			sizeof(fr_defnatipage),		IPFT_WRDISABLED },
5707	{ { &fr_defnaticmpage }, "fr_defnaticmpage",	1,	0x7fffffff,
5708			sizeof(fr_defnaticmpage),	IPFT_WRDISABLED },
5709	/* frag */
5710	{ { &ipfr_size },	"ipfr_size",		1,	0x7fffffff,
5711			sizeof(ipfr_size),		IPFT_WRDISABLED },
5712	{ { &fr_ipfrttl },	"fr_ipfrttl",		1,	0x7fffffff,
5713			sizeof(fr_ipfrttl),		IPFT_WRDISABLED },
5714#ifdef IPFILTER_LOG
5715	/* log */
5716	{ { &ipl_suppress },	"ipl_suppress",		0,	1,
5717			sizeof(ipl_suppress),		0 },
5718	{ { &ipl_buffer_sz },	"ipl_buffer_sz",	0,	0,
5719			sizeof(ipl_buffer_sz),		IPFT_RDONLY },
5720	{ { &ipl_logmax },	"ipl_logmax",		0,	0x7fffffff,
5721			sizeof(ipl_logmax),		IPFT_WRDISABLED },
5722	{ { &ipl_logall },	"ipl_logall",		0,	1,
5723			sizeof(ipl_logall),		0 },
5724	{ { &ipl_logsize },	"ipl_logsize",		0,	0x80000,
5725			sizeof(ipl_logsize),		0 },
5726#endif
5727	{ { NULL },		NULL,			0,	0 }
5728};
5729
5730static ipftuneable_t *ipf_tunelist = NULL;
5731
5732
5733/* ------------------------------------------------------------------------ */
5734/* Function:    fr_findtunebycookie                                         */
5735/* Returns:     NULL = search failed, else pointer to tune struct           */
5736/* Parameters:  cookie(I) - cookie value to search for amongst tuneables    */
5737/*              next(O)   - pointer to place to store the cookie for the    */
5738/*                          "next" tuneable, if it is desired.              */
5739/*                                                                          */
5740/* This function is used to walk through all of the existing tunables with  */
5741/* successive calls.  It searches the known tunables for the one which has  */
5742/* a matching value for "cookie" - ie its address.  When returning a match, */
5743/* the next one to be found may be returned inside next.                    */
5744/* ------------------------------------------------------------------------ */
5745static ipftuneable_t *fr_findtunebycookie(cookie, next)
5746void *cookie, **next;
5747{
5748	ipftuneable_t *ta, **tap;
5749
5750	for (ta = ipf_tuneables; ta->ipft_name != NULL; ta++)
5751		if (ta == cookie) {
5752			if (next != NULL) {
5753				/*
5754				 * If the next entry in the array has a name
5755				 * present, then return a pointer to it for
5756				 * where to go next, else return a pointer to
5757				 * the dynaminc list as a key to search there
5758				 * next.  This facilitates a weak linking of
5759				 * the two "lists" together.
5760				 */
5761				if ((ta + 1)->ipft_name != NULL)
5762					*next = ta + 1;
5763				else
5764					*next = &ipf_tunelist;
5765			}
5766			return ta;
5767		}
5768
5769	for (tap = &ipf_tunelist; (ta = *tap) != NULL; tap = &ta->ipft_next)
5770		if (tap == cookie) {
5771			if (next != NULL)
5772				*next = &ta->ipft_next;
5773			return ta;
5774		}
5775
5776	if (next != NULL)
5777		*next = NULL;
5778	return NULL;
5779}
5780
5781
5782/* ------------------------------------------------------------------------ */
5783/* Function:    fr_findtunebyname                                           */
5784/* Returns:     NULL = search failed, else pointer to tune struct           */
5785/* Parameters:  name(I) - name of the tuneable entry to find.               */
5786/*                                                                          */
5787/* Search the static array of tuneables and the list of dynamic tuneables   */
5788/* for an entry with a matching name.  If we can find one, return a pointer */
5789/* to the matching structure.                                               */
5790/* ------------------------------------------------------------------------ */
5791static ipftuneable_t *fr_findtunebyname(name)
5792char *name;
5793{
5794	ipftuneable_t *ta;
5795
5796	for (ta = ipf_tuneables; ta->ipft_name != NULL; ta++)
5797		if (!strcmp(ta->ipft_name, name)) {
5798			return ta;
5799		}
5800
5801	for (ta = ipf_tunelist; ta != NULL; ta = ta->ipft_next)
5802		if (!strcmp(ta->ipft_name, name)) {
5803			return ta;
5804		}
5805
5806	return NULL;
5807}
5808
5809
5810/* ------------------------------------------------------------------------ */
5811/* Function:    fr_addipftune                                               */
5812/* Returns:     int - 0 == success, else failure                            */
5813/* Parameters:  newtune - pointer to new tune struct to add to tuneables    */
5814/*                                                                          */
5815/* Appends the tune structure pointer to by "newtune" to the end of the     */
5816/* current list of "dynamic" tuneable parameters.  Once added, the owner    */
5817/* of the object is not expected to ever change "ipft_next".                */
5818/* ------------------------------------------------------------------------ */
5819int fr_addipftune(newtune)
5820ipftuneable_t *newtune;
5821{
5822	ipftuneable_t *ta, **tap;
5823
5824	ta = fr_findtunebyname(newtune->ipft_name);
5825	if (ta != NULL)
5826		return EEXIST;
5827
5828	for (tap = &ipf_tunelist; *tap != NULL; tap = &(*tap)->ipft_next)
5829		;
5830
5831	newtune->ipft_next = NULL;
5832	*tap = newtune;
5833	return 0;
5834}
5835
5836
5837/* ------------------------------------------------------------------------ */
5838/* Function:    fr_delipftune                                               */
5839/* Returns:     int - 0 == success, else failure                            */
5840/* Parameters:  oldtune - pointer to tune struct to remove from the list of */
5841/*                        current dynamic tuneables                         */
5842/*                                                                          */
5843/* Search for the tune structure, by pointer, in the list of those that are */
5844/* dynamically added at run time.  If found, adjust the list so that this   */
5845/* structure is no longer part of it.                                       */
5846/* ------------------------------------------------------------------------ */
5847int fr_delipftune(oldtune)
5848ipftuneable_t *oldtune;
5849{
5850	ipftuneable_t *ta, **tap;
5851
5852	for (tap = &ipf_tunelist; (ta = *tap) != NULL; tap = &ta->ipft_next)
5853		if (ta == oldtune) {
5854			*tap = oldtune->ipft_next;
5855			oldtune->ipft_next = NULL;
5856			return 0;
5857		}
5858
5859	return ESRCH;
5860}
5861
5862
5863/* ------------------------------------------------------------------------ */
5864/* Function:    fr_ipftune                                                  */
5865/* Returns:     int - 0 == success, else failure                            */
5866/* Parameters:  cmd(I)  - ioctl command number                              */
5867/*              data(I) - pointer to ioctl data structure                   */
5868/*                                                                          */
5869/* Implement handling of SIOCIPFGETNEXT, SIOCIPFGET and SIOCIPFSET.  These  */
5870/* three ioctls provide the means to access and control global variables    */
5871/* within IPFilter, allowing (for example) timeouts and table sizes to be   */
5872/* changed without rebooting, reloading or recompiling.  The initialisation */
5873/* and 'destruction' routines of the various components of ipfilter are all */
5874/* each responsible for handling their own values being too big.            */
5875/* ------------------------------------------------------------------------ */
5876int fr_ipftune(cmd, data)
5877ioctlcmd_t cmd;
5878void *data;
5879{
5880	ipftuneable_t *ta;
5881	ipftune_t tu;
5882	void *cookie;
5883	int error;
5884
5885	error = fr_inobj(data, &tu, IPFOBJ_TUNEABLE);
5886	if (error != 0)
5887		return error;
5888
5889	tu.ipft_name[sizeof(tu.ipft_name) - 1] = '\0';
5890	cookie = tu.ipft_cookie;
5891	ta = NULL;
5892
5893	switch (cmd)
5894	{
5895	case SIOCIPFGETNEXT :
5896		/*
5897		 * If cookie is non-NULL, assume it to be a pointer to the last
5898		 * entry we looked at, so find it (if possible) and return a
5899		 * pointer to the next one after it.  The last entry in the
5900		 * the table is a NULL entry, so when we get to it, set cookie
5901		 * to NULL and return that, indicating end of list, erstwhile
5902		 * if we come in with cookie set to NULL, we are starting anew
5903		 * at the front of the list.
5904		 */
5905		if (cookie != NULL) {
5906			ta = fr_findtunebycookie(cookie, &tu.ipft_cookie);
5907		} else {
5908			ta = ipf_tuneables;
5909			tu.ipft_cookie = ta + 1;
5910		}
5911		if (ta != NULL) {
5912			/*
5913			 * Entry found, but does the data pointed to by that
5914			 * row fit in what we can return?
5915			 */
5916			if (ta->ipft_sz > sizeof(tu.ipft_un))
5917				return EINVAL;
5918
5919			tu.ipft_vlong = 0;
5920			if (ta->ipft_sz == sizeof(u_long))
5921				tu.ipft_vlong = *ta->ipft_plong;
5922			else if (ta->ipft_sz == sizeof(u_int))
5923				tu.ipft_vint = *ta->ipft_pint;
5924			else if (ta->ipft_sz == sizeof(u_short))
5925				tu.ipft_vshort = *ta->ipft_pshort;
5926			else if (ta->ipft_sz == sizeof(u_char))
5927				tu.ipft_vchar = *ta->ipft_pchar;
5928
5929			tu.ipft_sz = ta->ipft_sz;
5930			tu.ipft_min = ta->ipft_min;
5931			tu.ipft_max = ta->ipft_max;
5932			tu.ipft_flags = ta->ipft_flags;
5933			bcopy(ta->ipft_name, tu.ipft_name,
5934			      MIN(sizeof(tu.ipft_name),
5935				  strlen(ta->ipft_name) + 1));
5936		}
5937		error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE);
5938		break;
5939
5940	case SIOCIPFGET :
5941	case SIOCIPFSET :
5942		/*
5943		 * Search by name or by cookie value for a particular entry
5944		 * in the tuning paramter table.
5945		 */
5946		error = ESRCH;
5947		if (cookie != NULL) {
5948			ta = fr_findtunebycookie(cookie, NULL);
5949			if (ta != NULL)
5950				error = 0;
5951		} else if (tu.ipft_name[0] != '\0') {
5952			ta = fr_findtunebyname(tu.ipft_name);
5953			if (ta != NULL)
5954				error = 0;
5955		}
5956		if (error != 0)
5957			break;
5958
5959		if (cmd == (ioctlcmd_t)SIOCIPFGET) {
5960			/*
5961			 * Fetch the tuning parameters for a particular value
5962			 */
5963			tu.ipft_vlong = 0;
5964			if (ta->ipft_sz == sizeof(u_long))
5965				tu.ipft_vlong = *ta->ipft_plong;
5966			else if (ta->ipft_sz == sizeof(u_int))
5967				tu.ipft_vint = *ta->ipft_pint;
5968			else if (ta->ipft_sz == sizeof(u_short))
5969				tu.ipft_vshort = *ta->ipft_pshort;
5970			else if (ta->ipft_sz == sizeof(u_char))
5971				tu.ipft_vchar = *ta->ipft_pchar;
5972			tu.ipft_cookie = ta;
5973			tu.ipft_sz = ta->ipft_sz;
5974			tu.ipft_min = ta->ipft_min;
5975			tu.ipft_max = ta->ipft_max;
5976			tu.ipft_flags = ta->ipft_flags;
5977			error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE);
5978
5979		} else if (cmd == (ioctlcmd_t)SIOCIPFSET) {
5980			/*
5981			 * Set an internal parameter.  The hard part here is
5982			 * getting the new value safely and correctly out of
5983			 * the kernel (given we only know its size, not type.)
5984			 */
5985			u_long in;
5986
5987			if (((ta->ipft_flags & IPFT_WRDISABLED) != 0) &&
5988			    (fr_running > 0)) {
5989				error = EBUSY;
5990				break;
5991			}
5992
5993			in = tu.ipft_vlong;
5994			if (in < ta->ipft_min || in > ta->ipft_max) {
5995				error = EINVAL;
5996				break;
5997			}
5998
5999			if (ta->ipft_sz == sizeof(u_long)) {
6000				tu.ipft_vlong = *ta->ipft_plong;
6001				*ta->ipft_plong = in;
6002			} else if (ta->ipft_sz == sizeof(u_int)) {
6003				tu.ipft_vint = *ta->ipft_pint;
6004				*ta->ipft_pint = (u_int)(in & 0xffffffff);
6005			} else if (ta->ipft_sz == sizeof(u_short)) {
6006				tu.ipft_vshort = *ta->ipft_pshort;
6007				*ta->ipft_pshort = (u_short)(in & 0xffff);
6008			} else if (ta->ipft_sz == sizeof(u_char)) {
6009				tu.ipft_vchar = *ta->ipft_pchar;
6010				*ta->ipft_pchar = (u_char)(in & 0xff);
6011			}
6012			error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE);
6013		}
6014		break;
6015
6016	default :
6017		error = EINVAL;
6018		break;
6019	}
6020
6021	return error;
6022}
6023
6024
6025/* ------------------------------------------------------------------------ */
6026/* Function:    fr_initialise                                               */
6027/* Returns:     int - 0 == success,  < 0 == failure                         */
6028/* Parameters:  None.                                                       */
6029/*                                                                          */
6030/* Call of the initialise functions for all the various subsystems inside   */
6031/* of IPFilter.  If any of them should fail, return immeadiately a failure  */
6032/* BUT do not try to recover from the error here.                           */
6033/* ------------------------------------------------------------------------ */
6034int fr_initialise()
6035{
6036	int i;
6037
6038#ifdef IPFILTER_LOG
6039	i = fr_loginit();
6040	if (i < 0)
6041		return -10 + i;
6042#endif
6043	i = fr_natinit();
6044	if (i < 0)
6045		return -20 + i;
6046
6047	i = fr_stateinit();
6048	if (i < 0)
6049		return -30 + i;
6050
6051	i = fr_authinit();
6052	if (i < 0)
6053		return -40 + i;
6054
6055	i = fr_fraginit();
6056	if (i < 0)
6057		return -50 + i;
6058
6059	i = appr_init();
6060	if (i < 0)
6061		return -60 + i;
6062
6063#ifdef IPFILTER_SYNC
6064	i = ipfsync_init();
6065	if (i < 0)
6066		return -70 + i;
6067#endif
6068#ifdef IPFILTER_SCAN
6069	i = ipsc_init();
6070	if (i < 0)
6071		return -80 + i;
6072#endif
6073#ifdef IPFILTER_LOOKUP
6074	i = ip_lookup_init();
6075	if (i < 0)
6076		return -90 + i;
6077#endif
6078#ifdef IPFILTER_COMPILED
6079	ipfrule_add();
6080#endif
6081	return 0;
6082}
6083
6084
6085/* ------------------------------------------------------------------------ */
6086/* Function:    fr_deinitialise                                             */
6087/* Returns:     None.                                                       */
6088/* Parameters:  None.                                                       */
6089/*                                                                          */
6090/* Call all the various subsystem cleanup routines to deallocate memory or  */
6091/* destroy locks or whatever they've done that they need to now undo.       */
6092/* The order here IS important as there are some cross references of        */
6093/* internal data structures.                                                */
6094/* ------------------------------------------------------------------------ */
6095void fr_deinitialise()
6096{
6097	fr_fragunload();
6098	fr_authunload();
6099	fr_natunload();
6100	fr_stateunload();
6101#ifdef IPFILTER_SCAN
6102	fr_scanunload();
6103#endif
6104	appr_unload();
6105
6106#ifdef IPFILTER_COMPILED
6107	ipfrule_remove();
6108#endif
6109
6110	(void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE);
6111	(void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE);
6112	(void) frflush(IPL_LOGCOUNT, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE);
6113	(void) frflush(IPL_LOGCOUNT, 0, FR_INQUE|FR_OUTQUE);
6114
6115#ifdef IPFILTER_LOOKUP
6116	ip_lookup_unload();
6117#endif
6118
6119#ifdef IPFILTER_LOG
6120	fr_logunload();
6121#endif
6122}
6123
6124
6125/* ------------------------------------------------------------------------ */
6126/* Function:    fr_zerostats                                                */
6127/* Returns:     int - 0 = success, else failure                             */
6128/* Parameters:  data(O) - pointer to pointer for copying data back to       */
6129/*                                                                          */
6130/* Copies the current statistics out to userspace and then zero's the       */
6131/* current ones in the kernel. The lock is only held across the bzero() as  */
6132/* the copyout may result in paging (ie network activity.)                  */
6133/* ------------------------------------------------------------------------ */
6134int	fr_zerostats(data)
6135caddr_t	data;
6136{
6137	friostat_t fio;
6138	int error;
6139
6140	fr_getstat(&fio);
6141	error = copyoutptr(&fio, data, sizeof(fio));
6142	if (error)
6143		return EFAULT;
6144
6145	WRITE_ENTER(&ipf_mutex);
6146	bzero((char *)frstats, sizeof(*frstats) * 2);
6147	RWLOCK_EXIT(&ipf_mutex);
6148
6149	return 0;
6150}
6151
6152
6153/* ------------------------------------------------------------------------ */
6154/* Function:    fr_resolvedest                                              */
6155/* Returns:     Nil                                                         */
6156/* Parameters:  fdp(IO) - pointer to destination information to resolve     */
6157/*              v(I)    - IP protocol version to match                      */
6158/*                                                                          */
6159/* Looks up an interface name in the frdest structure pointed to by fdp and */
6160/* if a matching name can be found for the particular IP protocol version   */
6161/* then store the interface pointer in the frdest struct.  If no match is   */
6162/* found, then set the interface pointer to be -1 as NULL is considered to  */
6163/* indicate there is no information at all in the structure.                */
6164/* ------------------------------------------------------------------------ */
6165void fr_resolvedest(fdp, v)
6166frdest_t *fdp;
6167int v;
6168{
6169	void *ifp;
6170
6171	ifp = NULL;
6172	v = v;		/* LINT */
6173
6174	if (*fdp->fd_ifname != '\0') {
6175		ifp = GETIFP(fdp->fd_ifname, v);
6176		if (ifp == NULL)
6177			ifp = (void *)-1;
6178	}
6179	fdp->fd_ifp = ifp;
6180}
6181
6182
6183/* ------------------------------------------------------------------------ */
6184/* Function:    fr_icmp4errortype                                           */
6185/* Returns:     int - 1 == success, 0 == failure                            */
6186/* Parameters:  icmptype(I) - ICMP type number                              */
6187/*                                                                          */
6188/* Tests to see if the ICMP type number passed is an error type or not.     */
6189/* ------------------------------------------------------------------------ */
6190int fr_icmp4errortype(icmptype)
6191int icmptype;
6192{
6193
6194	switch (icmptype)
6195	{
6196	case ICMP_SOURCEQUENCH :
6197	case ICMP_PARAMPROB :
6198	case ICMP_REDIRECT :
6199	case ICMP_TIMXCEED :
6200	case ICMP_UNREACH :
6201		return 1;
6202	default:
6203		return 0;
6204	}
6205}
6206
6207
6208/* ------------------------------------------------------------------------ */
6209/* Function:    fr_resolvenic                                               */
6210/* Returns:     void* - NULL = wildcard name, -1 = failed to find NIC, else */
6211/*                      pointer to interface structure for NIC              */
6212/* Parameters:  name(I) - complete interface name                           */
6213/*              v(I)    - IP protocol version                               */
6214/*                                                                          */
6215/* Look for a network interface structure that firstly has a matching name  */
6216/* to that passed in and that is also being used for that IP protocol       */
6217/* version (necessary on some platforms where there are separate listings   */
6218/* for both IPv4 and IPv6 on the same physical NIC.                         */
6219/*                                                                          */
6220/* One might wonder why name gets terminated with a \0 byte in here.  The   */
6221/* reason is an interface name could get into the kernel structures of ipf  */
6222/* in any number of ways and so long as they all use the same sized array   */
6223/* to put the name in, it makes sense to ensure it gets null terminated     */
6224/* before it is used for its intended purpose - finding its match in the    */
6225/* kernel's list of configured interfaces.                                  */
6226/*                                                                          */
6227/* NOTE: This SHOULD ONLY be used with IPFilter structures that have an     */
6228/*       array for the name that is LIFNAMSIZ bytes (at least) in length.   */
6229/* ------------------------------------------------------------------------ */
6230void *fr_resolvenic(name, v)
6231char *name;
6232int v;
6233{
6234	void *nic;
6235
6236	if (name[0] == '\0')
6237		return NULL;
6238
6239	if ((name[1] == '\0') && ((name[0] == '-') || (name[0] == '*'))) {
6240		return NULL;
6241	}
6242
6243	name[LIFNAMSIZ - 1] = '\0';
6244
6245	nic = GETIFP(name, v);
6246	if (nic == NULL)
6247		nic = (void *)-1;
6248	return nic;
6249}
6250