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