ip_auth.c revision 153876
1/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 153876 2005-12-30 11:32:23Z guido $	*/
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 153876 2005-12-30 11:32:23Z guido $";
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	SPL_INT(s);
374#endif
375	frauth_t auth, *au = &auth, *fra;
376	int i, error = 0, len;
377	char *t;
378
379	switch (cmd)
380	{
381	case SIOCSTLCK :
382		if (!(mode & FWRITE)) {
383			error = EPERM;
384			break;
385		}
386		fr_lock(data, &fr_auth_lock);
387		break;
388
389	case SIOCATHST:
390		fr_authstats.fas_faelist = fae_list;
391		error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT);
392		break;
393
394	case SIOCIPFFL:
395		SPL_NET(s);
396		WRITE_ENTER(&ipf_auth);
397		i = fr_authflush();
398		RWLOCK_EXIT(&ipf_auth);
399		SPL_X(s);
400		error = copyoutptr((char *)&i, data, sizeof(i));
401		break;
402
403	case SIOCAUTHW:
404fr_authioctlloop:
405		error = fr_inobj(data, au, IPFOBJ_FRAUTH);
406		READ_ENTER(&ipf_auth);
407		if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) {
408			error = fr_outobj(data, &fr_auth[fr_authnext],
409					  IPFOBJ_FRAUTH);
410			if (auth.fra_len != 0 && auth.fra_buf != NULL) {
411				/*
412				 * Copy packet contents out to user space if
413				 * requested.  Bail on an error.
414				 */
415				m = fr_authpkts[fr_authnext];
416				len = MSGDSIZE(m);
417				if (len > auth.fra_len)
418					len = auth.fra_len;
419				auth.fra_len = len;
420				for (t = auth.fra_buf; m && (len > 0); ) {
421					i = MIN(M_LEN(m), len);
422					error = copyoutptr(MTOD(m, char *),
423							  t, i);
424					len -= i;
425					t += i;
426					if (error != 0)
427						break;
428				}
429			}
430			RWLOCK_EXIT(&ipf_auth);
431			if (error != 0)
432				break;
433			SPL_NET(s);
434			WRITE_ENTER(&ipf_auth);
435			fr_authnext++;
436			if (fr_authnext == fr_authsize)
437				fr_authnext = 0;
438			RWLOCK_EXIT(&ipf_auth);
439			SPL_X(s);
440			return 0;
441		}
442		RWLOCK_EXIT(&ipf_auth);
443		/*
444		 * We exit ipf_global here because a program that enters in
445		 * here will have a lock on it and goto sleep having this lock.
446		 * If someone were to do an 'ipf -D' the system would then
447		 * deadlock.  The catch with releasing it here is that the
448		 * caller of this function expects it to be held when we
449		 * return so we have to reacquire it in here.
450		 */
451		RWLOCK_EXIT(&ipf_global);
452
453		MUTEX_ENTER(&ipf_authmx);
454#ifdef	_KERNEL
455# if	SOLARIS
456		error = 0;
457		if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk))
458			error = EINTR;
459# else /* SOLARIS */
460#  ifdef __hpux
461		{
462		lock_t *l;
463
464		l = get_sleep_lock(&fr_authnext);
465		error = sleep(&fr_authnext, PZERO+1);
466		spinunlock(l);
467		}
468#  else
469#   ifdef __osf__
470		error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0,
471				&ipf_authmx, MS_LOCK_SIMPLE);
472#   else
473		error = SLEEP(&fr_authnext, "fr_authnext");
474#   endif /* __osf__ */
475#  endif /* __hpux */
476# endif /* SOLARIS */
477#endif
478		MUTEX_EXIT(&ipf_authmx);
479		READ_ENTER(&ipf_global);
480		if (error == 0) {
481			READ_ENTER(&ipf_auth);
482			goto fr_authioctlloop;
483		}
484		break;
485
486	case SIOCAUTHR:
487		error = fr_inobj(data, &auth, IPFOBJ_FRAUTH);
488		if (error != 0)
489			return error;
490		SPL_NET(s);
491		WRITE_ENTER(&ipf_auth);
492		i = au->fra_index;
493		fra = fr_auth + i;
494		if ((i < 0) || (i >= fr_authsize) ||
495		    (fra->fra_info.fin_id != au->fra_info.fin_id)) {
496			RWLOCK_EXIT(&ipf_auth);
497			SPL_X(s);
498			return ESRCH;
499		}
500		m = fr_authpkts[i];
501		fra->fra_index = -2;
502		fra->fra_pass = au->fra_pass;
503		fr_authpkts[i] = NULL;
504		RWLOCK_EXIT(&ipf_auth);
505#ifdef	_KERNEL
506		if ((m != NULL) && (au->fra_info.fin_out != 0)) {
507# ifdef MENTAT
508			error = !putq(fra->fra_q, m);
509# else /* MENTAT */
510#  if defined(linux) || defined(AIX)
511#  else
512#   if (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802) || \
513       (defined(__OpenBSD__)) || \
514       (defined(__sgi) && (IRIX >= 60500) || \
515       (defined(__FreeBSD__) && (__FreeBSD_version >= 470102)))
516			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL,
517					  NULL);
518#   else
519			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL);
520#   endif
521#  endif /* Linux */
522# endif /* MENTAT */
523			if (error != 0)
524				fr_authstats.fas_sendfail++;
525			else
526				fr_authstats.fas_sendok++;
527		} else if (m) {
528# ifdef MENTAT
529			error = !putq(fra->fra_q, m);
530# else /* MENTAT */
531#  if defined(linux) || defined(AIX)
532#  else
533#   if (__FreeBSD_version >= 501000)
534			netisr_dispatch(NETISR_IP, m);
535#   else
536#    if (IRIX >= 60516)
537			ifq = &((struct ifnet *)fra->fra_info.fin_ifp)->if_snd;
538#    else
539			ifq = &ipintrq;
540#    endif
541			if (IF_QFULL(ifq)) {
542				IF_DROP(ifq);
543				FREE_MB_T(m);
544				error = ENOBUFS;
545			} else {
546				IF_ENQUEUE(ifq, m);
547#    if IRIX < 60500
548				schednetisr(NETISR_IP);
549#    endif
550			}
551#   endif
552#  endif /* Linux */
553# endif /* MENTAT */
554			if (error != 0)
555				fr_authstats.fas_quefail++;
556			else
557				fr_authstats.fas_queok++;
558		} else
559			error = EINVAL;
560# ifdef MENTAT
561		if (error != 0)
562			error = EINVAL;
563# else /* MENTAT */
564		/*
565		 * If we experience an error which will result in the packet
566		 * not being processed, make sure we advance to the next one.
567		 */
568		if (error == ENOBUFS) {
569			fr_authused--;
570			fra->fra_index = -1;
571			fra->fra_pass = 0;
572			if (i == fr_authstart) {
573				while (fra->fra_index == -1) {
574					i++;
575					if (i == fr_authsize)
576						i = 0;
577					fr_authstart = i;
578					if (i == fr_authend)
579						break;
580				}
581				if (fr_authstart == fr_authend) {
582					fr_authnext = 0;
583					fr_authstart = fr_authend = 0;
584				}
585			}
586		}
587# endif /* MENTAT */
588#endif /* _KERNEL */
589		SPL_X(s);
590		break;
591
592	default :
593		error = EINVAL;
594		break;
595	}
596	return error;
597}
598
599
600/*
601 * Free all network buffer memory used to keep saved packets.
602 */
603void fr_authunload()
604{
605	register int i;
606	register frauthent_t *fae, **faep;
607	frentry_t *fr, **frp;
608	mb_t *m;
609
610	if (fr_auth != NULL) {
611		KFREES(fr_auth, fr_authsize * sizeof(*fr_auth));
612		fr_auth = NULL;
613	}
614
615	if (fr_authpkts != NULL) {
616		for (i = 0; i < fr_authsize; i++) {
617			m = fr_authpkts[i];
618			if (m != NULL) {
619				FREE_MB_T(m);
620				fr_authpkts[i] = NULL;
621			}
622		}
623		KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
624		fr_authpkts = NULL;
625	}
626
627	faep = &fae_list;
628	while ((fae = *faep) != NULL) {
629		*faep = fae->fae_next;
630		KFREE(fae);
631	}
632	ipauth = NULL;
633
634	if (fr_authlist != NULL) {
635		for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
636			if (fr->fr_ref == 1) {
637				*frp = fr->fr_next;
638				KFREE(fr);
639			} else
640				frp = &fr->fr_next;
641		}
642	}
643
644	if (fr_auth_init == 1) {
645# if SOLARIS && defined(_KERNEL)
646		cv_destroy(&ipfauthwait);
647# endif
648		MUTEX_DESTROY(&ipf_authmx);
649		RW_DESTROY(&ipf_auth);
650
651		fr_auth_init = 0;
652	}
653}
654
655
656/*
657 * Slowly expire held auth records.  Timeouts are set
658 * in expectation of this being called twice per second.
659 */
660void fr_authexpire()
661{
662	register int i;
663	register frauth_t *fra;
664	register frauthent_t *fae, **faep;
665	register frentry_t *fr, **frp;
666	mb_t *m;
667	SPL_INT(s);
668
669	if (fr_auth_lock)
670		return;
671
672	SPL_NET(s);
673	WRITE_ENTER(&ipf_auth);
674	for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) {
675		fra->fra_age--;
676		if ((fra->fra_age == 0) && (m = fr_authpkts[i])) {
677			FREE_MB_T(m);
678			fr_authpkts[i] = NULL;
679			fr_auth[i].fra_index = -1;
680			fr_authstats.fas_expire++;
681			fr_authused--;
682		}
683	}
684
685	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
686		fae->fae_age--;
687		if (fae->fae_age == 0) {
688			*faep = fae->fae_next;
689			KFREE(fae);
690			fr_authstats.fas_expire++;
691		} else
692			faep = &fae->fae_next;
693	}
694	if (fae_list != NULL)
695		ipauth = &fae_list->fae_fr;
696	else
697		ipauth = NULL;
698
699	for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
700		if (fr->fr_ref == 1) {
701			*frp = fr->fr_next;
702			KFREE(fr);
703		} else
704			frp = &fr->fr_next;
705	}
706	RWLOCK_EXIT(&ipf_auth);
707	SPL_X(s);
708}
709
710int fr_preauthcmd(cmd, fr, frptr)
711ioctlcmd_t cmd;
712frentry_t *fr, **frptr;
713{
714	frauthent_t *fae, **faep;
715	int error = 0;
716	SPL_INT(s);
717
718	if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR))
719		return EIO;
720
721	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
722		if (&fae->fae_fr == fr)
723			break;
724		else
725			faep = &fae->fae_next;
726	}
727
728	if (cmd == (ioctlcmd_t)SIOCRMAFR) {
729		if (fr == NULL || frptr == NULL)
730			error = EINVAL;
731		else if (fae == NULL)
732			error = ESRCH;
733		else {
734			SPL_NET(s);
735			WRITE_ENTER(&ipf_auth);
736			*faep = fae->fae_next;
737			if (ipauth == &fae->fae_fr)
738				ipauth = fae_list ? &fae_list->fae_fr : NULL;
739			RWLOCK_EXIT(&ipf_auth);
740			SPL_X(s);
741
742			KFREE(fae);
743		}
744	} else if (fr != NULL && frptr != NULL) {
745		KMALLOC(fae, frauthent_t *);
746		if (fae != NULL) {
747			bcopy((char *)fr, (char *)&fae->fae_fr,
748			      sizeof(*fr));
749			SPL_NET(s);
750			WRITE_ENTER(&ipf_auth);
751			fae->fae_age = fr_defaultauthage;
752			fae->fae_fr.fr_hits = 0;
753			fae->fae_fr.fr_next = *frptr;
754			*frptr = &fae->fae_fr;
755			fae->fae_next = *faep;
756			*faep = fae;
757			ipauth = &fae_list->fae_fr;
758			RWLOCK_EXIT(&ipf_auth);
759			SPL_X(s);
760		} else
761			error = ENOMEM;
762	} else
763		error = EINVAL;
764	return error;
765}
766
767
768/*
769 * Flush held packets.
770 * Must already be properly SPL'ed and Locked on &ipf_auth.
771 *
772 */
773int fr_authflush()
774{
775	register int i, num_flushed;
776	mb_t *m;
777
778	if (fr_auth_lock)
779		return -1;
780
781	num_flushed = 0;
782
783	for (i = 0 ; i < fr_authsize; i++) {
784		m = fr_authpkts[i];
785		if (m != NULL) {
786			FREE_MB_T(m);
787			fr_authpkts[i] = NULL;
788			fr_auth[i].fra_index = -1;
789			/* perhaps add & use a flush counter inst.*/
790			fr_authstats.fas_expire++;
791			fr_authused--;
792			num_flushed++;
793		}
794	}
795
796	fr_authstart = 0;
797	fr_authend = 0;
798	fr_authnext = 0;
799
800	return num_flushed;
801}
802