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 mtx_destroy(&ct->ct_lock); 179 mem_free(ct, sizeof (struct ct_data)); 180 mem_free(cl, sizeof (CLIENT)); 181 return (NULL); 182 } 183 184 enum clnt_stat 185 clnt_bck_call( 186 CLIENT *cl, /* client handle */ 187 struct rpc_callextra *ext, /* call metadata */ 188 rpcproc_t proc, /* procedure number */ 189 struct mbuf *args, /* pointer to args */ 190 struct mbuf **resultsp, /* pointer to results */ 191 struct timeval utimeout, 192 SVCXPRT *xprt) 193 { 194 struct ct_data *ct = (struct ct_data *) cl->cl_private; 195 AUTH *auth; 196 struct rpc_err *errp; 197 enum clnt_stat stat; 198 XDR xdrs; 199 struct rpc_msg reply_msg; 200 bool_t ok; 201 int nrefreshes = 2; /* number of times to refresh cred */ 202 struct timeval timeout; 203 uint32_t xid; 204 struct mbuf *mreq = NULL, *results; 205 struct ct_request *cr; 206 int error; 207 208 cr = malloc(sizeof(struct ct_request), M_RPC, M_WAITOK); 209 210 mtx_lock(&ct->ct_lock); 211 212 if (ct->ct_closing || ct->ct_closed) { 213 mtx_unlock(&ct->ct_lock); 214 free(cr, M_RPC); 215 return (RPC_CANTSEND); 216 } 217 ct->ct_threads++; 218 219 if (ext) { 220 auth = ext->rc_auth; 221 errp = &ext->rc_err; 222 } else { 223 auth = cl->cl_auth; 224 errp = &ct->ct_error; 225 } 226 227 cr->cr_mrep = NULL; 228 cr->cr_error = 0; 229 230 if (ct->ct_wait.tv_usec == -1) 231 timeout = utimeout; /* use supplied timeout */ 232 else 233 timeout = ct->ct_wait; /* use default timeout */ 234 235 call_again: 236 mtx_assert(&ct->ct_lock, MA_OWNED); 237 238 ct->ct_xid++; 239 xid = ct->ct_xid; 240 241 mtx_unlock(&ct->ct_lock); 242 243 /* 244 * Leave space to pre-pend the record mark. 245 */ 246 mreq = m_gethdr(M_WAITOK, MT_DATA); 247 mreq->m_data += sizeof(uint32_t); 248 KASSERT(ct->ct_mpos + sizeof(uint32_t) <= MHLEN, 249 ("RPC header too big")); 250 bcopy(ct->ct_mcallc, mreq->m_data, ct->ct_mpos); 251 mreq->m_len = ct->ct_mpos; 252 253 /* 254 * The XID is the first thing in the request. 255 */ 256 *mtod(mreq, uint32_t *) = htonl(xid); 257 258 xdrmbuf_create(&xdrs, mreq, XDR_ENCODE); 259 260 errp->re_status = stat = RPC_SUCCESS; 261 262 if ((!XDR_PUTINT32(&xdrs, &proc)) || 263 (!AUTH_MARSHALL(auth, xid, &xdrs, 264 m_copym(args, 0, M_COPYALL, M_WAITOK)))) { 265 errp->re_status = stat = RPC_CANTENCODEARGS; 266 mtx_lock(&ct->ct_lock); 267 goto out; 268 } 269 mreq->m_pkthdr.len = m_length(mreq, NULL); 270 271 /* 272 * Prepend a record marker containing the packet length. 273 */ 274 M_PREPEND(mreq, sizeof(uint32_t), M_WAITOK); 275 *mtod(mreq, uint32_t *) = 276 htonl(0x80000000 | (mreq->m_pkthdr.len - sizeof(uint32_t))); 277 278 cr->cr_xid = xid; 279 mtx_lock(&ct->ct_lock); 280 /* 281 * Check to see if the client end has already started to close down 282 * the connection. The svc code will have set ct_error.re_status 283 * to RPC_CANTRECV if this is the case. 284 * If the client starts to close down the connection after this 285 * point, it will be detected later when cr_error is checked, 286 * since the request is in the ct_pending queue. 287 */ 288 if (ct->ct_error.re_status == RPC_CANTRECV) { 289 if (errp != &ct->ct_error) { 290 errp->re_errno = ct->ct_error.re_errno; 291 errp->re_status = RPC_CANTRECV; 292 } 293 stat = RPC_CANTRECV; 294 goto out; 295 } 296 TAILQ_INSERT_TAIL(&ct->ct_pending, cr, cr_link); 297 mtx_unlock(&ct->ct_lock); 298 299 /* 300 * sosend consumes mreq. 301 */ 302 sx_xlock(&xprt->xp_lock); 303 error = sosend(xprt->xp_socket, NULL, NULL, mreq, NULL, 0, curthread); 304 if (error != 0) printf("sosend=%d\n", error); 305 mreq = NULL; 306 if (error == EMSGSIZE) { 307 printf("emsgsize\n"); 308 SOCKBUF_LOCK(&xprt->xp_socket->so_snd); 309 sbwait(&xprt->xp_socket->so_snd); 310 SOCKBUF_UNLOCK(&xprt->xp_socket->so_snd); 311 sx_xunlock(&xprt->xp_lock); 312 AUTH_VALIDATE(auth, xid, NULL, NULL); 313 mtx_lock(&ct->ct_lock); 314 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 315 goto call_again; 316 } 317 sx_xunlock(&xprt->xp_lock); 318 319 reply_msg.acpted_rply.ar_verf.oa_flavor = AUTH_NULL; 320 reply_msg.acpted_rply.ar_verf.oa_base = cr->cr_verf; 321 reply_msg.acpted_rply.ar_verf.oa_length = 0; 322 reply_msg.acpted_rply.ar_results.where = NULL; 323 reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; 324 325 mtx_lock(&ct->ct_lock); 326 if (error) { 327 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 328 errp->re_errno = error; 329 errp->re_status = stat = RPC_CANTSEND; 330 goto out; 331 } 332 333 /* 334 * Check to see if we got an upcall while waiting for the 335 * lock. In both these cases, the request has been removed 336 * from ct->ct_pending. 337 */ 338 if (cr->cr_error) { 339 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 340 errp->re_errno = cr->cr_error; 341 errp->re_status = stat = RPC_CANTRECV; 342 goto out; 343 } 344 if (cr->cr_mrep) { 345 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 346 goto got_reply; 347 } 348 349 /* 350 * Hack to provide rpc-based message passing 351 */ 352 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { 353 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 354 errp->re_status = stat = RPC_TIMEDOUT; 355 goto out; 356 } 357 358 error = msleep(cr, &ct->ct_lock, ct->ct_waitflag, ct->ct_waitchan, 359 tvtohz(&timeout)); 360 361 TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 362 363 if (error) { 364 /* 365 * The sleep returned an error so our request is still 366 * on the list. Turn the error code into an 367 * appropriate client status. 368 */ 369 errp->re_errno = error; 370 switch (error) { 371 case EINTR: 372 stat = RPC_INTR; 373 break; 374 case EWOULDBLOCK: 375 stat = RPC_TIMEDOUT; 376 break; 377 default: 378 stat = RPC_CANTRECV; 379 } 380 errp->re_status = stat; 381 goto out; 382 } else { 383 /* 384 * We were woken up by the svc thread. If the 385 * upcall had a receive error, report that, 386 * otherwise we have a reply. 387 */ 388 if (cr->cr_error) { 389 errp->re_errno = cr->cr_error; 390 errp->re_status = stat = RPC_CANTRECV; 391 goto out; 392 } 393 } 394 395 got_reply: 396 /* 397 * Now decode and validate the response. We need to drop the 398 * lock since xdr_replymsg may end up sleeping in malloc. 399 */ 400 mtx_unlock(&ct->ct_lock); 401 402 if (ext && ext->rc_feedback) 403 ext->rc_feedback(FEEDBACK_OK, proc, ext->rc_feedback_arg); 404 405 xdrmbuf_create(&xdrs, cr->cr_mrep, XDR_DECODE); 406 ok = xdr_replymsg(&xdrs, &reply_msg); 407 cr->cr_mrep = NULL; 408 409 if (ok) { 410 if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && 411 (reply_msg.acpted_rply.ar_stat == SUCCESS)) 412 errp->re_status = stat = RPC_SUCCESS; 413 else 414 stat = _seterr_reply(&reply_msg, errp); 415 416 if (stat == RPC_SUCCESS) { 417 results = xdrmbuf_getall(&xdrs); 418 if (!AUTH_VALIDATE(auth, xid, 419 &reply_msg.acpted_rply.ar_verf, &results)) { 420 errp->re_status = stat = RPC_AUTHERROR; 421 errp->re_why = AUTH_INVALIDRESP; 422 } else { 423 KASSERT(results, 424 ("auth validated but no result")); 425 *resultsp = results; 426 } 427 } /* end successful completion */ 428 /* 429 * If unsuccessful AND error is an authentication error 430 * then refresh credentials and try again, else break 431 */ 432 else if (stat == RPC_AUTHERROR) 433 /* maybe our credentials need to be refreshed ... */ 434 if (nrefreshes > 0 && AUTH_REFRESH(auth, &reply_msg)) { 435 nrefreshes--; 436 XDR_DESTROY(&xdrs); 437 mtx_lock(&ct->ct_lock); 438 goto call_again; 439 } 440 /* end of unsuccessful completion */ 441 /* end of valid reply message */ 442 } else 443 errp->re_status = stat = RPC_CANTDECODERES; 444 XDR_DESTROY(&xdrs); 445 mtx_lock(&ct->ct_lock); 446 out: 447 mtx_assert(&ct->ct_lock, MA_OWNED); 448 449 KASSERT(stat != RPC_SUCCESS || *resultsp, 450 ("RPC_SUCCESS without reply")); 451 452 if (mreq != NULL) 453 m_freem(mreq); 454 if (cr->cr_mrep != NULL) 455 m_freem(cr->cr_mrep); 456 457 ct->ct_threads--; 458 if (ct->ct_closing) 459 wakeup(ct); 460 461 mtx_unlock(&ct->ct_lock); 462 463 if (auth && stat != RPC_SUCCESS) 464 AUTH_VALIDATE(auth, xid, NULL, NULL); 465 466 free(cr, M_RPC); 467 468 return (stat); 469 } 470 471 static void 472 clnt_bck_geterr(CLIENT *cl, struct rpc_err *errp) 473 { 474 struct ct_data *ct = (struct ct_data *) cl->cl_private; 475 476 *errp = ct->ct_error; 477 } 478 479 static bool_t 480 clnt_bck_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr) 481 { 482 XDR xdrs; 483 bool_t dummy; 484 485 xdrs.x_op = XDR_FREE; 486 dummy = (*xdr_res)(&xdrs, res_ptr); 487 488 return (dummy); 489 } 490 491 /*ARGSUSED*/ 492 static void 493 clnt_bck_abort(CLIENT *cl) 494 { 495 } 496 497 static bool_t 498 clnt_bck_control(CLIENT *cl, u_int request, void *info) 499 { 500 501 return (TRUE); 502 } 503 504 static void 505 clnt_bck_close(CLIENT *cl) 506 { 507 struct ct_data *ct = (struct ct_data *) cl->cl_private; 508 509 mtx_lock(&ct->ct_lock); 510 511 if (ct->ct_closed) { 512 mtx_unlock(&ct->ct_lock); 513 return; 514 } 515 516 if (ct->ct_closing) { 517 while (ct->ct_closing) 518 msleep(ct, &ct->ct_lock, 0, "rpcclose", 0); 519 KASSERT(ct->ct_closed, ("client should be closed")); 520 mtx_unlock(&ct->ct_lock); 521 return; 522 } 523 524 ct->ct_closing = FALSE; 525 ct->ct_closed = TRUE; 526 mtx_unlock(&ct->ct_lock); 527 wakeup(ct); 528 } 529 530 static void 531 clnt_bck_destroy(CLIENT *cl) 532 { 533 struct ct_data *ct = (struct ct_data *) cl->cl_private; 534 535 clnt_bck_close(cl); 536 537 mtx_destroy(&ct->ct_lock); 538 mem_free(ct, sizeof(struct ct_data)); 539 if (cl->cl_netid && cl->cl_netid[0]) 540 mem_free(cl->cl_netid, strlen(cl->cl_netid) +1); 541 if (cl->cl_tp && cl->cl_tp[0]) 542 mem_free(cl->cl_tp, strlen(cl->cl_tp) +1); 543 mem_free(cl, sizeof(CLIENT)); 544 } 545 546 /* 547 * This call is done by the svc code when a backchannel RPC reply is 548 * received. 549 */ 550 void 551 clnt_bck_svccall(void *arg, struct mbuf *mrep, uint32_t xid) 552 { 553 struct ct_data *ct = (struct ct_data *)arg; 554 struct ct_request *cr; 555 int foundreq; 556 557 mtx_lock(&ct->ct_lock); 558 ct->ct_upcallrefs++; 559 /* 560 * See if we can match this reply to a request. 561 */ 562 foundreq = 0; 563 TAILQ_FOREACH(cr, &ct->ct_pending, cr_link) { 564 if (cr->cr_xid == xid) { 565 /* 566 * This one matches. We leave the reply mbuf list in 567 * cr->cr_mrep. Set the XID to zero so that we will 568 * ignore any duplicated replies. 569 */ 570 cr->cr_xid = 0; 571 cr->cr_mrep = mrep; 572 cr->cr_error = 0; 573 foundreq = 1; 574 wakeup(cr); 575 break; 576 } 577 } 578 579 ct->ct_upcallrefs--; 580 if (ct->ct_upcallrefs < 0) 581 panic("rpcvc svccall refcnt"); 582 if (ct->ct_upcallrefs == 0) 583 wakeup(&ct->ct_upcallrefs); 584 mtx_unlock(&ct->ct_lock); 585 if (foundreq == 0) 586 m_freem(mrep); 587 } 588 589