xref: /illumos-gate/usr/src/uts/common/inet/ipf/ip_auth.c (revision 0173c38a73f34277e0c97a19fedfd25d81ba8380)
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 #if !defined(linux)
35 # include <sys/protosw.h>
36 #endif
37 #include <sys/socket.h>
38 #if defined(_KERNEL)
39 # include <sys/systm.h>
40 # if !defined(__SVR4) && !defined(__svr4__) && !defined(linux)
41 #  include <sys/mbuf.h>
42 # endif
43 #endif
44 #if defined(__SVR4) || defined(__svr4__)
45 # include <sys/filio.h>
46 # include <sys/byteorder.h>
47 # ifdef _KERNEL
48 #  include <sys/dditypes.h>
49 # endif
50 # include <sys/stream.h>
51 # include <sys/kmem.h>
52 #endif
53 #if (_BSDI_VERSION >= 199802) || (__FreeBSD_version >= 400000)
54 # include <sys/queue.h>
55 #endif
56 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi)
57 # include <machine/cpu.h>
58 #endif
59 #if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000)
60 # include <sys/proc.h>
61 #endif
62 #include <net/if.h>
63 #ifdef sun
64 # include <net/af.h>
65 #endif
66 #include <net/route.h>
67 #include <netinet/in.h>
68 #include <netinet/in_systm.h>
69 #include <netinet/ip.h>
70 #if !defined(_KERNEL) && !defined(__osf__) && !defined(__sgi)
71 # define	KERNEL
72 # define	_KERNEL
73 # define	NOT_KERNEL
74 #endif
75 #if !defined(linux)
76 # include <netinet/ip_var.h>
77 #endif
78 #ifdef	NOT_KERNEL
79 # undef	_KERNEL
80 # undef	KERNEL
81 #endif
82 #include <netinet/tcp.h>
83 #if defined(IRIX) && (IRIX < 60516) /* IRIX < 6 */
84 extern struct ifqueue   ipintrq;		/* ip packet input queue */
85 #else
86 # if !defined(__hpux) && !defined(linux)
87 #  if __FreeBSD_version >= 300000
88 #   include <net/if_var.h>
89 #   if __FreeBSD_version >= 500042
90 #    define IF_QFULL _IF_QFULL
91 #    define IF_DROP _IF_DROP
92 #   endif /* __FreeBSD_version >= 500042 */
93 #  endif
94 #  include <netinet/in_var.h>
95 #  include <netinet/tcp_fsm.h>
96 # endif
97 #endif
98 #include <netinet/udp.h>
99 #include <netinet/ip_icmp.h>
100 #include "netinet/ip_compat.h"
101 #include <netinet/tcpip.h>
102 #include "netinet/ip_fil.h"
103 #include "netinet/ip_auth.h"
104 #if !defined(MENTAT) && !defined(linux)
105 # include <net/netisr.h>
106 # ifdef __FreeBSD__
107 #  include <machine/cpufunc.h>
108 # endif
109 #endif
110 #if (__FreeBSD_version >= 300000)
111 # include <sys/malloc.h>
112 # if defined(_KERNEL) && !defined(IPFILTER_LKM)
113 #  include <sys/libkern.h>
114 #  include <sys/systm.h>
115 # endif
116 #endif
117 /* END OF INCLUDES */
118 
119 #if !defined(lint)
120 static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.73.2.5 2005/06/12 07:18:14 darrenr Exp $";
121 #endif
122 
123 
124 #if SOLARIS
125 extern kcondvar_t ipfauthwait;
126 #endif /* SOLARIS */
127 #if defined(linux) && defined(_KERNEL)
128 wait_queue_head_t     fr_authnext_linux;
129 #endif
130 
131 int	fr_authsize = FR_NUMAUTH;
132 int	fr_authused = 0;
133 int	fr_defaultauthage = 600;
134 int	fr_auth_lock = 0;
135 int	fr_auth_init = 0;
136 fr_authstat_t	fr_authstats;
137 static frauth_t *fr_auth = NULL;
138 mb_t	**fr_authpkts = NULL;
139 int	fr_authstart = 0, fr_authend = 0, fr_authnext = 0;
140 frauthent_t	*fae_list = NULL;
141 frentry_t	*ipauth = NULL,
142 		*fr_authlist = NULL;
143 
144 
145 int fr_authinit()
146 {
147 	KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth));
148 	if (fr_auth != NULL)
149 		bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth));
150 	else
151 		return -1;
152 
153 	KMALLOCS(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts));
154 	if (fr_authpkts != NULL)
155 		bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
156 	else
157 		return -2;
158 
159 	MUTEX_INIT(&ipf_authmx, "ipf auth log mutex");
160 	RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock");
161 #if SOLARIS && defined(_KERNEL)
162 	cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL);
163 #endif
164 #if defined(linux) && defined(_KERNEL)
165 	init_waitqueue_head(&fr_authnext_linux);
166 #endif
167 
168 	fr_auth_init = 1;
169 
170 	return 0;
171 }
172 
173 
174 /*
175  * Check if a packet has authorization.  If the packet is found to match an
176  * authorization result and that would result in a feedback loop (i.e. it
177  * will end up returning FR_AUTH) then return FR_BLOCK instead.
178  */
179 frentry_t *fr_checkauth(fin, passp)
180 fr_info_t *fin;
181 u_32_t *passp;
182 {
183 	frentry_t *fr;
184 	frauth_t *fra;
185 	u_32_t pass;
186 	u_short id;
187 	ip_t *ip;
188 	int i;
189 
190 	if (fr_auth_lock || !fr_authused)
191 		return NULL;
192 
193 	ip = fin->fin_ip;
194 	id = ip->ip_id;
195 
196 	READ_ENTER(&ipf_auth);
197 	for (i = fr_authstart; i != fr_authend; ) {
198 		/*
199 		 * index becomes -2 only after an SIOCAUTHW.  Check this in
200 		 * case the same packet gets sent again and it hasn't yet been
201 		 * auth'd.
202 		 */
203 		fra = fr_auth + i;
204 		if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) &&
205 		    !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) {
206 			/*
207 			 * Avoid feedback loop.
208 			 */
209 			if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass)))
210 				pass = FR_BLOCK;
211 			/*
212 			 * Create a dummy rule for the stateful checking to
213 			 * use and return.  Zero out any values we don't
214 			 * trust from userland!
215 			 */
216 			if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) &&
217 			     (fin->fin_flx & FI_FRAG))) {
218 				KMALLOC(fr, frentry_t *);
219 				if (fr) {
220 					bcopy((char *)fra->fra_info.fin_fr,
221 					      (char *)fr, sizeof(*fr));
222 					fr->fr_grp = NULL;
223 					fr->fr_ifa = fin->fin_ifp;
224 					fr->fr_func = NULL;
225 					fr->fr_ref = 1;
226 					fr->fr_flags = pass;
227 					fr->fr_ifas[1] = NULL;
228 					fr->fr_ifas[2] = NULL;
229 					fr->fr_ifas[3] = NULL;
230 				}
231 			} else
232 				fr = fra->fra_info.fin_fr;
233 			fin->fin_fr = fr;
234 			RWLOCK_EXIT(&ipf_auth);
235 			WRITE_ENTER(&ipf_auth);
236 			if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) {
237 				fr->fr_next = fr_authlist;
238 				fr_authlist = fr;
239 			}
240 			fr_authstats.fas_hits++;
241 			fra->fra_index = -1;
242 			fr_authused--;
243 			if (i == fr_authstart) {
244 				while (fra->fra_index == -1) {
245 					i++;
246 					fra++;
247 					if (i == fr_authsize) {
248 						i = 0;
249 						fra = fr_auth;
250 					}
251 					fr_authstart = i;
252 					if (i == fr_authend)
253 						break;
254 				}
255 				if (fr_authstart == fr_authend) {
256 					fr_authnext = 0;
257 					fr_authstart = fr_authend = 0;
258 				}
259 			}
260 			RWLOCK_EXIT(&ipf_auth);
261 			if (passp != NULL)
262 				*passp = pass;
263 			ATOMIC_INC64(fr_authstats.fas_hits);
264 			return fr;
265 		}
266 		i++;
267 		if (i == fr_authsize)
268 			i = 0;
269 	}
270 	fr_authstats.fas_miss++;
271 	RWLOCK_EXIT(&ipf_auth);
272 	ATOMIC_INC64(fr_authstats.fas_miss);
273 	return NULL;
274 }
275 
276 
277 /*
278  * Check if we have room in the auth array to hold details for another packet.
279  * If we do, store it and wake up any user programs which are waiting to
280  * hear about these events.
281  */
282 int fr_newauth(m, fin)
283 mb_t *m;
284 fr_info_t *fin;
285 {
286 #if defined(_KERNEL) && defined(MENTAT)
287 	qpktinfo_t *qpi = fin->fin_qpi;
288 #endif
289 	frauth_t *fra;
290 #if !defined(sparc) && !defined(m68k)
291 	ip_t *ip;
292 #endif
293 	int i;
294 
295 	if (fr_auth_lock)
296 		return 0;
297 
298 	WRITE_ENTER(&ipf_auth);
299 	if (fr_authstart > fr_authend) {
300 		fr_authstats.fas_nospace++;
301 		RWLOCK_EXIT(&ipf_auth);
302 		return 0;
303 	} else {
304 		if (fr_authused == fr_authsize) {
305 			fr_authstats.fas_nospace++;
306 			RWLOCK_EXIT(&ipf_auth);
307 			return 0;
308 		}
309 	}
310 
311 	fr_authstats.fas_added++;
312 	fr_authused++;
313 	i = fr_authend++;
314 	if (fr_authend == fr_authsize)
315 		fr_authend = 0;
316 	RWLOCK_EXIT(&ipf_auth);
317 
318 	fra = fr_auth + i;
319 	fra->fra_index = i;
320 	fra->fra_pass = 0;
321 	fra->fra_age = fr_defaultauthage;
322 	bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin));
323 #if !defined(sparc) && !defined(m68k)
324 	/*
325 	 * No need to copyback here as we want to undo the changes, not keep
326 	 * them.
327 	 */
328 	ip = fin->fin_ip;
329 # if defined(MENTAT) && defined(_KERNEL)
330 	if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4))
331 # endif
332 	{
333 		register u_short bo;
334 
335 		bo = ip->ip_len;
336 		ip->ip_len = htons(bo);
337 		bo = ip->ip_off;
338 		ip->ip_off = htons(bo);
339 	}
340 #endif
341 #if SOLARIS && defined(_KERNEL)
342 	m->b_rptr -= qpi->qpi_off;
343 	fr_authpkts[i] = *(mblk_t **)fin->fin_mp;
344 	fra->fra_q = qpi->qpi_q;	/* The queue can disappear! */
345 	cv_signal(&ipfauthwait);
346 #else
347 # if defined(BSD) && !defined(sparc) && (BSD >= 199306)
348 	if (!fin->fin_out) {
349 		ip->ip_len = htons(ip->ip_len);
350 		ip->ip_off = htons(ip->ip_off);
351 	}
352 # endif
353 	fr_authpkts[i] = m;
354 	WAKEUP(&fr_authnext,0);
355 #endif
356 	return 1;
357 }
358 
359 
360 int fr_auth_ioctl(data, cmd, mode)
361 caddr_t data;
362 ioctlcmd_t cmd;
363 int mode;
364 {
365 	mb_t *m;
366 #if defined(_KERNEL) && !defined(MENTAT) && !defined(linux) && \
367     (!defined(__FreeBSD_version) || (__FreeBSD_version < 501000))
368 	struct ifqueue *ifq;
369 	SPL_INT(s);
370 #endif
371 	frauth_t auth, *au = &auth, *fra;
372 	int i, error = 0, len;
373 	char *t;
374 
375 	switch (cmd)
376 	{
377 	case SIOCSTLCK :
378 		if (!(mode & FWRITE)) {
379 			error = EPERM;
380 			break;
381 		}
382 		fr_lock(data, &fr_auth_lock);
383 		break;
384 
385 	case SIOCATHST:
386 		fr_authstats.fas_faelist = fae_list;
387 		error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT);
388 		break;
389 
390 	case SIOCIPFFL:
391 		SPL_NET(s);
392 		WRITE_ENTER(&ipf_auth);
393 		i = fr_authflush();
394 		RWLOCK_EXIT(&ipf_auth);
395 		SPL_X(s);
396 		error = copyoutptr((char *)&i, data, sizeof(i));
397 		break;
398 
399 	case SIOCAUTHW:
400 fr_authioctlloop:
401 		error = fr_inobj(data, au, IPFOBJ_FRAUTH);
402 		READ_ENTER(&ipf_auth);
403 		if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) {
404 			error = fr_outobj(data, &fr_auth[fr_authnext],
405 					  IPFOBJ_FRAUTH);
406 			if (auth.fra_len != 0 && auth.fra_buf != NULL) {
407 				/*
408 				 * Copy packet contents out to user space if
409 				 * requested.  Bail on an error.
410 				 */
411 				m = fr_authpkts[fr_authnext];
412 				len = MSGDSIZE(m);
413 				if (len > auth.fra_len)
414 					len = auth.fra_len;
415 				auth.fra_len = len;
416 				for (t = auth.fra_buf; m && (len > 0); ) {
417 					i = MIN(M_LEN(m), len);
418 					error = copyoutptr(MTOD(m, char *),
419 							  t, i);
420 					len -= i;
421 					t += i;
422 					if (error != 0)
423 						break;
424 				}
425 			}
426 			RWLOCK_EXIT(&ipf_auth);
427 			if (error != 0)
428 				break;
429 			SPL_NET(s);
430 			WRITE_ENTER(&ipf_auth);
431 			fr_authnext++;
432 			if (fr_authnext == fr_authsize)
433 				fr_authnext = 0;
434 			RWLOCK_EXIT(&ipf_auth);
435 			SPL_X(s);
436 			return 0;
437 		}
438 		RWLOCK_EXIT(&ipf_auth);
439 		/*
440 		 * We exit ipf_global here because a program that enters in
441 		 * here will have a lock on it and goto sleep having this lock.
442 		 * If someone were to do an 'ipf -D' the system would then
443 		 * deadlock.  The catch with releasing it here is that the
444 		 * caller of this function expects it to be held when we
445 		 * return so we have to reacquire it in here.
446 		 */
447 		RWLOCK_EXIT(&ipf_global);
448 
449 		MUTEX_ENTER(&ipf_authmx);
450 #ifdef	_KERNEL
451 # if	SOLARIS
452 		error = 0;
453 		if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk))
454 			error = EINTR;
455 # else /* SOLARIS */
456 #  ifdef __hpux
457 		{
458 		lock_t *l;
459 
460 		l = get_sleep_lock(&fr_authnext);
461 		error = sleep(&fr_authnext, PZERO+1);
462 		spinunlock(l);
463 		}
464 #  else
465 #   ifdef __osf__
466 		error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0,
467 				&ipf_authmx, MS_LOCK_SIMPLE);
468 #   else
469 		error = SLEEP(&fr_authnext, "fr_authnext");
470 #   endif /* __osf__ */
471 #  endif /* __hpux */
472 # endif /* SOLARIS */
473 #endif
474 		MUTEX_EXIT(&ipf_authmx);
475 		READ_ENTER(&ipf_global);
476 		if (error == 0) {
477 			READ_ENTER(&ipf_auth);
478 			goto fr_authioctlloop;
479 		}
480 		break;
481 
482 	case SIOCAUTHR:
483 		error = fr_inobj(data, &auth, IPFOBJ_FRAUTH);
484 		if (error != 0)
485 			return error;
486 		SPL_NET(s);
487 		WRITE_ENTER(&ipf_auth);
488 		i = au->fra_index;
489 		fra = fr_auth + i;
490 		if ((i < 0) || (i >= fr_authsize) ||
491 		    (fra->fra_info.fin_id != au->fra_info.fin_id)) {
492 			RWLOCK_EXIT(&ipf_auth);
493 			SPL_X(s);
494 			return ESRCH;
495 		}
496 		m = fr_authpkts[i];
497 		fra->fra_index = -2;
498 		fra->fra_pass = au->fra_pass;
499 		fr_authpkts[i] = NULL;
500 		RWLOCK_EXIT(&ipf_auth);
501 #ifdef	_KERNEL
502 		if ((m != NULL) && (au->fra_info.fin_out != 0)) {
503 # ifdef MENTAT
504 			error = !putq(fra->fra_q, m);
505 # else /* MENTAT */
506 #  if defined(linux) || defined(AIX)
507 #  else
508 #   if (_BSDI_VERSION >= 199802) || defined(__OpenBSD__) || \
509        (defined(__sgi) && (IRIX >= 60500) || defined(AIX) || \
510        (defined(__FreeBSD__) && (__FreeBSD_version >= 470102)))
511 			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL,
512 					  NULL);
513 #   else
514 			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL);
515 #   endif
516 #  endif /* Linux */
517 # endif /* MENTAT */
518 			if (error != 0)
519 				fr_authstats.fas_sendfail++;
520 			else
521 				fr_authstats.fas_sendok++;
522 		} else if (m) {
523 # ifdef MENTAT
524 			error = !putq(fra->fra_q, m);
525 # else /* MENTAT */
526 #  if defined(linux) || defined(AIX)
527 #  else
528 #   if (__FreeBSD_version >= 501000)
529 			netisr_dispatch(NETISR_IP, m);
530 #   else
531 #    if (IRIX >= 60516)
532 			ifq = &((struct ifnet *)fra->fra_info.fin_ifp)->if_snd;
533 #    else
534 			ifq = &ipintrq;
535 #    endif
536 			if (IF_QFULL(ifq)) {
537 				IF_DROP(ifq);
538 				FREE_MB_T(m);
539 				error = ENOBUFS;
540 			} else {
541 				IF_ENQUEUE(ifq, m);
542 #    if IRIX < 60500
543 				schednetisr(NETISR_IP);
544 #    endif
545 			}
546 #   endif
547 #  endif /* Linux */
548 # endif /* MENTAT */
549 			if (error != 0)
550 				fr_authstats.fas_quefail++;
551 			else
552 				fr_authstats.fas_queok++;
553 		} else
554 			error = EINVAL;
555 # ifdef MENTAT
556 		if (error != 0)
557 			error = EINVAL;
558 # else /* MENTAT */
559 		/*
560 		 * If we experience an error which will result in the packet
561 		 * not being processed, make sure we advance to the next one.
562 		 */
563 		if (error == ENOBUFS) {
564 			fr_authused--;
565 			fra->fra_index = -1;
566 			fra->fra_pass = 0;
567 			if (i == fr_authstart) {
568 				while (fra->fra_index == -1) {
569 					i++;
570 					if (i == fr_authsize)
571 						i = 0;
572 					fr_authstart = i;
573 					if (i == fr_authend)
574 						break;
575 				}
576 				if (fr_authstart == fr_authend) {
577 					fr_authnext = 0;
578 					fr_authstart = fr_authend = 0;
579 				}
580 			}
581 		}
582 # endif /* MENTAT */
583 #endif /* _KERNEL */
584 		SPL_X(s);
585 		break;
586 
587 	default :
588 		error = EINVAL;
589 		break;
590 	}
591 	return error;
592 }
593 
594 
595 /*
596  * Free all network buffer memory used to keep saved packets.
597  */
598 void fr_authunload()
599 {
600 	register int i;
601 	register frauthent_t *fae, **faep;
602 	frentry_t *fr, **frp;
603 	mb_t *m;
604 
605 	if (fr_auth != NULL) {
606 		KFREES(fr_auth, fr_authsize * sizeof(*fr_auth));
607 		fr_auth = NULL;
608 	}
609 
610 	if (fr_authpkts != NULL) {
611 		for (i = 0; i < fr_authsize; i++) {
612 			m = fr_authpkts[i];
613 			if (m != NULL) {
614 				FREE_MB_T(m);
615 				fr_authpkts[i] = NULL;
616 			}
617 		}
618 		KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
619 		fr_authpkts = NULL;
620 	}
621 
622 	faep = &fae_list;
623 	while ((fae = *faep) != NULL) {
624 		*faep = fae->fae_next;
625 		KFREE(fae);
626 	}
627 	ipauth = NULL;
628 
629 	if (fr_authlist != NULL) {
630 		for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
631 			if (fr->fr_ref == 1) {
632 				*frp = fr->fr_next;
633 				KFREE(fr);
634 			} else
635 				frp = &fr->fr_next;
636 		}
637 	}
638 
639 	if (fr_auth_init == 1) {
640 # if SOLARIS && defined(_KERNEL)
641 		cv_destroy(&ipfauthwait);
642 # endif
643 		MUTEX_DESTROY(&ipf_authmx);
644 		RW_DESTROY(&ipf_auth);
645 
646 		fr_auth_init = 0;
647 	}
648 }
649 
650 
651 /*
652  * Slowly expire held auth records.  Timeouts are set
653  * in expectation of this being called twice per second.
654  */
655 void fr_authexpire()
656 {
657 	register int i;
658 	register frauth_t *fra;
659 	register frauthent_t *fae, **faep;
660 	register frentry_t *fr, **frp;
661 	mb_t *m;
662 	SPL_INT(s);
663 
664 	if (fr_auth_lock)
665 		return;
666 
667 	SPL_NET(s);
668 	WRITE_ENTER(&ipf_auth);
669 	for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) {
670 		fra->fra_age--;
671 		if ((fra->fra_age == 0) && (m = fr_authpkts[i])) {
672 			FREE_MB_T(m);
673 			fr_authpkts[i] = NULL;
674 			fr_auth[i].fra_index = -1;
675 			fr_authstats.fas_expire++;
676 			fr_authused--;
677 		}
678 	}
679 
680 	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
681 		fae->fae_age--;
682 		if (fae->fae_age == 0) {
683 			*faep = fae->fae_next;
684 			KFREE(fae);
685 			fr_authstats.fas_expire++;
686 		} else
687 			faep = &fae->fae_next;
688 	}
689 	if (fae_list != NULL)
690 		ipauth = &fae_list->fae_fr;
691 	else
692 		ipauth = NULL;
693 
694 	for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
695 		if (fr->fr_ref == 1) {
696 			*frp = fr->fr_next;
697 			KFREE(fr);
698 		} else
699 			frp = &fr->fr_next;
700 	}
701 	RWLOCK_EXIT(&ipf_auth);
702 	SPL_X(s);
703 }
704 
705 int fr_preauthcmd(cmd, fr, frptr)
706 ioctlcmd_t cmd;
707 frentry_t *fr, **frptr;
708 {
709 	frauthent_t *fae, **faep;
710 	int error = 0;
711 	SPL_INT(s);
712 
713 	if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR))
714 		return EIO;
715 
716 	for (faep = &fae_list; ((fae = *faep) != NULL); ) {
717 		if (&fae->fae_fr == fr)
718 			break;
719 		else
720 			faep = &fae->fae_next;
721 	}
722 
723 	if (cmd == (ioctlcmd_t)SIOCRMAFR) {
724 		if (fr == NULL || frptr == NULL)
725 			error = EINVAL;
726 		else if (fae == NULL)
727 			error = ESRCH;
728 		else {
729 			SPL_NET(s);
730 			WRITE_ENTER(&ipf_auth);
731 			*faep = fae->fae_next;
732 			if (ipauth == &fae->fae_fr)
733 				ipauth = fae_list ? &fae_list->fae_fr : NULL;
734 			RWLOCK_EXIT(&ipf_auth);
735 			SPL_X(s);
736 
737 			KFREE(fae);
738 		}
739 	} else if (fr != NULL && frptr != NULL) {
740 		KMALLOC(fae, frauthent_t *);
741 		if (fae != NULL) {
742 			bcopy((char *)fr, (char *)&fae->fae_fr,
743 			      sizeof(*fr));
744 			SPL_NET(s);
745 			WRITE_ENTER(&ipf_auth);
746 			fae->fae_age = fr_defaultauthage;
747 			fae->fae_fr.fr_hits = 0;
748 			fae->fae_fr.fr_next = *frptr;
749 			*frptr = &fae->fae_fr;
750 			fae->fae_next = *faep;
751 			*faep = fae;
752 			ipauth = &fae_list->fae_fr;
753 			RWLOCK_EXIT(&ipf_auth);
754 			SPL_X(s);
755 		} else
756 			error = ENOMEM;
757 	} else
758 		error = EINVAL;
759 	return error;
760 }
761 
762 
763 /*
764  * Flush held packets.
765  * Must already be properly SPL'ed and Locked on &ipf_auth.
766  *
767  */
768 int fr_authflush()
769 {
770 	register int i, num_flushed;
771 	mb_t *m;
772 
773 	if (fr_auth_lock)
774 		return -1;
775 
776 	num_flushed = 0;
777 
778 	for (i = 0 ; i < fr_authsize; i++) {
779 		m = fr_authpkts[i];
780 		if (m != NULL) {
781 			FREE_MB_T(m);
782 			fr_authpkts[i] = NULL;
783 			fr_auth[i].fra_index = -1;
784 			/* perhaps add & use a flush counter inst.*/
785 			fr_authstats.fas_expire++;
786 			fr_authused--;
787 			num_flushed++;
788 		}
789 	}
790 
791 	fr_authstart = 0;
792 	fr_authend = 0;
793 	fr_authnext = 0;
794 
795 	return num_flushed;
796 }
797