ip_auth.c revision 153084
1/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 153084 2005-12-04 10:06:06Z ru $	*/
2
3/*
4 * Copyright (C) 1998-2003 by Darren Reed & Guido van Rooij.
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#include <sys/file.h>
19#if !defined(_KERNEL)
20# include <stdio.h>
21# include <stdlib.h>
22# include <string.h>
23# define _KERNEL
24# ifdef __OpenBSD__
25struct file;
26# endif
27# include <sys/uio.h>
28# undef _KERNEL
29#endif
30#if defined(_KERNEL) && (__FreeBSD_version >= 220000)
31# include <sys/filio.h>
32# include <sys/fcntl.h>
33#else
34# include <sys/ioctl.h>
35#endif
36#if !defined(linux)
37# include <sys/protosw.h>
38#endif
39#include <sys/socket.h>
40#if defined(_KERNEL)
41# include <sys/systm.h>
42# if !defined(__SVR4) && !defined(__svr4__) && !defined(linux)
43#  include <sys/mbuf.h>
44# endif
45#endif
46#if defined(__SVR4) || defined(__svr4__)
47# include <sys/filio.h>
48# include <sys/byteorder.h>
49# ifdef _KERNEL
50#  include <sys/dditypes.h>
51# endif
52# include <sys/stream.h>
53# include <sys/kmem.h>
54#endif
55#if (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802) || \
56    (__FreeBSD_version >= 400000)
57# include <sys/queue.h>
58#endif
59#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi)
60# include <machine/cpu.h>
61#endif
62#if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000)
63# include <sys/proc.h>
64#endif
65#include <net/if.h>
66#ifdef sun
67# include <net/af.h>
68#endif
69#include <net/route.h>
70#include <netinet/in.h>
71#include <netinet/in_systm.h>
72#include <netinet/ip.h>
73#if !defined(_KERNEL) && !defined(__osf__) && !defined(__sgi)
74# define	KERNEL
75# define	_KERNEL
76# define	NOT_KERNEL
77#endif
78#if !defined(linux)
79# include <netinet/ip_var.h>
80#endif
81#ifdef	NOT_KERNEL
82# undef	_KERNEL
83# undef	KERNEL
84#endif
85#include <netinet/tcp.h>
86#if defined(IRIX) && (IRIX < 60516) /* IRIX < 6 */
87extern struct ifqueue   ipintrq;		/* ip packet input queue */
88#else
89# if !defined(__hpux) && !defined(linux)
90#  if __FreeBSD_version >= 300000
91#   include <net/if_var.h>
92#   if __FreeBSD_version >= 500042
93#    define IF_QFULL _IF_QFULL
94#    define IF_DROP _IF_DROP
95#   endif /* __FreeBSD_version >= 500042 */
96#  endif
97#  include <netinet/in_var.h>
98#  include <netinet/tcp_fsm.h>
99# endif
100#endif
101#include <netinet/udp.h>
102#include <netinet/ip_icmp.h>
103#include "netinet/ip_compat.h"
104#include <netinet/tcpip.h>
105#include "netinet/ip_fil.h"
106#include "netinet/ip_auth.h"
107#if !defined(MENTAT) && !defined(linux)
108# include <net/netisr.h>
109# ifdef __FreeBSD__
110#  include <machine/cpufunc.h>
111# endif
112#endif
113#if (__FreeBSD_version >= 300000)
114# include <sys/malloc.h>
115# if defined(_KERNEL) && !defined(IPFILTER_LKM)
116#  include <sys/libkern.h>
117#  include <sys/systm.h>
118# endif
119#endif
120/* END OF INCLUDES */
121
122#if !defined(lint)
123static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 153084 2005-12-04 10:06:06Z ru $";
124/* static const char rcsid[] = "@(#)Id: ip_auth.c,v 2.73.2.3 2004/08/26 11:25:21 darrenr Exp"; */
125#endif
126
127
128#if SOLARIS
129extern kcondvar_t ipfauthwait;
130#endif /* SOLARIS */
131#if defined(linux) && defined(_KERNEL)
132wait_queue_head_t     fr_authnext_linux;
133#endif
134
135int	fr_authsize = FR_NUMAUTH;
136int	fr_authused = 0;
137int	fr_defaultauthage = 600;
138int	fr_auth_lock = 0;
139int	fr_auth_init = 0;
140fr_authstat_t	fr_authstats;
141static frauth_t *fr_auth = NULL;
142mb_t	**fr_authpkts = NULL;
143int	fr_authstart = 0, fr_authend = 0, fr_authnext = 0;
144frauthent_t	*fae_list = NULL;
145frentry_t	*ipauth = NULL,
146		*fr_authlist = NULL;
147
148
149int fr_authinit()
150{
151	KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth));
152	if (fr_auth != NULL)
153		bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth));
154	else
155		return -1;
156
157	KMALLOCS(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts));
158	if (fr_authpkts != NULL)
159		bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
160	else
161		return -2;
162
163	MUTEX_INIT(&ipf_authmx, "ipf auth log mutex");
164	RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock");
165#if SOLARIS && defined(_KERNEL)
166	cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL);
167#endif
168#if defined(linux) && defined(_KERNEL)
169	init_waitqueue_head(&fr_authnext_linux);
170#endif
171
172	fr_auth_init = 1;
173
174	return 0;
175}
176
177
178/*
179 * Check if a packet has authorization.  If the packet is found to match an
180 * authorization result and that would result in a feedback loop (i.e. it
181 * will end up returning FR_AUTH) then return FR_BLOCK instead.
182 */
183frentry_t *fr_checkauth(fin, passp)
184fr_info_t *fin;
185u_32_t *passp;
186{
187	frentry_t *fr;
188	frauth_t *fra;
189	u_32_t pass;
190	u_short id;
191	ip_t *ip;
192	int i;
193
194	if (fr_auth_lock || !fr_authused)
195		return NULL;
196
197	ip = fin->fin_ip;
198	id = ip->ip_id;
199
200	READ_ENTER(&ipf_auth);
201	for (i = fr_authstart; i != fr_authend; ) {
202		/*
203		 * index becomes -2 only after an SIOCAUTHW.  Check this in
204		 * case the same packet gets sent again and it hasn't yet been
205		 * auth'd.
206		 */
207		fra = fr_auth + i;
208		if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) &&
209		    !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) {
210			/*
211			 * Avoid feedback loop.
212			 */
213			if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass)))
214				pass = FR_BLOCK;
215			/*
216			 * Create a dummy rule for the stateful checking to
217			 * use and return.  Zero out any values we don't
218			 * trust from userland!
219			 */
220			if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) &&
221			     (fin->fin_flx & FI_FRAG))) {
222				KMALLOC(fr, frentry_t *);
223				if (fr) {
224					bcopy((char *)fra->fra_info.fin_fr,
225					      (char *)fr, sizeof(*fr));
226					fr->fr_grp = NULL;
227					fr->fr_ifa = fin->fin_ifp;
228					fr->fr_func = NULL;
229					fr->fr_ref = 1;
230					fr->fr_flags = pass;
231					fr->fr_ifas[1] = NULL;
232					fr->fr_ifas[2] = NULL;
233					fr->fr_ifas[3] = NULL;
234				}
235			} else
236				fr = fra->fra_info.fin_fr;
237			fin->fin_fr = fr;
238			RWLOCK_EXIT(&ipf_auth);
239			WRITE_ENTER(&ipf_auth);
240			if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) {
241				fr->fr_next = fr_authlist;
242				fr_authlist = fr;
243			}
244			fr_authstats.fas_hits++;
245			fra->fra_index = -1;
246			fr_authused--;
247			if (i == fr_authstart) {
248				while (fra->fra_index == -1) {
249					i++;
250					fra++;
251					if (i == fr_authsize) {
252						i = 0;
253						fra = fr_auth;
254					}
255					fr_authstart = i;
256					if (i == fr_authend)
257						break;
258				}
259				if (fr_authstart == fr_authend) {
260					fr_authnext = 0;
261					fr_authstart = fr_authend = 0;
262				}
263			}
264			RWLOCK_EXIT(&ipf_auth);
265			if (passp != NULL)
266				*passp = pass;
267			ATOMIC_INC64(fr_authstats.fas_hits);
268			return fr;
269		}
270		i++;
271		if (i == fr_authsize)
272			i = 0;
273	}
274	fr_authstats.fas_miss++;
275	RWLOCK_EXIT(&ipf_auth);
276	ATOMIC_INC64(fr_authstats.fas_miss);
277	return NULL;
278}
279
280
281/*
282 * Check if we have room in the auth array to hold details for another packet.
283 * If we do, store it and wake up any user programs which are waiting to
284 * hear about these events.
285 */
286int fr_newauth(m, fin)
287mb_t *m;
288fr_info_t *fin;
289{
290#if defined(_KERNEL) && defined(MENTAT)
291	qpktinfo_t *qpi = fin->fin_qpi;
292#endif
293	frauth_t *fra;
294#if !defined(sparc) && !defined(m68k)
295	ip_t *ip;
296#endif
297	int i;
298
299	if (fr_auth_lock)
300		return 0;
301
302	WRITE_ENTER(&ipf_auth);
303	if (fr_authstart > fr_authend) {
304		fr_authstats.fas_nospace++;
305		RWLOCK_EXIT(&ipf_auth);
306		return 0;
307	} else {
308		if (fr_authused == fr_authsize) {
309			fr_authstats.fas_nospace++;
310			RWLOCK_EXIT(&ipf_auth);
311			return 0;
312		}
313	}
314
315	fr_authstats.fas_added++;
316	fr_authused++;
317	i = fr_authend++;
318	if (fr_authend == fr_authsize)
319		fr_authend = 0;
320	RWLOCK_EXIT(&ipf_auth);
321
322	fra = fr_auth + i;
323	fra->fra_index = i;
324	fra->fra_pass = 0;
325	fra->fra_age = fr_defaultauthage;
326	bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin));
327#if !defined(sparc) && !defined(m68k)
328	/*
329	 * No need to copyback here as we want to undo the changes, not keep
330	 * them.
331	 */
332	ip = fin->fin_ip;
333# if defined(MENTAT) && defined(_KERNEL)
334	if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4))
335# endif
336	{
337		register u_short bo;
338
339		bo = ip->ip_len;
340		ip->ip_len = htons(bo);
341		bo = ip->ip_off;
342		ip->ip_off = htons(bo);
343	}
344#endif
345#if SOLARIS && defined(_KERNEL)
346	m->b_rptr -= qpi->qpi_off;
347	fr_authpkts[i] = *(mblk_t **)fin->fin_mp;
348	fra->fra_q = qpi->qpi_q;	/* The queue can disappear! */
349	cv_signal(&ipfauthwait);
350#else
351# if defined(BSD) && !defined(sparc) && (BSD >= 199306)
352	if (!fin->fin_out) {
353		ip->ip_len = htons(ip->ip_len);
354		ip->ip_off = htons(ip->ip_off);
355	}
356# endif
357	fr_authpkts[i] = m;
358	WAKEUP(&fr_authnext,0);
359#endif
360	return 1;
361}
362
363
364int fr_auth_ioctl(data, cmd, mode)
365caddr_t data;
366ioctlcmd_t cmd;
367int mode;
368{
369	mb_t *m;
370#if defined(_KERNEL) && !defined(MENTAT) && !defined(linux) && \
371    (!defined(__FreeBSD_version) || (__FreeBSD_version < 501000))
372	struct ifqueue *ifq;
373# ifdef USE_SPL
374	int s;
375# endif /* USE_SPL */
376#endif
377	frauth_t auth, *au = &auth, *fra;
378	int i, error = 0, len;
379	char *t;
380
381	switch (cmd)
382	{
383	case SIOCSTLCK :
384		if (!(mode & FWRITE)) {
385			error = EPERM;
386			break;
387		}
388		fr_lock(data, &fr_auth_lock);
389		break;
390
391	case SIOCATHST:
392		fr_authstats.fas_faelist = fae_list;
393		error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT);
394		break;
395
396	case SIOCIPFFL:
397		SPL_NET(s);
398		WRITE_ENTER(&ipf_auth);
399		i = fr_authflush();
400		RWLOCK_EXIT(&ipf_auth);
401		SPL_X(s);
402		error = copyoutptr((char *)&i, data, sizeof(i));
403		break;
404
405	case SIOCAUTHW:
406fr_authioctlloop:
407		error = fr_inobj(data, au, IPFOBJ_FRAUTH);
408		READ_ENTER(&ipf_auth);
409		if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) {
410			error = fr_outobj(data, &fr_auth[fr_authnext],
411					  IPFOBJ_FRAUTH);
412			if (auth.fra_len != 0 && auth.fra_buf != NULL) {
413				/*
414				 * Copy packet contents out to user space if
415				 * requested.  Bail on an error.
416				 */
417				m = fr_authpkts[fr_authnext];
418				len = MSGDSIZE(m);
419				if (len > auth.fra_len)
420					len = auth.fra_len;
421				auth.fra_len = len;
422				for (t = auth.fra_buf; m && (len > 0); ) {
423					i = MIN(M_LEN(m), len);
424					error = copyoutptr(MTOD(m, char *),
425							  t, i);
426					len -= i;
427					t += i;
428					if (error != 0)
429						break;
430				}
431			}
432			RWLOCK_EXIT(&ipf_auth);
433			if (error != 0)
434				break;
435			SPL_NET(s);
436			WRITE_ENTER(&ipf_auth);
437			fr_authnext++;
438			if (fr_authnext == fr_authsize)
439				fr_authnext = 0;
440			RWLOCK_EXIT(&ipf_auth);
441			SPL_X(s);
442			return 0;
443		}
444		RWLOCK_EXIT(&ipf_auth);
445		/*
446		 * We exit ipf_global here because a program that enters in
447		 * here will have a lock on it and goto sleep having this lock.
448		 * If someone were to do an 'ipf -D' the system would then
449		 * deadlock.  The catch with releasing it here is that the
450		 * caller of this function expects it to be held when we
451		 * return so we have to reacquire it in here.
452		 */
453		RWLOCK_EXIT(&ipf_global);
454
455		MUTEX_ENTER(&ipf_authmx);
456#ifdef	_KERNEL
457# if	SOLARIS
458		error = 0;
459		if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk))
460			error = EINTR;
461# else /* SOLARIS */
462#  ifdef __hpux
463		{
464		lock_t *l;
465
466		l = get_sleep_lock(&fr_authnext);
467		error = sleep(&fr_authnext, PZERO+1);
468		spinunlock(l);
469		}
470#  else
471#   ifdef __osf__
472		error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0,
473				&ipf_authmx, MS_LOCK_SIMPLE);
474#   else
475		error = SLEEP(&fr_authnext, "fr_authnext");
476#   endif /* __osf__ */
477#  endif /* __hpux */
478# endif /* SOLARIS */
479#endif
480		MUTEX_EXIT(&ipf_authmx);
481		READ_ENTER(&ipf_global);
482		if (error == 0) {
483			READ_ENTER(&ipf_auth);
484			goto fr_authioctlloop;
485		}
486		break;
487
488	case SIOCAUTHR:
489		error = fr_inobj(data, &auth, IPFOBJ_FRAUTH);
490		if (error != 0)
491			return error;
492		SPL_NET(s);
493		WRITE_ENTER(&ipf_auth);
494		i = au->fra_index;
495		fra = fr_auth + i;
496		if ((i < 0) || (i >= fr_authsize) ||
497		    (fra->fra_info.fin_id != au->fra_info.fin_id)) {
498			RWLOCK_EXIT(&ipf_auth);
499			SPL_X(s);
500			return ESRCH;
501		}
502		m = fr_authpkts[i];
503		fra->fra_index = -2;
504		fra->fra_pass = au->fra_pass;
505		fr_authpkts[i] = NULL;
506		RWLOCK_EXIT(&ipf_auth);
507#ifdef	_KERNEL
508		if ((m != NULL) && (au->fra_info.fin_out != 0)) {
509# ifdef MENTAT
510			error = !putq(fra->fra_q, m);
511# else /* MENTAT */
512#  ifdef linux
513#  else
514#   if (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802) || \
515       (defined(__OpenBSD__)) || \
516       (defined(__sgi) && (IRIX >= 60500) || \
517       (defined(__FreeBSD__) && (__FreeBSD_version >= 470102)))
518			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL,
519					  NULL);
520#   else
521			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL);
522#   endif
523#  endif /* Linux */
524# endif /* MENTAT */
525			if (error != 0)
526				fr_authstats.fas_sendfail++;
527			else
528				fr_authstats.fas_sendok++;
529		} else if (m) {
530# ifdef MENTAT
531			error = !putq(fra->fra_q, m);
532# else /* MENTAT */
533#  ifdef linux
534#  else
535#   if __FreeBSD_version >= 501000
536			netisr_dispatch(NETISR_IP, m);
537#   else
538#    if IRIX >= 60516
539			ifq = &((struct ifnet *)fra->fra_info.fin_ifp)->if_snd;
540#    else
541			ifq = &ipintrq;
542#    endif
543			if (IF_QFULL(ifq)) {
544				IF_DROP(ifq);
545				FREE_MB_T(m);
546				error = ENOBUFS;
547			} else {
548				IF_ENQUEUE(ifq, m);
549#    if IRIX < 60500
550				schednetisr(NETISR_IP);
551#    endif
552			}
553#   endif
554#  endif /* Linux */
555# endif /* MENTAT */
556			if (error != 0)
557				fr_authstats.fas_quefail++;
558			else
559				fr_authstats.fas_queok++;
560		} else
561			error = EINVAL;
562# ifdef MENTAT
563		if (error != 0)
564			error = EINVAL;
565# else /* MENTAT */
566		/*
567		 * If we experience an error which will result in the packet
568		 * not being processed, make sure we advance to the next one.
569		 */
570		if (error == ENOBUFS) {
571			fr_authused--;
572			fra->fra_index = -1;
573			fra->fra_pass = 0;
574			if (i == fr_authstart) {
575				while (fra->fra_index == -1) {
576					i++;
577					if (i == fr_authsize)
578						i = 0;
579					fr_authstart = i;
580					if (i == fr_authend)
581						break;
582				}
583				if (fr_authstart == fr_authend) {
584					fr_authnext = 0;
585					fr_authstart = fr_authend = 0;
586				}
587			}
588		}
589# endif /* MENTAT */
590#endif /* _KERNEL */
591		SPL_X(s);
592		break;
593
594	default :
595		error = EINVAL;
596		break;
597	}
598	return error;
599}
600
601
602/*
603 * Free all network buffer memory used to keep saved packets.
604 */
605void fr_authunload()
606{
607	register int i;
608	register frauthent_t *fae, **faep;
609	frentry_t *fr, **frp;
610	mb_t *m;
611
612	if (fr_auth != NULL) {
613		KFREES(fr_auth, fr_authsize * sizeof(*fr_auth));
614		fr_auth = NULL;
615	}
616
617	if (fr_authpkts != NULL) {
618		for (i = 0; i < fr_authsize; i++) {
619			m = fr_authpkts[i];
620			if (m != NULL) {
621				FREE_MB_T(m);
622				fr_authpkts[i] = NULL;
623			}
624		}
625		KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
626		fr_authpkts = NULL;
627	}
628
629	faep = &fae_list;
630	while ((fae = *faep) != NULL) {
631		*faep = fae->fae_next;
632		KFREE(fae);
633	}
634	ipauth = NULL;
635
636	if (fr_authlist != NULL) {
637		for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
638			if (fr->fr_ref == 1) {
639				*frp = fr->fr_next;
640				KFREE(fr);
641			} else
642				frp = &fr->fr_next;
643		}
644	}
645
646	if (fr_auth_init == 1) {
647# if SOLARIS && defined(_KERNEL)
648		cv_destroy(&ipfauthwait);
649# endif
650		MUTEX_DESTROY(&ipf_authmx);
651		RW_DESTROY(&ipf_auth);
652
653		fr_auth_init = 0;
654	}
655}
656
657
658/*
659 * Slowly expire held auth records.  Timeouts are set
660 * in expectation of this being called twice per second.
661 */
662void fr_authexpire()
663{
664	register int i;
665	register frauth_t *fra;
666	register frauthent_t *fae, **faep;
667	register frentry_t *fr, **frp;
668	mb_t *m;
669# if !defined(MENAT) && defined(_KERNEL) && defined(USE_SPL)
670	int s;
671# endif
672
673	if (fr_auth_lock)
674		return;
675
676	SPL_NET(s);
677	WRITE_ENTER(&ipf_auth);
678	for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) {
679		fra->fra_age--;
680		if ((fra->fra_age == 0) && (m = fr_authpkts[i])) {
681			FREE_MB_T(m);
682			fr_authpkts[i] = NULL;
683			fr_auth[i].fra_index = -1;
684			fr_authstats.fas_expire++;
685			fr_authused--;
686		}
687	}
688
689	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
690		fae->fae_age--;
691		if (fae->fae_age == 0) {
692			*faep = fae->fae_next;
693			KFREE(fae);
694			fr_authstats.fas_expire++;
695		} else
696			faep = &fae->fae_next;
697	}
698	if (fae_list != NULL)
699		ipauth = &fae_list->fae_fr;
700	else
701		ipauth = NULL;
702
703	for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
704		if (fr->fr_ref == 1) {
705			*frp = fr->fr_next;
706			KFREE(fr);
707		} else
708			frp = &fr->fr_next;
709	}
710	RWLOCK_EXIT(&ipf_auth);
711	SPL_X(s);
712}
713
714int fr_preauthcmd(cmd, fr, frptr)
715ioctlcmd_t cmd;
716frentry_t *fr, **frptr;
717{
718	frauthent_t *fae, **faep;
719	int error = 0;
720# if !defined(MENAT) && defined(_KERNEL) && defined(USE_SPL)
721	int s;
722#endif
723
724	if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR))
725		return EIO;
726
727	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
728		if (&fae->fae_fr == fr)
729			break;
730		else
731			faep = &fae->fae_next;
732	}
733
734	if (cmd == (ioctlcmd_t)SIOCRMAFR) {
735		if (fr == NULL || frptr == NULL)
736			error = EINVAL;
737		else if (fae == NULL)
738			error = ESRCH;
739		else {
740			SPL_NET(s);
741			WRITE_ENTER(&ipf_auth);
742			*faep = fae->fae_next;
743			if (ipauth == &fae->fae_fr)
744				ipauth = fae_list ? &fae_list->fae_fr : NULL;
745			RWLOCK_EXIT(&ipf_auth);
746			SPL_X(s);
747
748			KFREE(fae);
749		}
750	} else if (fr != NULL && frptr != NULL) {
751		KMALLOC(fae, frauthent_t *);
752		if (fae != NULL) {
753			bcopy((char *)fr, (char *)&fae->fae_fr,
754			      sizeof(*fr));
755			SPL_NET(s);
756			WRITE_ENTER(&ipf_auth);
757			fae->fae_age = fr_defaultauthage;
758			fae->fae_fr.fr_hits = 0;
759			fae->fae_fr.fr_next = *frptr;
760			*frptr = &fae->fae_fr;
761			fae->fae_next = *faep;
762			*faep = fae;
763			ipauth = &fae_list->fae_fr;
764			RWLOCK_EXIT(&ipf_auth);
765			SPL_X(s);
766		} else
767			error = ENOMEM;
768	} else
769		error = EINVAL;
770	return error;
771}
772
773
774/*
775 * Flush held packets.
776 * Must already be properly SPL'ed and Locked on &ipf_auth.
777 *
778 */
779int fr_authflush()
780{
781	register int i, num_flushed;
782	mb_t *m;
783
784	if (fr_auth_lock)
785		return -1;
786
787	num_flushed = 0;
788
789	for (i = 0 ; i < fr_authsize; i++) {
790		m = fr_authpkts[i];
791		if (m != NULL) {
792			FREE_MB_T(m);
793			fr_authpkts[i] = NULL;
794			fr_auth[i].fra_index = -1;
795			/* perhaps add & use a flush counter inst.*/
796			fr_authstats.fas_expire++;
797			fr_authused--;
798			num_flushed++;
799		}
800	}
801
802	fr_authstart = 0;
803	fr_authend = 0;
804	fr_authnext = 0;
805
806	return num_flushed;
807}
808