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