xref: /illumos-gate/usr/src/uts/common/inet/ipf/ip_auth.c (revision 7c478bd9)
1 /*
2  * Copyright (C) 1998-2003 by Darren Reed & Guido van Rooij.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  */
6 #if defined(KERNEL) || defined(_KERNEL)
7 # undef KERNEL
8 # undef _KERNEL
9 # define        KERNEL	1
10 # define        _KERNEL	1
11 #endif
12 #include <sys/errno.h>
13 #include <sys/types.h>
14 #include <sys/param.h>
15 #include <sys/time.h>
16 #include <sys/file.h>
17 #if !defined(_KERNEL)
18 # include <stdio.h>
19 # include <stdlib.h>
20 # include <string.h>
21 # define _KERNEL
22 # ifdef __OpenBSD__
23 struct file;
24 # endif
25 # include <sys/uio.h>
26 # undef _KERNEL
27 #endif
28 #if defined(_KERNEL) && (__FreeBSD_version >= 220000)
29 # include <sys/filio.h>
30 # include <sys/fcntl.h>
31 #else
32 # include <sys/ioctl.h>
33 #endif
34 #include <sys/protosw.h>
35 #include <sys/socket.h>
36 #if defined(_KERNEL)
37 # include <sys/systm.h>
38 # if !defined(__SVR4) && !defined(__svr4__)
39 #  include <sys/mbuf.h>
40 # endif
41 #endif
42 #if defined(__SVR4) || defined(__svr4__)
43 # include <sys/filio.h>
44 # include <sys/byteorder.h>
45 # ifdef _KERNEL
46 #  include <sys/dditypes.h>
47 # endif
48 # include <sys/stream.h>
49 # include <sys/kmem.h>
50 #endif
51 #if (_BSDI_VERSION >= 199802) || (__FreeBSD_version >= 400000)
52 # include <sys/queue.h>
53 #endif
54 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi)
55 # include <machine/cpu.h>
56 #endif
57 #if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000)
58 # include <sys/proc.h>
59 #endif
60 #include <net/if.h>
61 #ifdef sun
62 # include <net/af.h>
63 #endif
64 #include <net/route.h>
65 #include <netinet/in.h>
66 #include <netinet/in_systm.h>
67 #include <netinet/ip.h>
68 #if !defined(_KERNEL) && !defined(__osf__)
69 # define	KERNEL
70 # define	_KERNEL
71 # define	NOT_KERNEL
72 #endif
73 #include <netinet/ip_var.h>
74 #ifdef	NOT_KERNEL
75 # undef	_KERNEL
76 # undef	KERNEL
77 #endif
78 #ifdef __sgi
79 # ifdef IFF_DRVRLOCK /* IRIX6 */
80 #  include <sys/hashing.h>
81 # endif
82 #endif
83 #include <netinet/tcp.h>
84 #if defined(__sgi) && !defined(IFF_DRVRLOCK) /* IRIX < 6 */
85 extern struct ifqueue   ipintrq;		/* ip packet input queue */
86 #else
87 # if !defined(__hpux)
88 #  if __FreeBSD_version >= 300000
89 #   include <net/if_var.h>
90 #   if __FreeBSD_version >= 500042
91 #    define IF_QFULL _IF_QFULL
92 #    define IF_DROP _IF_DROP
93 #   endif /* __FreeBSD_version >= 500042 */
94 #  endif
95 #  include <netinet/in_var.h>
96 #  include <netinet/tcp_fsm.h>
97 # endif
98 #endif
99 #include <netinet/udp.h>
100 #include <netinet/ip_icmp.h>
101 #include <netinet/tcpip.h>
102 #if SOLARIS2 >= 10
103 #include "ip_compat.h"
104 #include "ip_fil.h"
105 #include "ip_auth.h"
106 #else
107 #include "netinet/ip_compat.h"
108 #include "netinet/ip_fil.h"
109 #include "netinet/ip_auth.h"
110 #endif
111 #if !defined(MENTAT)
112 # include <net/netisr.h>
113 # ifdef __FreeBSD__
114 #  include <machine/cpufunc.h>
115 # endif
116 #endif
117 #if (__FreeBSD_version >= 300000)
118 # include <sys/malloc.h>
119 # if defined(_KERNEL) && !defined(IPFILTER_LKM)
120 #  include <sys/libkern.h>
121 #  include <sys/systm.h>
122 # endif
123 #endif
124 
125 #if !defined(lint)
126 static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.56 2003/06/28 17:01:55 darrenr Exp $";
127 #endif
128 
129 
130 #ifdef USE_MUTEXES
131 extern ipfrwlock_t ipf_auth;
132 extern ipfmutex_t ipf_authmx;
133 # if SOLARIS
134 extern kcondvar_t ipfauthwait;
135 # endif /* SOLARIS */
136 #endif /* USE_MUTEXES */
137 
138 int	fr_authsize = FR_NUMAUTH;
139 int	fr_authused = 0;
140 int	fr_defaultauthage = 600;
141 int	fr_auth_lock = 0;
142 int	fr_auth_init = 0;
143 fr_authstat_t	fr_authstats;
144 static frauth_t *fr_auth = NULL;
145 mb_t	**fr_authpkts = NULL;
146 int	fr_authstart = 0, fr_authend = 0, fr_authnext = 0;
147 frauthent_t	*fae_list = NULL;
148 frentry_t	*ipauth = NULL,
149 		*fr_authlist = NULL;
150 
151 
152 int fr_authinit()
153 {
154 	KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth));
155 	if (fr_auth != NULL)
156 		bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth));
157 	else
158 		return -1;
159 
160 	KMALLOCS(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts));
161 	if (fr_authpkts != NULL)
162 		bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
163 	else
164 		return -1;
165 
166 	MUTEX_INIT(&ipf_authmx, "ipf auth log mutex");
167 	RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock");
168 #if SOLARIS && defined(_KERNEL)
169 	cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL);
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  */
183 frentry_t *fr_checkauth(fin, passp)
184 fr_info_t *fin;
185 u_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_INCL(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_INCL(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  */
286 int fr_newauth(m, fin)
287 mb_t *m;
288 fr_info_t *fin;
289 {
290 #if defined(_KERNEL) && defined(MENTAT)
291 	qif_t *qif = fin->fin_qif;
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 -= qif->qf_off;
347 	fr_authpkts[i] = *(mblk_t **)fin->fin_mp;
348 	fra->fra_q = qif->qf_q;
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);
359 #endif
360 	return 1;
361 }
362 
363 
364 int fr_auth_ioctl(data, cmd, mode)
365 caddr_t data;
366 #if defined(__NetBSD__) || defined(__OpenBSD__) || \
367     (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300003)
368 u_long cmd;
369 #else
370 int cmd;
371 #endif
372 int mode;
373 {
374 	mb_t *m;
375 #if defined(_KERNEL) && !defined(MENTAT)
376 	struct ifqueue *ifq;
377 # ifdef USE_SPL
378 	int s;
379 # endif /* USE_SPL */
380 #endif
381 	frauth_t auth, *au = &auth, *fra;
382 	int i, error = 0, len;
383 	char *t;
384 
385 	switch (cmd)
386 	{
387 	case SIOCSTLCK :
388 		if (!(mode & FWRITE)) {
389 			error = EPERM;
390 			break;
391 		}
392 		error = fr_lock(data, &fr_auth_lock);
393 		break;
394 
395 	case SIOCATHST:
396 		fr_authstats.fas_faelist = fae_list;
397 		error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT);
398 		break;
399 
400 	case SIOCIPFFL:
401 		SPL_NET(s);
402 		WRITE_ENTER(&ipf_auth);
403 		i = fr_authflush();
404 		RWLOCK_EXIT(&ipf_auth);
405 		SPL_X(s);
406 		error = copyoutptr((char *)&i, data, sizeof(i));
407 		break;
408 
409 	case SIOCAUTHW:
410 fr_authioctlloop:
411 		error = fr_inobj(data, au, IPFOBJ_FRAUTH);
412 		READ_ENTER(&ipf_auth);
413 		if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) {
414 			error = fr_outobj(data, &fr_auth[fr_authnext],
415 					  IPFOBJ_FRAUTH);
416 			if (auth.fra_len != 0 && auth.fra_buf != NULL) {
417 				/*
418 				 * Copy packet contents out to user space if
419 				 * requested.  Bail on an error.
420 				 */
421 				m = fr_authpkts[fr_authnext];
422 				len = MSGDSIZE(m);
423 				if (len > auth.fra_len)
424 					len = auth.fra_len;
425 				auth.fra_len = len;
426 				for (t = auth.fra_buf; m && (len > 0); ) {
427 					i = MIN(M_LEN(m), len);
428 					error = copyoutptr(MTOD(m, char *),
429 							  t, i);
430 					len -= i;
431 					t += i;
432 					if (error != 0)
433 						break;
434 				}
435 			}
436 			RWLOCK_EXIT(&ipf_auth);
437 			if (error != 0)
438 				break;
439 			SPL_NET(s);
440 			WRITE_ENTER(&ipf_auth);
441 			fr_authnext++;
442 			if (fr_authnext == fr_authsize)
443 				fr_authnext = 0;
444 			RWLOCK_EXIT(&ipf_auth);
445 			SPL_X(s);
446 			return 0;
447 		}
448 		RWLOCK_EXIT(&ipf_auth);
449 		/*
450 		 * We exit ipf_global here because a program that enters in
451 		 * here will have a lock on it and goto sleep having this lock.
452 		 * If someone were to do an 'ipf -D' the system would then
453 		 * deadlock.  The catch with releasing it here is that the
454 		 * caller of this function expects it to be held when we
455 		 * return so we have to reacquire it in here.
456 		 */
457 		RWLOCK_EXIT(&ipf_global);
458 
459 		MUTEX_ENTER(&ipf_authmx);
460 #ifdef	_KERNEL
461 # if	SOLARIS
462 		error = 0;
463 		if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk))
464 			error = EINTR;
465 # else /* SOLARIS */
466 #  ifdef __hpux
467 		{
468 		lock_t *l;
469 
470 		l = get_sleep_lock(&fr_authnext);
471 		error = sleep(&fr_authnext, PZERO+1);
472 		spinunlock(l);
473 		}
474 #  else
475 #   ifdef __osf__
476 		error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0,
477 				&ipf_authmx, MS_LOCK_SIMPLE);
478 #   else
479 		error = SLEEP(&fr_authnext, "fr_authnext");
480 #   endif /* __osf__ */
481 #  endif /* __hpux */
482 # endif /* SOLARIS */
483 #endif
484 		MUTEX_EXIT(&ipf_authmx);
485 		READ_ENTER(&ipf_global);
486 		if (error == 0) {
487 			READ_ENTER(&ipf_auth);
488 			goto fr_authioctlloop;
489 		}
490 		break;
491 
492 	case SIOCAUTHR:
493 		error = fr_inobj(data, &auth, IPFOBJ_FRAUTH);
494 		if (error != 0)
495 			return error;
496 		SPL_NET(s);
497 		WRITE_ENTER(&ipf_auth);
498 		i = au->fra_index;
499 		fra = fr_auth + i;
500 		if ((i < 0) || (i >= fr_authsize) ||
501 		    (fra->fra_info.fin_id != au->fra_info.fin_id)) {
502 			RWLOCK_EXIT(&ipf_auth);
503 			SPL_X(s);
504 			return ESRCH;
505 		}
506 		m = fr_authpkts[i];
507 		fra->fra_index = -2;
508 		fra->fra_pass = au->fra_pass;
509 		fr_authpkts[i] = NULL;
510 		RWLOCK_EXIT(&ipf_auth);
511 #ifdef	_KERNEL
512 		if ((m != NULL) && (au->fra_info.fin_out != 0)) {
513 # ifdef MENTAT
514 			error = !putq(fra->fra_q, m);
515 # else /* MENTAT */
516 #  if (_BSDI_VERSION >= 199802) || defined(__OpenBSD__) || \
517       (defined(__sgi) && (IRIX >= 605) || \
518       (defined(__FreeBSD__) && (__FreeBSD_version >= 500043)))
519 			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL,
520 					  NULL);
521 #  else
522 			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL);
523 #  endif
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 			ifq = &ipintrq;
534 			if (IF_QFULL(ifq)) {
535 				IF_DROP(ifq);
536 				m_freem(m);
537 				error = ENOBUFS;
538 			} else {
539 				IF_ENQUEUE(ifq, m);
540 #  if IRIX < 605
541 				schednetisr(NETISR_IP);
542 #  endif
543 			}
544 # endif /* MENTAT */
545 			if (error != 0)
546 				fr_authstats.fas_quefail++;
547 			else
548 				fr_authstats.fas_queok++;
549 		} else
550 			error = EINVAL;
551 # ifdef MENTAT
552 		if (error != 0)
553 			error = EINVAL;
554 # else /* MENTAT */
555 		/*
556 		 * If we experience an error which will result in the packet
557 		 * not being processed, make sure we advance to the next one.
558 		 */
559 		if (error == ENOBUFS) {
560 			fr_authused--;
561 			fra->fra_index = -1;
562 			fra->fra_pass = 0;
563 			if (i == fr_authstart) {
564 				while (fra->fra_index == -1) {
565 					i++;
566 					if (i == fr_authsize)
567 						i = 0;
568 					fr_authstart = i;
569 					if (i == fr_authend)
570 						break;
571 				}
572 				if (fr_authstart == fr_authend) {
573 					fr_authnext = 0;
574 					fr_authstart = fr_authend = 0;
575 				}
576 			}
577 		}
578 # endif /* MENTAT */
579 #endif /* _KERNEL */
580 		SPL_X(s);
581 		break;
582 
583 	default :
584 		error = EINVAL;
585 		break;
586 	}
587 	return error;
588 }
589 
590 
591 /*
592  * Free all network buffer memory used to keep saved packets.
593  */
594 void fr_authunload()
595 {
596 	register int i;
597 	register frauthent_t *fae, **faep;
598 	frentry_t *fr, **frp;
599 	mb_t *m;
600 
601 	if (fr_auth != NULL) {
602 		KFREES(fr_auth, fr_authsize * sizeof(*fr_auth));
603 		fr_auth = NULL;
604 	}
605 
606 	if (fr_authpkts != NULL) {
607 		for (i = 0; i < fr_authsize; i++) {
608 			m = fr_authpkts[i];
609 			if (m != NULL) {
610 				FREE_MB_T(m);
611 				fr_authpkts[i] = NULL;
612 			}
613 		}
614 		KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
615 		fr_authpkts = NULL;
616 	}
617 
618 	faep = &fae_list;
619 	while ((fae = *faep) != NULL) {
620 		*faep = fae->fae_next;
621 		KFREE(fae);
622 	}
623 	ipauth = NULL;
624 
625 	if (fr_authlist != NULL) {
626 		for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
627 			if (fr->fr_ref == 1) {
628 				*frp = fr->fr_next;
629 				KFREE(fr);
630 			} else
631 				frp = &fr->fr_next;
632 		}
633 	}
634 
635 	if (fr_auth_init == 1) {
636 # if SOLARIS && defined(_KERNEL)
637 		cv_destroy(&ipfauthwait);
638 # endif
639 		MUTEX_DESTROY(&ipf_authmx);
640 		RW_DESTROY(&ipf_auth);
641 
642 		fr_auth_init = 0;
643 	}
644 }
645 
646 
647 /*
648  * Slowly expire held auth records.  Timeouts are set
649  * in expectation of this being called twice per second.
650  */
651 void fr_authexpire()
652 {
653 	register int i;
654 	register frauth_t *fra;
655 	register frauthent_t *fae, **faep;
656 	register frentry_t *fr, **frp;
657 	mb_t *m;
658 # if !defined(MENAT) && defined(_KERNEL) && defined(USE_SPL)
659 	int s;
660 # endif
661 
662 	if (fr_auth_lock)
663 		return;
664 
665 	SPL_NET(s);
666 	WRITE_ENTER(&ipf_auth);
667 	for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) {
668 		fra->fra_age--;
669 		if ((fra->fra_age == 0) && (m = fr_authpkts[i])) {
670 			FREE_MB_T(m);
671 			fr_authpkts[i] = NULL;
672 			fr_auth[i].fra_index = -1;
673 			fr_authstats.fas_expire++;
674 			fr_authused--;
675 		}
676 	}
677 
678 	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
679 		fae->fae_age--;
680 		if (fae->fae_age == 0) {
681 			*faep = fae->fae_next;
682 			KFREE(fae);
683 			fr_authstats.fas_expire++;
684 		} else
685 			faep = &fae->fae_next;
686 	}
687 	if (fae_list != NULL)
688 		ipauth = &fae_list->fae_fr;
689 	else
690 		ipauth = NULL;
691 
692 	for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
693 		if (fr->fr_ref == 1) {
694 			*frp = fr->fr_next;
695 			KFREE(fr);
696 		} else
697 			frp = &fr->fr_next;
698 	}
699 	RWLOCK_EXIT(&ipf_auth);
700 	SPL_X(s);
701 }
702 
703 int fr_preauthcmd(cmd, fr, frptr)
704 #if defined(__NetBSD__) || defined(__OpenBSD__) || \
705 	(_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000)
706 u_long cmd;
707 #else
708 int cmd;
709 #endif
710 frentry_t *fr, **frptr;
711 {
712 	frauthent_t *fae, **faep;
713 	int error = 0;
714 # if !defined(MENAT) && defined(_KERNEL) && defined(USE_SPL)
715 	int s;
716 #endif
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 == 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  */
773 int 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