NFSv4.1: Don't rebind to the same source port when reconnecting to the server
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Thu, 17 Oct 2019 15:13:54 +0000 (11:13 -0400)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Mon, 4 Nov 2019 02:28:45 +0000 (21:28 -0500)
NFSv2, v3 and NFSv4 servers often have duplicate replay caches that look
at the source port when deciding whether or not an RPC call is a replay
of a previous call. This requires clients to perform strange TCP gymnastics
in order to ensure that when they reconnect to the server, they bind
to the same source port.

NFSv4.1 and NFSv4.2 have sessions that provide proper replay semantics,
that do not look at the source port of the connection. This patch therefore
ensures they can ignore the rebind requirement.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/lockd/host.c
fs/nfs/client.c
fs/nfs/nfs4client.c
include/linux/nfs_fs_sb.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/xprt.h
net/sunrpc/clnt.c
net/sunrpc/xprtsock.c

index 7d46fafdbbe5a6fb219941e16f9335910fe82225..0afb6d59bad03be506566979d20356ca163213df 100644 (file)
@@ -464,7 +464,8 @@ nlm_bind_host(struct nlm_host *host)
                        .version        = host->h_version,
                        .authflavor     = RPC_AUTH_UNIX,
                        .flags          = (RPC_CLNT_CREATE_NOPING |
-                                          RPC_CLNT_CREATE_AUTOBIND),
+                                          RPC_CLNT_CREATE_AUTOBIND |
+                                          RPC_CLNT_CREATE_REUSEPORT),
                        .cred           = host->h_cred,
                };
 
index bd6575ee3b8e98b4ecadc37c79f0cdd88c6a4228..02110a30a49ea136d5254e84cca72e5311700d5a 100644 (file)
@@ -523,6 +523,8 @@ int nfs_create_rpc_client(struct nfs_client *clp,
                args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS;
        if (test_bit(NFS_CS_NOPING, &clp->cl_flags))
                args.flags |= RPC_CLNT_CREATE_NOPING;
+       if (test_bit(NFS_CS_REUSEPORT, &clp->cl_flags))
+               args.flags |= RPC_CLNT_CREATE_REUSEPORT;
 
        if (!IS_ERR(clp->cl_rpcclient))
                return 0;
@@ -670,6 +672,7 @@ static int nfs_init_server(struct nfs_server *server,
                .timeparms = &timeparms,
                .cred = server->cred,
                .nconnect = data->nfs_server.nconnect,
+               .init_flags = (1UL << NFS_CS_REUSEPORT),
        };
        struct nfs_client *clp;
        int error;
index ebc960dd89ff43619b6f3ef8116d070db252151a..abd5af77fe9423b77d2ce28c3577d9facace0c03 100644 (file)
@@ -879,8 +879,11 @@ static int nfs4_set_client(struct nfs_server *server,
        };
        struct nfs_client *clp;
 
-       if (minorversion > 0 && proto == XPRT_TRANSPORT_TCP)
+       if (minorversion == 0)
+               __set_bit(NFS_CS_REUSEPORT, &cl_init.init_flags);
+       else if (proto == XPRT_TRANSPORT_TCP)
                cl_init.nconnect = nconnect;
+
        if (server->flags & NFS_MOUNT_NORESVPORT)
                __set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
        if (server->options & NFS_OPTION_MIGRATION)
index 69e80cef5a814f184c1fd58a43308ebd3ecfda50..df61ff8981e8ff0b60ad4246cfdf5c0d1a6e70b2 100644 (file)
@@ -47,6 +47,7 @@ struct nfs_client {
 #define NFS_CS_TSM_POSSIBLE    5               /* - Maybe state migration */
 #define NFS_CS_NOPING          6               /* - don't ping on connect */
 #define NFS_CS_DS              7               /* - Server is a DS */
+#define NFS_CS_REUSEPORT       8               /* - reuse src port on reconnect */
        struct sockaddr_storage cl_addr;        /* server identifier */
        size_t                  cl_addrlen;
        char *                  cl_hostname;    /* hostname of server */
index abc63bd1be2b5c792ab17747b6917ff571bf86bc..ec52e78d432baeb72d820aa16076cc1e8cb17681 100644 (file)
@@ -149,6 +149,7 @@ struct rpc_add_xprt_test {
 #define RPC_CLNT_CREATE_NO_IDLE_TIMEOUT        (1UL << 8)
 #define RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT     (1UL << 9)
 #define RPC_CLNT_CREATE_SOFTERR                (1UL << 10)
+#define RPC_CLNT_CREATE_REUSEPORT      (1UL << 11)
 
 struct rpc_clnt *rpc_create(struct rpc_create_args *args);
 struct rpc_clnt        *rpc_bind_new_program(struct rpc_clnt *,
index d783e15ba898cab33d779e1d4a0b2e1dcd27fdd2..ccd35cf4fc41a9e9fea4d91b9d4df0ed036be2e6 100644 (file)
@@ -207,7 +207,8 @@ struct rpc_xprt {
        unsigned int            min_reqs;       /* min number of slots */
        unsigned int            num_reqs;       /* total slots */
        unsigned long           state;          /* transport state */
-       unsigned char           resvport   : 1; /* use a reserved port */
+       unsigned char           resvport   : 1, /* use a reserved port */
+                               reuseport  : 1; /* reuse port on reconnect */
        atomic_t                swapper;        /* we're swapping over this
                                                   transport */
        unsigned int            bind_index;     /* bind function index */
index f7f78566be463ef229ffccc04b140525cf948043..5baf9b9be2e8e5ecd8c8c352af2ee36097bfef89 100644 (file)
@@ -591,6 +591,9 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
        xprt->resvport = 1;
        if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT)
                xprt->resvport = 0;
+       xprt->reuseport = 0;
+       if (args->flags & RPC_CLNT_CREATE_REUSEPORT)
+               xprt->reuseport = 1;
 
        clnt = rpc_create_xprt(args, xprt);
        if (IS_ERR(clnt) || args->nconnect <= 1)
@@ -2906,7 +2909,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
        struct rpc_xprt *xprt;
        unsigned long connect_timeout;
        unsigned long reconnect_timeout;
-       unsigned char resvport;
+       unsigned char resvport, reuseport;
        int ret = 0;
 
        rcu_read_lock();
@@ -2918,6 +2921,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
                return -EAGAIN;
        }
        resvport = xprt->resvport;
+       reuseport = xprt->reuseport;
        connect_timeout = xprt->connect_timeout;
        reconnect_timeout = xprt->max_reconnect_timeout;
        rcu_read_unlock();
@@ -2928,6 +2932,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
                goto out_put_switch;
        }
        xprt->resvport = resvport;
+       xprt->reuseport = reuseport;
        if (xprt->ops->set_connect_timeout != NULL)
                xprt->ops->set_connect_timeout(xprt,
                                connect_timeout,
index 70e52f567b2a2c84e7a33787df3296c3ae23e0cc..98e2d40b2d6abc1b7734ceed6600b1b5887daea0 100644 (file)
@@ -1752,7 +1752,7 @@ static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)
 
 static void xs_set_srcport(struct sock_xprt *transport, struct socket *sock)
 {
-       if (transport->srcport == 0)
+       if (transport->srcport == 0 && transport->xprt.reuseport)
                transport->srcport = xs_sock_getport(sock);
 }