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