nfsd: fix IPv6 address handling in the DRC
authorJeff Layton <jlayton@redhat.com>
Mon, 28 Jan 2013 19:41:07 +0000 (14:41 -0500)
committerJ. Bruce Fields <bfields@redhat.com>
Mon, 4 Feb 2013 14:16:19 +0000 (09:16 -0500)
Currently, it only stores the first 16 bytes of any address. struct
sockaddr_in6 is 28 bytes however, so we're currently ignoring the last
12 bytes of the address.

Expand the c_addr field to a sockaddr_in6, and cast it to a sockaddr_in
as necessary. Also fix the comparitor to use the existing RPC
helpers for this.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/cache.h
fs/nfsd/nfscache.c
include/linux/sunrpc/clnt.h

index 93cc9d34c459cbf2e7b3b51332bbc2ad9123ff5f..2cac76c63b97faef900e2e247eef7dd7b0fb1c47 100644 (file)
 
 /*
  * Representation of a reply cache entry.
+ *
+ * Note that we use a sockaddr_in6 to hold the address instead of the more
+ * typical sockaddr_storage. This is for space reasons, since sockaddr_storage
+ * is much larger than a sockaddr_in6.
  */
 struct svc_cacherep {
        struct hlist_node       c_hash;
@@ -20,7 +24,7 @@ struct svc_cacherep {
        unsigned char           c_state,        /* unused, inprog, done */
                                c_type,         /* status, buffer */
                                c_secure : 1;   /* req came from port < 1024 */
-       struct sockaddr_in      c_addr;
+       struct sockaddr_in6     c_addr;
        __be32                  c_xid;
        u32                     c_prot;
        u32                     c_proc;
index 2cbac34a55da1ac6cebeefabe2bdfdd5b883b9f7..5dd9ec2a177f97fdb4a0949613d30433030b18b7 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include <linux/slab.h>
+#include <linux/sunrpc/clnt.h>
 
 #include "nfsd.h"
 #include "cache.h"
@@ -146,7 +147,8 @@ nfsd_cache_lookup(struct svc_rqst *rqstp)
                    xid == rp->c_xid && proc == rp->c_proc &&
                    proto == rp->c_prot && vers == rp->c_vers &&
                    time_before(jiffies, rp->c_timestamp + 120*HZ) &&
-                   memcmp((char*)&rqstp->rq_addr, (char*)&rp->c_addr, sizeof(rp->c_addr))==0) {
+                   rpc_cmp_addr(svc_addr(rqstp), (struct sockaddr *)&rp->c_addr) &&
+                   rpc_get_port(svc_addr(rqstp)) == rpc_get_port((struct sockaddr *)&rp->c_addr)) {
                        nfsdstats.rchits++;
                        goto found_entry;
                }
@@ -183,7 +185,8 @@ nfsd_cache_lookup(struct svc_rqst *rqstp)
        rp->c_state = RC_INPROG;
        rp->c_xid = xid;
        rp->c_proc = proc;
-       memcpy(&rp->c_addr, svc_addr_in(rqstp), sizeof(rp->c_addr));
+       rpc_copy_addr((struct sockaddr *)&rp->c_addr, svc_addr(rqstp));
+       rpc_set_port((struct sockaddr *)&rp->c_addr, rpc_get_port(svc_addr(rqstp)));
        rp->c_prot = proto;
        rp->c_vers = vers;
        rp->c_timestamp = jiffies;
index 34206b84d8dac9208401c48e92713c1d8ca11a1f..47354a25a9277e2eed69f7bee7644923a5e4faaa 100644 (file)
@@ -263,7 +263,9 @@ static inline bool __rpc_copy_addr6(struct sockaddr *dst,
  * @sap1: first sockaddr
  * @sap2: second sockaddr
  *
- * Just compares the family and address portion. Ignores port, scope, etc.
+ * Just compares the family and address portion. Ignores port, but
+ * compares the scope if it's a link-local address.
+ *
  * Returns true if the addrs are equal, false if they aren't.
  */
 static inline bool rpc_cmp_addr(const struct sockaddr *sap1,