Home | History | Annotate | Download | only in nfs
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/param.h>
     27 #include <sys/errno.h>
     28 #include <sys/vfs.h>
     29 #include <sys/vnode.h>
     30 #include <sys/cred.h>
     31 #include <sys/cmn_err.h>
     32 #include <sys/systm.h>
     33 #include <sys/kmem.h>
     34 #include <sys/pathname.h>
     35 #include <sys/utsname.h>
     36 #include <sys/debug.h>
     37 #include <sys/door.h>
     38 #include <sys/sdt.h>
     39 
     40 #include <rpc/types.h>
     41 #include <rpc/auth.h>
     42 #include <rpc/clnt.h>
     43 
     44 #include <nfs/nfs.h>
     45 #include <nfs/export.h>
     46 #include <nfs/nfs_clnt.h>
     47 #include <nfs/auth.h>
     48 
     49 #define	EQADDR(a1, a2)  \
     50 	(bcmp((char *)(a1)->buf, (char *)(a2)->buf, (a1)->len) == 0 && \
     51 	(a1)->len == (a2)->len)
     52 
     53 static struct knetconfig auth_knconf;
     54 static servinfo_t svp;
     55 static clinfo_t ci;
     56 
     57 static struct kmem_cache *exi_cache_handle;
     58 static void exi_cache_reclaim(void *);
     59 static void exi_cache_trim(struct exportinfo *exi);
     60 
     61 int nfsauth_cache_hit;
     62 int nfsauth_cache_miss;
     63 int nfsauth_cache_reclaim;
     64 
     65 /*
     66  * Number of seconds to wait for an NFSAUTH upcall.
     67  */
     68 static int nfsauth_timeout = 20;
     69 
     70 /*
     71  * mountd is a server-side only daemon. This will need to be
     72  * revisited if the NFS server is ever made zones-aware.
     73  */
     74 kmutex_t	mountd_lock;
     75 door_handle_t   mountd_dh;
     76 
     77 void
     78 mountd_args(uint_t did)
     79 {
     80 	mutex_enter(&mountd_lock);
     81 	if (mountd_dh)
     82 		door_ki_rele(mountd_dh);
     83 	mountd_dh = door_ki_lookup(did);
     84 	mutex_exit(&mountd_lock);
     85 }
     86 
     87 void
     88 nfsauth_init(void)
     89 {
     90 	/*
     91 	 * mountd can be restarted by smf(5). We need to make sure
     92 	 * the updated door handle will safely make it to mountd_dh
     93 	 */
     94 	mutex_init(&mountd_lock, NULL, MUTEX_DEFAULT, NULL);
     95 
     96 	/*
     97 	 * Allocate nfsauth cache handle
     98 	 */
     99 	exi_cache_handle = kmem_cache_create("exi_cache_handle",
    100 	    sizeof (struct auth_cache), 0, NULL, NULL,
    101 	    exi_cache_reclaim, NULL, NULL, 0);
    102 }
    103 
    104 /*
    105  * Finalization routine for nfsauth. It is important to call this routine
    106  * before destroying the exported_lock.
    107  */
    108 void
    109 nfsauth_fini(void)
    110 {
    111 	/*
    112 	 * Deallocate nfsauth cache handle
    113 	 */
    114 	kmem_cache_destroy(exi_cache_handle);
    115 }
    116 
    117 /*
    118  * Convert the address in a netbuf to
    119  * a hash index for the auth_cache table.
    120  */
    121 static int
    122 hash(struct netbuf *a)
    123 {
    124 	int i, h = 0;
    125 
    126 	for (i = 0; i < a->len; i++)
    127 		h ^= a->buf[i];
    128 
    129 	return (h & (AUTH_TABLESIZE - 1));
    130 }
    131 
    132 /*
    133  * Mask out the components of an
    134  * address that do not identify
    135  * a host. For socket addresses the
    136  * masking gets rid of the port number.
    137  */
    138 static void
    139 addrmask(struct netbuf *addr, struct netbuf *mask)
    140 {
    141 	int i;
    142 
    143 	for (i = 0; i < addr->len; i++)
    144 		addr->buf[i] &= mask->buf[i];
    145 }
    146 
    147 /*
    148  * nfsauth4_access is used for NFS V4 auth checking. Besides doing
    149  * the common nfsauth_access(), it will check if the client can
    150  * have a limited access to this vnode even if the security flavor
    151  * used does not meet the policy.
    152  */
    153 int
    154 nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req)
    155 {
    156 	int access;
    157 
    158 	access = nfsauth_access(exi, req);
    159 
    160 	/*
    161 	 * There are cases that the server needs to allow the client
    162 	 * to have a limited view.
    163 	 *
    164 	 * e.g.
    165 	 * /export is shared as "sec=sys,rw=dfs-test-4,sec=krb5,rw"
    166 	 * /export/home is shared as "sec=sys,rw"
    167 	 *
    168 	 * When the client mounts /export with sec=sys, the client
    169 	 * would get a limited view with RO access on /export to see
    170 	 * "home" only because the client is allowed to access
    171 	 * /export/home with auth_sys.
    172 	 */
    173 	if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
    174 		/*
    175 		 * Allow ro permission with LIMITED view if there is a
    176 		 * sub-dir exported under vp.
    177 		 */
    178 		if (has_visible(exi, vp))
    179 			return (NFSAUTH_LIMITED);
    180 	}
    181 
    182 	return (access);
    183 }
    184 
    185 static void
    186 sys_log(const char *msg)
    187 {
    188 	static time_t	tstamp = 0;
    189 	time_t		now;
    190 
    191 	/*
    192 	 * msg is shown (at most) once per minute
    193 	 */
    194 	now = gethrestime_sec();
    195 	if ((tstamp + 60) < now) {
    196 		tstamp = now;
    197 		cmn_err(CE_WARN, msg);
    198 	}
    199 }
    200 
    201 /*
    202  * Get the access information from the cache or callup to the mountd
    203  * to get and cache the access information in the kernel.
    204  */
    205 int
    206 nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor)
    207 {
    208 	struct netbuf		  addr;
    209 	struct netbuf		 *claddr;
    210 	struct auth_cache	**head;
    211 	struct auth_cache	 *ap;
    212 	int			  access;
    213 	varg_t			  varg = {0};
    214 	nfsauth_res_t		  res = {0};
    215 	XDR			  xdrs_a;
    216 	XDR			  xdrs_r;
    217 	size_t			  absz;
    218 	caddr_t			  abuf;
    219 	size_t			  rbsz = (size_t)(BYTES_PER_XDR_UNIT * 2);
    220 	char			  result[BYTES_PER_XDR_UNIT * 2] = {0};
    221 	caddr_t			  rbuf = (caddr_t)&result;
    222 	int			  last = 0;
    223 	door_arg_t		  da;
    224 	door_info_t		  di;
    225 	door_handle_t		  dh;
    226 	uint_t			  ntries = 0;
    227 
    228 	/*
    229 	 * Now check whether this client already
    230 	 * has an entry for this flavor in the cache
    231 	 * for this export.
    232 	 * Get the caller's address, mask off the
    233 	 * parts of the address that do not identify
    234 	 * the host (port number, etc), and then hash
    235 	 * it to find the chain of cache entries.
    236 	 */
    237 
    238 	claddr = svc_getrpccaller(req->rq_xprt);
    239 	addr = *claddr;
    240 	addr.buf = kmem_alloc(addr.len, KM_SLEEP);
    241 	bcopy(claddr->buf, addr.buf, claddr->len);
    242 	addrmask(&addr, svc_getaddrmask(req->rq_xprt));
    243 	head = &exi->exi_cache[hash(&addr)];
    244 
    245 	rw_enter(&exi->exi_cache_lock, RW_READER);
    246 	for (ap = *head; ap; ap = ap->auth_next) {
    247 		if (EQADDR(&addr, &ap->auth_addr) && flavor == ap->auth_flavor)
    248 			break;
    249 	}
    250 	if (ap) {				/* cache hit */
    251 		access = ap->auth_access;
    252 		ap->auth_time = gethrestime_sec();
    253 		nfsauth_cache_hit++;
    254 	}
    255 
    256 	rw_exit(&exi->exi_cache_lock);
    257 
    258 	if (ap) {
    259 		kmem_free(addr.buf, addr.len);
    260 		return (access);
    261 	}
    262 
    263 	nfsauth_cache_miss++;
    264 
    265 	/*
    266 	 * No entry in the cache for this client/flavor
    267 	 * so we need to call the nfsauth service in the
    268 	 * mount daemon.
    269 	 */
    270 retry:
    271 	mutex_enter(&mountd_lock);
    272 	dh = mountd_dh;
    273 	if (dh)
    274 		door_ki_hold(dh);
    275 	mutex_exit(&mountd_lock);
    276 
    277 	if (dh == NULL) {
    278 		/*
    279 		 * The rendezvous point has not been established yet !
    280 		 * This could mean that either mountd(1m) has not yet
    281 		 * been started or that _this_ routine nuked the door
    282 		 * handle after receiving an EINTR for a REVOKED door.
    283 		 *
    284 		 * Returning NFSAUTH_DROP will cause the NFS client
    285 		 * to retransmit the request, so let's try to be more
    286 		 * rescillient and attempt for ntries before we bail.
    287 		 */
    288 		if (++ntries % NFSAUTH_DR_TRYCNT) {
    289 			delay(hz);
    290 			goto retry;
    291 		}
    292 		sys_log("nfsauth: mountd has not established door");
    293 		kmem_free(addr.buf, addr.len);
    294 		return (NFSAUTH_DROP);
    295 	}
    296 	ntries = 0;
    297 	varg.vers = V_PROTO;
    298 	varg.arg_u.arg.cmd = NFSAUTH_ACCESS;
    299 	varg.arg_u.arg.areq.req_client.n_len = addr.len;
    300 	varg.arg_u.arg.areq.req_client.n_bytes = addr.buf;
    301 	varg.arg_u.arg.areq.req_netid = svc_getnetid(req->rq_xprt);
    302 	varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path;
    303 	varg.arg_u.arg.areq.req_flavor = flavor;
    304 
    305 	/*
    306 	 * Setup the XDR stream for encoding the arguments. Notice that
    307 	 * in addition to the args having variable fields (req_netid and
    308 	 * req_path), the argument data structure is itself versioned,
    309 	 * so we need to make sure we can size the arguments buffer
    310 	 * appropriately to encode all the args. If we can't get sizing
    311 	 * info _or_ properly encode the arguments, there's really no
    312 	 * point in continuting, so we fail the request.
    313 	 */
    314 	DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
    315 	if ((absz = xdr_sizeof(xdr_varg, (void *)&varg)) == 0) {
    316 		door_ki_rele(dh);
    317 		kmem_free(addr.buf, addr.len);
    318 		return (NFSAUTH_DENIED);
    319 	}
    320 	abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP);
    321 	xdrmem_create(&xdrs_a, abuf, absz, XDR_ENCODE);
    322 	if (!xdr_varg(&xdrs_a, &varg)) {
    323 		door_ki_rele(dh);
    324 		goto fail;
    325 	}
    326 	XDR_DESTROY(&xdrs_a);
    327 
    328 	/*
    329 	 * The result (nfsauth_res_t) is always two int's, so we don't
    330 	 * have to dynamically size (or allocate) the results buffer.
    331 	 * Now that we've got what we need, we prep the door arguments
    332 	 * and place the call.
    333 	 */
    334 	da.data_ptr = (char *)abuf;
    335 	da.data_size = absz;
    336 	da.desc_ptr = NULL;
    337 	da.desc_num = 0;
    338 	da.rbuf = (char *)rbuf;
    339 	da.rsize = rbsz;
    340 
    341 	switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
    342 		case 0:				/* Success */
    343 			if (da.data_ptr != da.rbuf && da.data_size == 0) {
    344 				/*
    345 				 * The door_return that contained the data
    346 				 * failed ! We're here because of the 2nd
    347 				 * door_return (w/o data) such that we can
    348 				 * get control of the thread (and exit
    349 				 * gracefully).
    350 				 */
    351 				DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
    352 				    door_arg_t *, &da);
    353 				door_ki_rele(dh);
    354 				goto fail;
    355 
    356 			} else if (rbuf != da.rbuf) {
    357 				/*
    358 				 * The only time this should be true
    359 				 * is iff userland wanted to hand us
    360 				 * a bigger response than what we
    361 				 * expect; that should not happen
    362 				 * (nfsauth_res_t is only 2 int's),
    363 				 * but we check nevertheless.
    364 				 */
    365 				rbuf = da.rbuf;
    366 				rbsz = da.rsize;
    367 
    368 			} else if (rbsz > da.data_size) {
    369 				/*
    370 				 * We were expecting two int's; but if
    371 				 * userland fails in encoding the XDR
    372 				 * stream, we detect that here, since
    373 				 * the mountd forces down only one byte
    374 				 * in such scenario.
    375 				 */
    376 				door_ki_rele(dh);
    377 				goto fail;
    378 			}
    379 			door_ki_rele(dh);
    380 			break;
    381 
    382 		case EAGAIN:
    383 			/*
    384 			 * Server out of resources; back off for a bit
    385 			 */
    386 			door_ki_rele(dh);
    387 			kmem_free(abuf, absz);
    388 			delay(hz);
    389 			goto retry;
    390 			/* NOTREACHED */
    391 
    392 		case EINTR:
    393 			if (!door_ki_info(dh, &di)) {
    394 				if (di.di_attributes & DOOR_REVOKED) {
    395 					/*
    396 					 * The server barfed and revoked
    397 					 * the (existing) door on us; we
    398 					 * want to wait to give smf(5) a
    399 					 * chance to restart mountd(1m)
    400 					 * and establish a new door handle.
    401 					 */
    402 					mutex_enter(&mountd_lock);
    403 					if (dh == mountd_dh)
    404 						mountd_dh = NULL;
    405 					mutex_exit(&mountd_lock);
    406 					door_ki_rele(dh);
    407 					kmem_free(abuf, absz);
    408 					delay(hz);
    409 					goto retry;
    410 				}
    411 				/*
    412 				 * If the door was _not_ revoked on us,
    413 				 * then more than likely we took an INTR,
    414 				 * so we need to fail the operation.
    415 				 */
    416 				door_ki_rele(dh);
    417 				goto fail;
    418 			}
    419 			/*
    420 			 * The only failure that can occur from getting
    421 			 * the door info is EINVAL, so we let the code
    422 			 * below handle it.
    423 			 */
    424 			/* FALLTHROUGH */
    425 
    426 		case EBADF:
    427 		case EINVAL:
    428 		default:
    429 			/*
    430 			 * If we have a stale door handle, give smf a last
    431 			 * chance to start it by sleeping for a little bit.
    432 			 * If we're still hosed, we'll fail the call.
    433 			 *
    434 			 * Since we're going to reacquire the door handle
    435 			 * upon the retry, we opt to sleep for a bit and
    436 			 * _not_ to clear mountd_dh. If mountd restarted
    437 			 * and was able to set mountd_dh, we should see
    438 			 * the new instance; if not, we won't get caught
    439 			 * up in the retry/DELAY loop.
    440 			 */
    441 			door_ki_rele(dh);
    442 			if (!last) {
    443 				delay(hz);
    444 				last++;
    445 				goto retry;
    446 			}
    447 			sys_log("nfsauth: stale mountd door handle");
    448 			goto fail;
    449 	}
    450 
    451 	/*
    452 	 * No door errors encountered; setup the XDR stream for decoding
    453 	 * the results. If we fail to decode the results, we've got no
    454 	 * other recourse than to fail the request.
    455 	 */
    456 	xdrmem_create(&xdrs_r, rbuf, rbsz, XDR_DECODE);
    457 	if (!xdr_nfsauth_res(&xdrs_r, &res))
    458 		goto fail;
    459 	XDR_DESTROY(&xdrs_r);
    460 
    461 	DTRACE_PROBE1(nfsserv__func__nfsauth__results, nfsauth_res_t *, &res);
    462 	switch (res.stat) {
    463 		case NFSAUTH_DR_OKAY:
    464 			access = res.ares.auth_perm;
    465 			kmem_free(abuf, absz);
    466 			break;
    467 
    468 		case NFSAUTH_DR_EFAIL:
    469 		case NFSAUTH_DR_DECERR:
    470 		case NFSAUTH_DR_BADCMD:
    471 		default:
    472 fail:
    473 			kmem_free(addr.buf, addr.len);
    474 			kmem_free(abuf, absz);
    475 			return (NFSAUTH_DENIED);
    476 			/* NOTREACHED */
    477 	}
    478 
    479 	/*
    480 	 * Now cache the result on the cache chain
    481 	 * for this export (if there's enough memory)
    482 	 */
    483 	ap = kmem_cache_alloc(exi_cache_handle, KM_NOSLEEP);
    484 	if (ap) {
    485 		ap->auth_addr = addr;
    486 		ap->auth_flavor = flavor;
    487 		ap->auth_access = access;
    488 		ap->auth_time = gethrestime_sec();
    489 		rw_enter(&exi->exi_cache_lock, RW_WRITER);
    490 		ap->auth_next = *head;
    491 		*head = ap;
    492 		rw_exit(&exi->exi_cache_lock);
    493 	} else {
    494 		kmem_free(addr.buf, addr.len);
    495 	}
    496 
    497 	return (access);
    498 }
    499 
    500 /*
    501  * Check if the requesting client has access to the filesystem with
    502  * a given nfs flavor number which is an explicitly shared flavor.
    503  */
    504 int
    505 nfsauth4_secinfo_access(struct exportinfo *exi, struct svc_req *req,
    506 			int flavor, int perm)
    507 {
    508 	int access;
    509 
    510 	if (! (perm & M_4SEC_EXPORTED)) {
    511 		return (NFSAUTH_DENIED);
    512 	}
    513 
    514 	/*
    515 	 * Optimize if there are no lists
    516 	 */
    517 	if ((perm & (M_ROOT|M_NONE)) == 0) {
    518 		perm &= ~M_4SEC_EXPORTED;
    519 		if (perm == M_RO)
    520 			return (NFSAUTH_RO);
    521 		if (perm == M_RW)
    522 			return (NFSAUTH_RW);
    523 	}
    524 
    525 	access = nfsauth_cache_get(exi, req, flavor);
    526 
    527 	return (access);
    528 }
    529 
    530 int
    531 nfsauth_access(struct exportinfo *exi, struct svc_req *req)
    532 {
    533 	int access, mapaccess;
    534 	struct secinfo *sp;
    535 	int i, flavor, perm;
    536 	int authnone_entry = -1;
    537 
    538 	/*
    539 	 *  Get the nfs flavor number from xprt.
    540 	 */
    541 	flavor = (int)(uintptr_t)req->rq_xprt->xp_cookie;
    542 
    543 	/*
    544 	 * First check the access restrictions on the filesystem.  If
    545 	 * there are no lists associated with this flavor then there's no
    546 	 * need to make an expensive call to the nfsauth service or to
    547 	 * cache anything.
    548 	 */
    549 
    550 	sp = exi->exi_export.ex_secinfo;
    551 	for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
    552 		if (flavor != sp[i].s_secinfo.sc_nfsnum) {
    553 			if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE)
    554 				authnone_entry = i;
    555 			continue;
    556 		}
    557 		break;
    558 	}
    559 
    560 	mapaccess = 0;
    561 
    562 	if (i >= exi->exi_export.ex_seccnt) {
    563 		/*
    564 		 * Flavor not found, but use AUTH_NONE if it exists
    565 		 */
    566 		if (authnone_entry == -1)
    567 			return (NFSAUTH_DENIED);
    568 		flavor = AUTH_NONE;
    569 		mapaccess = NFSAUTH_MAPNONE;
    570 		i = authnone_entry;
    571 	}
    572 
    573 	/*
    574 	 * If the flavor is in the ex_secinfo list, but not an explicitly
    575 	 * shared flavor by the user, it is a result of the nfsv4 server
    576 	 * namespace setup. We will grant an RO permission similar for
    577 	 * a pseudo node except that this node is a shared one.
    578 	 *
    579 	 * e.g. flavor in (flavor) indicates that it is not explictly
    580 	 *	shared by the user:
    581 	 *
    582 	 *		/	(sys, krb5)
    583 	 *		|
    584 	 *		export  #share -o sec=sys (krb5)
    585 	 *		|
    586 	 *		secure  #share -o sec=krb5
    587 	 *
    588 	 *	In this case, when a krb5 request coming in to access
    589 	 *	/export, RO permission is granted.
    590 	 */
    591 	if (!(sp[i].s_flags & M_4SEC_EXPORTED))
    592 		return (mapaccess | NFSAUTH_RO);
    593 
    594 	/*
    595 	 * Optimize if there are no lists
    596 	 */
    597 	perm = sp[i].s_flags;
    598 	if ((perm & (M_ROOT|M_NONE)) == 0) {
    599 		perm &= ~M_4SEC_EXPORTED;
    600 		if (perm == M_RO)
    601 			return (mapaccess | NFSAUTH_RO);
    602 		if (perm == M_RW)
    603 			return (mapaccess | NFSAUTH_RW);
    604 	}
    605 
    606 	access = nfsauth_cache_get(exi, req, flavor);
    607 
    608 	/*
    609 	 * Client's security flavor doesn't match with "ro" or
    610 	 * "rw" list. Try again using AUTH_NONE if present.
    611 	 */
    612 	if ((access & NFSAUTH_WRONGSEC) && (flavor != AUTH_NONE)) {
    613 		/*
    614 		 * Have we already encountered AUTH_NONE ?
    615 		 */
    616 		if (authnone_entry != -1) {
    617 			mapaccess = NFSAUTH_MAPNONE;
    618 			access = nfsauth_cache_get(exi, req, AUTH_NONE);
    619 		} else {
    620 			/*
    621 			 * Check for AUTH_NONE presence.
    622 			 */
    623 			for (; i < exi->exi_export.ex_seccnt; i++) {
    624 				if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE) {
    625 					mapaccess = NFSAUTH_MAPNONE;
    626 					access = nfsauth_cache_get(exi, req,
    627 					    AUTH_NONE);
    628 					break;
    629 				}
    630 			}
    631 		}
    632 	}
    633 
    634 	if (access & NFSAUTH_DENIED)
    635 		access = NFSAUTH_DENIED;
    636 
    637 	return (access | mapaccess);
    638 }
    639 
    640 /*
    641  * Free the nfsauth cache for a given export
    642  */
    643 void
    644 nfsauth_cache_free(struct exportinfo *exi)
    645 {
    646 	int i;
    647 	struct auth_cache *p, *next;
    648 
    649 	for (i = 0; i < AUTH_TABLESIZE; i++) {
    650 		for (p = exi->exi_cache[i]; p; p = next) {
    651 			kmem_free(p->auth_addr.buf, p->auth_addr.len);
    652 			next = p->auth_next;
    653 			kmem_cache_free(exi_cache_handle, (void *)p);
    654 		}
    655 	}
    656 }
    657 
    658 /*
    659  * Called by the kernel memory allocator when
    660  * memory is low. Free unused cache entries.
    661  * If that's not enough, the VM system will
    662  * call again for some more.
    663  */
    664 /*ARGSUSED*/
    665 void
    666 exi_cache_reclaim(void *cdrarg)
    667 {
    668 	int i;
    669 	struct exportinfo *exi;
    670 
    671 	rw_enter(&exported_lock, RW_READER);
    672 
    673 	for (i = 0; i < EXPTABLESIZE; i++) {
    674 		for (exi = exptable[i]; exi; exi = exi->exi_hash) {
    675 			exi_cache_trim(exi);
    676 		}
    677 	}
    678 	nfsauth_cache_reclaim++;
    679 
    680 	rw_exit(&exported_lock);
    681 }
    682 
    683 /*
    684  * Don't reclaim entries until they've been
    685  * in the cache for at least exi_cache_time
    686  * seconds.
    687  */
    688 time_t exi_cache_time = 60 * 60;
    689 
    690 void
    691 exi_cache_trim(struct exportinfo *exi)
    692 {
    693 	struct auth_cache *p;
    694 	struct auth_cache *prev, *next;
    695 	int i;
    696 	time_t stale_time;
    697 
    698 	stale_time = gethrestime_sec() - exi_cache_time;
    699 
    700 	rw_enter(&exi->exi_cache_lock, RW_WRITER);
    701 
    702 	for (i = 0; i < AUTH_TABLESIZE; i++) {
    703 
    704 		/*
    705 		 * Free entries that have not been
    706 		 * used for exi_cache_time seconds.
    707 		 */
    708 		prev = NULL;
    709 		for (p = exi->exi_cache[i]; p; p = next) {
    710 			next = p->auth_next;
    711 			if (p->auth_time > stale_time) {
    712 				prev = p;
    713 				continue;
    714 			}
    715 
    716 			kmem_free(p->auth_addr.buf, p->auth_addr.len);
    717 			kmem_cache_free(exi_cache_handle, (void *)p);
    718 			if (prev == NULL)
    719 				exi->exi_cache[i] = next;
    720 			else
    721 				prev->auth_next = next;
    722 		}
    723 	}
    724 
    725 	rw_exit(&exi->exi_cache_lock);
    726 }
    727