xref: /illumos-gate/usr/src/lib/libnsl/rpc/svc_dg.c (revision 0d8b5334)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 /*
30  * Portions of this source code were derived from Berkeley
31  * 4.3 BSD under license from the Regents of the University of
32  * California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  * svc_dg.c, Server side for connectionless RPC.
39  *
40  * Does some caching in the hopes of achieving execute-at-most-once semantics.
41  */
42 
43 #include "mt.h"
44 #include "rpc_mt.h"
45 #include <stdio.h>
46 #include <sys/types.h>
47 #include <rpc/rpc.h>
48 #include <errno.h>
49 #include <syslog.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 #ifdef RPC_CACHE_DEBUG
54 #include <netconfig.h>
55 #include <netdir.h>
56 #endif
57 
58 #ifndef MAX
59 #define	MAX(a, b)	(((a) > (b)) ? (a) : (b))
60 #endif
61 
62 static struct xp_ops *svc_dg_ops();
63 static void cache_set();
64 static int cache_get();
65 
66 #define	MAX_OPT_WORDS	128		/* needs to fit a ucred */
67 
68 /*
69  * kept in xprt->xp_p2
70  */
71 struct svc_dg_data {
72 	/* XXX: optbuf should be the first field, used by ti_opts.c code */
73 	struct	netbuf optbuf;			/* netbuf for options */
74 	int	opts[MAX_OPT_WORDS];		/* options */
75 	uint_t   su_iosz;			/* size of send.recv buffer */
76 	uint32_t	su_xid;			/* transaction id */
77 	XDR	su_xdrs;			/* XDR handle */
78 	char	su_verfbody[MAX_AUTH_BYTES];	/* verifier body */
79 	char 	*su_cache;			/* cached data, NULL if none */
80 	struct t_unitdata   su_tudata;		/* tu_data for recv */
81 };
82 #define	su_data(xprt)	((struct svc_dg_data *)(xprt->xp_p2))
83 #define	rpc_buffer(xprt) ((xprt)->xp_p1)
84 
85 /*
86  * Usage:
87  *	xprt = svc_dg_create(sock, sendsize, recvsize);
88  * Does other connectionless specific initializations.
89  * Once *xprt is initialized, it is registered.
90  * see (svc.h, xprt_register). If recvsize or sendsize are 0 suitable
91  * system defaults are chosen.
92  * The routines returns NULL if a problem occurred.
93  */
94 static const char svc_dg_str[] = "svc_dg_create: %s";
95 static const char svc_dg_err1[] = "could not get transport information";
96 static const char svc_dg_err2[] = " transport does not support data transfer";
97 static const char svc_dg_err3[] =
98 		"fd > FD_SETSIZE; Use rpc_control(RPC_SVC_USE_POLLFD,...);";
99 static const char __no_mem_str[] = "out of memory";
100 
101 /* Structure used to initialize SVC_XP_AUTH(xprt).svc_ah_ops. */
102 extern struct svc_auth_ops svc_auth_any_ops;
103 extern int __rpc_get_ltaddr(struct netbuf *, struct netbuf *);
104 
105 void
106 svc_dg_xprtfree(SVCXPRT *xprt)
107 {
108 /* LINTED pointer alignment */
109 	SVCXPRT_EXT		*xt = xprt ? SVCEXT(xprt) : NULL;
110 /* LINTED pointer alignment */
111 	struct svc_dg_data	*su = xprt ? su_data(xprt) : NULL;
112 
113 	if (xprt == NULL)
114 		return;
115 	if (xprt->xp_netid)
116 		free(xprt->xp_netid);
117 	if (xprt->xp_tp)
118 		free(xprt->xp_tp);
119 	if (xt->parent == NULL)
120 		if (xprt->xp_ltaddr.buf)
121 			free(xprt->xp_ltaddr.buf);
122 	if (xprt->xp_rtaddr.buf)
123 		free(xprt->xp_rtaddr.buf);
124 	if (su != NULL) {
125 		XDR_DESTROY(&(su->su_xdrs));
126 		free(su);
127 	}
128 	if (rpc_buffer(xprt))
129 		free(rpc_buffer(xprt));
130 	svc_xprt_free(xprt);
131 }
132 
133 SVCXPRT *
134 svc_dg_create_private(int fd, uint_t sendsize, uint_t recvsize)
135 {
136 	SVCXPRT *xprt;
137 	struct svc_dg_data *su = NULL;
138 	struct t_info tinfo;
139 
140 	if (RPC_FD_NOTIN_FDSET(fd)) {
141 		errno = EBADF;
142 		t_errno = TBADF;
143 		syslog(LOG_ERR, svc_dg_str, svc_dg_err3);
144 		return (NULL);
145 	}
146 
147 	if (t_getinfo(fd, &tinfo) == -1) {
148 		syslog(LOG_ERR, svc_dg_str, svc_dg_err1);
149 		return (NULL);
150 	}
151 	/*
152 	 * Find the receive and the send size
153 	 */
154 	sendsize = __rpc_get_t_size((int)sendsize, tinfo.tsdu);
155 	recvsize = __rpc_get_t_size((int)recvsize, tinfo.tsdu);
156 	if ((sendsize == 0) || (recvsize == 0)) {
157 		syslog(LOG_ERR, svc_dg_str, svc_dg_err2);
158 		return (NULL);
159 	}
160 
161 	if ((xprt = svc_xprt_alloc()) == NULL)
162 		goto freedata;
163 /* LINTED pointer alignment */
164 	svc_flags(xprt) |= SVC_DGRAM;
165 
166 	su = malloc(sizeof (*su));
167 	if (su == NULL)
168 		goto freedata;
169 	su->su_iosz = ((MAX(sendsize, recvsize) + 3) / 4) * 4;
170 	if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL)
171 		goto freedata;
172 	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz,
173 		XDR_DECODE);
174 	su->su_cache = NULL;
175 	xprt->xp_fd = fd;
176 	xprt->xp_p2 = (caddr_t)su;
177 	xprt->xp_verf.oa_base = su->su_verfbody;
178 	xprt->xp_ops = svc_dg_ops();
179 
180 	su->su_tudata.addr.maxlen =  0; /* Fill in later */
181 
182 	su->su_tudata.udata.buf = (char *)rpc_buffer(xprt);
183 	su->su_tudata.opt.buf = (char *)su->opts;
184 	su->su_tudata.udata.maxlen = su->su_iosz;
185 	su->su_tudata.opt.maxlen = MAX_OPT_WORDS << 2;  /* no of bytes */
186 /* LINTED pointer alignment */
187 	SVC_XP_AUTH(xprt).svc_ah_ops = svc_auth_any_ops;
188 /* LINTED pointer alignment */
189 	SVC_XP_AUTH(xprt).svc_ah_private = NULL;
190 	return (xprt);
191 freedata:
192 	(void) syslog(LOG_ERR, svc_dg_str, __no_mem_str);
193 	if (xprt)
194 		svc_dg_xprtfree(xprt);
195 	return (NULL);
196 }
197 
198 SVCXPRT *
199 svc_dg_create(const int fd, const uint_t sendsize, const uint_t recvsize)
200 {
201 	SVCXPRT *xprt;
202 
203 	if ((xprt = svc_dg_create_private(fd, sendsize, recvsize)) != NULL)
204 		xprt_register(xprt);
205 	return (xprt);
206 }
207 
208 SVCXPRT *
209 svc_dg_xprtcopy(SVCXPRT *parent)
210 {
211 	SVCXPRT			*xprt;
212 	struct svc_dg_data	*su;
213 
214 	if ((xprt = svc_xprt_alloc()) == NULL)
215 		return (NULL);
216 
217 /* LINTED pointer alignment */
218 	SVCEXT(xprt)->parent = parent;
219 /* LINTED pointer alignment */
220 	SVCEXT(xprt)->flags = SVCEXT(parent)->flags;
221 
222 	xprt->xp_fd = parent->xp_fd;
223 	xprt->xp_port = parent->xp_port;
224 	xprt->xp_ops = svc_dg_ops();
225 	if (parent->xp_tp) {
226 		xprt->xp_tp = (char *)strdup(parent->xp_tp);
227 		if (xprt->xp_tp == NULL) {
228 			syslog(LOG_ERR, "svc_dg_xprtcopy: strdup failed");
229 			svc_dg_xprtfree(xprt);
230 			return (NULL);
231 		}
232 	}
233 	if (parent->xp_netid) {
234 		xprt->xp_netid = (char *)strdup(parent->xp_netid);
235 		if (xprt->xp_netid == NULL) {
236 			syslog(LOG_ERR, "svc_dg_xprtcopy: strdup failed");
237 			if (parent->xp_tp)
238 				free(parent->xp_tp);
239 			svc_dg_xprtfree(xprt);
240 			return (NULL);
241 		}
242 	}
243 	xprt->xp_ltaddr = parent->xp_ltaddr;	/* shared with parent */
244 
245 	xprt->xp_rtaddr = parent->xp_rtaddr;
246 	xprt->xp_rtaddr.buf = malloc(xprt->xp_rtaddr.maxlen);
247 	if (xprt->xp_rtaddr.buf == NULL) {
248 		svc_dg_xprtfree(xprt);
249 		return (NULL);
250 	}
251 	(void) memcpy(xprt->xp_rtaddr.buf, parent->xp_rtaddr.buf,
252 						xprt->xp_rtaddr.maxlen);
253 	xprt->xp_type = parent->xp_type;
254 
255 	if ((su = malloc(sizeof (struct svc_dg_data))) == NULL) {
256 		svc_dg_xprtfree(xprt);
257 		return (NULL);
258 	}
259 /* LINTED pointer alignment */
260 	su->su_iosz = su_data(parent)->su_iosz;
261 	if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL) {
262 		svc_dg_xprtfree(xprt);
263 		free(su);
264 		return (NULL);
265 	}
266 	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz,
267 		XDR_DECODE);
268 	su->su_cache = NULL;
269 	su->su_tudata.addr.maxlen =  0; /* Fill in later */
270 	su->su_tudata.udata.buf = (char *)rpc_buffer(xprt);
271 	su->su_tudata.opt.buf = (char *)su->opts;
272 	su->su_tudata.udata.maxlen = su->su_iosz;
273 	su->su_tudata.opt.maxlen = MAX_OPT_WORDS << 2;  /* no of bytes */
274 	xprt->xp_p2 = (caddr_t)su;	/* su_data(xprt) = su */
275 	xprt->xp_verf.oa_base = su->su_verfbody;
276 
277 	return (xprt);
278 }
279 
280 /*ARGSUSED*/
281 static enum xprt_stat
282 svc_dg_stat(SVCXPRT *xprt)
283 {
284 	return (XPRT_IDLE);
285 }
286 
287 static bool_t
288 svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg)
289 {
290 /* LINTED pointer alignment */
291 	struct svc_dg_data *su = su_data(xprt);
292 	XDR *xdrs = &(su->su_xdrs);
293 	struct t_unitdata *tu_data = &(su->su_tudata);
294 	int moreflag;
295 	struct netbuf *nbufp;
296 	struct netconfig *nconf;
297 
298 	/* XXX: tudata should have been made a part of the server handle */
299 	if (tu_data->addr.maxlen == 0)
300 		tu_data->addr = xprt->xp_rtaddr;
301 again:
302 	tu_data->addr.len = 0;
303 	tu_data->opt.len  = 0;
304 	tu_data->udata.len  = 0;
305 
306 	moreflag = 0;
307 	if (t_rcvudata(xprt->xp_fd, tu_data, &moreflag) == -1) {
308 #ifdef RPC_DEBUG
309 		syslog(LOG_ERR, "svc_dg_recv: t_rcvudata t_errno=%d errno=%d\n",
310 				t_errno, errno);
311 #endif
312 		if (t_errno == TLOOK) {
313 			int lookres;
314 
315 			lookres = t_look(xprt->xp_fd);
316 			if ((lookres & T_UDERR) &&
317 				(t_rcvuderr(xprt->xp_fd,
318 					(struct t_uderr *)0) < 0)) {
319 				/*EMPTY*/
320 #ifdef RPC_DEBUG
321 				syslog(LOG_ERR,
322 				"svc_dg_recv: t_rcvuderr t_errno = %d\n",
323 					t_errno);
324 #endif
325 			}
326 			if (lookres & T_DATA)
327 				goto again;
328 		} else if ((errno == EINTR) && (t_errno == TSYSERR))
329 			goto again;
330 		else {
331 			return (FALSE);
332 		}
333 	}
334 
335 	if ((moreflag) ||
336 		(tu_data->udata.len < 4 * (uint_t)sizeof (uint32_t))) {
337 		/*
338 		 * If moreflag is set, drop that data packet. Something wrong
339 		 */
340 		return (FALSE);
341 	}
342 	su->optbuf = tu_data->opt;
343 	xprt->xp_rtaddr.len = tu_data->addr.len;
344 	xdrs->x_op = XDR_DECODE;
345 	XDR_SETPOS(xdrs, 0);
346 	if (!xdr_callmsg(xdrs, msg))
347 		return (FALSE);
348 	su->su_xid = msg->rm_xid;
349 	if (su->su_cache != NULL) {
350 		char *reply;
351 		uint32_t replylen;
352 
353 		if (cache_get(xprt, msg, &reply, &replylen)) {
354 			/* tu_data.addr is already set */
355 			tu_data->udata.buf = reply;
356 			tu_data->udata.len = (uint_t)replylen;
357 			tu_data->opt.len = 0;
358 			(void) t_sndudata(xprt->xp_fd, tu_data);
359 			tu_data->udata.buf = (char *)rpc_buffer(xprt);
360 			return (FALSE);
361 		}
362 	}
363 
364 	/*
365 	 * get local ip address
366 	 */
367 
368 	if ((nconf = getnetconfigent(xprt->xp_netid)) != NULL) {
369 	    if (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
370 		strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
371 		if (nconf->nc_semantics == NC_TPI_CLTS) {
372 		    /* LINTED pointer cast */
373 		    nbufp = (struct netbuf *)(xprt->xp_p2);
374 		    if (__rpc_get_ltaddr(nbufp, &xprt->xp_ltaddr) < 0) {
375 			if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
376 			    syslog(LOG_ERR,
377 				"svc_dg_recv: ip(udp), t_errno=%d, errno=%d",
378 					t_errno, errno);
379 			}
380 			if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
381 			    syslog(LOG_ERR,
382 				"svc_dg_recv: ip (udp6), t_errno=%d, errno=%d",
383 					t_errno, errno);
384 			}
385 			freenetconfigent(nconf);
386 			return (FALSE);
387 		    }
388 		}
389 	    }
390 	    freenetconfigent(nconf);
391 	}
392 	return (TRUE);
393 }
394 
395 static bool_t
396 svc_dg_reply(SVCXPRT *xprt, struct rpc_msg *msg)
397 {
398 /* LINTED pointer alignment */
399 	struct svc_dg_data *su = su_data(xprt);
400 	XDR *xdrs = &(su->su_xdrs);
401 	bool_t stat = FALSE;
402 	xdrproc_t xdr_results;
403 	caddr_t xdr_location;
404 	bool_t has_args;
405 
406 	if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
407 				msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
408 		has_args = TRUE;
409 		xdr_results = msg->acpted_rply.ar_results.proc;
410 		xdr_location = msg->acpted_rply.ar_results.where;
411 		msg->acpted_rply.ar_results.proc = xdr_void;
412 		msg->acpted_rply.ar_results.where = NULL;
413 	} else
414 		has_args = FALSE;
415 
416 	xdrs->x_op = XDR_ENCODE;
417 	XDR_SETPOS(xdrs, 0);
418 	msg->rm_xid = su->su_xid;
419 	if (xdr_replymsg(xdrs, msg) && (!has_args ||
420 /* LINTED pointer alignment */
421 		SVCAUTH_WRAP(&SVC_XP_AUTH(xprt), xdrs, xdr_results,
422 							xdr_location))) {
423 		int slen;
424 		struct t_unitdata *tu_data = &(su->su_tudata);
425 
426 		slen = (int)XDR_GETPOS(xdrs);
427 		tu_data->udata.len = slen;
428 		tu_data->opt.len = 0;
429 try_again:
430 		if (t_sndudata(xprt->xp_fd, tu_data) == 0) {
431 			stat = TRUE;
432 			if (su->su_cache && slen >= 0) {
433 				cache_set(xprt, (uint32_t)slen);
434 			}
435 		} else {
436 			if (errno == EINTR)
437 				goto try_again;
438 
439 			syslog(LOG_ERR,
440 			"svc_dg_reply: t_sndudata error t_errno=%d errno=%d\n",
441 				t_errno, errno);
442 		}
443 	}
444 	return (stat);
445 }
446 
447 static bool_t
448 svc_dg_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
449 {
450 	if (svc_mt_mode != RPC_SVC_MT_NONE)
451 		svc_args_done(xprt);
452 /* LINTED pointer alignment */
453 	return (SVCAUTH_UNWRAP(&SVC_XP_AUTH(xprt),
454 				&(su_data(xprt)->su_xdrs), xdr_args, args_ptr));
455 }
456 
457 static bool_t
458 svc_dg_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
459 {
460 /* LINTED pointer alignment */
461 	XDR *xdrs = &(su_data(xprt)->su_xdrs);
462 
463 	xdrs->x_op = XDR_FREE;
464 	return ((*xdr_args)(xdrs, args_ptr));
465 }
466 
467 static void
468 svc_dg_destroy(SVCXPRT *xprt)
469 {
470 	(void) mutex_lock(&svc_mutex);
471 	_svc_dg_destroy_private(xprt);
472 	(void) mutex_unlock(&svc_mutex);
473 }
474 
475 void
476 _svc_dg_destroy_private(SVCXPRT *xprt)
477 {
478 	if (svc_mt_mode != RPC_SVC_MT_NONE) {
479 /* LINTED pointer alignment */
480 		if (SVCEXT(xprt)->parent)
481 /* LINTED pointer alignment */
482 			xprt = SVCEXT(xprt)->parent;
483 /* LINTED pointer alignment */
484 		svc_flags(xprt) |= SVC_DEFUNCT;
485 /* LINTED pointer alignment */
486 		if (SVCEXT(xprt)->refcnt > 0)
487 			return;
488 	}
489 
490 	xprt_unregister(xprt);
491 	(void) t_close(xprt->xp_fd);
492 
493 	if (svc_mt_mode != RPC_SVC_MT_NONE)
494 		svc_xprt_destroy(xprt);
495 	else
496 		svc_dg_xprtfree(xprt);
497 }
498 
499 /*ARGSUSED*/
500 static bool_t
501 svc_dg_control(SVCXPRT *xprt, const uint_t rq, void *in)
502 {
503 	switch (rq) {
504 	case SVCGET_XID:
505 		if (xprt->xp_p2 == NULL)
506 			return (FALSE);
507 		/* LINTED pointer alignment */
508 		*(uint32_t *)in = ((struct svc_dg_data *)(xprt->xp_p2))->su_xid;
509 		return (TRUE);
510 	default:
511 		return (FALSE);
512 	}
513 }
514 
515 static struct xp_ops *
516 svc_dg_ops(void)
517 {
518 	static struct xp_ops ops;
519 	extern mutex_t ops_lock;
520 
521 /* VARIABLES PROTECTED BY ops_lock: ops */
522 
523 	(void) mutex_lock(&ops_lock);
524 	if (ops.xp_recv == NULL) {
525 		ops.xp_recv = svc_dg_recv;
526 		ops.xp_stat = svc_dg_stat;
527 		ops.xp_getargs = svc_dg_getargs;
528 		ops.xp_reply = svc_dg_reply;
529 		ops.xp_freeargs = svc_dg_freeargs;
530 		ops.xp_destroy = svc_dg_destroy;
531 		ops.xp_control = svc_dg_control;
532 	}
533 	(void) mutex_unlock(&ops_lock);
534 	return (&ops);
535 }
536 
537 /*  The CACHING COMPONENT */
538 
539 /*
540  * Could have been a separate file, but some part of it depends upon the
541  * private structure of the client handle.
542  *
543  * Fifo cache for cl server
544  * Copies pointers to reply buffers into fifo cache
545  * Buffers are sent again if retransmissions are detected.
546  */
547 
548 #define	SPARSENESS 4	/* 75% sparse */
549 
550 /*
551  * An entry in the cache
552  */
553 typedef struct cache_node *cache_ptr;
554 struct cache_node {
555 	/*
556 	 * Index into cache is xid, proc, vers, prog and address
557 	 */
558 	uint32_t cache_xid;
559 	rpcproc_t cache_proc;
560 	rpcvers_t cache_vers;
561 	rpcprog_t cache_prog;
562 	struct netbuf cache_addr;
563 	/*
564 	 * The cached reply and length
565 	 */
566 	char *cache_reply;
567 	uint32_t cache_replylen;
568 	/*
569 	 * Next node on the list, if there is a collision
570 	 */
571 	cache_ptr cache_next;
572 };
573 
574 /*
575  * The entire cache
576  */
577 struct cl_cache {
578 	uint32_t uc_size;		/* size of cache */
579 	cache_ptr *uc_entries;	/* hash table of entries in cache */
580 	cache_ptr *uc_fifo;	/* fifo list of entries in cache */
581 	uint32_t uc_nextvictim;	/* points to next victim in fifo list */
582 	rpcprog_t uc_prog;	/* saved program number */
583 	rpcvers_t uc_vers;	/* saved version number */
584 	rpcproc_t uc_proc;	/* saved procedure number */
585 };
586 
587 
588 /*
589  * the hashing function
590  */
591 #define	CACHE_LOC(transp, xid)	\
592 	(xid % (SPARSENESS * ((struct cl_cache *) \
593 		su_data(transp)->su_cache)->uc_size))
594 
595 extern mutex_t	dupreq_lock;
596 
597 /*
598  * Enable use of the cache. Returns 1 on success, 0 on failure.
599  * Note: there is no disable.
600  */
601 static const char cache_enable_str[] = "svc_enablecache: %s %s";
602 static const char alloc_err[] = "could not allocate cache ";
603 static const char enable_err[] = "cache already enabled";
604 
605 int
606 svc_dg_enablecache(SVCXPRT *xprt, const uint_t size)
607 {
608 	SVCXPRT *transp;
609 	struct svc_dg_data *su;
610 	struct cl_cache *uc;
611 
612 /* LINTED pointer alignment */
613 	if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL)
614 /* LINTED pointer alignment */
615 		transp = SVCEXT(xprt)->parent;
616 	else
617 		transp = xprt;
618 /* LINTED pointer alignment */
619 	su = su_data(transp);
620 
621 	(void) mutex_lock(&dupreq_lock);
622 	if (su->su_cache != NULL) {
623 		(void) syslog(LOG_ERR, cache_enable_str,
624 				enable_err, " ");
625 		(void) mutex_unlock(&dupreq_lock);
626 		return (0);
627 	}
628 	uc = malloc(sizeof (struct cl_cache));
629 	if (uc == NULL) {
630 		(void) syslog(LOG_ERR, cache_enable_str,
631 			alloc_err, " ");
632 		(void) mutex_unlock(&dupreq_lock);
633 		return (0);
634 	}
635 	uc->uc_size = size;
636 	uc->uc_nextvictim = 0;
637 	uc->uc_entries = calloc(size * SPARSENESS, sizeof (cache_ptr));
638 	if (uc->uc_entries == NULL) {
639 		(void) syslog(LOG_ERR, cache_enable_str, alloc_err, "data");
640 		free(uc);
641 		(void) mutex_unlock(&dupreq_lock);
642 		return (0);
643 	}
644 	uc->uc_fifo = calloc(size, sizeof (cache_ptr));
645 	if (uc->uc_fifo == NULL) {
646 		(void) syslog(LOG_ERR, cache_enable_str, alloc_err, "fifo");
647 		free(uc->uc_entries);
648 		free(uc);
649 		(void) mutex_unlock(&dupreq_lock);
650 		return (0);
651 	}
652 	su->su_cache = (char *)uc;
653 	(void) mutex_unlock(&dupreq_lock);
654 	return (1);
655 }
656 
657 /*
658  * Set an entry in the cache.  It assumes that the uc entry is set from
659  * the earlier call to cache_get() for the same procedure.  This will always
660  * happen because cache_get() is calle by svc_dg_recv and cache_set() is called
661  * by svc_dg_reply().  All this hoopla because the right RPC parameters are
662  * not available at svc_dg_reply time.
663  */
664 
665 static const char cache_set_str[] = "cache_set: %s";
666 static const char cache_set_err1[] = "victim not found";
667 static const char cache_set_err2[] = "victim alloc failed";
668 static const char cache_set_err3[] = "could not allocate new rpc buffer";
669 
670 static void
671 cache_set(SVCXPRT *xprt, uint32_t replylen)
672 {
673 	SVCXPRT *parent;
674 	cache_ptr victim;
675 	cache_ptr *vicp;
676 	struct svc_dg_data *su;
677 	struct cl_cache *uc;
678 	uint_t loc;
679 	char *newbuf, *newbuf2;
680 	int my_mallocs = 0;
681 #ifdef RPC_CACHE_DEBUG
682 	struct netconfig *nconf;
683 	char *uaddr;
684 #endif
685 
686 /* LINTED pointer alignment */
687 	if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL)
688 /* LINTED pointer alignment */
689 		parent = SVCEXT(xprt)->parent;
690 	else
691 		parent = xprt;
692 /* LINTED pointer alignment */
693 	su = su_data(xprt);
694 /* LINTED pointer alignment */
695 	uc = (struct cl_cache *)su_data(parent)->su_cache;
696 
697 	(void) mutex_lock(&dupreq_lock);
698 	/*
699 	 * Find space for the new entry, either by
700 	 * reusing an old entry, or by mallocing a new one
701 	 */
702 	victim = uc->uc_fifo[uc->uc_nextvictim];
703 	if (victim != NULL) {
704 /* LINTED pointer alignment */
705 		loc = CACHE_LOC(parent, victim->cache_xid);
706 		for (vicp = &uc->uc_entries[loc];
707 			*vicp != NULL && *vicp != victim;
708 			vicp = &(*vicp)->cache_next)
709 			;
710 		if (*vicp == NULL) {
711 			(void) syslog(LOG_ERR, cache_set_str, cache_set_err1);
712 			(void) mutex_unlock(&dupreq_lock);
713 			return;
714 		}
715 		*vicp = victim->cache_next;	/* remove from cache */
716 		newbuf = victim->cache_reply;
717 	} else {
718 		victim = malloc(sizeof (struct cache_node));
719 		if (victim == NULL) {
720 			(void) syslog(LOG_ERR, cache_set_str, cache_set_err2);
721 			(void) mutex_unlock(&dupreq_lock);
722 			return;
723 		}
724 		newbuf = malloc(su->su_iosz);
725 		if (newbuf == NULL) {
726 			(void) syslog(LOG_ERR, cache_set_str, cache_set_err3);
727 			free(victim);
728 			(void) mutex_unlock(&dupreq_lock);
729 			return;
730 		}
731 		my_mallocs = 1;
732 	}
733 
734 	/*
735 	 * Store it away
736 	 */
737 #ifdef RPC_CACHE_DEBUG
738 	if (nconf = getnetconfigent(xprt->xp_netid)) {
739 		uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
740 		freenetconfigent(nconf);
741 		printf(
742 	"cache set for xid= %x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
743 			su->su_xid, uc->uc_prog, uc->uc_vers,
744 			uc->uc_proc, uaddr);
745 		free(uaddr);
746 	}
747 #endif
748 	newbuf2 = malloc(sizeof (char) * xprt->xp_rtaddr.len);
749 	if (newbuf2 == NULL) {
750 		syslog(LOG_ERR, "cache_set : out of memory");
751 		if (my_mallocs) {
752 			free(victim);
753 			free(newbuf);
754 		}
755 		(void) mutex_unlock(&dupreq_lock);
756 		return;
757 	}
758 	victim->cache_replylen = replylen;
759 	victim->cache_reply = rpc_buffer(xprt);
760 	rpc_buffer(xprt) = newbuf;
761 	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt),
762 			su->su_iosz, XDR_ENCODE);
763 	su->su_tudata.udata.buf = (char *)rpc_buffer(xprt);
764 	victim->cache_xid = su->su_xid;
765 	victim->cache_proc = uc->uc_proc;
766 	victim->cache_vers = uc->uc_vers;
767 	victim->cache_prog = uc->uc_prog;
768 	victim->cache_addr = xprt->xp_rtaddr;
769 	victim->cache_addr.buf = newbuf2;
770 	(void) memcpy(victim->cache_addr.buf, xprt->xp_rtaddr.buf,
771 			(int)xprt->xp_rtaddr.len);
772 /* LINTED pointer alignment */
773 	loc = CACHE_LOC(parent, victim->cache_xid);
774 	victim->cache_next = uc->uc_entries[loc];
775 	uc->uc_entries[loc] = victim;
776 	uc->uc_fifo[uc->uc_nextvictim++] = victim;
777 	uc->uc_nextvictim %= uc->uc_size;
778 	(void) mutex_unlock(&dupreq_lock);
779 }
780 
781 /*
782  * Try to get an entry from the cache
783  * return 1 if found, 0 if not found and set the stage for cache_set()
784  */
785 static int
786 cache_get(SVCXPRT *xprt, struct rpc_msg *msg, char **replyp,
787 							uint32_t *replylenp)
788 {
789 	SVCXPRT *parent;
790 	uint_t loc;
791 	cache_ptr ent;
792 	struct svc_dg_data *su;
793 	struct cl_cache *uc;
794 #ifdef RPC_CACHE_DEBUG
795 	struct netconfig *nconf;
796 	char *uaddr;
797 #endif
798 
799 /* LINTED pointer alignment */
800 	if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL)
801 /* LINTED pointer alignment */
802 		parent = SVCEXT(xprt)->parent;
803 	else
804 		parent = xprt;
805 /* LINTED pointer alignment */
806 	su = su_data(xprt);
807 /* LINTED pointer alignment */
808 	uc = (struct cl_cache *)su_data(parent)->su_cache;
809 
810 	(void) mutex_lock(&dupreq_lock);
811 /* LINTED pointer alignment */
812 	loc = CACHE_LOC(parent, su->su_xid);
813 	for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
814 		if (ent->cache_xid == su->su_xid &&
815 			ent->cache_proc == msg->rm_call.cb_proc &&
816 			ent->cache_vers == msg->rm_call.cb_vers &&
817 			ent->cache_prog == msg->rm_call.cb_prog &&
818 			ent->cache_addr.len == xprt->xp_rtaddr.len &&
819 			(memcmp(ent->cache_addr.buf, xprt->xp_rtaddr.buf,
820 				xprt->xp_rtaddr.len) == 0)) {
821 #ifdef RPC_CACHE_DEBUG
822 			if (nconf = getnetconfigent(xprt->xp_netid)) {
823 				uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
824 				freenetconfigent(nconf);
825 				printf(
826 	"cache entry found for xid=%x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
827 					su->su_xid, msg->rm_call.cb_prog,
828 					msg->rm_call.cb_vers,
829 					msg->rm_call.cb_proc, uaddr);
830 				free(uaddr);
831 			}
832 #endif
833 			*replyp = ent->cache_reply;
834 			*replylenp = ent->cache_replylen;
835 			(void) mutex_unlock(&dupreq_lock);
836 			return (1);
837 		}
838 	}
839 	/*
840 	 * Failed to find entry
841 	 * Remember a few things so we can do a set later
842 	 */
843 	uc->uc_proc = msg->rm_call.cb_proc;
844 	uc->uc_vers = msg->rm_call.cb_vers;
845 	uc->uc_prog = msg->rm_call.cb_prog;
846 	(void) mutex_unlock(&dupreq_lock);
847 	return (0);
848 }
849