Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 1 Jun 2018 19:47:41 +0000 (UTC)
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r334492 - in head/sys/fs: nfs nfsserver
Message-ID:  <201806011947.w51Jlf9F009761@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rmacklem
Date: Fri Jun  1 19:47:41 2018
New Revision: 334492
URL: https://svnweb.freebsd.org/changeset/base/334492

Log:
  Add the BindConnectiontoSession operation to the NFSv4.1 server.
  
  Under some fairly unusual circumstances, the Linux NFSv4.1 client is
  doing a BindConnectiontoSession operation for TCP connections.
  It is also used by the ESXi6.5 NFSv4.1 client.
  This patch adds this operation to the NFSv4.1 server.
  
  Reported by:	andreas.nagy@frequentis.com
  Tested by:	andreas.nagy@frequentis.com
  MFC after:	2 weeks

Modified:
  head/sys/fs/nfs/nfs.h
  head/sys/fs/nfs/nfs_commonsubs.c
  head/sys/fs/nfs/nfs_var.h
  head/sys/fs/nfs/nfsproto.h
  head/sys/fs/nfsserver/nfs_nfsdserv.c
  head/sys/fs/nfsserver/nfs_nfsdsocket.c
  head/sys/fs/nfsserver/nfs_nfsdstate.c

Modified: head/sys/fs/nfs/nfs.h
==============================================================================
--- head/sys/fs/nfs/nfs.h	Fri Jun  1 19:42:59 2018	(r334491)
+++ head/sys/fs/nfs/nfs.h	Fri Jun  1 19:47:41 2018	(r334492)
@@ -301,6 +301,7 @@ struct nfsreferral {
 #define	LCL_ADMINREVOKED	0x00008000
 #define	LCL_RECLAIMCOMPLETE	0x00010000
 #define	LCL_NFSV41		0x00020000
+#define	LCL_DONEBINDCONN	0x00040000
 
 #define	LCL_GSS		LCL_KERBV	/* Or of all mechs */
 

Modified: head/sys/fs/nfs/nfs_commonsubs.c
==============================================================================
--- head/sys/fs/nfs/nfs_commonsubs.c	Fri Jun  1 19:42:59 2018	(r334491)
+++ head/sys/fs/nfs/nfs_commonsubs.c	Fri Jun  1 19:47:41 2018	(r334492)
@@ -138,7 +138,7 @@ struct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS] = {
 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1, 0 },		/* Write */
 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* ReleaseLockOwner */
 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Backchannel Ctrl */
-	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Bind Conn to Sess */
+	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Bind Conn to Sess */
 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Exchange ID */
 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Create Session */
 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Destroy Session */

Modified: head/sys/fs/nfs/nfs_var.h
==============================================================================
--- head/sys/fs/nfs/nfs_var.h	Fri Jun  1 19:42:59 2018	(r334491)
+++ head/sys/fs/nfs/nfs_var.h	Fri Jun  1 19:47:41 2018	(r334492)
@@ -97,6 +97,7 @@ int nfsrv_getclient(nfsquad_t, int, struct nfsclient *
     nfsquad_t, uint32_t, struct nfsrv_descript *, NFSPROC_T *);
 int nfsrv_destroyclient(nfsquad_t, NFSPROC_T *);
 int nfsrv_destroysession(struct nfsrv_descript *, uint8_t *);
+int nfsrv_bindconnsess(struct nfsrv_descript *, uint8_t *, int *);
 int nfsrv_freestateid(struct nfsrv_descript *, nfsv4stateid_t *, NFSPROC_T *);
 int nfsrv_teststateid(struct nfsrv_descript *, nfsv4stateid_t *, NFSPROC_T *);
 int nfsrv_adminrevoke(struct nfsd_clid *, NFSPROC_T *);
@@ -232,6 +233,8 @@ int nfsrvd_sequence(struct nfsrv_descript *, int,
 int nfsrvd_reclaimcomplete(struct nfsrv_descript *, int,
     vnode_t, NFSPROC_T *, struct nfsexstuff *);
 int nfsrvd_destroyclientid(struct nfsrv_descript *, int,
+    vnode_t, NFSPROC_T *, struct nfsexstuff *);
+int nfsrvd_bindconnsess(struct nfsrv_descript *, int,
     vnode_t, NFSPROC_T *, struct nfsexstuff *);
 int nfsrvd_destroysession(struct nfsrv_descript *, int,
     vnode_t, NFSPROC_T *, struct nfsexstuff *);

Modified: head/sys/fs/nfs/nfsproto.h
==============================================================================
--- head/sys/fs/nfs/nfsproto.h	Fri Jun  1 19:42:59 2018	(r334491)
+++ head/sys/fs/nfs/nfsproto.h	Fri Jun  1 19:47:41 2018	(r334492)
@@ -659,6 +659,15 @@
 #define	NFSFLEXFLAG_NO_READIO		0x00000004
 #define	NFSFLEXFLAG_WRITE_ONEMIRROR	0x00000008
 
+/* Enum values for Bind Connection to Session. */
+#define	NFSCDFC4_FORE		0x1
+#define	NFSCDFC4_BACK		0x2
+#define	NFSCDFC4_FORE_OR_BOTH	0x3
+#define	NFSCDFC4_BACK_OR_BOTH	0x7
+#define	NFSCDFS4_FORE		0x1
+#define	NFSCDFS4_BACK		0x2
+#define	NFSCDFS4_BOTH		0x3
+
 /* Conversion macros */
 #define	vtonfsv2_mode(t,m) 						\
 		txdr_unsigned(((t) == VFIFO) ? MAKEIMODE(VCHR, (m)) : 	\

Modified: head/sys/fs/nfsserver/nfs_nfsdserv.c
==============================================================================
--- head/sys/fs/nfsserver/nfs_nfsdserv.c	Fri Jun  1 19:42:59 2018	(r334491)
+++ head/sys/fs/nfsserver/nfs_nfsdserv.c	Fri Jun  1 19:47:41 2018	(r334492)
@@ -4055,6 +4055,45 @@ nfsmout:
 }
 
 /*
+ * nfsv4 bind connection to session service
+ */
+APPLESTATIC int
+nfsrvd_bindconnsess(struct nfsrv_descript *nd, __unused int isdgram,
+    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+	uint32_t *tl;
+	uint8_t sessid[NFSX_V4SESSIONID];
+	int error = 0, foreaft;
+
+	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
+		nd->nd_repstat = NFSERR_WRONGSEC;
+		goto nfsmout;
+	}
+	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + 2 * NFSX_UNSIGNED);
+	NFSBCOPY(tl, sessid, NFSX_V4SESSIONID);
+	tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
+	foreaft = fxdr_unsigned(int, *tl++);
+	if (*tl == newnfs_true) {
+		/* RDMA is not supported. */
+		nd->nd_repstat = NFSERR_NOTSUPP;
+		goto nfsmout;
+	}
+
+	nd->nd_repstat = nfsrv_bindconnsess(nd, sessid, &foreaft);
+	if (nd->nd_repstat == 0) {
+		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 2 *
+		    NFSX_UNSIGNED);
+		NFSBCOPY(sessid, tl, NFSX_V4SESSIONID);
+		tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
+		*tl++ = txdr_unsigned(foreaft);
+		*tl = newnfs_false;
+	}
+nfsmout:
+	NFSEXITCODE2(error, nd);
+	return (error);
+}
+
+/*
  * nfsv4 destroy session service
  */
 APPLESTATIC int

Modified: head/sys/fs/nfsserver/nfs_nfsdsocket.c
==============================================================================
--- head/sys/fs/nfsserver/nfs_nfsdsocket.c	Fri Jun  1 19:42:59 2018	(r334491)
+++ head/sys/fs/nfsserver/nfs_nfsdsocket.c	Fri Jun  1 19:47:41 2018	(r334492)
@@ -178,7 +178,7 @@ int (*nfsrv4_ops0[NFSV41_NOPS])(struct nfsrv_descript 
 	nfsrvd_write,
 	nfsrvd_releaselckown,
 	nfsrvd_notsupp,
-	nfsrvd_notsupp,
+	nfsrvd_bindconnsess,
 	nfsrvd_exchangeid,
 	nfsrvd_createsession,
 	nfsrvd_destroysession,

Modified: head/sys/fs/nfsserver/nfs_nfsdstate.c
==============================================================================
--- head/sys/fs/nfsserver/nfs_nfsdstate.c	Fri Jun  1 19:42:59 2018	(r334491)
+++ head/sys/fs/nfsserver/nfs_nfsdstate.c	Fri Jun  1 19:47:41 2018	(r334492)
@@ -43,6 +43,7 @@ extern struct nfsstatsv1 nfsstatsv1;
 extern int nfsrv_lease;
 extern struct timeval nfsboottime;
 extern u_int32_t newnfs_true, newnfs_false;
+extern int nfsd_debuglevel;
 NFSV4ROOTLOCKMUTEX;
 NFSSTATESPINLOCK;
 
@@ -630,10 +631,11 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struc
 		    if (nsep != NULL) {
 			/* Hold a reference on the xprt for a backchannel. */
 			if ((nsep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN)
-			    != 0 && clp->lc_req.nr_client == NULL) {
-			    clp->lc_req.nr_client = (struct __rpc_client *)
-				clnt_bck_create(nd->nd_xprt->xp_socket,
-				cbprogram, NFSV4_CBVERS);
+			    != 0) {
+			    if (clp->lc_req.nr_client == NULL)
+				clp->lc_req.nr_client = (struct __rpc_client *)
+				    clnt_bck_create(nd->nd_xprt->xp_socket,
+				    cbprogram, NFSV4_CBVERS);
 			    if (clp->lc_req.nr_client != NULL) {
 				SVC_ACQUIRE(nd->nd_xprt);
 				nd->nd_xprt->xp_p2 =
@@ -5900,9 +5902,18 @@ nfsrv_checksequence(struct nfsrv_descript *nd, uint32_
 	/*
 	 * If this session handles the backchannel, save the nd_xprt for this
 	 * RPC, since this is the one being used.
+	 * RFC-5661 specifies that the fore channel will be implicitly
+	 * bound by a Sequence operation.  However, since some NFSv4.1 clients
+	 * erroneously assumed that the back channel would be implicitly
+	 * bound as well, do the implicit binding unless a
+	 * BindConnectiontoSession has already been done on the session.
 	 */
 	if (sep->sess_clp->lc_req.nr_client != NULL &&
-	    (sep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0) {
+	    sep->sess_cbsess.nfsess_xprt != nd->nd_xprt &&
+	    (sep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0 &&
+	    (sep->sess_clp->lc_flags & LCL_DONEBINDCONN) == 0) {
+		NFSD_DEBUG(2,
+		    "nfsrv_checksequence: implicit back channel bind\n");
 		savxprt = sep->sess_cbsess.nfsess_xprt;
 		SVC_ACQUIRE(nd->nd_xprt);
 		nd->nd_xprt->xp_p2 =
@@ -6032,6 +6043,80 @@ nfsrv_destroysession(struct nfsrv_descript *nd, uint8_
 	NFSLOCKV4ROOTMUTEX();
 	nfsv4_unlock(&nfsv4rootfs_lock, 1);
 	NFSUNLOCKV4ROOTMUTEX();
+	return (error);
+}
+
+/*
+ * Bind a connection to a session.
+ * For now, only certain variants are supported, since the current session
+ * structure can only handle a single backchannel entry, which will be
+ * applied to all connections if it is set.
+ */
+int
+nfsrv_bindconnsess(struct nfsrv_descript *nd, uint8_t *sessionid, int *foreaftp)
+{
+	struct nfssessionhash *shp;
+	struct nfsdsession *sep;
+	struct nfsclient *clp;
+	SVCXPRT *savxprt;
+	int error;
+
+	error = 0;
+	shp = NFSSESSIONHASH(sessionid);
+	NFSLOCKSTATE();
+	NFSLOCKSESSION(shp);
+	sep = nfsrv_findsession(sessionid);
+	if (sep != NULL) {
+		clp = sep->sess_clp;
+		if (*foreaftp == NFSCDFC4_BACK ||
+		    *foreaftp == NFSCDFC4_BACK_OR_BOTH ||
+		    *foreaftp == NFSCDFC4_FORE_OR_BOTH) {
+			/* Try to set up a backchannel. */
+			if (clp->lc_req.nr_client == NULL) {
+				NFSD_DEBUG(2, "nfsrv_bindconnsess: acquire "
+				    "backchannel\n");
+				clp->lc_req.nr_client = (struct __rpc_client *)
+				    clnt_bck_create(nd->nd_xprt->xp_socket,
+				    sep->sess_cbprogram, NFSV4_CBVERS);
+			}
+			if (clp->lc_req.nr_client != NULL) {
+				NFSD_DEBUG(2, "nfsrv_bindconnsess: set up "
+				    "backchannel\n");
+				savxprt = sep->sess_cbsess.nfsess_xprt;
+				SVC_ACQUIRE(nd->nd_xprt);
+				nd->nd_xprt->xp_p2 =
+				    clp->lc_req.nr_client->cl_private;
+				/* Disable idle timeout. */
+				nd->nd_xprt->xp_idletimeout = 0;
+				sep->sess_cbsess.nfsess_xprt = nd->nd_xprt;
+				if (savxprt != NULL)
+					SVC_RELEASE(savxprt);
+				sep->sess_crflags |= NFSV4CRSESS_CONNBACKCHAN;
+				clp->lc_flags |= LCL_DONEBINDCONN;
+				if (*foreaftp == NFSCDFS4_BACK)
+					*foreaftp = NFSCDFS4_BACK;
+				else
+					*foreaftp = NFSCDFS4_BOTH;
+			} else if (*foreaftp != NFSCDFC4_BACK) {
+				NFSD_DEBUG(2, "nfsrv_bindconnsess: can't set "
+				    "up backchannel\n");
+				sep->sess_crflags &= ~NFSV4CRSESS_CONNBACKCHAN;
+				clp->lc_flags |= LCL_DONEBINDCONN;
+				*foreaftp = NFSCDFS4_FORE;
+			} else {
+				error = NFSERR_NOTSUPP;
+				printf("nfsrv_bindconnsess: Can't add "
+				    "backchannel\n");
+			}
+		} else {
+			NFSD_DEBUG(2, "nfsrv_bindconnsess: Set forechannel\n");
+			clp->lc_flags |= LCL_DONEBINDCONN;
+			*foreaftp = NFSCDFS4_FORE;
+		}
+	} else
+		error = NFSERR_BADSESSION;
+	NFSUNLOCKSESSION(shp);
+	NFSUNLOCKSTATE();
 	return (error);
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201806011947.w51Jlf9F009761>