Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

NFSv4.1: Don't rebind to the same source port when reconnecting to the server

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>

+20 -5
+2 -1
fs/lockd/host.c
··· 464 464 .version = host->h_version, 465 465 .authflavor = RPC_AUTH_UNIX, 466 466 .flags = (RPC_CLNT_CREATE_NOPING | 467 - RPC_CLNT_CREATE_AUTOBIND), 467 + RPC_CLNT_CREATE_AUTOBIND | 468 + RPC_CLNT_CREATE_REUSEPORT), 468 469 .cred = host->h_cred, 469 470 }; 470 471
+3
fs/nfs/client.c
··· 523 523 args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS; 524 524 if (test_bit(NFS_CS_NOPING, &clp->cl_flags)) 525 525 args.flags |= RPC_CLNT_CREATE_NOPING; 526 + if (test_bit(NFS_CS_REUSEPORT, &clp->cl_flags)) 527 + args.flags |= RPC_CLNT_CREATE_REUSEPORT; 526 528 527 529 if (!IS_ERR(clp->cl_rpcclient)) 528 530 return 0; ··· 672 670 .timeparms = &timeparms, 673 671 .cred = server->cred, 674 672 .nconnect = data->nfs_server.nconnect, 673 + .init_flags = (1UL << NFS_CS_REUSEPORT), 675 674 }; 676 675 struct nfs_client *clp; 677 676 int error;
+4 -1
fs/nfs/nfs4client.c
··· 879 879 }; 880 880 struct nfs_client *clp; 881 881 882 - if (minorversion > 0 && proto == XPRT_TRANSPORT_TCP) 882 + if (minorversion == 0) 883 + __set_bit(NFS_CS_REUSEPORT, &cl_init.init_flags); 884 + else if (proto == XPRT_TRANSPORT_TCP) 883 885 cl_init.nconnect = nconnect; 886 + 884 887 if (server->flags & NFS_MOUNT_NORESVPORT) 885 888 __set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); 886 889 if (server->options & NFS_OPTION_MIGRATION)
+1
include/linux/nfs_fs_sb.h
··· 47 47 #define NFS_CS_TSM_POSSIBLE 5 /* - Maybe state migration */ 48 48 #define NFS_CS_NOPING 6 /* - don't ping on connect */ 49 49 #define NFS_CS_DS 7 /* - Server is a DS */ 50 + #define NFS_CS_REUSEPORT 8 /* - reuse src port on reconnect */ 50 51 struct sockaddr_storage cl_addr; /* server identifier */ 51 52 size_t cl_addrlen; 52 53 char * cl_hostname; /* hostname of server */
+1
include/linux/sunrpc/clnt.h
··· 149 149 #define RPC_CLNT_CREATE_NO_IDLE_TIMEOUT (1UL << 8) 150 150 #define RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT (1UL << 9) 151 151 #define RPC_CLNT_CREATE_SOFTERR (1UL << 10) 152 + #define RPC_CLNT_CREATE_REUSEPORT (1UL << 11) 152 153 153 154 struct rpc_clnt *rpc_create(struct rpc_create_args *args); 154 155 struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,
+2 -1
include/linux/sunrpc/xprt.h
··· 207 207 unsigned int min_reqs; /* min number of slots */ 208 208 unsigned int num_reqs; /* total slots */ 209 209 unsigned long state; /* transport state */ 210 - unsigned char resvport : 1; /* use a reserved port */ 210 + unsigned char resvport : 1, /* use a reserved port */ 211 + reuseport : 1; /* reuse port on reconnect */ 211 212 atomic_t swapper; /* we're swapping over this 212 213 transport */ 213 214 unsigned int bind_index; /* bind function index */
+6 -1
net/sunrpc/clnt.c
··· 591 591 xprt->resvport = 1; 592 592 if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT) 593 593 xprt->resvport = 0; 594 + xprt->reuseport = 0; 595 + if (args->flags & RPC_CLNT_CREATE_REUSEPORT) 596 + xprt->reuseport = 1; 594 597 595 598 clnt = rpc_create_xprt(args, xprt); 596 599 if (IS_ERR(clnt) || args->nconnect <= 1) ··· 2909 2906 struct rpc_xprt *xprt; 2910 2907 unsigned long connect_timeout; 2911 2908 unsigned long reconnect_timeout; 2912 - unsigned char resvport; 2909 + unsigned char resvport, reuseport; 2913 2910 int ret = 0; 2914 2911 2915 2912 rcu_read_lock(); ··· 2921 2918 return -EAGAIN; 2922 2919 } 2923 2920 resvport = xprt->resvport; 2921 + reuseport = xprt->reuseport; 2924 2922 connect_timeout = xprt->connect_timeout; 2925 2923 reconnect_timeout = xprt->max_reconnect_timeout; 2926 2924 rcu_read_unlock(); ··· 2932 2928 goto out_put_switch; 2933 2929 } 2934 2930 xprt->resvport = resvport; 2931 + xprt->reuseport = reuseport; 2935 2932 if (xprt->ops->set_connect_timeout != NULL) 2936 2933 xprt->ops->set_connect_timeout(xprt, 2937 2934 connect_timeout,
+1 -1
net/sunrpc/xprtsock.c
··· 1752 1752 1753 1753 static void xs_set_srcport(struct sock_xprt *transport, struct socket *sock) 1754 1754 { 1755 - if (transport->srcport == 0) 1755 + if (transport->srcport == 0 && transport->xprt.reuseport) 1756 1756 transport->srcport = xs_sock_getport(sock); 1757 1757 } 1758 1758