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