xref: /illumos-gate/usr/src/lib/libnsl/rpc/svc_dg.c (revision 3db86aab)
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 2006 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 <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 /*
288  * Find the SCM_UCRED in src and place a pointer to that option alone in dest.
289  * Note that these two 'netbuf' structures might be the same one, so the code
290  * has to be careful about referring to src after changing dest.
291  */
292 static void
293 extract_cred(const struct netbuf *src, struct netbuf *dest)
294 {
295 	char *cp = src->buf;
296 	unsigned int len = src->len;
297 	const struct T_opthdr *opt;
298 	unsigned int olen;
299 
300 	while (len >= sizeof (*opt)) {
301 		/* LINTED: pointer alignment */
302 		opt = (const struct T_opthdr *)cp;
303 		olen = opt->len;
304 		if (olen > len || olen < sizeof (*opt) ||
305 		    !IS_P2ALIGNED(olen, sizeof (t_uscalar_t)))
306 			break;
307 		if (opt->level == SOL_SOCKET && opt->name == SCM_UCRED) {
308 			dest->buf = cp;
309 			dest->len = olen;
310 			return;
311 		}
312 		cp += olen;
313 		len -= olen;
314 	}
315 	dest->len = 0;
316 }
317 
318 static bool_t
319 svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg)
320 {
321 /* LINTED pointer alignment */
322 	struct svc_dg_data *su = su_data(xprt);
323 	XDR *xdrs = &(su->su_xdrs);
324 	struct t_unitdata *tu_data = &(su->su_tudata);
325 	int moreflag;
326 	struct netbuf *nbufp;
327 	struct netconfig *nconf;
328 
329 	/* XXX: tudata should have been made a part of the server handle */
330 	if (tu_data->addr.maxlen == 0)
331 		tu_data->addr = xprt->xp_rtaddr;
332 again:
333 	tu_data->addr.len = 0;
334 	tu_data->opt.len  = 0;
335 	tu_data->udata.len  = 0;
336 
337 	moreflag = 0;
338 	if (t_rcvudata(xprt->xp_fd, tu_data, &moreflag) == -1) {
339 #ifdef RPC_DEBUG
340 		syslog(LOG_ERR, "svc_dg_recv: t_rcvudata t_errno=%d errno=%d\n",
341 				t_errno, errno);
342 #endif
343 		if (t_errno == TLOOK) {
344 			int lookres;
345 
346 			lookres = t_look(xprt->xp_fd);
347 			if ((lookres & T_UDERR) &&
348 				(t_rcvuderr(xprt->xp_fd,
349 					(struct t_uderr *)0) < 0)) {
350 				/*EMPTY*/
351 #ifdef RPC_DEBUG
352 				syslog(LOG_ERR,
353 				"svc_dg_recv: t_rcvuderr t_errno = %d\n",
354 					t_errno);
355 #endif
356 			}
357 			if (lookres & T_DATA)
358 				goto again;
359 		} else if ((errno == EINTR) && (t_errno == TSYSERR))
360 			goto again;
361 		else {
362 			return (FALSE);
363 		}
364 	}
365 
366 	if ((moreflag) ||
367 		(tu_data->udata.len < 4 * (uint_t)sizeof (uint32_t))) {
368 		/*
369 		 * If moreflag is set, drop that data packet. Something wrong
370 		 */
371 		return (FALSE);
372 	}
373 	su->optbuf = tu_data->opt;
374 	xprt->xp_rtaddr.len = tu_data->addr.len;
375 	xdrs->x_op = XDR_DECODE;
376 	XDR_SETPOS(xdrs, 0);
377 	if (!xdr_callmsg(xdrs, msg))
378 		return (FALSE);
379 	su->su_xid = msg->rm_xid;
380 	if (su->su_cache != NULL) {
381 		char *reply;
382 		uint32_t replylen;
383 
384 		if (cache_get(xprt, msg, &reply, &replylen)) {
385 			/* tu_data.addr is already set */
386 			tu_data->udata.buf = reply;
387 			tu_data->udata.len = (uint_t)replylen;
388 			extract_cred(&tu_data->opt, &tu_data->opt);
389 			(void) t_sndudata(xprt->xp_fd, tu_data);
390 			tu_data->udata.buf = (char *)rpc_buffer(xprt);
391 			tu_data->opt.buf = (char *)su->opts;
392 			return (FALSE);
393 		}
394 	}
395 
396 	/*
397 	 * get local ip address
398 	 */
399 
400 	if ((nconf = getnetconfigent(xprt->xp_netid)) != NULL) {
401 	    if (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
402 		strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
403 		if (nconf->nc_semantics == NC_TPI_CLTS) {
404 		    /* LINTED pointer cast */
405 		    nbufp = (struct netbuf *)(xprt->xp_p2);
406 		    if (__rpc_get_ltaddr(nbufp, &xprt->xp_ltaddr) < 0) {
407 			if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
408 			    syslog(LOG_ERR,
409 				"svc_dg_recv: ip(udp), t_errno=%d, errno=%d",
410 					t_errno, errno);
411 			}
412 			if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
413 			    syslog(LOG_ERR,
414 				"svc_dg_recv: ip (udp6), t_errno=%d, errno=%d",
415 					t_errno, errno);
416 			}
417 			freenetconfigent(nconf);
418 			return (FALSE);
419 		    }
420 		}
421 	    }
422 	    freenetconfigent(nconf);
423 	}
424 	return (TRUE);
425 }
426 
427 static bool_t
428 svc_dg_reply(SVCXPRT *xprt, struct rpc_msg *msg)
429 {
430 /* LINTED pointer alignment */
431 	struct svc_dg_data *su = su_data(xprt);
432 	XDR *xdrs = &(su->su_xdrs);
433 	bool_t stat = FALSE;
434 	xdrproc_t xdr_results;
435 	caddr_t xdr_location;
436 	bool_t has_args;
437 
438 	if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
439 				msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
440 		has_args = TRUE;
441 		xdr_results = msg->acpted_rply.ar_results.proc;
442 		xdr_location = msg->acpted_rply.ar_results.where;
443 		msg->acpted_rply.ar_results.proc = xdr_void;
444 		msg->acpted_rply.ar_results.where = NULL;
445 	} else
446 		has_args = FALSE;
447 
448 	xdrs->x_op = XDR_ENCODE;
449 	XDR_SETPOS(xdrs, 0);
450 	msg->rm_xid = su->su_xid;
451 	if (xdr_replymsg(xdrs, msg) && (!has_args ||
452 /* LINTED pointer alignment */
453 		SVCAUTH_WRAP(&SVC_XP_AUTH(xprt), xdrs, xdr_results,
454 							xdr_location))) {
455 		int slen;
456 		struct t_unitdata *tu_data = &(su->su_tudata);
457 
458 		slen = (int)XDR_GETPOS(xdrs);
459 		tu_data->udata.len = slen;
460 		extract_cred(&su->optbuf, &tu_data->opt);
461 try_again:
462 		if (t_sndudata(xprt->xp_fd, tu_data) == 0) {
463 			stat = TRUE;
464 			if (su->su_cache && slen >= 0) {
465 				cache_set(xprt, (uint32_t)slen);
466 			}
467 		} else {
468 			if (errno == EINTR)
469 				goto try_again;
470 
471 			syslog(LOG_ERR,
472 			"svc_dg_reply: t_sndudata error t_errno=%d errno=%d\n",
473 				t_errno, errno);
474 		}
475 		tu_data->opt.buf = (char *)su->opts;
476 	}
477 	return (stat);
478 }
479 
480 static bool_t
481 svc_dg_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
482 {
483 	if (svc_mt_mode != RPC_SVC_MT_NONE)
484 		svc_args_done(xprt);
485 /* LINTED pointer alignment */
486 	return (SVCAUTH_UNWRAP(&SVC_XP_AUTH(xprt),
487 				&(su_data(xprt)->su_xdrs), xdr_args, args_ptr));
488 }
489 
490 static bool_t
491 svc_dg_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
492 {
493 /* LINTED pointer alignment */
494 	XDR *xdrs = &(su_data(xprt)->su_xdrs);
495 
496 	xdrs->x_op = XDR_FREE;
497 	return ((*xdr_args)(xdrs, args_ptr));
498 }
499 
500 static void
501 svc_dg_destroy(SVCXPRT *xprt)
502 {
503 	(void) mutex_lock(&svc_mutex);
504 	_svc_dg_destroy_private(xprt);
505 	(void) mutex_unlock(&svc_mutex);
506 }
507 
508 void
509 _svc_dg_destroy_private(SVCXPRT *xprt)
510 {
511 	if (svc_mt_mode != RPC_SVC_MT_NONE) {
512 /* LINTED pointer alignment */
513 		if (SVCEXT(xprt)->parent)
514 /* LINTED pointer alignment */
515 			xprt = SVCEXT(xprt)->parent;
516 /* LINTED pointer alignment */
517 		svc_flags(xprt) |= SVC_DEFUNCT;
518 /* LINTED pointer alignment */
519 		if (SVCEXT(xprt)->refcnt > 0)
520 			return;
521 	}
522 
523 	xprt_unregister(xprt);
524 	(void) t_close(xprt->xp_fd);
525 
526 	if (svc_mt_mode != RPC_SVC_MT_NONE)
527 		svc_xprt_destroy(xprt);
528 	else
529 		svc_dg_xprtfree(xprt);
530 }
531 
532 /*ARGSUSED*/
533 static bool_t
534 svc_dg_control(SVCXPRT *xprt, const uint_t rq, void *in)
535 {
536 	switch (rq) {
537 	case SVCGET_XID:
538 		if (xprt->xp_p2 == NULL)
539 			return (FALSE);
540 		/* LINTED pointer alignment */
541 		*(uint32_t *)in = ((struct svc_dg_data *)(xprt->xp_p2))->su_xid;
542 		return (TRUE);
543 	default:
544 		return (FALSE);
545 	}
546 }
547 
548 static struct xp_ops *
549 svc_dg_ops(void)
550 {
551 	static struct xp_ops ops;
552 	extern mutex_t ops_lock;
553 
554 /* VARIABLES PROTECTED BY ops_lock: ops */
555 
556 	(void) mutex_lock(&ops_lock);
557 	if (ops.xp_recv == NULL) {
558 		ops.xp_recv = svc_dg_recv;
559 		ops.xp_stat = svc_dg_stat;
560 		ops.xp_getargs = svc_dg_getargs;
561 		ops.xp_reply = svc_dg_reply;
562 		ops.xp_freeargs = svc_dg_freeargs;
563 		ops.xp_destroy = svc_dg_destroy;
564 		ops.xp_control = svc_dg_control;
565 	}
566 	(void) mutex_unlock(&ops_lock);
567 	return (&ops);
568 }
569 
570 /*  The CACHING COMPONENT */
571 
572 /*
573  * Could have been a separate file, but some part of it depends upon the
574  * private structure of the client handle.
575  *
576  * Fifo cache for cl server
577  * Copies pointers to reply buffers into fifo cache
578  * Buffers are sent again if retransmissions are detected.
579  */
580 
581 #define	SPARSENESS 4	/* 75% sparse */
582 
583 /*
584  * An entry in the cache
585  */
586 typedef struct cache_node *cache_ptr;
587 struct cache_node {
588 	/*
589 	 * Index into cache is xid, proc, vers, prog and address
590 	 */
591 	uint32_t cache_xid;
592 	rpcproc_t cache_proc;
593 	rpcvers_t cache_vers;
594 	rpcprog_t cache_prog;
595 	struct netbuf cache_addr;
596 	/*
597 	 * The cached reply and length
598 	 */
599 	char *cache_reply;
600 	uint32_t cache_replylen;
601 	/*
602 	 * Next node on the list, if there is a collision
603 	 */
604 	cache_ptr cache_next;
605 };
606 
607 /*
608  * The entire cache
609  */
610 struct cl_cache {
611 	uint32_t uc_size;		/* size of cache */
612 	cache_ptr *uc_entries;	/* hash table of entries in cache */
613 	cache_ptr *uc_fifo;	/* fifo list of entries in cache */
614 	uint32_t uc_nextvictim;	/* points to next victim in fifo list */
615 	rpcprog_t uc_prog;	/* saved program number */
616 	rpcvers_t uc_vers;	/* saved version number */
617 	rpcproc_t uc_proc;	/* saved procedure number */
618 };
619 
620 
621 /*
622  * the hashing function
623  */
624 #define	CACHE_LOC(transp, xid)	\
625 	(xid % (SPARSENESS * ((struct cl_cache *) \
626 		su_data(transp)->su_cache)->uc_size))
627 
628 extern mutex_t	dupreq_lock;
629 
630 /*
631  * Enable use of the cache. Returns 1 on success, 0 on failure.
632  * Note: there is no disable.
633  */
634 static const char cache_enable_str[] = "svc_enablecache: %s %s";
635 static const char alloc_err[] = "could not allocate cache ";
636 static const char enable_err[] = "cache already enabled";
637 
638 int
639 svc_dg_enablecache(SVCXPRT *xprt, const uint_t size)
640 {
641 	SVCXPRT *transp;
642 	struct svc_dg_data *su;
643 	struct cl_cache *uc;
644 
645 /* LINTED pointer alignment */
646 	if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL)
647 /* LINTED pointer alignment */
648 		transp = SVCEXT(xprt)->parent;
649 	else
650 		transp = xprt;
651 /* LINTED pointer alignment */
652 	su = su_data(transp);
653 
654 	(void) mutex_lock(&dupreq_lock);
655 	if (su->su_cache != NULL) {
656 		(void) syslog(LOG_ERR, cache_enable_str,
657 				enable_err, " ");
658 		(void) mutex_unlock(&dupreq_lock);
659 		return (0);
660 	}
661 	uc = malloc(sizeof (struct cl_cache));
662 	if (uc == NULL) {
663 		(void) syslog(LOG_ERR, cache_enable_str,
664 			alloc_err, " ");
665 		(void) mutex_unlock(&dupreq_lock);
666 		return (0);
667 	}
668 	uc->uc_size = size;
669 	uc->uc_nextvictim = 0;
670 	uc->uc_entries = calloc(size * SPARSENESS, sizeof (cache_ptr));
671 	if (uc->uc_entries == NULL) {
672 		(void) syslog(LOG_ERR, cache_enable_str, alloc_err, "data");
673 		free(uc);
674 		(void) mutex_unlock(&dupreq_lock);
675 		return (0);
676 	}
677 	uc->uc_fifo = calloc(size, sizeof (cache_ptr));
678 	if (uc->uc_fifo == NULL) {
679 		(void) syslog(LOG_ERR, cache_enable_str, alloc_err, "fifo");
680 		free(uc->uc_entries);
681 		free(uc);
682 		(void) mutex_unlock(&dupreq_lock);
683 		return (0);
684 	}
685 	su->su_cache = (char *)uc;
686 	(void) mutex_unlock(&dupreq_lock);
687 	return (1);
688 }
689 
690 /*
691  * Set an entry in the cache.  It assumes that the uc entry is set from
692  * the earlier call to cache_get() for the same procedure.  This will always
693  * happen because cache_get() is calle by svc_dg_recv and cache_set() is called
694  * by svc_dg_reply().  All this hoopla because the right RPC parameters are
695  * not available at svc_dg_reply time.
696  */
697 
698 static const char cache_set_str[] = "cache_set: %s";
699 static const char cache_set_err1[] = "victim not found";
700 static const char cache_set_err2[] = "victim alloc failed";
701 static const char cache_set_err3[] = "could not allocate new rpc buffer";
702 
703 static void
704 cache_set(SVCXPRT *xprt, uint32_t replylen)
705 {
706 	SVCXPRT *parent;
707 	cache_ptr victim;
708 	cache_ptr *vicp;
709 	struct svc_dg_data *su;
710 	struct cl_cache *uc;
711 	uint_t loc;
712 	char *newbuf, *newbuf2;
713 	int my_mallocs = 0;
714 #ifdef RPC_CACHE_DEBUG
715 	struct netconfig *nconf;
716 	char *uaddr;
717 #endif
718 
719 /* LINTED pointer alignment */
720 	if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL)
721 /* LINTED pointer alignment */
722 		parent = SVCEXT(xprt)->parent;
723 	else
724 		parent = xprt;
725 /* LINTED pointer alignment */
726 	su = su_data(xprt);
727 /* LINTED pointer alignment */
728 	uc = (struct cl_cache *)su_data(parent)->su_cache;
729 
730 	(void) mutex_lock(&dupreq_lock);
731 	/*
732 	 * Find space for the new entry, either by
733 	 * reusing an old entry, or by mallocing a new one
734 	 */
735 	victim = uc->uc_fifo[uc->uc_nextvictim];
736 	if (victim != NULL) {
737 /* LINTED pointer alignment */
738 		loc = CACHE_LOC(parent, victim->cache_xid);
739 		for (vicp = &uc->uc_entries[loc];
740 			*vicp != NULL && *vicp != victim;
741 			vicp = &(*vicp)->cache_next)
742 			;
743 		if (*vicp == NULL) {
744 			(void) syslog(LOG_ERR, cache_set_str, cache_set_err1);
745 			(void) mutex_unlock(&dupreq_lock);
746 			return;
747 		}
748 		*vicp = victim->cache_next;	/* remove from cache */
749 		newbuf = victim->cache_reply;
750 	} else {
751 		victim = malloc(sizeof (struct cache_node));
752 		if (victim == NULL) {
753 			(void) syslog(LOG_ERR, cache_set_str, cache_set_err2);
754 			(void) mutex_unlock(&dupreq_lock);
755 			return;
756 		}
757 		newbuf = malloc(su->su_iosz);
758 		if (newbuf == NULL) {
759 			(void) syslog(LOG_ERR, cache_set_str, cache_set_err3);
760 			free(victim);
761 			(void) mutex_unlock(&dupreq_lock);
762 			return;
763 		}
764 		my_mallocs = 1;
765 	}
766 
767 	/*
768 	 * Store it away
769 	 */
770 #ifdef RPC_CACHE_DEBUG
771 	if (nconf = getnetconfigent(xprt->xp_netid)) {
772 		uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
773 		freenetconfigent(nconf);
774 		printf(
775 	"cache set for xid= %x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
776 			su->su_xid, uc->uc_prog, uc->uc_vers,
777 			uc->uc_proc, uaddr);
778 		free(uaddr);
779 	}
780 #endif
781 	newbuf2 = malloc(sizeof (char) * xprt->xp_rtaddr.len);
782 	if (newbuf2 == NULL) {
783 		syslog(LOG_ERR, "cache_set : out of memory");
784 		if (my_mallocs) {
785 			free(victim);
786 			free(newbuf);
787 		}
788 		(void) mutex_unlock(&dupreq_lock);
789 		return;
790 	}
791 	victim->cache_replylen = replylen;
792 	victim->cache_reply = rpc_buffer(xprt);
793 	rpc_buffer(xprt) = newbuf;
794 	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt),
795 			su->su_iosz, XDR_ENCODE);
796 	su->su_tudata.udata.buf = (char *)rpc_buffer(xprt);
797 	victim->cache_xid = su->su_xid;
798 	victim->cache_proc = uc->uc_proc;
799 	victim->cache_vers = uc->uc_vers;
800 	victim->cache_prog = uc->uc_prog;
801 	victim->cache_addr = xprt->xp_rtaddr;
802 	victim->cache_addr.buf = newbuf2;
803 	(void) memcpy(victim->cache_addr.buf, xprt->xp_rtaddr.buf,
804 			(int)xprt->xp_rtaddr.len);
805 /* LINTED pointer alignment */
806 	loc = CACHE_LOC(parent, victim->cache_xid);
807 	victim->cache_next = uc->uc_entries[loc];
808 	uc->uc_entries[loc] = victim;
809 	uc->uc_fifo[uc->uc_nextvictim++] = victim;
810 	uc->uc_nextvictim %= uc->uc_size;
811 	(void) mutex_unlock(&dupreq_lock);
812 }
813 
814 /*
815  * Try to get an entry from the cache
816  * return 1 if found, 0 if not found and set the stage for cache_set()
817  */
818 static int
819 cache_get(SVCXPRT *xprt, struct rpc_msg *msg, char **replyp,
820 							uint32_t *replylenp)
821 {
822 	SVCXPRT *parent;
823 	uint_t loc;
824 	cache_ptr ent;
825 	struct svc_dg_data *su;
826 	struct cl_cache *uc;
827 #ifdef RPC_CACHE_DEBUG
828 	struct netconfig *nconf;
829 	char *uaddr;
830 #endif
831 
832 /* LINTED pointer alignment */
833 	if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL)
834 /* LINTED pointer alignment */
835 		parent = SVCEXT(xprt)->parent;
836 	else
837 		parent = xprt;
838 /* LINTED pointer alignment */
839 	su = su_data(xprt);
840 /* LINTED pointer alignment */
841 	uc = (struct cl_cache *)su_data(parent)->su_cache;
842 
843 	(void) mutex_lock(&dupreq_lock);
844 /* LINTED pointer alignment */
845 	loc = CACHE_LOC(parent, su->su_xid);
846 	for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
847 		if (ent->cache_xid == su->su_xid &&
848 			ent->cache_proc == msg->rm_call.cb_proc &&
849 			ent->cache_vers == msg->rm_call.cb_vers &&
850 			ent->cache_prog == msg->rm_call.cb_prog &&
851 			ent->cache_addr.len == xprt->xp_rtaddr.len &&
852 			(memcmp(ent->cache_addr.buf, xprt->xp_rtaddr.buf,
853 				xprt->xp_rtaddr.len) == 0)) {
854 #ifdef RPC_CACHE_DEBUG
855 			if (nconf = getnetconfigent(xprt->xp_netid)) {
856 				uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
857 				freenetconfigent(nconf);
858 				printf(
859 	"cache entry found for xid=%x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
860 					su->su_xid, msg->rm_call.cb_prog,
861 					msg->rm_call.cb_vers,
862 					msg->rm_call.cb_proc, uaddr);
863 				free(uaddr);
864 			}
865 #endif
866 			*replyp = ent->cache_reply;
867 			*replylenp = ent->cache_replylen;
868 			(void) mutex_unlock(&dupreq_lock);
869 			return (1);
870 		}
871 	}
872 	/*
873 	 * Failed to find entry
874 	 * Remember a few things so we can do a set later
875 	 */
876 	uc->uc_proc = msg->rm_call.cb_proc;
877 	uc->uc_vers = msg->rm_call.cb_vers;
878 	uc->uc_prog = msg->rm_call.cb_prog;
879 	(void) mutex_unlock(&dupreq_lock);
880 	return (0);
881 }
882