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