From 7763814fc9c27a98fefcbf582d7a936ea43af23a Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Sun, 11 Apr 2021 14:34:57 -0700 Subject: nfsv4 client: do the BindConnectionToSession as required During a recent testing event, it was reported that the NFSv4.1/4.2 server erroneously bound the back channel to a new TCP connection. RFC5661 specifies that the fore channel is implicitly bound to a new TCP connection when an RPC with Sequence (almost any of them) is done on it. For the back channel to be bound to the new TCP connection, an explicit BindConnectionToSession must be done as the first RPC on the new connection. Since new TCP connections are created by the "reconnect" layer (sys/rpc/clnt_rc.c) of the krpc, this patch adds an optional upcall done by the krpc whenever a new connection is created. The patch also adds the specific upcall function that does a BindConnectionToSession and configures the krpc to call it when required. This is necessary for correct interoperability with NFSv4.1/NFSv4.2 servers when the nfscbd daemon is running. If doing NFSv4.1/NFSv4.2 mounts without this patch, it is recommended that the nfscbd daemon not be running and that the "pnfs" mount option not be specified. PR: 254840 Comments by: asomers MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D29475 --- sys/rpc/clnt_rc.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'sys/rpc/clnt_rc.c') diff --git a/sys/rpc/clnt_rc.c b/sys/rpc/clnt_rc.c index 8c204989d0ea..ae3b2985a891 100644 --- a/sys/rpc/clnt_rc.c +++ b/sys/rpc/clnt_rc.c @@ -111,6 +111,8 @@ clnt_reconnect_create( rc->rc_client = NULL; rc->rc_tls = false; rc->rc_tlscertname = NULL; + rc->rc_reconcall = NULL; + rc->rc_reconarg = NULL; cl->cl_refs = 1; cl->cl_ops = &clnt_reconnect_ops; @@ -213,6 +215,9 @@ clnt_reconnect_connect(CLIENT *cl) goto out; } } + if (newclient != NULL && rc->rc_reconcall != NULL) + (*rc->rc_reconcall)(newclient, rc->rc_reconarg, + rc->rc_ucred); } td->td_ucred = oldcred; @@ -408,6 +413,7 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info) struct rc_data *rc = (struct rc_data *)cl->cl_private; SVCXPRT *xprt; size_t slen; + struct rpc_reconupcall *upcp; if (info == NULL) { return (FALSE); @@ -513,6 +519,12 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info) strlcpy(rc->rc_tlscertname, info, slen); break; + case CLSET_RECONUPCALL: + upcp = (struct rpc_reconupcall *)info; + rc->rc_reconcall = upcp->call; + rc->rc_reconarg = upcp->arg; + break; + default: return (FALSE); } @@ -555,12 +567,15 @@ clnt_reconnect_destroy(CLIENT *cl) CLNT_DESTROY(rc->rc_client); if (rc->rc_backchannel) { xprt = (SVCXPRT *)rc->rc_backchannel; + KASSERT(xprt->xp_socket == NULL, + ("clnt_reconnect_destroy: xp_socket not NULL")); xprt_unregister(xprt); SVC_RELEASE(xprt); } crfree(rc->rc_ucred); mtx_destroy(&rc->rc_lock); mem_free(rc->rc_tlscertname, 0); /* 0 ok, since arg. ignored. */ + mem_free(rc->rc_reconarg, 0); mem_free(rc, sizeof(*rc)); mem_free(cl, sizeof (CLIENT)); } -- cgit v1.2.3