ip_auth.c revision 161356
1/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 161356 2006-08-16 12:06:35Z 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 161356 2006-08-16 12:06:35Z guido $";
124/* static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.73.2.13 2006/03/29 11:19:55 darrenr Exp $"; */
125#endif
126
127
128#if SOLARIS && defined(_KERNEL)
129extern kcondvar_t ipfauthwait;
130extern struct pollhead iplpollhead[IPL_LOGSIZE];
131#endif /* SOLARIS */
132#if defined(linux) && defined(_KERNEL)
133wait_queue_head_t     fr_authnext_linux;
134#endif
135
136int	fr_authsize = FR_NUMAUTH;
137int	fr_authused = 0;
138int	fr_defaultauthage = 600;
139int	fr_auth_lock = 0;
140int	fr_auth_init = 0;
141fr_authstat_t	fr_authstats;
142static frauth_t *fr_auth = NULL;
143mb_t	**fr_authpkts = NULL;
144int	fr_authstart = 0, fr_authend = 0, fr_authnext = 0;
145frauthent_t	*fae_list = NULL;
146frentry_t	*ipauth = NULL,
147		*fr_authlist = NULL;
148
149
150int fr_authinit()
151{
152	KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth));
153	if (fr_auth != NULL)
154		bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth));
155	else
156		return -1;
157
158	KMALLOCS(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts));
159	if (fr_authpkts != NULL)
160		bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
161	else
162		return -2;
163
164	MUTEX_INIT(&ipf_authmx, "ipf auth log mutex");
165	RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock");
166#if SOLARIS && defined(_KERNEL)
167	cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL);
168#endif
169#if defined(linux) && defined(_KERNEL)
170	init_waitqueue_head(&fr_authnext_linux);
171#endif
172
173	fr_auth_init = 1;
174
175	return 0;
176}
177
178
179/*
180 * Check if a packet has authorization.  If the packet is found to match an
181 * authorization result and that would result in a feedback loop (i.e. it
182 * will end up returning FR_AUTH) then return FR_BLOCK instead.
183 */
184frentry_t *fr_checkauth(fin, passp)
185fr_info_t *fin;
186u_32_t *passp;
187{
188	frentry_t *fr;
189	frauth_t *fra;
190	u_32_t pass;
191	u_short id;
192	ip_t *ip;
193	int i;
194
195	if (fr_auth_lock || !fr_authused)
196		return NULL;
197
198	ip = fin->fin_ip;
199	id = ip->ip_id;
200
201	READ_ENTER(&ipf_auth);
202	for (i = fr_authstart; i != fr_authend; ) {
203		/*
204		 * index becomes -2 only after an SIOCAUTHW.  Check this in
205		 * case the same packet gets sent again and it hasn't yet been
206		 * auth'd.
207		 */
208		fra = fr_auth + i;
209		if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) &&
210		    !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) {
211			/*
212			 * Avoid feedback loop.
213			 */
214			if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass)))
215				pass = FR_BLOCK;
216			/*
217			 * Create a dummy rule for the stateful checking to
218			 * use and return.  Zero out any values we don't
219			 * trust from userland!
220			 */
221			if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) &&
222			     (fin->fin_flx & FI_FRAG))) {
223				KMALLOC(fr, frentry_t *);
224				if (fr) {
225					bcopy((char *)fra->fra_info.fin_fr,
226					      (char *)fr, sizeof(*fr));
227					fr->fr_grp = NULL;
228					fr->fr_ifa = fin->fin_ifp;
229					fr->fr_func = NULL;
230					fr->fr_ref = 1;
231					fr->fr_flags = pass;
232					fr->fr_ifas[1] = NULL;
233					fr->fr_ifas[2] = NULL;
234					fr->fr_ifas[3] = NULL;
235				}
236			} else
237				fr = fra->fra_info.fin_fr;
238			fin->fin_fr = fr;
239			RWLOCK_EXIT(&ipf_auth);
240			WRITE_ENTER(&ipf_auth);
241			if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) {
242				fr->fr_next = fr_authlist;
243				fr_authlist = fr;
244			}
245			fr_authstats.fas_hits++;
246			fra->fra_index = -1;
247			fr_authused--;
248			if (i == fr_authstart) {
249				while (fra->fra_index == -1) {
250					i++;
251					fra++;
252					if (i == fr_authsize) {
253						i = 0;
254						fra = fr_auth;
255					}
256					fr_authstart = i;
257					if (i == fr_authend)
258						break;
259				}
260				if (fr_authstart == fr_authend) {
261					fr_authnext = 0;
262					fr_authstart = fr_authend = 0;
263				}
264			}
265			RWLOCK_EXIT(&ipf_auth);
266			if (passp != NULL)
267				*passp = pass;
268			ATOMIC_INC64(fr_authstats.fas_hits);
269			return fr;
270		}
271		i++;
272		if (i == fr_authsize)
273			i = 0;
274	}
275	fr_authstats.fas_miss++;
276	RWLOCK_EXIT(&ipf_auth);
277	ATOMIC_INC64(fr_authstats.fas_miss);
278	return NULL;
279}
280
281
282/*
283 * Check if we have room in the auth array to hold details for another packet.
284 * If we do, store it and wake up any user programs which are waiting to
285 * hear about these events.
286 */
287int fr_newauth(m, fin)
288mb_t *m;
289fr_info_t *fin;
290{
291#if defined(_KERNEL) && defined(MENTAT)
292	qpktinfo_t *qpi = fin->fin_qpi;
293#endif
294	frauth_t *fra;
295#if !defined(sparc) && !defined(m68k)
296	ip_t *ip;
297#endif
298	int i;
299
300	if (fr_auth_lock)
301		return 0;
302
303	WRITE_ENTER(&ipf_auth);
304	if (fr_authstart > fr_authend) {
305		fr_authstats.fas_nospace++;
306		RWLOCK_EXIT(&ipf_auth);
307		return 0;
308	} else {
309		if (fr_authused == fr_authsize) {
310			fr_authstats.fas_nospace++;
311			RWLOCK_EXIT(&ipf_auth);
312			return 0;
313		}
314	}
315
316	fr_authstats.fas_added++;
317	fr_authused++;
318	i = fr_authend++;
319	if (fr_authend == fr_authsize)
320		fr_authend = 0;
321	RWLOCK_EXIT(&ipf_auth);
322
323	fra = fr_auth + i;
324	fra->fra_index = i;
325	fra->fra_pass = fin->fin_fr->fr_flags;
326	fra->fra_age = fr_defaultauthage;
327	bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin));
328#if !defined(sparc) && !defined(m68k)
329	/*
330	 * No need to copyback here as we want to undo the changes, not keep
331	 * them.
332	 */
333	ip = fin->fin_ip;
334# if defined(MENTAT) && defined(_KERNEL)
335	if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4))
336# endif
337	{
338		register u_short bo;
339
340		bo = ip->ip_len;
341		ip->ip_len = htons(bo);
342		bo = ip->ip_off;
343		ip->ip_off = htons(bo);
344	}
345#endif
346#if SOLARIS && defined(_KERNEL)
347	COPYIFNAME(fin->fin_ifp, fra->fra_info.fin_ifname);
348	m->b_rptr -= qpi->qpi_off;
349	fr_authpkts[i] = *(mblk_t **)fin->fin_mp;
350	fra->fra_q = qpi->qpi_q;	/* The queue can disappear! */
351	fra->fra_m = *fin->fin_mp;
352	fra->fra_info.fin_mp = &fra->fra_m;
353	cv_signal(&ipfauthwait);
354	pollwakeup(&iplpollhead[IPL_LOGAUTH], POLLIN|POLLRDNORM);
355#else
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	frauth_t auth, *au = &auth, *fra;
369	int i, error = 0, len;
370	char *t;
371	mb_t *m;
372#if defined(_KERNEL) && !defined(MENTAT) && !defined(linux) && \
373    (!defined(__FreeBSD_version) || (__FreeBSD_version < 501000))
374	struct ifqueue *ifq;
375	SPL_INT(s);
376#endif
377
378	switch (cmd)
379	{
380	case SIOCSTLCK :
381		if (!(mode & FWRITE)) {
382			error = EPERM;
383			break;
384		}
385		fr_lock(data, &fr_auth_lock);
386		break;
387
388	case SIOCATHST:
389		fr_authstats.fas_faelist = fae_list;
390		error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT);
391		break;
392
393	case SIOCIPFFL:
394		SPL_NET(s);
395		WRITE_ENTER(&ipf_auth);
396		i = fr_authflush();
397		RWLOCK_EXIT(&ipf_auth);
398		SPL_X(s);
399		error = copyoutptr((char *)&i, data, sizeof(i));
400		break;
401
402	case SIOCAUTHW:
403fr_authioctlloop:
404		error = fr_inobj(data, au, IPFOBJ_FRAUTH);
405		if (error != 0)
406			break;
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 (error != 0)
412				break;
413			if (auth.fra_len != 0 && auth.fra_buf != NULL) {
414				/*
415				 * Copy packet contents out to user space if
416				 * requested.  Bail on an error.
417				 */
418				m = fr_authpkts[fr_authnext];
419				len = MSGDSIZE(m);
420				if (len > auth.fra_len)
421					len = auth.fra_len;
422				auth.fra_len = len;
423				for (t = auth.fra_buf; m && (len > 0); ) {
424					i = MIN(M_LEN(m), len);
425					error = copyoutptr(MTOD(m, char *),
426							   &t, i);
427					len -= i;
428					t += i;
429					if (error != 0)
430						break;
431					m = m->m_next;
432				}
433			}
434			RWLOCK_EXIT(&ipf_auth);
435			if (error != 0)
436				break;
437			SPL_NET(s);
438			WRITE_ENTER(&ipf_auth);
439			fr_authnext++;
440			if (fr_authnext == fr_authsize)
441				fr_authnext = 0;
442			RWLOCK_EXIT(&ipf_auth);
443			SPL_X(s);
444			return 0;
445		}
446		RWLOCK_EXIT(&ipf_auth);
447		/*
448		 * We exit ipf_global here because a program that enters in
449		 * here will have a lock on it and goto sleep having this lock.
450		 * If someone were to do an 'ipf -D' the system would then
451		 * deadlock.  The catch with releasing it here is that the
452		 * caller of this function expects it to be held when we
453		 * return so we have to reacquire it in here.
454		 */
455		RWLOCK_EXIT(&ipf_global);
456
457		MUTEX_ENTER(&ipf_authmx);
458#ifdef	_KERNEL
459# if	SOLARIS
460		error = 0;
461		if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk))
462			error = EINTR;
463# else /* SOLARIS */
464#  ifdef __hpux
465		{
466		lock_t *l;
467
468		l = get_sleep_lock(&fr_authnext);
469		error = sleep(&fr_authnext, PZERO+1);
470		spinunlock(l);
471		}
472#  else
473#   ifdef __osf__
474		error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0,
475				&ipf_authmx, MS_LOCK_SIMPLE);
476#   else
477		error = SLEEP(&fr_authnext, "fr_authnext");
478#   endif /* __osf__ */
479#  endif /* __hpux */
480# endif /* SOLARIS */
481#endif
482		MUTEX_EXIT(&ipf_authmx);
483		READ_ENTER(&ipf_global);
484		if (error == 0)
485			goto fr_authioctlloop;
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		error = 0;
497		if ((i < 0) || (i >= fr_authsize) ||
498		    (fra->fra_info.fin_id != au->fra_info.fin_id)) {
499			RWLOCK_EXIT(&ipf_auth);
500			SPL_X(s);
501			return ESRCH;
502		}
503		m = fr_authpkts[i];
504		fra->fra_index = -2;
505		fra->fra_pass = au->fra_pass;
506		fr_authpkts[i] = NULL;
507		RWLOCK_EXIT(&ipf_auth);
508#ifdef	_KERNEL
509		if ((m != NULL) && (au->fra_info.fin_out != 0)) {
510# ifdef MENTAT
511			error = ipf_inject(&fra->fra_info);
512			if (error != 0) {
513				FREE_MB_T(m);
514				error = ENOBUFS;
515			}
516# else /* MENTAT */
517#  if defined(linux) || defined(AIX)
518#  else
519#   if (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802) || \
520       (defined(__OpenBSD__)) || \
521       (defined(__sgi) && (IRIX >= 60500) || \
522       (defined(__FreeBSD__) && (__FreeBSD_version >= 470102)))
523			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL,
524					  NULL);
525#   else
526			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL);
527#   endif
528#  endif /* Linux */
529# endif /* MENTAT */
530			if (error != 0)
531				fr_authstats.fas_sendfail++;
532			else
533				fr_authstats.fas_sendok++;
534		} else if (m) {
535# ifdef MENTAT
536			error = ipf_inject(&fra->fra_info);
537			if (error != 0) {
538				FREE_MB_T(m);
539				error = ENOBUFS;
540			}
541# else /* MENTAT */
542#  if defined(linux) || defined(AIX)
543#  else
544#   if (__FreeBSD_version >= 501000)
545			netisr_dispatch(NETISR_IP, m);
546#   else
547#    if (IRIX >= 60516)
548			ifq = &((struct ifnet *)fra->fra_info.fin_ifp)->if_snd;
549#    else
550			ifq = &ipintrq;
551#    endif
552			if (IF_QFULL(ifq)) {
553				IF_DROP(ifq);
554				FREE_MB_T(m);
555				error = ENOBUFS;
556			} else {
557				IF_ENQUEUE(ifq, m);
558#    if IRIX < 60500
559				schednetisr(NETISR_IP);
560#    endif
561			}
562#   endif
563#  endif /* Linux */
564# endif /* MENTAT */
565			if (error != 0)
566				fr_authstats.fas_quefail++;
567			else
568				fr_authstats.fas_queok++;
569		} else
570			error = EINVAL;
571		/*
572		 * If we experience an error which will result in the packet
573		 * not being processed, make sure we advance to the next one.
574		 */
575		if (error == ENOBUFS) {
576			fr_authused--;
577			fra->fra_index = -1;
578			fra->fra_pass = 0;
579			if (i == fr_authstart) {
580				while (fra->fra_index == -1) {
581					i++;
582					if (i == fr_authsize)
583						i = 0;
584					fr_authstart = i;
585					if (i == fr_authend)
586						break;
587				}
588				if (fr_authstart == fr_authend) {
589					fr_authnext = 0;
590					fr_authstart = fr_authend = 0;
591				}
592			}
593		}
594#endif /* _KERNEL */
595		SPL_X(s);
596		break;
597
598	default :
599		error = EINVAL;
600		break;
601	}
602	return error;
603}
604
605
606/*
607 * Free all network buffer memory used to keep saved packets.
608 */
609void fr_authunload()
610{
611	register int i;
612	register frauthent_t *fae, **faep;
613	frentry_t *fr, **frp;
614	mb_t *m;
615
616	if (fr_auth != NULL) {
617		KFREES(fr_auth, fr_authsize * sizeof(*fr_auth));
618		fr_auth = NULL;
619	}
620
621	if (fr_authpkts != NULL) {
622		for (i = 0; i < fr_authsize; i++) {
623			m = fr_authpkts[i];
624			if (m != NULL) {
625				FREE_MB_T(m);
626				fr_authpkts[i] = NULL;
627			}
628		}
629		KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
630		fr_authpkts = NULL;
631	}
632
633	faep = &fae_list;
634	while ((fae = *faep) != NULL) {
635		*faep = fae->fae_next;
636		KFREE(fae);
637	}
638	ipauth = NULL;
639
640	if (fr_authlist != NULL) {
641		for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
642			if (fr->fr_ref == 1) {
643				*frp = fr->fr_next;
644				KFREE(fr);
645			} else
646				frp = &fr->fr_next;
647		}
648	}
649
650	if (fr_auth_init == 1) {
651# if SOLARIS && defined(_KERNEL)
652		cv_destroy(&ipfauthwait);
653# endif
654		MUTEX_DESTROY(&ipf_authmx);
655		RW_DESTROY(&ipf_auth);
656
657		fr_auth_init = 0;
658	}
659}
660
661
662/*
663 * Slowly expire held auth records.  Timeouts are set
664 * in expectation of this being called twice per second.
665 */
666void fr_authexpire()
667{
668	register int i;
669	register frauth_t *fra;
670	register frauthent_t *fae, **faep;
671	register frentry_t *fr, **frp;
672	mb_t *m;
673	SPL_INT(s);
674
675	if (fr_auth_lock)
676		return;
677
678	SPL_NET(s);
679	WRITE_ENTER(&ipf_auth);
680	for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) {
681		fra->fra_age--;
682		if ((fra->fra_age == 0) && (m = fr_authpkts[i])) {
683			FREE_MB_T(m);
684			fr_authpkts[i] = NULL;
685			fr_auth[i].fra_index = -1;
686			fr_authstats.fas_expire++;
687			fr_authused--;
688		}
689	}
690
691	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
692		fae->fae_age--;
693		if (fae->fae_age == 0) {
694			*faep = fae->fae_next;
695			KFREE(fae);
696			fr_authstats.fas_expire++;
697		} else
698			faep = &fae->fae_next;
699	}
700	if (fae_list != NULL)
701		ipauth = &fae_list->fae_fr;
702	else
703		ipauth = NULL;
704
705	for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
706		if (fr->fr_ref == 1) {
707			*frp = fr->fr_next;
708			KFREE(fr);
709		} else
710			frp = &fr->fr_next;
711	}
712	RWLOCK_EXIT(&ipf_auth);
713	SPL_X(s);
714}
715
716int fr_preauthcmd(cmd, fr, frptr)
717ioctlcmd_t cmd;
718frentry_t *fr, **frptr;
719{
720	frauthent_t *fae, **faep;
721	int error = 0;
722	SPL_INT(s);
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
809
810int fr_auth_waiting()
811{
812	return (fr_authnext != fr_authend) && fr_authpkts[fr_authnext];
813}
814