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