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