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 2015 Nexenta Systems, Inc. All rights reserved. 24 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 25 * Copyright (c) 2012 by Delphix. All rights reserved. 26 * Copyright 2012 Marcel Telka <marcel@telka.sk> 27 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. 28 */ 29 30 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 31 /* All Rights Reserved */ 32 33 /* 34 * Portions of this source code were derived from Berkeley 4.3 BSD 35 * under license from the Regents of the University of California. 36 */ 37 38 /* 39 * svc_clts.c 40 * Server side for RPC in the kernel. 41 * 42 */ 43 44 #include <sys/param.h> 45 #include <sys/types.h> 46 #include <sys/sysmacros.h> 47 #include <sys/file.h> 48 #include <sys/stream.h> 49 #include <sys/strsun.h> 50 #include <sys/strsubr.h> 51 #include <sys/tihdr.h> 52 #include <sys/tiuser.h> 53 #include <sys/t_kuser.h> 54 #include <sys/fcntl.h> 55 #include <sys/errno.h> 56 #include <sys/kmem.h> 57 #include <sys/systm.h> 58 #include <sys/cmn_err.h> 59 #include <sys/kstat.h> 60 #include <sys/vtrace.h> 61 #include <sys/debug.h> 62 63 #include <rpc/types.h> 64 #include <rpc/xdr.h> 65 #include <rpc/auth.h> 66 #include <rpc/clnt.h> 67 #include <rpc/rpc_msg.h> 68 #include <rpc/svc.h> 69 #include <inet/ip.h> 70 71 /* 72 * Routines exported through ops vector. 73 */ 74 static bool_t svc_clts_krecv(SVCXPRT *, mblk_t *, struct rpc_msg *); 75 static bool_t svc_clts_ksend(SVCXPRT *, struct rpc_msg *); 76 static bool_t svc_clts_kgetargs(SVCXPRT *, xdrproc_t, caddr_t); 77 static bool_t svc_clts_kfreeargs(SVCXPRT *, xdrproc_t, caddr_t); 78 static void svc_clts_kdestroy(SVCMASTERXPRT *); 79 static int svc_clts_kdup(struct svc_req *, caddr_t, int, 80 struct dupreq **, bool_t *); 81 static void svc_clts_kdupdone(struct dupreq *, caddr_t, 82 void (*)(), int, int); 83 static int32_t *svc_clts_kgetres(SVCXPRT *, int); 84 static void svc_clts_kclone_destroy(SVCXPRT *); 85 static void svc_clts_kfreeres(SVCXPRT *); 86 static void svc_clts_kstart(SVCMASTERXPRT *); 87 static void svc_clts_kclone_xprt(SVCXPRT *, SVCXPRT *); 88 static void svc_clts_ktattrs(SVCXPRT *, int, void **); 89 90 /* 91 * Server transport operations vector. 92 */ 93 struct svc_ops svc_clts_op = { 94 svc_clts_krecv, /* Get requests */ 95 svc_clts_kgetargs, /* Deserialize arguments */ 96 svc_clts_ksend, /* Send reply */ 97 svc_clts_kfreeargs, /* Free argument data space */ 98 svc_clts_kdestroy, /* Destroy transport handle */ 99 svc_clts_kdup, /* Check entry in dup req cache */ 100 svc_clts_kdupdone, /* Mark entry in dup req cache as done */ 101 svc_clts_kgetres, /* Get pointer to response buffer */ 102 svc_clts_kfreeres, /* Destroy pre-serialized response header */ 103 svc_clts_kclone_destroy, /* Destroy a clone xprt */ 104 svc_clts_kstart, /* Tell `ready-to-receive' to rpcmod */ 105 svc_clts_kclone_xprt, /* transport specific clone xprt function */ 106 svc_clts_ktattrs, /* Transport specific attributes */ 107 rpcmod_hold, /* Increment transport reference count */ 108 rpcmod_release /* Decrement transport reference count */ 109 }; 110 111 /* 112 * Transport private data. 113 * Kept in xprt->xp_p2buf. 114 */ 115 struct udp_data { 116 mblk_t *ud_resp; /* buffer for response */ 117 mblk_t *ud_inmp; /* mblk chain of request */ 118 }; 119 120 #define UD_MAXSIZE 8800 121 #define UD_INITSIZE 2048 122 123 /* 124 * Connectionless server statistics 125 */ 126 static const struct rpc_clts_server { 127 kstat_named_t rscalls; 128 kstat_named_t rsbadcalls; 129 kstat_named_t rsnullrecv; 130 kstat_named_t rsbadlen; 131 kstat_named_t rsxdrcall; 132 kstat_named_t rsdupchecks; 133 kstat_named_t rsdupreqs; 134 } clts_rsstat_tmpl = { 135 { "calls", KSTAT_DATA_UINT64 }, 136 { "badcalls", KSTAT_DATA_UINT64 }, 137 { "nullrecv", KSTAT_DATA_UINT64 }, 138 { "badlen", KSTAT_DATA_UINT64 }, 139 { "xdrcall", KSTAT_DATA_UINT64 }, 140 { "dupchecks", KSTAT_DATA_UINT64 }, 141 { "dupreqs", KSTAT_DATA_UINT64 } 142 }; 143 144 static uint_t clts_rsstat_ndata = 145 sizeof (clts_rsstat_tmpl) / sizeof (kstat_named_t); 146 147 #define CLONE2STATS(clone_xprt) \ 148 (struct rpc_clts_server *)(clone_xprt)->xp_master->xp_p2 149 150 #define RSSTAT_INCR(stats, x) \ 151 atomic_inc_64(&(stats)->x.value.ui64) 152 153 /* 154 * Create a transport record. 155 * The transport record, output buffer, and private data structure 156 * are allocated. The output buffer is serialized into using xdrmem. 157 * There is one transport record per user process which implements a 158 * set of services. 159 */ 160 /* ARGSUSED */ 161 int 162 svc_clts_kcreate(file_t *fp, uint_t sendsz, struct T_info_ack *tinfo, 163 SVCMASTERXPRT **nxprt) 164 { 165 SVCMASTERXPRT *xprt; 166 struct rpcstat *rpcstat; 167 168 if (nxprt == NULL) 169 return (EINVAL); 170 171 rpcstat = zone_getspecific(rpcstat_zone_key, curproc->p_zone); 172 ASSERT(rpcstat != NULL); 173 174 xprt = kmem_zalloc(sizeof (*xprt), KM_SLEEP); 175 xprt->xp_lcladdr.buf = kmem_zalloc(sizeof (sin6_t), KM_SLEEP); 176 xprt->xp_p2 = (caddr_t)rpcstat->rpc_clts_server; 177 xprt->xp_ops = &svc_clts_op; 178 xprt->xp_msg_size = tinfo->TSDU_size; 179 180 xprt->xp_rtaddr.buf = NULL; 181 xprt->xp_rtaddr.maxlen = tinfo->ADDR_size; 182 xprt->xp_rtaddr.len = 0; 183 184 *nxprt = xprt; 185 186 return (0); 187 } 188 189 /* 190 * Destroy a transport record. 191 * Frees the space allocated for a transport record. 192 */ 193 static void 194 svc_clts_kdestroy(SVCMASTERXPRT *xprt) 195 { 196 if (xprt->xp_netid) 197 kmem_free(xprt->xp_netid, strlen(xprt->xp_netid) + 1); 198 if (xprt->xp_addrmask.maxlen) 199 kmem_free(xprt->xp_addrmask.buf, xprt->xp_addrmask.maxlen); 200 201 mutex_destroy(&xprt->xp_req_lock); 202 mutex_destroy(&xprt->xp_thread_lock); 203 204 kmem_free(xprt->xp_lcladdr.buf, sizeof (sin6_t)); 205 kmem_free(xprt, sizeof (SVCMASTERXPRT)); 206 } 207 208 /* 209 * Transport-type specific part of svc_xprt_cleanup(). 210 * Frees the message buffer space allocated for a clone of a transport record 211 */ 212 static void 213 svc_clts_kclone_destroy(SVCXPRT *clone_xprt) 214 { 215 /* LINTED pointer alignment */ 216 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 217 218 if (ud->ud_resp) { 219 /* 220 * There should not be any left over results buffer. 221 */ 222 ASSERT(ud->ud_resp->b_cont == NULL); 223 224 /* 225 * Free the T_UNITDATA_{REQ/IND} that svc_clts_krecv 226 * saved. 227 */ 228 freeb(ud->ud_resp); 229 } 230 if (ud->ud_inmp) 231 freemsg(ud->ud_inmp); 232 } 233 234 /* 235 * svc_tli_kcreate() calls this function at the end to tell 236 * rpcmod that the transport is ready to receive requests. 237 */ 238 /* ARGSUSED */ 239 static void 240 svc_clts_kstart(SVCMASTERXPRT *xprt) 241 { 242 } 243 244 static void 245 svc_clts_kclone_xprt(SVCXPRT *src_xprt, SVCXPRT *dst_xprt) 246 { 247 struct udp_data *ud_src = 248 (struct udp_data *)src_xprt->xp_p2buf; 249 struct udp_data *ud_dst = 250 (struct udp_data *)dst_xprt->xp_p2buf; 251 252 if (ud_src->ud_resp) 253 ud_dst->ud_resp = dupb(ud_src->ud_resp); 254 255 } 256 257 static void 258 svc_clts_ktattrs(SVCXPRT *clone_xprt, int attrflag, void **tattr) 259 { 260 *tattr = NULL; 261 262 switch (attrflag) { 263 case SVC_TATTR_ADDRMASK: 264 *tattr = (void *)&clone_xprt->xp_master->xp_addrmask; 265 } 266 } 267 268 /* 269 * Receive rpc requests. 270 * Pulls a request in off the socket, checks if the packet is intact, 271 * and deserializes the call packet. 272 */ 273 static bool_t 274 svc_clts_krecv(SVCXPRT *clone_xprt, mblk_t *mp, struct rpc_msg *msg) 275 { 276 /* LINTED pointer alignment */ 277 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 278 XDR *xdrs = &clone_xprt->xp_xdrin; 279 struct rpc_clts_server *stats = CLONE2STATS(clone_xprt); 280 union T_primitives *pptr; 281 int hdrsz; 282 cred_t *cr; 283 284 TRACE_0(TR_FAC_KRPC, TR_SVC_CLTS_KRECV_START, 285 "svc_clts_krecv_start:"); 286 287 RSSTAT_INCR(stats, rscalls); 288 289 /* 290 * The incoming request should start with an M_PROTO message. 291 */ 292 if (mp->b_datap->db_type != M_PROTO) { 293 goto bad; 294 } 295 296 /* 297 * The incoming request should be an T_UNITDTA_IND. There 298 * might be other messages coming up the stream, but we can 299 * ignore them. 300 */ 301 pptr = (union T_primitives *)mp->b_rptr; 302 if (pptr->type != T_UNITDATA_IND) { 303 goto bad; 304 } 305 /* 306 * Do some checking to make sure that the header at least looks okay. 307 */ 308 hdrsz = (int)(mp->b_wptr - mp->b_rptr); 309 if (hdrsz < TUNITDATAINDSZ || 310 hdrsz < (pptr->unitdata_ind.OPT_offset + 311 pptr->unitdata_ind.OPT_length) || 312 hdrsz < (pptr->unitdata_ind.SRC_offset + 313 pptr->unitdata_ind.SRC_length)) { 314 goto bad; 315 } 316 317 /* 318 * Make sure that the transport provided a usable address. 319 */ 320 if (pptr->unitdata_ind.SRC_length <= 0) { 321 goto bad; 322 } 323 /* 324 * Point the remote transport address in the service_transport 325 * handle at the address in the request. 326 */ 327 clone_xprt->xp_rtaddr.buf = (char *)mp->b_rptr + 328 pptr->unitdata_ind.SRC_offset; 329 clone_xprt->xp_rtaddr.len = pptr->unitdata_ind.SRC_length; 330 331 /* 332 * Copy the local transport address in the service_transport 333 * handle at the address in the request. We will have only 334 * the local IP address in options. 335 */ 336 ((sin_t *)(clone_xprt->xp_lcladdr.buf))->sin_family = AF_UNSPEC; 337 if (pptr->unitdata_ind.OPT_length && pptr->unitdata_ind.OPT_offset) { 338 char *dstopt = (char *)mp->b_rptr + 339 pptr->unitdata_ind.OPT_offset; 340 struct T_opthdr *toh = (struct T_opthdr *)dstopt; 341 342 if (toh->level == IPPROTO_IPV6 && toh->status == 0 && 343 toh->name == IPV6_PKTINFO) { 344 struct in6_pktinfo *pkti; 345 346 dstopt += sizeof (struct T_opthdr); 347 pkti = (struct in6_pktinfo *)dstopt; 348 ((sin6_t *)(clone_xprt->xp_lcladdr.buf))->sin6_addr 349 = pkti->ipi6_addr; 350 ((sin6_t *)(clone_xprt->xp_lcladdr.buf))->sin6_family 351 = AF_INET6; 352 } else if (toh->level == IPPROTO_IP && toh->status == 0 && 353 toh->name == IP_RECVDSTADDR) { 354 dstopt += sizeof (struct T_opthdr); 355 ((sin_t *)(clone_xprt->xp_lcladdr.buf))->sin_addr 356 = *(struct in_addr *)dstopt; 357 ((sin_t *)(clone_xprt->xp_lcladdr.buf))->sin_family 358 = AF_INET; 359 } 360 } 361 362 /* 363 * Save the first mblk which contains the T_unidata_ind in 364 * ud_resp. It will be used to generate the T_unitdata_req 365 * during the reply. 366 * We reuse any options in the T_unitdata_ind for the T_unitdata_req 367 * since we must pass any SCM_UCRED across in order for TX to 368 * work. We also make sure any cred_t is carried across. 369 */ 370 if (ud->ud_resp) { 371 if (ud->ud_resp->b_cont != NULL) { 372 cmn_err(CE_WARN, "svc_clts_krecv: ud_resp %p, " 373 "b_cont %p", (void *)ud->ud_resp, 374 (void *)ud->ud_resp->b_cont); 375 } 376 freeb(ud->ud_resp); 377 } 378 /* Move any cred_t to the first mblk in the message */ 379 cr = msg_getcred(mp, NULL); 380 if (cr != NULL) 381 mblk_setcred(mp, cr, NOPID); 382 383 ud->ud_resp = mp; 384 mp = mp->b_cont; 385 ud->ud_resp->b_cont = NULL; 386 387 xdrmblk_init(xdrs, mp, XDR_DECODE, 0); 388 389 TRACE_0(TR_FAC_KRPC, TR_XDR_CALLMSG_START, 390 "xdr_callmsg_start:"); 391 if (! xdr_callmsg(xdrs, msg)) { 392 XDR_DESTROY(xdrs); 393 TRACE_1(TR_FAC_KRPC, TR_XDR_CALLMSG_END, 394 "xdr_callmsg_end:(%S)", "bad"); 395 RSSTAT_INCR(stats, rsxdrcall); 396 goto bad; 397 } 398 TRACE_1(TR_FAC_KRPC, TR_XDR_CALLMSG_END, 399 "xdr_callmsg_end:(%S)", "good"); 400 401 clone_xprt->xp_xid = msg->rm_xid; 402 ud->ud_inmp = mp; 403 404 TRACE_1(TR_FAC_KRPC, TR_SVC_CLTS_KRECV_END, 405 "svc_clts_krecv_end:(%S)", "good"); 406 return (TRUE); 407 408 bad: 409 freemsg(mp); 410 if (ud->ud_resp) { 411 /* 412 * There should not be any left over results buffer. 413 */ 414 ASSERT(ud->ud_resp->b_cont == NULL); 415 freeb(ud->ud_resp); 416 ud->ud_resp = NULL; 417 } 418 419 RSSTAT_INCR(stats, rsbadcalls); 420 TRACE_1(TR_FAC_KRPC, TR_SVC_CLTS_KRECV_END, 421 "svc_clts_krecv_end:(%S)", "bad"); 422 return (FALSE); 423 } 424 425 /* 426 * Send rpc reply. 427 * Serialize the reply packet into the output buffer then 428 * call t_ksndudata to send it. 429 */ 430 static bool_t 431 svc_clts_ksend(SVCXPRT *clone_xprt, struct rpc_msg *msg) 432 { 433 /* LINTED pointer alignment */ 434 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 435 XDR *xdrs = &clone_xprt->xp_xdrout; 436 int stat = FALSE; 437 mblk_t *mp; 438 int msgsz; 439 struct T_unitdata_req *udreq; 440 xdrproc_t xdr_results; 441 caddr_t xdr_location; 442 bool_t has_args; 443 444 TRACE_0(TR_FAC_KRPC, TR_SVC_CLTS_KSEND_START, 445 "svc_clts_ksend_start:"); 446 447 ASSERT(ud->ud_resp != NULL); 448 449 /* 450 * If there is a result procedure specified in the reply message, 451 * it will be processed in the xdr_replymsg and SVCAUTH_WRAP. 452 * We need to make sure it won't be processed twice, so we null 453 * it for xdr_replymsg here. 454 */ 455 has_args = FALSE; 456 if (msg->rm_reply.rp_stat == MSG_ACCEPTED && 457 msg->rm_reply.rp_acpt.ar_stat == SUCCESS) { 458 if ((xdr_results = msg->acpted_rply.ar_results.proc) != NULL) { 459 has_args = TRUE; 460 xdr_location = msg->acpted_rply.ar_results.where; 461 msg->acpted_rply.ar_results.proc = xdr_void; 462 msg->acpted_rply.ar_results.where = NULL; 463 } 464 } 465 466 if (ud->ud_resp->b_cont == NULL) { 467 /* 468 * Allocate an initial mblk for the response data. 469 */ 470 while ((mp = allocb(UD_INITSIZE, BPRI_LO)) == NULL) { 471 if (strwaitbuf(UD_INITSIZE, BPRI_LO)) { 472 TRACE_1(TR_FAC_KRPC, TR_SVC_CLTS_KSEND_END, 473 "svc_clts_ksend_end:(%S)", "strwaitbuf"); 474 return (FALSE); 475 } 476 } 477 478 /* 479 * Initialize the XDR encode stream. Additional mblks 480 * will be allocated if necessary. They will be UD_MAXSIZE 481 * sized. 482 */ 483 xdrmblk_init(xdrs, mp, XDR_ENCODE, UD_MAXSIZE); 484 485 /* 486 * Leave some space for protocol headers. 487 */ 488 (void) XDR_SETPOS(xdrs, 512); 489 mp->b_rptr += 512; 490 491 msg->rm_xid = clone_xprt->xp_xid; 492 493 ud->ud_resp->b_cont = mp; 494 495 TRACE_0(TR_FAC_KRPC, TR_XDR_REPLYMSG_START, 496 "xdr_replymsg_start:"); 497 if (!(xdr_replymsg(xdrs, msg) && 498 (!has_args || SVCAUTH_WRAP(&clone_xprt->xp_auth, xdrs, 499 xdr_results, xdr_location)))) { 500 XDR_DESTROY(xdrs); 501 TRACE_1(TR_FAC_KRPC, TR_XDR_REPLYMSG_END, 502 "xdr_replymsg_end:(%S)", "bad"); 503 RPCLOG0(1, "xdr_replymsg/SVCAUTH_WRAP failed\n"); 504 goto out; 505 } 506 TRACE_1(TR_FAC_KRPC, TR_XDR_REPLYMSG_END, 507 "xdr_replymsg_end:(%S)", "good"); 508 509 } else if (!(xdr_replymsg_body(xdrs, msg) && 510 (!has_args || SVCAUTH_WRAP(&clone_xprt->xp_auth, xdrs, 511 xdr_results, xdr_location)))) { 512 XDR_DESTROY(xdrs); 513 RPCLOG0(1, "xdr_replymsg_body/SVCAUTH_WRAP failed\n"); 514 goto out; 515 } 516 517 XDR_DESTROY(xdrs); 518 519 msgsz = (int)xmsgsize(ud->ud_resp->b_cont); 520 521 if (msgsz <= 0 || (clone_xprt->xp_msg_size != -1 && 522 msgsz > clone_xprt->xp_msg_size)) { 523 #ifdef DEBUG 524 cmn_err(CE_NOTE, 525 "KRPC: server response message of %d bytes; transport limits are [0, %d]", 526 msgsz, clone_xprt->xp_msg_size); 527 #endif 528 goto out; 529 } 530 531 /* 532 * Construct the T_unitdata_req. We take advantage of the fact that 533 * T_unitdata_ind looks just like T_unitdata_req, except for the 534 * primitive type. Reusing it means we preserve the SCM_UCRED, and 535 * we must preserve it for TX to work. 536 * 537 * This has the side effect that we can also pass certain receive-side 538 * options like IPV6_PKTINFO back down the send side. This implies 539 * that we can not ASSERT on a non-NULL db_credp when we have send-side 540 * options in UDP. 541 */ 542 ASSERT(MBLKL(ud->ud_resp) >= TUNITDATAREQSZ); 543 udreq = (struct T_unitdata_req *)ud->ud_resp->b_rptr; 544 ASSERT(udreq->PRIM_type == T_UNITDATA_IND); 545 udreq->PRIM_type = T_UNITDATA_REQ; 546 547 /* 548 * If the local IPv4 transport address is known use it as a source 549 * address for the outgoing UDP packet. 550 */ 551 if (((sin_t *)(clone_xprt->xp_lcladdr.buf))->sin_family == AF_INET) { 552 struct T_opthdr *opthdr; 553 in_pktinfo_t *pktinfo; 554 size_t size; 555 556 if (udreq->DEST_length == 0) 557 udreq->OPT_offset = _TPI_ALIGN_TOPT(TUNITDATAREQSZ); 558 else 559 udreq->OPT_offset = _TPI_ALIGN_TOPT(udreq->DEST_offset + 560 udreq->DEST_length); 561 562 udreq->OPT_length = sizeof (struct T_opthdr) + 563 sizeof (in_pktinfo_t); 564 565 size = udreq->OPT_length + udreq->OPT_offset; 566 567 /* make sure we have enough space for the option data */ 568 mp = reallocb(ud->ud_resp, size, 1); 569 if (mp == NULL) 570 goto out; 571 ud->ud_resp = mp; 572 udreq = (struct T_unitdata_req *)mp->b_rptr; 573 574 /* set desired option header */ 575 opthdr = (struct T_opthdr *)(mp->b_rptr + udreq->OPT_offset); 576 opthdr->len = udreq->OPT_length; 577 opthdr->level = IPPROTO_IP; 578 opthdr->name = IP_PKTINFO; 579 580 /* 581 * 1. set source IP of outbound packet 582 * 2. value '0' for index means IP layer uses this as source 583 * address 584 */ 585 pktinfo = (in_pktinfo_t *)(opthdr + 1); 586 (void) memset(pktinfo, 0, sizeof (in_pktinfo_t)); 587 pktinfo->ipi_spec_dst.s_addr = 588 ((sin_t *)(clone_xprt->xp_lcladdr.buf))->sin_addr.s_addr; 589 pktinfo->ipi_ifindex = 0; 590 591 /* adjust the end of active data */ 592 mp->b_wptr = mp->b_rptr + size; 593 } 594 595 put(clone_xprt->xp_wq, ud->ud_resp); 596 stat = TRUE; 597 ud->ud_resp = NULL; 598 599 out: 600 if (stat == FALSE) { 601 freemsg(ud->ud_resp); 602 ud->ud_resp = NULL; 603 } 604 605 /* 606 * This is completely disgusting. If public is set it is 607 * a pointer to a structure whose first field is the address 608 * of the function to free that structure and any related 609 * stuff. (see rrokfree in nfs_xdr.c). 610 */ 611 if (xdrs->x_public) { 612 /* LINTED pointer alignment */ 613 (**((int (**)())xdrs->x_public))(xdrs->x_public); 614 } 615 616 TRACE_1(TR_FAC_KRPC, TR_SVC_CLTS_KSEND_END, 617 "svc_clts_ksend_end:(%S)", "done"); 618 return (stat); 619 } 620 621 /* 622 * Deserialize arguments. 623 */ 624 static bool_t 625 svc_clts_kgetargs(SVCXPRT *clone_xprt, xdrproc_t xdr_args, 626 caddr_t args_ptr) 627 { 628 629 /* LINTED pointer alignment */ 630 return (SVCAUTH_UNWRAP(&clone_xprt->xp_auth, &clone_xprt->xp_xdrin, 631 xdr_args, args_ptr)); 632 633 } 634 635 static bool_t 636 svc_clts_kfreeargs(SVCXPRT *clone_xprt, xdrproc_t xdr_args, 637 caddr_t args_ptr) 638 { 639 /* LINTED pointer alignment */ 640 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 641 XDR *xdrs = &clone_xprt->xp_xdrin; 642 bool_t retval; 643 644 if (args_ptr) { 645 xdrs->x_op = XDR_FREE; 646 retval = (*xdr_args)(xdrs, args_ptr); 647 } else 648 retval = TRUE; 649 650 XDR_DESTROY(xdrs); 651 652 if (ud->ud_inmp) { 653 freemsg(ud->ud_inmp); 654 ud->ud_inmp = NULL; 655 } 656 657 return (retval); 658 } 659 660 static int32_t * 661 svc_clts_kgetres(SVCXPRT *clone_xprt, int size) 662 { 663 /* LINTED pointer alignment */ 664 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 665 XDR *xdrs = &clone_xprt->xp_xdrout; 666 mblk_t *mp; 667 int32_t *buf; 668 struct rpc_msg rply; 669 670 /* 671 * Allocate an initial mblk for the response data. 672 */ 673 while ((mp = allocb(UD_INITSIZE, BPRI_LO)) == NULL) { 674 if (strwaitbuf(UD_INITSIZE, BPRI_LO)) { 675 return (NULL); 676 } 677 } 678 679 mp->b_cont = NULL; 680 681 /* 682 * Initialize the XDR encode stream. Additional mblks 683 * will be allocated if necessary. They will be UD_MAXSIZE 684 * sized. 685 */ 686 xdrmblk_init(xdrs, mp, XDR_ENCODE, UD_MAXSIZE); 687 688 /* 689 * Leave some space for protocol headers. 690 */ 691 (void) XDR_SETPOS(xdrs, 512); 692 mp->b_rptr += 512; 693 694 /* 695 * Assume a successful RPC since most of them are. 696 */ 697 rply.rm_xid = clone_xprt->xp_xid; 698 rply.rm_direction = REPLY; 699 rply.rm_reply.rp_stat = MSG_ACCEPTED; 700 rply.acpted_rply.ar_verf = clone_xprt->xp_verf; 701 rply.acpted_rply.ar_stat = SUCCESS; 702 703 if (!xdr_replymsg_hdr(xdrs, &rply)) { 704 XDR_DESTROY(xdrs); 705 freeb(mp); 706 return (NULL); 707 } 708 709 buf = XDR_INLINE(xdrs, size); 710 711 if (buf == NULL) { 712 XDR_DESTROY(xdrs); 713 freeb(mp); 714 } else { 715 ud->ud_resp->b_cont = mp; 716 } 717 718 return (buf); 719 } 720 721 static void 722 svc_clts_kfreeres(SVCXPRT *clone_xprt) 723 { 724 /* LINTED pointer alignment */ 725 struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; 726 727 if (ud->ud_resp == NULL || ud->ud_resp->b_cont == NULL) 728 return; 729 730 XDR_DESTROY(&clone_xprt->xp_xdrout); 731 732 /* 733 * SVC_FREERES() is called whenever the server decides not to 734 * send normal reply. Thus, we expect only one mblk to be allocated, 735 * because we have not attempted any XDR encoding. 736 * If we do any XDR encoding and we get an error, then SVC_REPLY() 737 * will freemsg(ud->ud_resp); 738 */ 739 ASSERT(ud->ud_resp->b_cont->b_cont == NULL); 740 freeb(ud->ud_resp->b_cont); 741 ud->ud_resp->b_cont = NULL; 742 } 743 744 /* 745 * the dup cacheing routines below provide a cache of non-failure 746 * transaction id's. rpc service routines can use this to detect 747 * retransmissions and re-send a non-failure response. 748 */ 749 750 /* 751 * MAXDUPREQS is the number of cached items. It should be adjusted 752 * to the service load so that there is likely to be a response entry 753 * when the first retransmission comes in. 754 */ 755 #define MAXDUPREQS 8192 756 757 /* 758 * This should be appropriately scaled to MAXDUPREQS. To produce as less as 759 * possible collisions it is suggested to set this to a prime. 760 */ 761 #define DRHASHSZ 2053 762 763 #define XIDHASH(xid) ((xid) % DRHASHSZ) 764 #define DRHASH(dr) XIDHASH((dr)->dr_xid) 765 #define REQTOXID(req) ((req)->rq_xprt->xp_xid) 766 767 static int ndupreqs = 0; 768 int maxdupreqs = MAXDUPREQS; 769 static kmutex_t dupreq_lock; 770 static struct dupreq *drhashtbl[DRHASHSZ]; 771 static int drhashstat[DRHASHSZ]; 772 773 static void unhash(struct dupreq *); 774 775 /* 776 * drmru points to the head of a circular linked list in lru order. 777 * drmru->dr_next == drlru 778 */ 779 struct dupreq *drmru; 780 781 /* 782 * PSARC 2003/523 Contract Private Interface 783 * svc_clts_kdup 784 * Changes must be reviewed by Solaris File Sharing 785 * Changes must be communicated to contract-2003-523@sun.com 786 * 787 * svc_clts_kdup searches the request cache and returns 0 if the 788 * request is not found in the cache. If it is found, then it 789 * returns the state of the request (in progress or done) and 790 * the status or attributes that were part of the original reply. 791 * 792 * If DUP_DONE (there is a duplicate) svc_clts_kdup copies over the 793 * value of the response. In that case, also return in *dupcachedp 794 * whether the response free routine is cached in the dupreq - in which case 795 * the caller should not be freeing it, because it will be done later 796 * in the svc_clts_kdup code when the dupreq is reused. 797 */ 798 static int 799 svc_clts_kdup(struct svc_req *req, caddr_t res, int size, struct dupreq **drpp, 800 bool_t *dupcachedp) 801 { 802 struct rpc_clts_server *stats = CLONE2STATS(req->rq_xprt); 803 struct dupreq *dr; 804 uint32_t xid; 805 uint32_t drhash; 806 int status; 807 808 xid = REQTOXID(req); 809 mutex_enter(&dupreq_lock); 810 RSSTAT_INCR(stats, rsdupchecks); 811 /* 812 * Check to see whether an entry already exists in the cache. 813 */ 814 dr = drhashtbl[XIDHASH(xid)]; 815 while (dr != NULL) { 816 if (dr->dr_xid == xid && 817 dr->dr_proc == req->rq_proc && 818 dr->dr_prog == req->rq_prog && 819 dr->dr_vers == req->rq_vers && 820 dr->dr_addr.len == req->rq_xprt->xp_rtaddr.len && 821 bcmp(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf, 822 dr->dr_addr.len) == 0) { 823 status = dr->dr_status; 824 if (status == DUP_DONE) { 825 bcopy(dr->dr_resp.buf, res, size); 826 if (dupcachedp != NULL) 827 *dupcachedp = (dr->dr_resfree != NULL); 828 } else { 829 dr->dr_status = DUP_INPROGRESS; 830 *drpp = dr; 831 } 832 RSSTAT_INCR(stats, rsdupreqs); 833 mutex_exit(&dupreq_lock); 834 return (status); 835 } 836 dr = dr->dr_chain; 837 } 838 839 /* 840 * There wasn't an entry, either allocate a new one or recycle 841 * an old one. 842 */ 843 if (ndupreqs < maxdupreqs) { 844 dr = kmem_alloc(sizeof (*dr), KM_NOSLEEP); 845 if (dr == NULL) { 846 mutex_exit(&dupreq_lock); 847 return (DUP_ERROR); 848 } 849 dr->dr_resp.buf = NULL; 850 dr->dr_resp.maxlen = 0; 851 dr->dr_addr.buf = NULL; 852 dr->dr_addr.maxlen = 0; 853 if (drmru) { 854 dr->dr_next = drmru->dr_next; 855 drmru->dr_next = dr; 856 } else { 857 dr->dr_next = dr; 858 } 859 ndupreqs++; 860 } else { 861 dr = drmru->dr_next; 862 while (dr->dr_status == DUP_INPROGRESS) { 863 dr = dr->dr_next; 864 if (dr == drmru->dr_next) { 865 cmn_err(CE_WARN, "svc_clts_kdup no slots free"); 866 mutex_exit(&dupreq_lock); 867 return (DUP_ERROR); 868 } 869 } 870 unhash(dr); 871 if (dr->dr_resfree) { 872 (*dr->dr_resfree)(dr->dr_resp.buf); 873 } 874 } 875 dr->dr_resfree = NULL; 876 drmru = dr; 877 878 dr->dr_xid = REQTOXID(req); 879 dr->dr_prog = req->rq_prog; 880 dr->dr_vers = req->rq_vers; 881 dr->dr_proc = req->rq_proc; 882 if (dr->dr_addr.maxlen < req->rq_xprt->xp_rtaddr.len) { 883 if (dr->dr_addr.buf != NULL) 884 kmem_free(dr->dr_addr.buf, dr->dr_addr.maxlen); 885 dr->dr_addr.maxlen = req->rq_xprt->xp_rtaddr.len; 886 dr->dr_addr.buf = kmem_alloc(dr->dr_addr.maxlen, 887 KM_NOSLEEP); 888 if (dr->dr_addr.buf == NULL) { 889 dr->dr_addr.maxlen = 0; 890 dr->dr_status = DUP_DROP; 891 mutex_exit(&dupreq_lock); 892 return (DUP_ERROR); 893 } 894 } 895 dr->dr_addr.len = req->rq_xprt->xp_rtaddr.len; 896 bcopy(req->rq_xprt->xp_rtaddr.buf, dr->dr_addr.buf, dr->dr_addr.len); 897 if (dr->dr_resp.maxlen < size) { 898 if (dr->dr_resp.buf != NULL) 899 kmem_free(dr->dr_resp.buf, dr->dr_resp.maxlen); 900 dr->dr_resp.maxlen = (unsigned int)size; 901 dr->dr_resp.buf = kmem_alloc(size, KM_NOSLEEP); 902 if (dr->dr_resp.buf == NULL) { 903 dr->dr_resp.maxlen = 0; 904 dr->dr_status = DUP_DROP; 905 mutex_exit(&dupreq_lock); 906 return (DUP_ERROR); 907 } 908 } 909 dr->dr_status = DUP_INPROGRESS; 910 911 drhash = (uint32_t)DRHASH(dr); 912 dr->dr_chain = drhashtbl[drhash]; 913 drhashtbl[drhash] = dr; 914 drhashstat[drhash]++; 915 mutex_exit(&dupreq_lock); 916 *drpp = dr; 917 return (DUP_NEW); 918 } 919 920 /* 921 * PSARC 2003/523 Contract Private Interface 922 * svc_clts_kdupdone 923 * Changes must be reviewed by Solaris File Sharing 924 * Changes must be communicated to contract-2003-523@sun.com 925 * 926 * svc_clts_kdupdone marks the request done (DUP_DONE or DUP_DROP) 927 * and stores the response. 928 */ 929 static void 930 svc_clts_kdupdone(struct dupreq *dr, caddr_t res, void (*dis_resfree)(), 931 int size, int status) 932 { 933 934 ASSERT(dr->dr_resfree == NULL); 935 if (status == DUP_DONE) { 936 bcopy(res, dr->dr_resp.buf, size); 937 dr->dr_resfree = dis_resfree; 938 } 939 dr->dr_status = status; 940 } 941 942 /* 943 * This routine expects that the mutex, dupreq_lock, is already held. 944 */ 945 static void 946 unhash(struct dupreq *dr) 947 { 948 struct dupreq *drt; 949 struct dupreq *drtprev = NULL; 950 uint32_t drhash; 951 952 ASSERT(MUTEX_HELD(&dupreq_lock)); 953 954 drhash = (uint32_t)DRHASH(dr); 955 drt = drhashtbl[drhash]; 956 while (drt != NULL) { 957 if (drt == dr) { 958 drhashstat[drhash]--; 959 if (drtprev == NULL) { 960 drhashtbl[drhash] = drt->dr_chain; 961 } else { 962 drtprev->dr_chain = drt->dr_chain; 963 } 964 return; 965 } 966 drtprev = drt; 967 drt = drt->dr_chain; 968 } 969 } 970 971 void 972 svc_clts_stats_init(zoneid_t zoneid, struct rpc_clts_server **statsp) 973 { 974 kstat_t *ksp; 975 kstat_named_t *knp; 976 977 knp = rpcstat_zone_init_common(zoneid, "unix", "rpc_clts_server", 978 (const kstat_named_t *)&clts_rsstat_tmpl, 979 sizeof (clts_rsstat_tmpl)); 980 /* 981 * Backwards compatibility for old kstat clients 982 */ 983 ksp = kstat_create_zone("unix", 0, "rpc_server", "rpc", 984 KSTAT_TYPE_NAMED, clts_rsstat_ndata, 985 KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE, zoneid); 986 if (ksp) { 987 ksp->ks_data = knp; 988 kstat_install(ksp); 989 } 990 *statsp = (struct rpc_clts_server *)knp; 991 } 992 993 void 994 svc_clts_stats_fini(zoneid_t zoneid, struct rpc_clts_server **statsp) 995 { 996 rpcstat_zone_fini_common(zoneid, "unix", "rpc_clts_server"); 997 kstat_delete_byname_zone("unix", 0, "rpc_server", zoneid); 998 kmem_free(*statsp, sizeof (clts_rsstat_tmpl)); 999 } 1000 1001 void 1002 svc_clts_init() 1003 { 1004 /* 1005 * Check to make sure that the clts private data will fit into 1006 * the stack buffer allocated by svc_run. The compiler should 1007 * remove this check, but it's a safety net if the udp_data 1008 * structure ever changes. 1009 */ 1010 /*CONSTANTCONDITION*/ 1011 ASSERT(sizeof (struct udp_data) <= SVC_P2LEN); 1012 1013 mutex_init(&dupreq_lock, NULL, MUTEX_DEFAULT, NULL); 1014 } 1015