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