The branch stable/13 has been updated by rmacklem:

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

commit 5a45802b3c8c3962649f47b01453c819dd137da1
Author:     Rick Macklem <rmack...@freebsd.org>
AuthorDate: 2021-04-11 21:34:57 +0000
Commit:     Rick Macklem <rmack...@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. */
_______________________________________________
dev-commits-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-all
To unsubscribe, send any mail to "dev-commits-src-all-unsubscr...@freebsd.org"

Reply via email to