xref: /illumos-gate/usr/src/lib/libnsl/rpc/svc_dg.c (revision 002c70ff32f5df6f93c15f88d351ce26443e6ee7)
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 
63 static struct xp_ops *svc_dg_ops();
64 static void cache_set();
65 static 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  */
78 static const char svc_dg_str[] = "svc_dg_create: %s";
79 static const char svc_dg_err1[] = "could not get transport information";
80 static const char svc_dg_err2[] = " transport does not support data transfer";
81 static const char svc_dg_err3[] =
82 		"fd > FD_SETSIZE; Use rpc_control(RPC_SVC_USE_POLLFD,...);";
83 static const char __no_mem_str[] = "out of memory";
84 
85 /* Structure used to initialize SVC_XP_AUTH(xprt).svc_ah_ops. */
86 extern struct svc_auth_ops svc_auth_any_ops;
87 extern int __rpc_get_ltaddr(struct netbuf *, struct netbuf *);
88 
89 void
90 svc_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 
117 SVCXPRT *
118 svc_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);
175 freedata:
176 	(void) syslog(LOG_ERR, svc_dg_str, __no_mem_str);
177 	if (xprt)
178 		svc_dg_xprtfree(xprt);
179 	return (NULL);
180 }
181 
182 SVCXPRT *
183 svc_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 
192 SVCXPRT *
193 svc_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*/
265 static enum xprt_stat
266 svc_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  */
276 static void
277 extract_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 
302 static bool_t
303 svc_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;
316 again:
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 
411 static bool_t
412 svc_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);
445 try_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 
464 static bool_t
465 svc_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 
475 static bool_t
476 svc_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 
485 static void
486 svc_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 
493 void
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*/
518 static bool_t
519 svc_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 
533 static struct xp_ops *
534 svc_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  */
571 typedef struct cache_node *cache_ptr;
572 struct 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  */
595 struct 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 
613 extern 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  */
619 static const char cache_enable_str[] = "svc_enablecache: %s %s";
620 static const char alloc_err[] = "could not allocate cache ";
621 static const char enable_err[] = "cache already enabled";
622 
623 int
624 svc_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 
683 static const char cache_set_str[] = "cache_set: %s";
684 static const char cache_set_err1[] = "victim not found";
685 static const char cache_set_err2[] = "victim alloc failed";
686 static const char cache_set_err3[] = "could not allocate new rpc buffer";
687 
688 static void
689 cache_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  */
803 static int
804 cache_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