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