ip_auth.c revision 145522
1/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 145522 2005-04-25 18:43:14Z darrenr $	*/
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 (_BSDI_VERSION >= 199802) || (__FreeBSD_version >= 400000)
56# include <sys/queue.h>
57#endif
58#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi)
59# include <machine/cpu.h>
60#endif
61#if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000)
62# include <sys/proc.h>
63#endif
64#include <net/if.h>
65#ifdef sun
66# include <net/af.h>
67#endif
68#include <net/route.h>
69#include <netinet/in.h>
70#include <netinet/in_systm.h>
71#include <netinet/ip.h>
72#if !defined(_KERNEL) && !defined(__osf__) && !defined(__sgi)
73# define	KERNEL
74# define	_KERNEL
75# define	NOT_KERNEL
76#endif
77#if !defined(linux)
78# include <netinet/ip_var.h>
79#endif
80#ifdef	NOT_KERNEL
81# undef	_KERNEL
82# undef	KERNEL
83#endif
84#include <netinet/tcp.h>
85#if defined(IRIX) && (IRIX < 60516) /* IRIX < 6 */
86extern struct ifqueue   ipintrq;		/* ip packet input queue */
87#else
88# if !defined(__hpux) && !defined(linux)
89#  if __FreeBSD_version >= 300000
90#   include <net/if_var.h>
91#   if __FreeBSD_version >= 500042
92#    define IF_QFULL _IF_QFULL
93#    define IF_DROP _IF_DROP
94#   endif /* __FreeBSD_version >= 500042 */
95#  endif
96#  include <netinet/in_var.h>
97#  include <netinet/tcp_fsm.h>
98# endif
99#endif
100#include <netinet/udp.h>
101#include <netinet/ip_icmp.h>
102#include "netinet/ip_compat.h"
103#include <netinet/tcpip.h>
104#include "netinet/ip_fil.h"
105#include "netinet/ip_auth.h"
106#if !defined(MENTAT) && !defined(linux)
107# include <net/netisr.h>
108# ifdef __FreeBSD__
109#  include <machine/cpufunc.h>
110# endif
111#endif
112#if (__FreeBSD_version >= 300000)
113# include <sys/malloc.h>
114# if defined(_KERNEL) && !defined(IPFILTER_LKM)
115#  include <sys/libkern.h>
116#  include <sys/systm.h>
117# endif
118#endif
119/* END OF INCLUDES */
120
121#if !defined(lint)
122static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 145522 2005-04-25 18:43:14Z darrenr $";
123static const char rcsid[] = "@(#)Id: ip_auth.c,v 2.73.2.3 2004/08/26 11:25:21 darrenr Exp";
124#endif
125
126
127#if SOLARIS
128extern kcondvar_t ipfauthwait;
129#endif /* SOLARIS */
130#if defined(linux) && defined(_KERNEL)
131wait_queue_head_t     fr_authnext_linux;
132#endif
133
134int	fr_authsize = FR_NUMAUTH;
135int	fr_authused = 0;
136int	fr_defaultauthage = 600;
137int	fr_auth_lock = 0;
138int	fr_auth_init = 0;
139fr_authstat_t	fr_authstats;
140static frauth_t *fr_auth = NULL;
141mb_t	**fr_authpkts = NULL;
142int	fr_authstart = 0, fr_authend = 0, fr_authnext = 0;
143frauthent_t	*fae_list = NULL;
144frentry_t	*ipauth = NULL,
145		*fr_authlist = NULL;
146
147
148int fr_authinit()
149{
150	KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth));
151	if (fr_auth != NULL)
152		bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth));
153	else
154		return -1;
155
156	KMALLOCS(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts));
157	if (fr_authpkts != NULL)
158		bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
159	else
160		return -2;
161
162	MUTEX_INIT(&ipf_authmx, "ipf auth log mutex");
163	RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock");
164#if SOLARIS && defined(_KERNEL)
165	cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL);
166#endif
167#if defined(linux) && defined(_KERNEL)
168	init_waitqueue_head(&fr_authnext_linux);
169#endif
170
171	fr_auth_init = 1;
172
173	return 0;
174}
175
176
177/*
178 * Check if a packet has authorization.  If the packet is found to match an
179 * authorization result and that would result in a feedback loop (i.e. it
180 * will end up returning FR_AUTH) then return FR_BLOCK instead.
181 */
182frentry_t *fr_checkauth(fin, passp)
183fr_info_t *fin;
184u_32_t *passp;
185{
186	frentry_t *fr;
187	frauth_t *fra;
188	u_32_t pass;
189	u_short id;
190	ip_t *ip;
191	int i;
192
193	if (fr_auth_lock || !fr_authused)
194		return NULL;
195
196	ip = fin->fin_ip;
197	id = ip->ip_id;
198
199	READ_ENTER(&ipf_auth);
200	for (i = fr_authstart; i != fr_authend; ) {
201		/*
202		 * index becomes -2 only after an SIOCAUTHW.  Check this in
203		 * case the same packet gets sent again and it hasn't yet been
204		 * auth'd.
205		 */
206		fra = fr_auth + i;
207		if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) &&
208		    !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) {
209			/*
210			 * Avoid feedback loop.
211			 */
212			if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass)))
213				pass = FR_BLOCK;
214			/*
215			 * Create a dummy rule for the stateful checking to
216			 * use and return.  Zero out any values we don't
217			 * trust from userland!
218			 */
219			if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) &&
220			     (fin->fin_flx & FI_FRAG))) {
221				KMALLOC(fr, frentry_t *);
222				if (fr) {
223					bcopy((char *)fra->fra_info.fin_fr,
224					      (char *)fr, sizeof(*fr));
225					fr->fr_grp = NULL;
226					fr->fr_ifa = fin->fin_ifp;
227					fr->fr_func = NULL;
228					fr->fr_ref = 1;
229					fr->fr_flags = pass;
230					fr->fr_ifas[1] = NULL;
231					fr->fr_ifas[2] = NULL;
232					fr->fr_ifas[3] = NULL;
233				}
234			} else
235				fr = fra->fra_info.fin_fr;
236			fin->fin_fr = fr;
237			RWLOCK_EXIT(&ipf_auth);
238			WRITE_ENTER(&ipf_auth);
239			if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) {
240				fr->fr_next = fr_authlist;
241				fr_authlist = fr;
242			}
243			fr_authstats.fas_hits++;
244			fra->fra_index = -1;
245			fr_authused--;
246			if (i == fr_authstart) {
247				while (fra->fra_index == -1) {
248					i++;
249					fra++;
250					if (i == fr_authsize) {
251						i = 0;
252						fra = fr_auth;
253					}
254					fr_authstart = i;
255					if (i == fr_authend)
256						break;
257				}
258				if (fr_authstart == fr_authend) {
259					fr_authnext = 0;
260					fr_authstart = fr_authend = 0;
261				}
262			}
263			RWLOCK_EXIT(&ipf_auth);
264			if (passp != NULL)
265				*passp = pass;
266			ATOMIC_INC64(fr_authstats.fas_hits);
267			return fr;
268		}
269		i++;
270		if (i == fr_authsize)
271			i = 0;
272	}
273	fr_authstats.fas_miss++;
274	RWLOCK_EXIT(&ipf_auth);
275	ATOMIC_INC64(fr_authstats.fas_miss);
276	return NULL;
277}
278
279
280/*
281 * Check if we have room in the auth array to hold details for another packet.
282 * If we do, store it and wake up any user programs which are waiting to
283 * hear about these events.
284 */
285int fr_newauth(m, fin)
286mb_t *m;
287fr_info_t *fin;
288{
289#if defined(_KERNEL) && defined(MENTAT)
290	qpktinfo_t *qpi = fin->fin_qpi;
291#endif
292	frauth_t *fra;
293#if !defined(sparc) && !defined(m68k)
294	ip_t *ip;
295#endif
296	int i;
297
298	if (fr_auth_lock)
299		return 0;
300
301	WRITE_ENTER(&ipf_auth);
302	if (fr_authstart > fr_authend) {
303		fr_authstats.fas_nospace++;
304		RWLOCK_EXIT(&ipf_auth);
305		return 0;
306	} else {
307		if (fr_authused == fr_authsize) {
308			fr_authstats.fas_nospace++;
309			RWLOCK_EXIT(&ipf_auth);
310			return 0;
311		}
312	}
313
314	fr_authstats.fas_added++;
315	fr_authused++;
316	i = fr_authend++;
317	if (fr_authend == fr_authsize)
318		fr_authend = 0;
319	RWLOCK_EXIT(&ipf_auth);
320
321	fra = fr_auth + i;
322	fra->fra_index = i;
323	fra->fra_pass = 0;
324	fra->fra_age = fr_defaultauthage;
325	bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin));
326#if !defined(sparc) && !defined(m68k)
327	/*
328	 * No need to copyback here as we want to undo the changes, not keep
329	 * them.
330	 */
331	ip = fin->fin_ip;
332# if defined(MENTAT) && defined(_KERNEL)
333	if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4))
334# endif
335	{
336		register u_short bo;
337
338		bo = ip->ip_len;
339		ip->ip_len = htons(bo);
340		bo = ip->ip_off;
341		ip->ip_off = htons(bo);
342	}
343#endif
344#if SOLARIS && defined(_KERNEL)
345	m->b_rptr -= qpi->qpi_off;
346	fr_authpkts[i] = *(mblk_t **)fin->fin_mp;
347	fra->fra_q = qpi->qpi_q;	/* The queue can disappear! */
348	cv_signal(&ipfauthwait);
349#else
350# if defined(BSD) && !defined(sparc) && (BSD >= 199306)
351	if (!fin->fin_out) {
352		ip->ip_len = htons(ip->ip_len);
353		ip->ip_off = htons(ip->ip_off);
354	}
355# endif
356	fr_authpkts[i] = m;
357	WAKEUP(&fr_authnext,0);
358#endif
359	return 1;
360}
361
362
363int fr_auth_ioctl(data, cmd, mode)
364caddr_t data;
365ioctlcmd_t cmd;
366int mode;
367{
368	mb_t *m;
369#if defined(_KERNEL) && !defined(MENTAT) && !defined(linux) && \
370    (!defined(__FreeBSD_version) || (__FreeBSD_version < 501000))
371	struct ifqueue *ifq;
372# ifdef USE_SPL
373	int s;
374# endif /* USE_SPL */
375#endif
376	frauth_t auth, *au = &auth, *fra;
377	int i, error = 0, len;
378	char *t;
379
380	switch (cmd)
381	{
382	case SIOCSTLCK :
383		if (!(mode & FWRITE)) {
384			error = EPERM;
385			break;
386		}
387		fr_lock(data, &fr_auth_lock);
388		break;
389
390	case SIOCATHST:
391		fr_authstats.fas_faelist = fae_list;
392		error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT);
393		break;
394
395	case SIOCIPFFL:
396		SPL_NET(s);
397		WRITE_ENTER(&ipf_auth);
398		i = fr_authflush();
399		RWLOCK_EXIT(&ipf_auth);
400		SPL_X(s);
401		error = copyoutptr((char *)&i, data, sizeof(i));
402		break;
403
404	case SIOCAUTHW:
405fr_authioctlloop:
406		error = fr_inobj(data, au, IPFOBJ_FRAUTH);
407		READ_ENTER(&ipf_auth);
408		if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) {
409			error = fr_outobj(data, &fr_auth[fr_authnext],
410					  IPFOBJ_FRAUTH);
411			if (auth.fra_len != 0 && auth.fra_buf != NULL) {
412				/*
413				 * Copy packet contents out to user space if
414				 * requested.  Bail on an error.
415				 */
416				m = fr_authpkts[fr_authnext];
417				len = MSGDSIZE(m);
418				if (len > auth.fra_len)
419					len = auth.fra_len;
420				auth.fra_len = len;
421				for (t = auth.fra_buf; m && (len > 0); ) {
422					i = MIN(M_LEN(m), len);
423					error = copyoutptr(MTOD(m, char *),
424							  t, i);
425					len -= i;
426					t += i;
427					if (error != 0)
428						break;
429				}
430			}
431			RWLOCK_EXIT(&ipf_auth);
432			if (error != 0)
433				break;
434			SPL_NET(s);
435			WRITE_ENTER(&ipf_auth);
436			fr_authnext++;
437			if (fr_authnext == fr_authsize)
438				fr_authnext = 0;
439			RWLOCK_EXIT(&ipf_auth);
440			SPL_X(s);
441			return 0;
442		}
443		RWLOCK_EXIT(&ipf_auth);
444		/*
445		 * We exit ipf_global here because a program that enters in
446		 * here will have a lock on it and goto sleep having this lock.
447		 * If someone were to do an 'ipf -D' the system would then
448		 * deadlock.  The catch with releasing it here is that the
449		 * caller of this function expects it to be held when we
450		 * return so we have to reacquire it in here.
451		 */
452		RWLOCK_EXIT(&ipf_global);
453
454		MUTEX_ENTER(&ipf_authmx);
455#ifdef	_KERNEL
456# if	SOLARIS
457		error = 0;
458		if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk))
459			error = EINTR;
460# else /* SOLARIS */
461#  ifdef __hpux
462		{
463		lock_t *l;
464
465		l = get_sleep_lock(&fr_authnext);
466		error = sleep(&fr_authnext, PZERO+1);
467		spinunlock(l);
468		}
469#  else
470#   ifdef __osf__
471		error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0,
472				&ipf_authmx, MS_LOCK_SIMPLE);
473#   else
474		error = SLEEP(&fr_authnext, "fr_authnext");
475#   endif /* __osf__ */
476#  endif /* __hpux */
477# endif /* SOLARIS */
478#endif
479		MUTEX_EXIT(&ipf_authmx);
480		READ_ENTER(&ipf_global);
481		if (error == 0) {
482			READ_ENTER(&ipf_auth);
483			goto fr_authioctlloop;
484		}
485		break;
486
487	case SIOCAUTHR:
488		error = fr_inobj(data, &auth, IPFOBJ_FRAUTH);
489		if (error != 0)
490			return error;
491		SPL_NET(s);
492		WRITE_ENTER(&ipf_auth);
493		i = au->fra_index;
494		fra = fr_auth + i;
495		if ((i < 0) || (i >= fr_authsize) ||
496		    (fra->fra_info.fin_id != au->fra_info.fin_id)) {
497			RWLOCK_EXIT(&ipf_auth);
498			SPL_X(s);
499			return ESRCH;
500		}
501		m = fr_authpkts[i];
502		fra->fra_index = -2;
503		fra->fra_pass = au->fra_pass;
504		fr_authpkts[i] = NULL;
505		RWLOCK_EXIT(&ipf_auth);
506#ifdef	_KERNEL
507		if ((m != NULL) && (au->fra_info.fin_out != 0)) {
508# ifdef MENTAT
509			error = !putq(fra->fra_q, m);
510# else /* MENTAT */
511#  ifdef linux
512#  else
513#   if (_BSDI_VERSION >= 199802) || 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#  ifdef linux
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# if !defined(MENAT) && defined(_KERNEL) && defined(USE_SPL)
668	int s;
669# endif
670
671	if (fr_auth_lock)
672		return;
673
674	SPL_NET(s);
675	WRITE_ENTER(&ipf_auth);
676	for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) {
677		fra->fra_age--;
678		if ((fra->fra_age == 0) && (m = fr_authpkts[i])) {
679			FREE_MB_T(m);
680			fr_authpkts[i] = NULL;
681			fr_auth[i].fra_index = -1;
682			fr_authstats.fas_expire++;
683			fr_authused--;
684		}
685	}
686
687	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
688		fae->fae_age--;
689		if (fae->fae_age == 0) {
690			*faep = fae->fae_next;
691			KFREE(fae);
692			fr_authstats.fas_expire++;
693		} else
694			faep = &fae->fae_next;
695	}
696	if (fae_list != NULL)
697		ipauth = &fae_list->fae_fr;
698	else
699		ipauth = NULL;
700
701	for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
702		if (fr->fr_ref == 1) {
703			*frp = fr->fr_next;
704			KFREE(fr);
705		} else
706			frp = &fr->fr_next;
707	}
708	RWLOCK_EXIT(&ipf_auth);
709	SPL_X(s);
710}
711
712int fr_preauthcmd(cmd, fr, frptr)
713ioctlcmd_t cmd;
714frentry_t *fr, **frptr;
715{
716	frauthent_t *fae, **faep;
717	int error = 0;
718# if !defined(MENAT) && defined(_KERNEL) && defined(USE_SPL)
719	int s;
720#endif
721
722	if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR))
723		return EIO;
724
725	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
726		if (&fae->fae_fr == fr)
727			break;
728		else
729			faep = &fae->fae_next;
730	}
731
732	if (cmd == (ioctlcmd_t)SIOCRMAFR) {
733		if (fr == NULL || frptr == NULL)
734			error = EINVAL;
735		else if (fae == NULL)
736			error = ESRCH;
737		else {
738			SPL_NET(s);
739			WRITE_ENTER(&ipf_auth);
740			*faep = fae->fae_next;
741			if (ipauth == &fae->fae_fr)
742				ipauth = fae_list ? &fae_list->fae_fr : NULL;
743			RWLOCK_EXIT(&ipf_auth);
744			SPL_X(s);
745
746			KFREE(fae);
747		}
748	} else if (fr != NULL && frptr != NULL) {
749		KMALLOC(fae, frauthent_t *);
750		if (fae != NULL) {
751			bcopy((char *)fr, (char *)&fae->fae_fr,
752			      sizeof(*fr));
753			SPL_NET(s);
754			WRITE_ENTER(&ipf_auth);
755			fae->fae_age = fr_defaultauthage;
756			fae->fae_fr.fr_hits = 0;
757			fae->fae_fr.fr_next = *frptr;
758			*frptr = &fae->fae_fr;
759			fae->fae_next = *faep;
760			*faep = fae;
761			ipauth = &fae_list->fae_fr;
762			RWLOCK_EXIT(&ipf_auth);
763			SPL_X(s);
764		} else
765			error = ENOMEM;
766	} else
767		error = EINVAL;
768	return error;
769}
770
771
772/*
773 * Flush held packets.
774 * Must already be properly SPL'ed and Locked on &ipf_auth.
775 *
776 */
777int fr_authflush()
778{
779	register int i, num_flushed;
780	mb_t *m;
781
782	if (fr_auth_lock)
783		return -1;
784
785	num_flushed = 0;
786
787	for (i = 0 ; i < fr_authsize; i++) {
788		m = fr_authpkts[i];
789		if (m != NULL) {
790			FREE_MB_T(m);
791			fr_authpkts[i] = NULL;
792			fr_auth[i].fra_index = -1;
793			/* perhaps add & use a flush counter inst.*/
794			fr_authstats.fas_expire++;
795			fr_authused--;
796			num_flushed++;
797		}
798	}
799
800	fr_authstart = 0;
801	fr_authend = 0;
802	fr_authnext = 0;
803
804	return num_flushed;
805}
806