ip_auth.c revision 145522
1145522Sdarrenr/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 145522 2005-04-25 18:43:14Z darrenr $	*/
2145522Sdarrenr
353642Sguido/*
4145522Sdarrenr * Copyright (C) 1998-2003 by Darren Reed & Guido van Rooij.
553642Sguido *
680482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
753642Sguido */
8145522Sdarrenr#if defined(KERNEL) || defined(_KERNEL)
9145522Sdarrenr# undef KERNEL
10145522Sdarrenr# undef _KERNEL
11145522Sdarrenr# define        KERNEL	1
12145522Sdarrenr# define        _KERNEL	1
1392685Sdarrenr#endif
1453642Sguido#include <sys/errno.h>
1553642Sguido#include <sys/types.h>
1653642Sguido#include <sys/param.h>
1753642Sguido#include <sys/time.h>
1853642Sguido#include <sys/file.h>
19145522Sdarrenr#if !defined(_KERNEL)
2053642Sguido# include <stdio.h>
2153642Sguido# include <stdlib.h>
2253642Sguido# include <string.h>
23145522Sdarrenr# define _KERNEL
24145522Sdarrenr# ifdef __OpenBSD__
25145522Sdarrenrstruct file;
26145522Sdarrenr# endif
27145522Sdarrenr# include <sys/uio.h>
28145522Sdarrenr# undef _KERNEL
2953642Sguido#endif
30145522Sdarrenr#if defined(_KERNEL) && (__FreeBSD_version >= 220000)
3153642Sguido# include <sys/filio.h>
3253642Sguido# include <sys/fcntl.h>
3353642Sguido#else
3453642Sguido# include <sys/ioctl.h>
3553642Sguido#endif
36145522Sdarrenr#if !defined(linux)
3753642Sguido# include <sys/protosw.h>
3853642Sguido#endif
3953642Sguido#include <sys/socket.h>
40145522Sdarrenr#if defined(_KERNEL)
4153642Sguido# include <sys/systm.h>
42145522Sdarrenr# if !defined(__SVR4) && !defined(__svr4__) && !defined(linux)
4353642Sguido#  include <sys/mbuf.h>
4453642Sguido# endif
45145522Sdarrenr#endif
46145522Sdarrenr#if defined(__SVR4) || defined(__svr4__)
4753642Sguido# include <sys/filio.h>
4853642Sguido# include <sys/byteorder.h>
4953642Sguido# ifdef _KERNEL
5053642Sguido#  include <sys/dditypes.h>
5153642Sguido# endif
5253642Sguido# include <sys/stream.h>
5353642Sguido# include <sys/kmem.h>
5453642Sguido#endif
5564105Sroberto#if (_BSDI_VERSION >= 199802) || (__FreeBSD_version >= 400000)
5653642Sguido# include <sys/queue.h>
5753642Sguido#endif
5853642Sguido#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi)
5953642Sguido# include <machine/cpu.h>
6053642Sguido#endif
61145522Sdarrenr#if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000)
62145522Sdarrenr# include <sys/proc.h>
63145522Sdarrenr#endif
6453642Sguido#include <net/if.h>
6553642Sguido#ifdef sun
6653642Sguido# include <net/af.h>
6753642Sguido#endif
6853642Sguido#include <net/route.h>
6953642Sguido#include <netinet/in.h>
7053642Sguido#include <netinet/in_systm.h>
7153642Sguido#include <netinet/ip.h>
72145522Sdarrenr#if !defined(_KERNEL) && !defined(__osf__) && !defined(__sgi)
7353642Sguido# define	KERNEL
74145522Sdarrenr# define	_KERNEL
7553642Sguido# define	NOT_KERNEL
7653642Sguido#endif
77145522Sdarrenr#if !defined(linux)
7853642Sguido# include <netinet/ip_var.h>
7953642Sguido#endif
8053642Sguido#ifdef	NOT_KERNEL
81145522Sdarrenr# undef	_KERNEL
8253642Sguido# undef	KERNEL
8353642Sguido#endif
8453642Sguido#include <netinet/tcp.h>
85145522Sdarrenr#if defined(IRIX) && (IRIX < 60516) /* IRIX < 6 */
8680482Sdarrenrextern struct ifqueue   ipintrq;		/* ip packet input queue */
8753642Sguido#else
88145522Sdarrenr# if !defined(__hpux) && !defined(linux)
8953642Sguido#  if __FreeBSD_version >= 300000
9053642Sguido#   include <net/if_var.h>
91145522Sdarrenr#   if __FreeBSD_version >= 500042
92145522Sdarrenr#    define IF_QFULL _IF_QFULL
93145522Sdarrenr#    define IF_DROP _IF_DROP
94145522Sdarrenr#   endif /* __FreeBSD_version >= 500042 */
9553642Sguido#  endif
9653642Sguido#  include <netinet/in_var.h>
9753642Sguido#  include <netinet/tcp_fsm.h>
9853642Sguido# endif
9953642Sguido#endif
10053642Sguido#include <netinet/udp.h>
10153642Sguido#include <netinet/ip_icmp.h>
10253642Sguido#include "netinet/ip_compat.h"
10353642Sguido#include <netinet/tcpip.h>
10453642Sguido#include "netinet/ip_fil.h"
10553642Sguido#include "netinet/ip_auth.h"
106145522Sdarrenr#if !defined(MENTAT) && !defined(linux)
10753642Sguido# include <net/netisr.h>
10853642Sguido# ifdef __FreeBSD__
10953642Sguido#  include <machine/cpufunc.h>
11053642Sguido# endif
11153642Sguido#endif
11253642Sguido#if (__FreeBSD_version >= 300000)
11353642Sguido# include <sys/malloc.h>
114145522Sdarrenr# if defined(_KERNEL) && !defined(IPFILTER_LKM)
11553642Sguido#  include <sys/libkern.h>
11653642Sguido#  include <sys/systm.h>
11753642Sguido# endif
11853642Sguido#endif
119145522Sdarrenr/* END OF INCLUDES */
12053642Sguido
12180482Sdarrenr#if !defined(lint)
12280482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 145522 2005-04-25 18:43:14Z darrenr $";
123145522Sdarrenrstatic const char rcsid[] = "@(#)Id: ip_auth.c,v 2.73.2.3 2004/08/26 11:25:21 darrenr Exp";
12480482Sdarrenr#endif
12553642Sguido
12653642Sguido
127145522Sdarrenr#if SOLARIS
12853642Sguidoextern kcondvar_t ipfauthwait;
129145522Sdarrenr#endif /* SOLARIS */
130145522Sdarrenr#if defined(linux) && defined(_KERNEL)
131145522Sdarrenrwait_queue_head_t     fr_authnext_linux;
13253642Sguido#endif
13353642Sguido
13453642Sguidoint	fr_authsize = FR_NUMAUTH;
13553642Sguidoint	fr_authused = 0;
13653642Sguidoint	fr_defaultauthage = 600;
13760850Sdarrenrint	fr_auth_lock = 0;
138145522Sdarrenrint	fr_auth_init = 0;
13953642Sguidofr_authstat_t	fr_authstats;
140145522Sdarrenrstatic frauth_t *fr_auth = NULL;
141145522Sdarrenrmb_t	**fr_authpkts = NULL;
142145522Sdarrenrint	fr_authstart = 0, fr_authend = 0, fr_authnext = 0;
143145522Sdarrenrfrauthent_t	*fae_list = NULL;
14480482Sdarrenrfrentry_t	*ipauth = NULL,
14580482Sdarrenr		*fr_authlist = NULL;
14653642Sguido
14753642Sguido
148145522Sdarrenrint fr_authinit()
149145522Sdarrenr{
150145522Sdarrenr	KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth));
151145522Sdarrenr	if (fr_auth != NULL)
152145522Sdarrenr		bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth));
153145522Sdarrenr	else
154145522Sdarrenr		return -1;
155145522Sdarrenr
156145522Sdarrenr	KMALLOCS(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts));
157145522Sdarrenr	if (fr_authpkts != NULL)
158145522Sdarrenr		bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
159145522Sdarrenr	else
160145522Sdarrenr		return -2;
161145522Sdarrenr
162145522Sdarrenr	MUTEX_INIT(&ipf_authmx, "ipf auth log mutex");
163145522Sdarrenr	RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock");
164145522Sdarrenr#if SOLARIS && defined(_KERNEL)
165145522Sdarrenr	cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL);
166145522Sdarrenr#endif
167145522Sdarrenr#if defined(linux) && defined(_KERNEL)
168145522Sdarrenr	init_waitqueue_head(&fr_authnext_linux);
169145522Sdarrenr#endif
170145522Sdarrenr
171145522Sdarrenr	fr_auth_init = 1;
172145522Sdarrenr
173145522Sdarrenr	return 0;
174145522Sdarrenr}
175145522Sdarrenr
176145522Sdarrenr
17753642Sguido/*
17853642Sguido * Check if a packet has authorization.  If the packet is found to match an
17953642Sguido * authorization result and that would result in a feedback loop (i.e. it
18053642Sguido * will end up returning FR_AUTH) then return FR_BLOCK instead.
18153642Sguido */
182145522Sdarrenrfrentry_t *fr_checkauth(fin, passp)
18353642Sguidofr_info_t *fin;
184145522Sdarrenru_32_t *passp;
18553642Sguido{
18680482Sdarrenr	frentry_t *fr;
18780482Sdarrenr	frauth_t *fra;
18853642Sguido	u_32_t pass;
189145522Sdarrenr	u_short id;
190145522Sdarrenr	ip_t *ip;
19153642Sguido	int i;
19253642Sguido
19380482Sdarrenr	if (fr_auth_lock || !fr_authused)
194145522Sdarrenr		return NULL;
19560850Sdarrenr
196145522Sdarrenr	ip = fin->fin_ip;
197145522Sdarrenr	id = ip->ip_id;
198145522Sdarrenr
19953642Sguido	READ_ENTER(&ipf_auth);
20053642Sguido	for (i = fr_authstart; i != fr_authend; ) {
20153642Sguido		/*
20253642Sguido		 * index becomes -2 only after an SIOCAUTHW.  Check this in
20353642Sguido		 * case the same packet gets sent again and it hasn't yet been
20453642Sguido		 * auth'd.
20553642Sguido		 */
20680482Sdarrenr		fra = fr_auth + i;
20780482Sdarrenr		if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) &&
20880482Sdarrenr		    !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) {
20953642Sguido			/*
21053642Sguido			 * Avoid feedback loop.
21153642Sguido			 */
212145522Sdarrenr			if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass)))
21353642Sguido				pass = FR_BLOCK;
21480482Sdarrenr			/*
21580482Sdarrenr			 * Create a dummy rule for the stateful checking to
21680482Sdarrenr			 * use and return.  Zero out any values we don't
21780482Sdarrenr			 * trust from userland!
21880482Sdarrenr			 */
21980482Sdarrenr			if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) &&
220145522Sdarrenr			     (fin->fin_flx & FI_FRAG))) {
22180482Sdarrenr				KMALLOC(fr, frentry_t *);
22280482Sdarrenr				if (fr) {
22380482Sdarrenr					bcopy((char *)fra->fra_info.fin_fr,
224145522Sdarrenr					      (char *)fr, sizeof(*fr));
22580482Sdarrenr					fr->fr_grp = NULL;
22680482Sdarrenr					fr->fr_ifa = fin->fin_ifp;
22780482Sdarrenr					fr->fr_func = NULL;
22880482Sdarrenr					fr->fr_ref = 1;
22980482Sdarrenr					fr->fr_flags = pass;
230145522Sdarrenr					fr->fr_ifas[1] = NULL;
231145522Sdarrenr					fr->fr_ifas[2] = NULL;
232145522Sdarrenr					fr->fr_ifas[3] = NULL;
23380482Sdarrenr				}
23480482Sdarrenr			} else
23580482Sdarrenr				fr = fra->fra_info.fin_fr;
23680482Sdarrenr			fin->fin_fr = fr;
23753642Sguido			RWLOCK_EXIT(&ipf_auth);
23853642Sguido			WRITE_ENTER(&ipf_auth);
239145522Sdarrenr			if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) {
24080482Sdarrenr				fr->fr_next = fr_authlist;
24180482Sdarrenr				fr_authlist = fr;
24280482Sdarrenr			}
24353642Sguido			fr_authstats.fas_hits++;
24480482Sdarrenr			fra->fra_index = -1;
24553642Sguido			fr_authused--;
24653642Sguido			if (i == fr_authstart) {
24780482Sdarrenr				while (fra->fra_index == -1) {
24853642Sguido					i++;
24980482Sdarrenr					fra++;
250145522Sdarrenr					if (i == fr_authsize) {
25153642Sguido						i = 0;
25280482Sdarrenr						fra = fr_auth;
25380482Sdarrenr					}
25453642Sguido					fr_authstart = i;
25553642Sguido					if (i == fr_authend)
25653642Sguido						break;
25753642Sguido				}
25853642Sguido				if (fr_authstart == fr_authend) {
25953642Sguido					fr_authnext = 0;
26053642Sguido					fr_authstart = fr_authend = 0;
26153642Sguido				}
26253642Sguido			}
26353642Sguido			RWLOCK_EXIT(&ipf_auth);
264145522Sdarrenr			if (passp != NULL)
265145522Sdarrenr				*passp = pass;
266145522Sdarrenr			ATOMIC_INC64(fr_authstats.fas_hits);
267145522Sdarrenr			return fr;
26853642Sguido		}
26953642Sguido		i++;
270145522Sdarrenr		if (i == fr_authsize)
27153642Sguido			i = 0;
27253642Sguido	}
27353642Sguido	fr_authstats.fas_miss++;
27453642Sguido	RWLOCK_EXIT(&ipf_auth);
275145522Sdarrenr	ATOMIC_INC64(fr_authstats.fas_miss);
276145522Sdarrenr	return NULL;
27753642Sguido}
27853642Sguido
27953642Sguido
28053642Sguido/*
28153642Sguido * Check if we have room in the auth array to hold details for another packet.
28253642Sguido * If we do, store it and wake up any user programs which are waiting to
28353642Sguido * hear about these events.
28453642Sguido */
285145522Sdarrenrint fr_newauth(m, fin)
28653642Sguidomb_t *m;
28753642Sguidofr_info_t *fin;
28853642Sguido{
289145522Sdarrenr#if defined(_KERNEL) && defined(MENTAT)
290145522Sdarrenr	qpktinfo_t *qpi = fin->fin_qpi;
29160850Sdarrenr#endif
29280482Sdarrenr	frauth_t *fra;
293145522Sdarrenr#if !defined(sparc) && !defined(m68k)
294145522Sdarrenr	ip_t *ip;
295145522Sdarrenr#endif
29653642Sguido	int i;
29753642Sguido
29860850Sdarrenr	if (fr_auth_lock)
29960850Sdarrenr		return 0;
30060850Sdarrenr
30153642Sguido	WRITE_ENTER(&ipf_auth);
30253642Sguido	if (fr_authstart > fr_authend) {
30353642Sguido		fr_authstats.fas_nospace++;
30453642Sguido		RWLOCK_EXIT(&ipf_auth);
30553642Sguido		return 0;
30653642Sguido	} else {
307145522Sdarrenr		if (fr_authused == fr_authsize) {
30853642Sguido			fr_authstats.fas_nospace++;
30953642Sguido			RWLOCK_EXIT(&ipf_auth);
31053642Sguido			return 0;
31153642Sguido		}
31253642Sguido	}
31353642Sguido
31453642Sguido	fr_authstats.fas_added++;
31553642Sguido	fr_authused++;
31653642Sguido	i = fr_authend++;
317145522Sdarrenr	if (fr_authend == fr_authsize)
31853642Sguido		fr_authend = 0;
31953642Sguido	RWLOCK_EXIT(&ipf_auth);
320145522Sdarrenr
32180482Sdarrenr	fra = fr_auth + i;
32280482Sdarrenr	fra->fra_index = i;
32380482Sdarrenr	fra->fra_pass = 0;
32480482Sdarrenr	fra->fra_age = fr_defaultauthage;
32580482Sdarrenr	bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin));
326145522Sdarrenr#if !defined(sparc) && !defined(m68k)
32753642Sguido	/*
32853642Sguido	 * No need to copyback here as we want to undo the changes, not keep
32953642Sguido	 * them.
33053642Sguido	 */
331145522Sdarrenr	ip = fin->fin_ip;
332145522Sdarrenr# if defined(MENTAT) && defined(_KERNEL)
333145522Sdarrenr	if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4))
334145522Sdarrenr# endif
33553642Sguido	{
33653642Sguido		register u_short bo;
33753642Sguido
33853642Sguido		bo = ip->ip_len;
33953642Sguido		ip->ip_len = htons(bo);
34053642Sguido		bo = ip->ip_off;
34153642Sguido		ip->ip_off = htons(bo);
34253642Sguido	}
343145522Sdarrenr#endif
344145522Sdarrenr#if SOLARIS && defined(_KERNEL)
345145522Sdarrenr	m->b_rptr -= qpi->qpi_off;
34653642Sguido	fr_authpkts[i] = *(mblk_t **)fin->fin_mp;
347145522Sdarrenr	fra->fra_q = qpi->qpi_q;	/* The queue can disappear! */
34853642Sguido	cv_signal(&ipfauthwait);
34953642Sguido#else
35080482Sdarrenr# if defined(BSD) && !defined(sparc) && (BSD >= 199306)
351145522Sdarrenr	if (!fin->fin_out) {
352110915Sdarrenr		ip->ip_len = htons(ip->ip_len);
353110915Sdarrenr		ip->ip_off = htons(ip->ip_off);
35480482Sdarrenr	}
35580482Sdarrenr# endif
35653642Sguido	fr_authpkts[i] = m;
357145522Sdarrenr	WAKEUP(&fr_authnext,0);
35853642Sguido#endif
35953642Sguido	return 1;
36053642Sguido}
36153642Sguido
36253642Sguido
363145522Sdarrenrint fr_auth_ioctl(data, cmd, mode)
36453642Sguidocaddr_t data;
365145522Sdarrenrioctlcmd_t cmd;
36680482Sdarrenrint mode;
36753642Sguido{
36853642Sguido	mb_t *m;
369145522Sdarrenr#if defined(_KERNEL) && !defined(MENTAT) && !defined(linux) && \
370145522Sdarrenr    (!defined(__FreeBSD_version) || (__FreeBSD_version < 501000))
371130886Sdarrenr	struct ifqueue *ifq;
372145522Sdarrenr# ifdef USE_SPL
37380482Sdarrenr	int s;
374145522Sdarrenr# endif /* USE_SPL */
37580482Sdarrenr#endif
37680482Sdarrenr	frauth_t auth, *au = &auth, *fra;
377145522Sdarrenr	int i, error = 0, len;
378145522Sdarrenr	char *t;
37953642Sguido
38053642Sguido	switch (cmd)
38153642Sguido	{
38260850Sdarrenr	case SIOCSTLCK :
383110915Sdarrenr		if (!(mode & FWRITE)) {
384110915Sdarrenr			error = EPERM;
385110915Sdarrenr			break;
386110915Sdarrenr		}
387145522Sdarrenr		fr_lock(data, &fr_auth_lock);
38860850Sdarrenr		break;
389145522Sdarrenr
39053642Sguido	case SIOCATHST:
39153642Sguido		fr_authstats.fas_faelist = fae_list;
392145522Sdarrenr		error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT);
39353642Sguido		break;
394145522Sdarrenr
395145522Sdarrenr	case SIOCIPFFL:
396145522Sdarrenr		SPL_NET(s);
397145522Sdarrenr		WRITE_ENTER(&ipf_auth);
398145522Sdarrenr		i = fr_authflush();
399145522Sdarrenr		RWLOCK_EXIT(&ipf_auth);
400145522Sdarrenr		SPL_X(s);
401145522Sdarrenr		error = copyoutptr((char *)&i, data, sizeof(i));
402145522Sdarrenr		break;
403145522Sdarrenr
40453642Sguido	case SIOCAUTHW:
40553642Sguidofr_authioctlloop:
406145522Sdarrenr		error = fr_inobj(data, au, IPFOBJ_FRAUTH);
40753642Sguido		READ_ENTER(&ipf_auth);
40853642Sguido		if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) {
409145522Sdarrenr			error = fr_outobj(data, &fr_auth[fr_authnext],
410145522Sdarrenr					  IPFOBJ_FRAUTH);
411145522Sdarrenr			if (auth.fra_len != 0 && auth.fra_buf != NULL) {
412145522Sdarrenr				/*
413145522Sdarrenr				 * Copy packet contents out to user space if
414145522Sdarrenr				 * requested.  Bail on an error.
415145522Sdarrenr				 */
416145522Sdarrenr				m = fr_authpkts[fr_authnext];
417145522Sdarrenr				len = MSGDSIZE(m);
418145522Sdarrenr				if (len > auth.fra_len)
419145522Sdarrenr					len = auth.fra_len;
420145522Sdarrenr				auth.fra_len = len;
421145522Sdarrenr				for (t = auth.fra_buf; m && (len > 0); ) {
422145522Sdarrenr					i = MIN(M_LEN(m), len);
423145522Sdarrenr					error = copyoutptr(MTOD(m, char *),
424145522Sdarrenr							  t, i);
425145522Sdarrenr					len -= i;
426145522Sdarrenr					t += i;
427145522Sdarrenr					if (error != 0)
428145522Sdarrenr						break;
429145522Sdarrenr				}
430145522Sdarrenr			}
43153642Sguido			RWLOCK_EXIT(&ipf_auth);
432145522Sdarrenr			if (error != 0)
43360850Sdarrenr				break;
434145522Sdarrenr			SPL_NET(s);
43553642Sguido			WRITE_ENTER(&ipf_auth);
43653642Sguido			fr_authnext++;
437145522Sdarrenr			if (fr_authnext == fr_authsize)
43853642Sguido				fr_authnext = 0;
439145522Sdarrenr			RWLOCK_EXIT(&ipf_auth);
44080482Sdarrenr			SPL_X(s);
44153642Sguido			return 0;
44253642Sguido		}
44395418Sdarrenr		RWLOCK_EXIT(&ipf_auth);
444145522Sdarrenr		/*
445145522Sdarrenr		 * We exit ipf_global here because a program that enters in
446145522Sdarrenr		 * here will have a lock on it and goto sleep having this lock.
447145522Sdarrenr		 * If someone were to do an 'ipf -D' the system would then
448145522Sdarrenr		 * deadlock.  The catch with releasing it here is that the
449145522Sdarrenr		 * caller of this function expects it to be held when we
450145522Sdarrenr		 * return so we have to reacquire it in here.
451145522Sdarrenr		 */
452145522Sdarrenr		RWLOCK_EXIT(&ipf_global);
453145522Sdarrenr
454145522Sdarrenr		MUTEX_ENTER(&ipf_authmx);
45553642Sguido#ifdef	_KERNEL
45653642Sguido# if	SOLARIS
457145522Sdarrenr		error = 0;
458145522Sdarrenr		if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk))
459145522Sdarrenr			error = EINTR;
460145522Sdarrenr# else /* SOLARIS */
461145522Sdarrenr#  ifdef __hpux
462145522Sdarrenr		{
463145522Sdarrenr		lock_t *l;
464145522Sdarrenr
465145522Sdarrenr		l = get_sleep_lock(&fr_authnext);
466145522Sdarrenr		error = sleep(&fr_authnext, PZERO+1);
467145522Sdarrenr		spinunlock(l);
46853642Sguido		}
469145522Sdarrenr#  else
470145522Sdarrenr#   ifdef __osf__
471145522Sdarrenr		error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0,
472145522Sdarrenr				&ipf_authmx, MS_LOCK_SIMPLE);
473145522Sdarrenr#   else
47453642Sguido		error = SLEEP(&fr_authnext, "fr_authnext");
475145522Sdarrenr#   endif /* __osf__ */
476145522Sdarrenr#  endif /* __hpux */
477145522Sdarrenr# endif /* SOLARIS */
47853642Sguido#endif
479145522Sdarrenr		MUTEX_EXIT(&ipf_authmx);
480145522Sdarrenr		READ_ENTER(&ipf_global);
481145522Sdarrenr		if (error == 0) {
482145522Sdarrenr			READ_ENTER(&ipf_auth);
48353642Sguido			goto fr_authioctlloop;
484145522Sdarrenr		}
48553642Sguido		break;
486145522Sdarrenr
48753642Sguido	case SIOCAUTHR:
488145522Sdarrenr		error = fr_inobj(data, &auth, IPFOBJ_FRAUTH);
489145522Sdarrenr		if (error != 0)
49060850Sdarrenr			return error;
491145522Sdarrenr		SPL_NET(s);
49253642Sguido		WRITE_ENTER(&ipf_auth);
49360857Sdarrenr		i = au->fra_index;
49480482Sdarrenr		fra = fr_auth + i;
495145522Sdarrenr		if ((i < 0) || (i >= fr_authsize) ||
49680482Sdarrenr		    (fra->fra_info.fin_id != au->fra_info.fin_id)) {
497145522Sdarrenr			RWLOCK_EXIT(&ipf_auth);
49880482Sdarrenr			SPL_X(s);
499145522Sdarrenr			return ESRCH;
50053642Sguido		}
50153642Sguido		m = fr_authpkts[i];
50280482Sdarrenr		fra->fra_index = -2;
50380482Sdarrenr		fra->fra_pass = au->fra_pass;
50453642Sguido		fr_authpkts[i] = NULL;
50580482Sdarrenr		RWLOCK_EXIT(&ipf_auth);
50653642Sguido#ifdef	_KERNEL
507145522Sdarrenr		if ((m != NULL) && (au->fra_info.fin_out != 0)) {
508145522Sdarrenr# ifdef MENTAT
509145522Sdarrenr			error = !putq(fra->fra_q, m);
510145522Sdarrenr# else /* MENTAT */
511145522Sdarrenr#  ifdef linux
512145522Sdarrenr#  else
513145522Sdarrenr#   if (_BSDI_VERSION >= 199802) || defined(__OpenBSD__) || \
514145522Sdarrenr       (defined(__sgi) && (IRIX >= 60500) || \
515145522Sdarrenr       (defined(__FreeBSD__) && (__FreeBSD_version >= 470102)))
516145522Sdarrenr			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL,
51753642Sguido					  NULL);
518145522Sdarrenr#   else
519145522Sdarrenr			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL);
520145522Sdarrenr#   endif
521145522Sdarrenr#  endif /* Linux */
522145522Sdarrenr# endif /* MENTAT */
523145522Sdarrenr			if (error != 0)
52453642Sguido				fr_authstats.fas_sendfail++;
52553642Sguido			else
52653642Sguido				fr_authstats.fas_sendok++;
52753642Sguido		} else if (m) {
528145522Sdarrenr# ifdef MENTAT
529145522Sdarrenr			error = !putq(fra->fra_q, m);
530145522Sdarrenr# else /* MENTAT */
531145522Sdarrenr#  ifdef linux
532145522Sdarrenr#  else
533145522Sdarrenr#   if __FreeBSD_version >= 501000
534145522Sdarrenr			netisr_dispatch(NETISR_IP, m);
535145522Sdarrenr#   else
536145522Sdarrenr#    if IRIX >= 60516
537145522Sdarrenr			ifq = &((struct ifnet *)fra->fra_info.fin_ifp)->if_snd;
538145522Sdarrenr#    else
539145522Sdarrenr			ifq = &ipintrq;
540145522Sdarrenr#    endif
541145522Sdarrenr			if (IF_QFULL(ifq)) {
542145522Sdarrenr				IF_DROP(ifq);
543145522Sdarrenr				FREE_MB_T(m);
54453642Sguido				error = ENOBUFS;
545145522Sdarrenr			} else {
546145522Sdarrenr				IF_ENQUEUE(ifq, m);
547145522Sdarrenr#    if IRIX < 60500
548145522Sdarrenr				schednetisr(NETISR_IP);
549145522Sdarrenr#    endif
550145522Sdarrenr			}
551145522Sdarrenr#   endif
552145522Sdarrenr#  endif /* Linux */
553145522Sdarrenr# endif /* MENTAT */
554145522Sdarrenr			if (error != 0)
55553642Sguido				fr_authstats.fas_quefail++;
55653642Sguido			else
55753642Sguido				fr_authstats.fas_queok++;
55853642Sguido		} else
55953642Sguido			error = EINVAL;
560145522Sdarrenr# ifdef MENTAT
561145522Sdarrenr		if (error != 0)
56253642Sguido			error = EINVAL;
563145522Sdarrenr# else /* MENTAT */
56453642Sguido		/*
56553642Sguido		 * If we experience an error which will result in the packet
56653642Sguido		 * not being processed, make sure we advance to the next one.
567145522Sdarrenr		 */
56853642Sguido		if (error == ENOBUFS) {
56953642Sguido			fr_authused--;
57080482Sdarrenr			fra->fra_index = -1;
57180482Sdarrenr			fra->fra_pass = 0;
57253642Sguido			if (i == fr_authstart) {
57380482Sdarrenr				while (fra->fra_index == -1) {
57453642Sguido					i++;
575145522Sdarrenr					if (i == fr_authsize)
57653642Sguido						i = 0;
57753642Sguido					fr_authstart = i;
57853642Sguido					if (i == fr_authend)
57953642Sguido						break;
58053642Sguido				}
58153642Sguido				if (fr_authstart == fr_authend) {
58253642Sguido					fr_authnext = 0;
58353642Sguido					fr_authstart = fr_authend = 0;
58453642Sguido				}
58553642Sguido			}
58653642Sguido		}
587145522Sdarrenr# endif /* MENTAT */
58853642Sguido#endif /* _KERNEL */
58980482Sdarrenr		SPL_X(s);
59053642Sguido		break;
591145522Sdarrenr
59253642Sguido	default :
59353642Sguido		error = EINVAL;
59453642Sguido		break;
59553642Sguido	}
59653642Sguido	return error;
59753642Sguido}
59853642Sguido
59953642Sguido
60053642Sguido/*
60153642Sguido * Free all network buffer memory used to keep saved packets.
60253642Sguido */
60353642Sguidovoid fr_authunload()
60453642Sguido{
60553642Sguido	register int i;
60653642Sguido	register frauthent_t *fae, **faep;
60780482Sdarrenr	frentry_t *fr, **frp;
60853642Sguido	mb_t *m;
60953642Sguido
610145522Sdarrenr	if (fr_auth != NULL) {
611145522Sdarrenr		KFREES(fr_auth, fr_authsize * sizeof(*fr_auth));
612145522Sdarrenr		fr_auth = NULL;
613145522Sdarrenr	}
614145522Sdarrenr
615145522Sdarrenr	if (fr_authpkts != NULL) {
616145522Sdarrenr		for (i = 0; i < fr_authsize; i++) {
617145522Sdarrenr			m = fr_authpkts[i];
618145522Sdarrenr			if (m != NULL) {
619145522Sdarrenr				FREE_MB_T(m);
620145522Sdarrenr				fr_authpkts[i] = NULL;
621145522Sdarrenr			}
62253642Sguido		}
623145522Sdarrenr		KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
624145522Sdarrenr		fr_authpkts = NULL;
62553642Sguido	}
62653642Sguido
627145522Sdarrenr	faep = &fae_list;
628145522Sdarrenr	while ((fae = *faep) != NULL) {
62953642Sguido		*faep = fae->fae_next;
63053642Sguido		KFREE(fae);
63153642Sguido	}
63253642Sguido	ipauth = NULL;
63380482Sdarrenr
634145522Sdarrenr	if (fr_authlist != NULL) {
635145522Sdarrenr		for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
63680482Sdarrenr			if (fr->fr_ref == 1) {
63780482Sdarrenr				*frp = fr->fr_next;
63880482Sdarrenr				KFREE(fr);
63980482Sdarrenr			} else
64080482Sdarrenr				frp = &fr->fr_next;
64180482Sdarrenr		}
64280482Sdarrenr	}
643145522Sdarrenr
644145522Sdarrenr	if (fr_auth_init == 1) {
645145522Sdarrenr# if SOLARIS && defined(_KERNEL)
646145522Sdarrenr		cv_destroy(&ipfauthwait);
647145522Sdarrenr# endif
648145522Sdarrenr		MUTEX_DESTROY(&ipf_authmx);
649145522Sdarrenr		RW_DESTROY(&ipf_auth);
650145522Sdarrenr
651145522Sdarrenr		fr_auth_init = 0;
652145522Sdarrenr	}
65353642Sguido}
65453642Sguido
65553642Sguido
65653642Sguido/*
65753642Sguido * Slowly expire held auth records.  Timeouts are set
65853642Sguido * in expectation of this being called twice per second.
65953642Sguido */
66053642Sguidovoid fr_authexpire()
66153642Sguido{
66253642Sguido	register int i;
66353642Sguido	register frauth_t *fra;
66453642Sguido	register frauthent_t *fae, **faep;
66580482Sdarrenr	register frentry_t *fr, **frp;
66653642Sguido	mb_t *m;
667145522Sdarrenr# if !defined(MENAT) && defined(_KERNEL) && defined(USE_SPL)
66853642Sguido	int s;
669145522Sdarrenr# endif
67053642Sguido
67160850Sdarrenr	if (fr_auth_lock)
67260850Sdarrenr		return;
67360850Sdarrenr
67453642Sguido	SPL_NET(s);
67553642Sguido	WRITE_ENTER(&ipf_auth);
676145522Sdarrenr	for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) {
677145522Sdarrenr		fra->fra_age--;
678145522Sdarrenr		if ((fra->fra_age == 0) && (m = fr_authpkts[i])) {
67953642Sguido			FREE_MB_T(m);
68053642Sguido			fr_authpkts[i] = NULL;
68153642Sguido			fr_auth[i].fra_index = -1;
68253642Sguido			fr_authstats.fas_expire++;
68353642Sguido			fr_authused--;
68453642Sguido		}
68553642Sguido	}
68653642Sguido
687145522Sdarrenr	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
688145522Sdarrenr		fae->fae_age--;
689145522Sdarrenr		if (fae->fae_age == 0) {
69053642Sguido			*faep = fae->fae_next;
69153642Sguido			KFREE(fae);
69253642Sguido			fr_authstats.fas_expire++;
69353642Sguido		} else
69453642Sguido			faep = &fae->fae_next;
69553642Sguido	}
69698004Sdarrenr	if (fae_list != NULL)
69798004Sdarrenr		ipauth = &fae_list->fae_fr;
69898004Sdarrenr	else
69998004Sdarrenr		ipauth = NULL;
70080482Sdarrenr
701145522Sdarrenr	for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
70280482Sdarrenr		if (fr->fr_ref == 1) {
70380482Sdarrenr			*frp = fr->fr_next;
70480482Sdarrenr			KFREE(fr);
70580482Sdarrenr		} else
70680482Sdarrenr			frp = &fr->fr_next;
70780482Sdarrenr	}
70853642Sguido	RWLOCK_EXIT(&ipf_auth);
70953642Sguido	SPL_X(s);
71053642Sguido}
711110915Sdarrenr
712110915Sdarrenrint fr_preauthcmd(cmd, fr, frptr)
713145522Sdarrenrioctlcmd_t cmd;
714110915Sdarrenrfrentry_t *fr, **frptr;
715110915Sdarrenr{
716110915Sdarrenr	frauthent_t *fae, **faep;
717110915Sdarrenr	int error = 0;
718145522Sdarrenr# if !defined(MENAT) && defined(_KERNEL) && defined(USE_SPL)
719110915Sdarrenr	int s;
720110915Sdarrenr#endif
721110915Sdarrenr
722145522Sdarrenr	if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR))
723110915Sdarrenr		return EIO;
724110915Sdarrenr
725145522Sdarrenr	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
726110915Sdarrenr		if (&fae->fae_fr == fr)
727110915Sdarrenr			break;
728110915Sdarrenr		else
729110915Sdarrenr			faep = &fae->fae_next;
730145522Sdarrenr	}
731145522Sdarrenr
732145522Sdarrenr	if (cmd == (ioctlcmd_t)SIOCRMAFR) {
733145522Sdarrenr		if (fr == NULL || frptr == NULL)
734110915Sdarrenr			error = EINVAL;
735145522Sdarrenr		else if (fae == NULL)
736110915Sdarrenr			error = ESRCH;
737110915Sdarrenr		else {
738145522Sdarrenr			SPL_NET(s);
739110915Sdarrenr			WRITE_ENTER(&ipf_auth);
740110915Sdarrenr			*faep = fae->fae_next;
741145522Sdarrenr			if (ipauth == &fae->fae_fr)
742145522Sdarrenr				ipauth = fae_list ? &fae_list->fae_fr : NULL;
743145522Sdarrenr			RWLOCK_EXIT(&ipf_auth);
744110915Sdarrenr			SPL_X(s);
745145522Sdarrenr
746110915Sdarrenr			KFREE(fae);
747110915Sdarrenr		}
748145522Sdarrenr	} else if (fr != NULL && frptr != NULL) {
749110915Sdarrenr		KMALLOC(fae, frauthent_t *);
750110915Sdarrenr		if (fae != NULL) {
751110915Sdarrenr			bcopy((char *)fr, (char *)&fae->fae_fr,
752110915Sdarrenr			      sizeof(*fr));
753145522Sdarrenr			SPL_NET(s);
754110915Sdarrenr			WRITE_ENTER(&ipf_auth);
755110915Sdarrenr			fae->fae_age = fr_defaultauthage;
756110915Sdarrenr			fae->fae_fr.fr_hits = 0;
757110915Sdarrenr			fae->fae_fr.fr_next = *frptr;
758110915Sdarrenr			*frptr = &fae->fae_fr;
759110915Sdarrenr			fae->fae_next = *faep;
760110915Sdarrenr			*faep = fae;
761110915Sdarrenr			ipauth = &fae_list->fae_fr;
762145522Sdarrenr			RWLOCK_EXIT(&ipf_auth);
763110915Sdarrenr			SPL_X(s);
764110915Sdarrenr		} else
765110915Sdarrenr			error = ENOMEM;
766110915Sdarrenr	} else
767110915Sdarrenr		error = EINVAL;
768110915Sdarrenr	return error;
769110915Sdarrenr}
770145522Sdarrenr
771145522Sdarrenr
772145522Sdarrenr/*
773145522Sdarrenr * Flush held packets.
774145522Sdarrenr * Must already be properly SPL'ed and Locked on &ipf_auth.
775145522Sdarrenr *
776145522Sdarrenr */
777145522Sdarrenrint fr_authflush()
778145522Sdarrenr{
779145522Sdarrenr	register int i, num_flushed;
780145522Sdarrenr	mb_t *m;
781145522Sdarrenr
782145522Sdarrenr	if (fr_auth_lock)
783145522Sdarrenr		return -1;
784145522Sdarrenr
785145522Sdarrenr	num_flushed = 0;
786145522Sdarrenr
787145522Sdarrenr	for (i = 0 ; i < fr_authsize; i++) {
788145522Sdarrenr		m = fr_authpkts[i];
789145522Sdarrenr		if (m != NULL) {
790145522Sdarrenr			FREE_MB_T(m);
791145522Sdarrenr			fr_authpkts[i] = NULL;
792145522Sdarrenr			fr_auth[i].fra_index = -1;
793145522Sdarrenr			/* perhaps add & use a flush counter inst.*/
794145522Sdarrenr			fr_authstats.fas_expire++;
795145522Sdarrenr			fr_authused--;
796145522Sdarrenr			num_flushed++;
797145522Sdarrenr		}
798145522Sdarrenr	}
799145522Sdarrenr
800145522Sdarrenr	fr_authstart = 0;
801145522Sdarrenr	fr_authend = 0;
802145522Sdarrenr	fr_authnext = 0;
803145522Sdarrenr
804145522Sdarrenr	return num_flushed;
805145522Sdarrenr}
806