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