1 /* $NetBSD: clnt_vc.c,v 1.4 2000/07/14 08:40:42 fvdl Exp $ */ 2 3 /*- 4 * Copyright (c) 2009, Sun Microsystems, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * - Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * - Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * - Neither the name of Sun Microsystems, Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #if defined(LIBC_SCCS) && !defined(lint) 32 static char *sccsid2 = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; 33 static char *sccsid = "@(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC"; 34 static char sccsid3[] = "@(#)clnt_vc.c 1.19 89/03/16 Copyr 1988 Sun Micro"; 35 #endif 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 38 39 /* 40 * clnt_tcp.c, Implements a TCP/IP based, client side RPC. 41 * 42 * Copyright (C) 1984, Sun Microsystems, Inc. 43 * 44 * TCP based RPC supports 'batched calls'. 45 * A sequence of calls may be batched-up in a send buffer. The rpc call 46 * return immediately to the client even though the call was not necessarily 47 * sent. The batching occurs if the results' xdr routine is NULL (0) AND 48 * the rpc timeout value is zero (see clnt.h, rpc). 49 * 50 * Clients should NOT casually batch calls that in fact return results; that is, 51 * the server side should be aware that a call is batched and not produce any 52 * return message. Batched calls that produce many result messages can 53 * deadlock (netlock) the client and the server.... 54 * 55 * Now go hang yourself. 56 */ 57 58 /* 59 * This code handles the special case of a NFSv4.n backchannel for 60 * callback RPCs. It is similar to clnt_vc.c, but uses the TCP 61 * connection provided by the client to the server. 62 */ 63 64 #include <sys/param.h> 65 #include <sys/systm.h> 66 #include <sys/lock.h> 67 #include <sys/malloc.h> 68 #include <sys/mbuf.h> 69 #include <sys/mutex.h> 70 #include <sys/pcpu.h> 71 #include <sys/proc.h> 72 #include <sys/protosw.h> 73 #include <sys/socket.h> 74 #include <sys/socketvar.h> 75 #include <sys/sx.h> 76 #include <sys/syslog.h> 77 #include <sys/time.h> 78 #include <sys/uio.h> 79 80 #include <net/vnet.h> 81 82 #include <netinet/tcp.h> 83 84 #include <rpc/rpc.h> 85 #include <rpc/rpc_com.h> 86 #include <rpc/krpc.h> 87 88 struct cmessage { 89 struct cmsghdr cmsg; 90 struct cmsgcred cmcred; 91 }; 92 93 static void clnt_bck_geterr(CLIENT *, struct rpc_err *); 94 static bool_t clnt_bck_freeres(CLIENT *, xdrproc_t, void *); 95 static void clnt_bck_abort(CLIENT *); 96 static bool_t clnt_bck_control(CLIENT *, u_int, void *); 97 static void clnt_bck_close(CLIENT *); 98 static void clnt_bck_destroy(CLIENT *); 99 100 static struct clnt_ops clnt_bck_ops = { 101 .cl_abort = clnt_bck_abort, 102 .cl_geterr = clnt_bck_geterr, 103 .cl_freeres = clnt_bck_freeres, 104 .cl_close = clnt_bck_close, 105 .cl_destroy = clnt_bck_destroy, 106 .cl_control = clnt_bck_control 107 }; 108 109 /* 110 * Create a client handle for a connection. 111 * Default options are set, which the user can change using clnt_control()'s. 112 * This code handles the special case of an NFSv4.1 session backchannel 113 * call, which is sent on a TCP connection created against the server 114 * by a client. 115 */ 116 void * 117 clnt_bck_create( 118 struct socket *so, /* Server transport socket. */ 119 const rpcprog_t prog, /* program number */ 120 const rpcvers_t vers) /* version number */ 121 { 122 CLIENT *cl; /* client handle */ 123 struct ct_data *ct = NULL; /* client handle */ 124 struct timeval now; 125 struct rpc_msg call_msg; 126 static uint32_t disrupt; 127 XDR xdrs; 128 129 if (disrupt == 0) 130 disrupt = (uint32_t)(long)so; 131 132 cl = (CLIENT *)mem_alloc(sizeof (*cl)); 133 ct = (struct ct_data *)mem_alloc(sizeof (*ct)); 134 135 mtx_init(&ct->ct_lock, "ct->ct_lock", NULL, MTX_DEF); 136 ct->ct_threads = 0; 137 ct->ct_closing = FALSE; 138 ct->ct_closed = FALSE; 139 ct->ct_upcallrefs = 0; 140 ct->ct_closeit = FALSE; 141 142 /* 143 * Set up private data struct 144 */ 145 ct->ct_wait.tv_sec = -1; 146 ct->ct_wait.tv_usec = -1; 147 148 /* 149 * Initialize call message 150 */ 151 getmicrotime(&now); 152 ct->ct_xid = ((uint32_t)++disrupt) ^ __RPC_GETXID(&now); 153 call_msg.rm_xid = ct->ct_xid; 154 call_msg.rm_direction = CALL; 155 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 156 call_msg.rm_call.cb_prog = (uint32_t)prog; 157 call_msg.rm_call.cb_vers = (uint32_t)vers; 158 159 /* 160 * pre-serialize the static part of the call msg and stash it away 161 */ 162 xdrmem_create(&xdrs, ct->ct_mcallc, MCALL_MSG_SIZE, 163 XDR_ENCODE); 164 if (!xdr_callhdr(&xdrs, &call_msg)) 165 goto err; 166 ct->ct_mpos = XDR_GETPOS(&xdrs); 167 XDR_DESTROY(&xdrs); 168 ct->ct_waitchan = "rpcbck"; 169 ct->ct_waitflag = 0; 170 cl->cl_refs = 1; 171 cl->cl_ops = &clnt_bck_ops; 172 cl->cl_private = ct; 173 cl->cl_auth = authnone_create(); 174 TAILQ_INIT(&ct->ct_pending); 175 return (cl); 176 177 err: 178 if (cl) { 179 if (ct) { 180 mtx_destroy(&ct->ct_lock); 181 mem_free(ct, sizeof (struct ct_data)); 182 } 183 if (cl) 184 mem_free(cl, sizeof (CLIENT)); 185 } 186 return (NULL); 187 } 188 189 enum clnt_stat 190 clnt_bck_call( 191 CLIENT *cl, /* client handle */ 192 struct rpc_callextra *ext, /* call metadata */ 193 rpcproc_t proc, /* procedure number */ 194 struct mbuf *args, /* pointer to args */ 195 struct mbuf **resultsp, /* pointer to results */ 196 struct timeval utimeout, 197 SVCXPRT *xprt) 198 { 199 struct ct_data *ct = (struct ct_data *) cl->cl_private; 200 AUTH *auth; 201 struct rpc_err *errp; 202 enum clnt_stat stat; 203 XDR xdrs; 204 struct rpc_msg reply_msg; 205 bool_t ok; 206 int nrefreshes = 2; /* number of times to refresh cred */ 207 struct timeval timeout; 208 uint32_t xid; 209 struct mbuf *mreq = NULL, *results; 210 struct ct_request *cr; 211 int error; 212 213 cr = malloc(sizeof(struct ct_request), M_RPC, M_WAITOK); 214 215 mtx_lock(&ct->ct_lock); 216 217 if (ct->ct_closing || ct->ct_closed) { 218 mtx_unlock(&ct->ct_lock); 219 free(cr, M_RPC); 220 return (RPC_CANTSEND); 221 } 222 ct->ct_threads++; 223 224 if (ext) { 225 auth = ext->rc_auth; 226 errp = &ext->rc_err; 227 } else { 228 auth = cl->cl_auth; 229 errp = &ct->ct_error; 230 } 231 232 cr->cr_mrep = NULL; 233 cr->cr_error = 0; 234 235 if (ct->ct_wait.tv_usec == -1) 236 timeout = utimeout; /* use supplied timeout */ 237 else 238 timeout = ct->ct_wait; /* use default timeout */ 239 240 call_again: 241 mtx_assert(&ct->ct_lock, MA_OWNED); 242 243 ct->ct_xid++; 244 xid = ct->ct_xid; 245 246 mtx_unlock(&ct->ct_lock); 247 248 /* 249 * Leave space to pre-pend the record mark. 250 */ 251 mreq = m_gethdr(M_WAITOK, MT_DATA); 252 mreq->m_data += sizeof(uint32_t); 253 KASSERT(ct->ct_mpos + sizeof(uint32_t) <= MHLEN, 254 ("RPC header too big")); 255 bcopy(ct->ct_mcallc, mreq->m_data, ct->ct_mpos); 256 mreq->m_len = ct->ct_mpos; 257 258 /* 259 * The XID is the first thing in the request. 260 */ 261 *mtod(mreq, uint32_t *) = htonl(xid); 262 263 xdrmbuf_create(&xdrs, mreq, XDR_ENCODE); 264 265 errp->re_status = stat = RPC_SUCCESS; 266 267 if ((!XDR_PUTINT32(&xdrs, &proc)) || 268 (!AUTH_MARSHALL(auth, xid, &xdrs, 269 m_copym(args, 0, M_COPYALL, M_WAITOK)))) { 270 errp->re_status = stat = RPC_CANTENCODEARGS; 271 mtx_lock(&ct->ct_lock); 272 goto out; 273 } 274 mreq->m_pkthdr.len = m_length(mreq, NULL); 275 276 /* 277 * Prepend a record marker containing the packet length. 278 */ 279 M_PREPEND(mreq, sizeof(uint32_t), M_WAITOK); 280 *mtod(mreq, uint32_t *) = 281 htonl(0x80000000 | (mreq->m_pkthdr.len - sizeof(uint32_t))); 282 283 cr->cr_xid = xid; 284 mtx_lock(&ct->ct_lock); 285 /* 286 * Check to see if the client end has already started to close down 287 * the connection. The svc code will have set ct_error.re_status 288 * to RPC_CANTRECV if this is the case. 289 * If the client starts to close down the connection after this 290 * point, it will be detected later when cr_error is checked, 291 * since the request is in the ct_pending queue. 292 */ 293 if (ct->ct_error.re_status == RPC_CANTRECV) { 294 if (errp != &ct->ct_error) { 295 errp->re_errno = ct->ct_error.re_errno; 296 errp->re_status = RPC_CANTRECV; 297 } 298 stat = RPC_CANTRECV; 299 goto out; 300 } 301 TAILQ_INSERT_TAIL(&ct->ct_pending, cr, cr_link); 302 mtx_unlock(&ct->ct_lock); 303 304 /* 305 * sosend consumes mreq. 306 */ 307 sx_xlock(&xprt->xp_lock); 308 error = sosend(xprt->xp_socket, NULL, NULL, mreq, NULL, 0, curthread); 309 if (error != 0) printf("sosend=%d\n", error); 310 mreq = NULL; 311 if (error == EMSGSIZE) { 312 printf("emsgsize\n"); 313 SOCKBUF_LOCK(&xprt->xp_socket->so_snd); 314 sbwait(&xprt->xp_socket->so_snd); 315 SOCKBUF_UNLOCK(&xprt->xp_socket->so_snd); 316 sx_xunlock(&xprt->xp_lock); 317 AUTH_VALIDATE(auth, xid, NULL, NULL); 318 mtx_lock(&ct->ct_lock); 319 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 320 goto call_again; 321 } 322 sx_xunlock(&xprt->xp_lock); 323 324 reply_msg.acpted_rply.ar_verf.oa_flavor = AUTH_NULL; 325 reply_msg.acpted_rply.ar_verf.oa_base = cr->cr_verf; 326 reply_msg.acpted_rply.ar_verf.oa_length = 0; 327 reply_msg.acpted_rply.ar_results.where = NULL; 328 reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; 329 330 mtx_lock(&ct->ct_lock); 331 if (error) { 332 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 333 errp->re_errno = error; 334 errp->re_status = stat = RPC_CANTSEND; 335 goto out; 336 } 337 338 /* 339 * Check to see if we got an upcall while waiting for the 340 * lock. In both these cases, the request has been removed 341 * from ct->ct_pending. 342 */ 343 if (cr->cr_error) { 344 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 345 errp->re_errno = cr->cr_error; 346 errp->re_status = stat = RPC_CANTRECV; 347 goto out; 348 } 349 if (cr->cr_mrep) { 350 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 351 goto got_reply; 352 } 353 354 /* 355 * Hack to provide rpc-based message passing 356 */ 357 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { 358 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 359 errp->re_status = stat = RPC_TIMEDOUT; 360 goto out; 361 } 362 363 error = msleep(cr, &ct->ct_lock, ct->ct_waitflag, ct->ct_waitchan, 364 tvtohz(&timeout)); 365 366 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 367 368 if (error) { 369 /* 370 * The sleep returned an error so our request is still 371 * on the list. Turn the error code into an 372 * appropriate client status. 373 */ 374 errp->re_errno = error; 375 switch (error) { 376 case EINTR: 377 stat = RPC_INTR; 378 break; 379 case EWOULDBLOCK: 380 stat = RPC_TIMEDOUT; 381 break; 382 default: 383 stat = RPC_CANTRECV; 384 }; 385 errp->re_status = stat; 386 goto out; 387 } else { 388 /* 389 * We were woken up by the svc thread. If the 390 * upcall had a receive error, report that, 391 * otherwise we have a reply. 392 */ 393 if (cr->cr_error) { 394 errp->re_errno = cr->cr_error; 395 errp->re_status = stat = RPC_CANTRECV; 396 goto out; 397 } 398 } 399 400 got_reply: 401 /* 402 * Now decode and validate the response. We need to drop the 403 * lock since xdr_replymsg may end up sleeping in malloc. 404 */ 405 mtx_unlock(&ct->ct_lock); 406 407 if (ext && ext->rc_feedback) 408 ext->rc_feedback(FEEDBACK_OK, proc, ext->rc_feedback_arg); 409 410 xdrmbuf_create(&xdrs, cr->cr_mrep, XDR_DECODE); 411 ok = xdr_replymsg(&xdrs, &reply_msg); 412 cr->cr_mrep = NULL; 413 414 if (ok) { 415 if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && 416 (reply_msg.acpted_rply.ar_stat == SUCCESS)) 417 errp->re_status = stat = RPC_SUCCESS; 418 else 419 stat = _seterr_reply(&reply_msg, errp); 420 421 if (stat == RPC_SUCCESS) { 422 results = xdrmbuf_getall(&xdrs); 423 if (!AUTH_VALIDATE(auth, xid, 424 &reply_msg.acpted_rply.ar_verf, &results)) { 425 errp->re_status = stat = RPC_AUTHERROR; 426 errp->re_why = AUTH_INVALIDRESP; 427 } else { 428 KASSERT(results, 429 ("auth validated but no result")); 430 *resultsp = results; 431 } 432 } /* end successful completion */ 433 /* 434 * If unsuccesful AND error is an authentication error 435 * then refresh credentials and try again, else break 436 */ 437 else if (stat == RPC_AUTHERROR) 438 /* maybe our credentials need to be refreshed ... */ 439 if (nrefreshes > 0 && AUTH_REFRESH(auth, &reply_msg)) { 440 nrefreshes--; 441 XDR_DESTROY(&xdrs); 442 mtx_lock(&ct->ct_lock); 443 goto call_again; 444 } 445 /* end of unsuccessful completion */ 446 /* end of valid reply message */ 447 } else 448 errp->re_status = stat = RPC_CANTDECODERES; 449 XDR_DESTROY(&xdrs); 450 mtx_lock(&ct->ct_lock); 451 out: 452 mtx_assert(&ct->ct_lock, MA_OWNED); 453 454 KASSERT(stat != RPC_SUCCESS || *resultsp, 455 ("RPC_SUCCESS without reply")); 456 457 if (mreq != NULL) 458 m_freem(mreq); 459 if (cr->cr_mrep != NULL) 460 m_freem(cr->cr_mrep); 461 462 ct->ct_threads--; 463 if (ct->ct_closing) 464 wakeup(ct); 465 466 mtx_unlock(&ct->ct_lock); 467 468 if (auth && stat != RPC_SUCCESS) 469 AUTH_VALIDATE(auth, xid, NULL, NULL); 470 471 free(cr, M_RPC); 472 473 return (stat); 474 } 475 476 static void 477 clnt_bck_geterr(CLIENT *cl, struct rpc_err *errp) 478 { 479 struct ct_data *ct = (struct ct_data *) cl->cl_private; 480 481 *errp = ct->ct_error; 482 } 483 484 static bool_t 485 clnt_bck_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr) 486 { 487 XDR xdrs; 488 bool_t dummy; 489 490 xdrs.x_op = XDR_FREE; 491 dummy = (*xdr_res)(&xdrs, res_ptr); 492 493 return (dummy); 494 } 495 496 /*ARGSUSED*/ 497 static void 498 clnt_bck_abort(CLIENT *cl) 499 { 500 } 501 502 static bool_t 503 clnt_bck_control(CLIENT *cl, u_int request, void *info) 504 { 505 506 return (TRUE); 507 } 508 509 static void 510 clnt_bck_close(CLIENT *cl) 511 { 512 struct ct_data *ct = (struct ct_data *) cl->cl_private; 513 514 mtx_lock(&ct->ct_lock); 515 516 if (ct->ct_closed) { 517 mtx_unlock(&ct->ct_lock); 518 return; 519 } 520 521 if (ct->ct_closing) { 522 while (ct->ct_closing) 523 msleep(ct, &ct->ct_lock, 0, "rpcclose", 0); 524 KASSERT(ct->ct_closed, ("client should be closed")); 525 mtx_unlock(&ct->ct_lock); 526 return; 527 } 528 529 ct->ct_closing = FALSE; 530 ct->ct_closed = TRUE; 531 mtx_unlock(&ct->ct_lock); 532 wakeup(ct); 533 } 534 535 static void 536 clnt_bck_destroy(CLIENT *cl) 537 { 538 struct ct_data *ct = (struct ct_data *) cl->cl_private; 539 540 clnt_bck_close(cl); 541 542 mtx_destroy(&ct->ct_lock); 543 mem_free(ct, sizeof(struct ct_data)); 544 if (cl->cl_netid && cl->cl_netid[0]) 545 mem_free(cl->cl_netid, strlen(cl->cl_netid) +1); 546 if (cl->cl_tp && cl->cl_tp[0]) 547 mem_free(cl->cl_tp, strlen(cl->cl_tp) +1); 548 mem_free(cl, sizeof(CLIENT)); 549 } 550 551 /* 552 * This call is done by the svc code when a backchannel RPC reply is 553 * received. 554 */ 555 void 556 clnt_bck_svccall(void *arg, struct mbuf *mrep, uint32_t xid) 557 { 558 struct ct_data *ct = (struct ct_data *)arg; 559 struct ct_request *cr; 560 int foundreq; 561 562 mtx_lock(&ct->ct_lock); 563 ct->ct_upcallrefs++; 564 /* 565 * See if we can match this reply to a request. 566 */ 567 foundreq = 0; 568 TAILQ_FOREACH(cr, &ct->ct_pending, cr_link) { 569 if (cr->cr_xid == xid) { 570 /* 571 * This one matches. We leave the reply mbuf list in 572 * cr->cr_mrep. Set the XID to zero so that we will 573 * ignore any duplicated replies. 574 */ 575 cr->cr_xid = 0; 576 cr->cr_mrep = mrep; 577 cr->cr_error = 0; 578 foundreq = 1; 579 wakeup(cr); 580 break; 581 } 582 } 583 584 ct->ct_upcallrefs--; 585 if (ct->ct_upcallrefs < 0) 586 panic("rpcvc svccall refcnt"); 587 if (ct->ct_upcallrefs == 0) 588 wakeup(&ct->ct_upcallrefs); 589 mtx_unlock(&ct->ct_lock); 590 if (foundreq == 0) 591 m_freem(mrep); 592 } 593 594