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