NFSv4.0: Remove cl_ipaddr from non-UCS client ID
authorChuck Lever <chuck.lever@oracle.com>
Mon, 4 Jun 2018 14:53:29 +0000 (10:53 -0400)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Wed, 6 Jun 2018 15:45:44 +0000 (11:45 -0400)
It is possible for two distinct clients to have the same cl_ipaddr:

 - if the client admin disables callback with clientaddr=0.0.0.0 on
   more than one client

 - if two clients behind separate NATs use the same private subnet
   number

 - if the client admin specifies the same address via clientaddr=
   mount option (pointing the server at the same NAT box, for
   example)

Because of the way the Linux NFSv4.0 client constructs its client
ID string by default, such clients could interfere with each others'
lease state when mounting the same server:

scnprintf(str, len, "Linux NFSv4.0 %s/%s %s",
clp->cl_ipaddr,
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR),
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_PROTO));

cl_ipaddr is set to the value of the clientaddr= mount option. Two
clients whose addresses are 192.168.3.77 that mount the same server
(whose public IP address is, say, 3.4.5.6) would both generate the
same client ID string when sending a SETCLIENTID:

  Linux NFSv4.0 192.168.3.77/3.4.5.6 tcp

and thus the server would not be able to distinguish the clients'
leases. If both clients are using AUTH_SYS when sending SETCLIENTID
then the server could possibly permit the two clients to interfere
with or purge each others' leases.

To better ensure that Linux's NFSv4.0 client ID strings are distinct
in these cases, remove cl_ipaddr from the client ID string and
replace it with something more likely to be unique. Note that the
replacement looks a lot like the uniform client ID string.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/nfs4proc.c

index e799877..fda45ff 100644 (file)
@@ -5618,13 +5618,16 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp)
                return 0;
 
        rcu_read_lock();
-       len = 14 + strlen(clp->cl_ipaddr) + 1 +
+       len = 14 +
+               strlen(clp->cl_rpcclient->cl_nodename) +
+               1 +
                strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)) +
                1 +
                strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_PROTO)) +
                1;
        rcu_read_unlock();
-
+       if (nfs4_client_id_uniquifier[0] != '\0')
+               len += strlen(nfs4_client_id_uniquifier) + 1;
        if (len > NFS4_OPAQUE_LIMIT + 1)
                return -EINVAL;
 
@@ -5638,10 +5641,21 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp)
                return -ENOMEM;
 
        rcu_read_lock();
-       scnprintf(str, len, "Linux NFSv4.0 %s/%s %s",
-                       clp->cl_ipaddr,
-                       rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR),
-                       rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_PROTO));
+       if (nfs4_client_id_uniquifier[0] != '\0')
+               scnprintf(str, len, "Linux NFSv4.0 %s/%s/%s %s",
+                         clp->cl_rpcclient->cl_nodename,
+                         nfs4_client_id_uniquifier,
+                         rpc_peeraddr2str(clp->cl_rpcclient,
+                                          RPC_DISPLAY_ADDR),
+                         rpc_peeraddr2str(clp->cl_rpcclient,
+                                          RPC_DISPLAY_PROTO));
+       else
+               scnprintf(str, len, "Linux NFSv4.0 %s/%s %s",
+                         clp->cl_rpcclient->cl_nodename,
+                         rpc_peeraddr2str(clp->cl_rpcclient,
+                                          RPC_DISPLAY_ADDR),
+                         rpc_peeraddr2str(clp->cl_rpcclient,
+                                          RPC_DISPLAY_PROTO));
        rcu_read_unlock();
 
        clp->cl_owner_id = str;