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