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