git: 5a45802b3c8c - stable/13 - nfsv4 client: do the BindConnectionToSession as required

Rick Macklem rmacklem at FreeBSD.org
Fri Apr 30 00:47:57 UTC 2021


The branch stable/13 has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=5a45802b3c8c3962649f47b01453c819dd137da1

commit 5a45802b3c8c3962649f47b01453c819dd137da1
Author:     Rick Macklem <rmacklem at FreeBSD.org>
AuthorDate: 2021-04-11 21:34:57 +0000
Commit:     Rick Macklem <rmacklem at FreeBSD.org>
CommitDate: 2021-04-30 00:43:50 +0000

    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
    
    (cherry picked from commit 7763814fc9c27a98fefcbf582d7a936ea43af23a)
---
 sys/fs/nfs/nfs_commonsubs.c     |  5 ++-
 sys/fs/nfs/nfs_var.h            |  1 +
 sys/fs/nfs/nfscl.h              |  5 +++
 sys/fs/nfs/nfsport.h            |  9 +++--
 sys/fs/nfs/nfsproto.h           |  5 ++-
 sys/fs/nfsclient/nfs_clrpcops.c | 90 +++++++++++++++++++++++++++++++++++++++++
 sys/rpc/clnt.h                  |  6 +++
 sys/rpc/clnt_rc.c               | 15 +++++++
 sys/rpc/krpc.h                  |  3 ++
 9 files changed, 133 insertions(+), 6 deletions(-)

diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index 43bb396d9cfd..4afa4c2d9ab4 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -218,7 +218,7 @@ static struct nfsrv_lughash	*nfsgroupnamehash;
 static int nfs_bigreply[NFSV42_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
-    1, 0, 0, 1 };
+    1, 0, 0, 1, 0 };
 
 /* local functions */
 static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
@@ -301,6 +301,7 @@ static struct {
 	{ NFSV4OP_SETXATTR, 2, "Setxattr", 8, },
 	{ NFSV4OP_REMOVEXATTR, 2, "Rmxattr", 7, },
 	{ NFSV4OP_LISTXATTRS, 2, "Listxattr", 9, },
+	{ NFSV4OP_BINDCONNTOSESS, 1, "BindConSess", 11, },
 };
 
 /*
@@ -309,7 +310,7 @@ static struct {
 static int nfs_bigrequest[NFSV42_NPROCS] = {
 	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
+	0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0
 };
 
 /*
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index aba5c5124e72..0297b52015f8 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -561,6 +561,7 @@ int nfsrpc_listextattr(vnode_t, uint64_t *, struct uio *, size_t *, bool *,
     struct nfsvattr *, int *, struct ucred *, NFSPROC_T *);
 int nfsrpc_rmextattr(vnode_t, const char *, struct nfsvattr *, int *,
     struct ucred *, NFSPROC_T *);
+void nfsrpc_bindconnsess(CLIENT *, void *, struct ucred *);
 
 /* nfs_clstate.c */
 int nfscl_open(vnode_t, u_int8_t *, int, u_int32_t, int,
diff --git a/sys/fs/nfs/nfscl.h b/sys/fs/nfs/nfscl.h
index 52da0af6fa51..3d7afaf68432 100644
--- a/sys/fs/nfs/nfscl.h
+++ b/sys/fs/nfs/nfscl.h
@@ -81,4 +81,9 @@ struct nfsv4node {
 			printf(__VA_ARGS__);				\
 	} while (0)
 
+struct nfscl_reconarg {
+	int	minorvers;
+	uint8_t	sessionid[NFSX_V4SESSIONID];
+};
+
 #endif	/* _NFS_NFSCL_H */
diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h
index 255c9a47ebdf..6777dc72f6a3 100644
--- a/sys/fs/nfs/nfsport.h
+++ b/sys/fs/nfs/nfsport.h
@@ -412,10 +412,13 @@
 #define	NFSPROC_RMEXTATTR	63
 #define	NFSPROC_LISTEXTATTR	64
 
+/* BindConnectionToSession, done by the krpc for a new connection. */
+#define	NFSPROC_BINDCONNTOSESS	65
+
 /*
  * Must be defined as one higher than the last NFSv4.2 Proc# above.
  */
-#define	NFSV42_NPROCS		65
+#define	NFSV42_NPROCS		66
 
 #endif	/* NFS_V3NPROCS */
 
@@ -444,7 +447,7 @@ struct nfsstatsv1 {
 	uint64_t	readlink_bios;
 	uint64_t	biocache_readdirs;
 	uint64_t	readdir_bios;
-	uint64_t	rpccnt[NFSV42_NPROCS + 15];
+	uint64_t	rpccnt[NFSV42_NPROCS + 14];
 	uint64_t	rpcretries;
 	uint64_t	srvrpccnt[NFSV42_NOPS + NFSV4OP_FAKENOPS + 15];
 	uint64_t	reserved_0;
@@ -509,7 +512,7 @@ struct nfsstatsov1 {
 	uint64_t	readlink_bios;
 	uint64_t	biocache_readdirs;
 	uint64_t	readdir_bios;
-	uint64_t	rpccnt[NFSV42_NPROCS + 4];
+	uint64_t	rpccnt[NFSV42_NPROCS + 3];
 	uint64_t	rpcretries;
 	uint64_t	srvrpccnt[NFSV42_PURENOPS + NFSV4OP_FAKENOPS];
 	uint64_t	reserved_0;
diff --git a/sys/fs/nfs/nfsproto.h b/sys/fs/nfs/nfsproto.h
index c123152a7cb7..236d8c14ff24 100644
--- a/sys/fs/nfs/nfsproto.h
+++ b/sys/fs/nfs/nfsproto.h
@@ -394,10 +394,13 @@
 #define	NFSPROC_RMEXTATTR	63
 #define	NFSPROC_LISTEXTATTR	64
 
+/* BindConnectionToSession, done by the krpc for a new connection. */
+#define	NFSPROC_BINDCONNTOSESS	65
+
 /*
  * Must be defined as one higher than the last NFSv4.2 Proc# above.
  */
-#define	NFSV42_NPROCS		65
+#define	NFSV42_NPROCS		66
 
 #endif	/* NFS_V3NPROCS */
 
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 527a47338b3f..db0b6ab1f5e8 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -946,6 +946,8 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
 	struct nfsclds *dsp, *odsp;
 	struct in6_addr a6;
 	struct nfsclsession *tsep;
+	struct rpc_reconupcall recon;
+	struct nfscl_reconarg *rcp;
 
 	if (nfsboottime.tv_sec == 0)
 		NFSSETBOOTTIME(nfsboottime);
@@ -1019,6 +1021,23 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
 			NFSCL_DEBUG(1, "aft createsess=%d\n", error);
 		}
 		if (error == 0) {
+			/*
+			 * If the session supports a backchannel, set up
+			 * the BindConnectionToSession call in the krpc
+			 * so that it is done on a reconnection.
+			 */
+			if (nfscl_enablecallb != 0 && nfs_numnfscbd > 0) {
+				rcp = mem_alloc(sizeof(*rcp));
+				rcp->minorvers = nmp->nm_minorvers;
+				memcpy(rcp->sessionid,
+				    dsp->nfsclds_sess.nfsess_sessionid,
+				    NFSX_V4SESSIONID);
+				recon.call = nfsrpc_bindconnsess;
+				recon.arg = rcp;
+				CLNT_CONTROL(nmp->nm_client, CLSET_RECONUPCALL,
+				    &recon);
+			}
+
 			NFSLOCKMNT(nmp);
 			/*
 			 * The old sessions cannot be safely free'd
@@ -8709,3 +8728,74 @@ nfsm_split(struct mbuf *mp, uint64_t xfer)
 	m->m_next = NULL;
 	return (m2);
 }
+
+/*
+ * Do the NFSv4.1 Bind Connection to Session.
+ * Called from the reconnect layer of the krpc (sys/rpc/clnt_rc.c).
+ */
+void
+nfsrpc_bindconnsess(CLIENT *cl, void *arg, struct ucred *cr)
+{
+	struct nfscl_reconarg *rcp = (struct nfscl_reconarg *)arg;
+	uint32_t res, *tl;
+	struct nfsrv_descript nfsd;
+	struct nfsrv_descript *nd = &nfsd;
+	struct rpc_callextra ext;
+	struct timeval utimeout;
+	enum clnt_stat stat;
+	int error;
+
+	nfscl_reqstart(nd, NFSPROC_BINDCONNTOSESS, NULL, NULL, 0, NULL, NULL,
+	    NFS_VER4, rcp->minorvers);
+	NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 2 * NFSX_UNSIGNED);
+	memcpy(tl, rcp->sessionid, NFSX_V4SESSIONID);
+	tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
+	*tl++ = txdr_unsigned(NFSCDFC4_FORE_OR_BOTH);
+	*tl = newnfs_false;
+
+	memset(&ext, 0, sizeof(ext));
+	utimeout.tv_sec = 30;
+	utimeout.tv_usec = 0;
+	ext.rc_auth = authunix_create(cr);
+	nd->nd_mrep = NULL;
+	stat = CLNT_CALL_MBUF(cl, &ext, NFSV4PROC_COMPOUND, nd->nd_mreq,
+	    &nd->nd_mrep, utimeout);
+	AUTH_DESTROY(ext.rc_auth);
+	if (stat != RPC_SUCCESS) {
+		printf("nfsrpc_bindconnsess: call failed stat=%d\n", stat);
+		return;
+	}
+	if (nd->nd_mrep == NULL) {
+		printf("nfsrpc_bindconnsess: no reply args\n");
+		return;
+	}
+	error = 0;
+	newnfs_realign(&nd->nd_mrep, M_WAITOK);
+	nd->nd_md = nd->nd_mrep;
+	nd->nd_dpos = mtod(nd->nd_md, char *);
+	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+	nd->nd_repstat = fxdr_unsigned(uint32_t, *tl++);
+	if (nd->nd_repstat == NFSERR_OK) {
+		res = fxdr_unsigned(uint32_t, *tl);
+		if (res > 0 && (error = nfsm_advance(nd, NFSM_RNDUP(res),
+		    -1)) != 0)
+			goto nfsmout;
+		NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
+		    4 * NFSX_UNSIGNED);
+		tl += 3;
+		if (!NFSBCMP(tl, rcp->sessionid, NFSX_V4SESSIONID)) {
+			tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
+			res = fxdr_unsigned(uint32_t, *tl);
+			if (res != NFSCDFS4_BOTH)
+				printf("nfsrpc_bindconnsess: did not "
+				    "return FS4_BOTH\n");
+		} else
+			printf("nfsrpc_bindconnsess: not same "
+			    "sessionid\n");
+	} else if (nd->nd_repstat != NFSERR_BADSESSION)
+		printf("nfsrpc_bindconnsess: returned %d\n", nd->nd_repstat);
+nfsmout:
+	if (error != 0)
+		printf("nfsrpc_bindconnsess: reply bad xdr\n");
+	m_freem(nd->nd_mrep);
+}
diff --git a/sys/rpc/clnt.h b/sys/rpc/clnt.h
index f4cc78b1c3b6..6f8f898ca918 100644
--- a/sys/rpc/clnt.h
+++ b/sys/rpc/clnt.h
@@ -360,6 +360,12 @@ enum clnt_stat clnt_call_private(CLIENT *, struct rpc_callextra *, rpcproc_t,
 #define	CLSET_TLS		30	/* set TLS for socket */
 #define	CLSET_BLOCKRCV		31	/* Temporarily block reception */
 #define	CLSET_TLSCERTNAME	32	/* TLS certificate file name */
+/* Structure used as the argument for CLSET_RECONUPCALL. */
+struct rpc_reconupcall {
+	void	(*call)(CLIENT *, void *, struct ucred *);
+	void	*arg;
+};
+#define	CLSET_RECONUPCALL	33	/* Reconnect upcall */
 #endif
 
 
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));
 }
diff --git a/sys/rpc/krpc.h b/sys/rpc/krpc.h
index 77facdcf16cc..48df782e481c 100644
--- a/sys/rpc/krpc.h
+++ b/sys/rpc/krpc.h
@@ -81,6 +81,9 @@ struct rc_data {
 	void			*rc_backchannel;
 	bool			rc_tls; /* Enable TLS on connection */
 	char			*rc_tlscertname;
+	void			(*rc_reconcall)(CLIENT *, void *,
+				    struct ucred *); /* reconection upcall */
+	void			*rc_reconarg;	/* upcall arg */
 };
 
 /* Bits for ct_rcvstate. */


More information about the dev-commits-src-all mailing list