From 971a006e5c61a607755d24ebb99b9f9126489a03 Mon Sep 17 00:00:00 2001 From: Ian Dall Date: Tue, 10 Mar 2009 20:33:22 -0400 Subject: [PATCH] --- yaml --- r: 132304 b: refs/heads/master c: d7371c41b0cda782256b1df759df4e8d4724584c h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/fs/nfs/client.c | 63 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/[refs] b/[refs] index 0b77588cd7e0..f4cf408ba2a4 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: a71ee337b31271e701f689d544b6153b75609bc5 +refs/heads/master: d7371c41b0cda782256b1df759df4e8d4724584c diff --git a/trunk/fs/nfs/client.c b/trunk/fs/nfs/client.c index 9b728f3565a1..06654b831d19 100644 --- a/trunk/fs/nfs/client.c +++ b/trunk/fs/nfs/client.c @@ -272,6 +272,65 @@ static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, } #endif +/* + * Test if two ip4 socket addresses refer to the same socket, by + * comparing relevant fields. The padding bytes specifically, are + * not compared. + * + * The caller should ensure both socket addresses are AF_INET. + */ +static int nfs_sockaddr_cmp_ip4(const struct sockaddr_in * saddr1, + const struct sockaddr_in * saddr2) +{ + if (saddr1->sin_addr.s_addr != saddr2->sin_addr.s_addr) + return 0; + return saddr1->sin_port == saddr2->sin_port; +} + +/* + * Test if two ip6 socket addresses refer to the same socket by + * comparing relevant fields. The padding bytes specifically, are not + * compared. sin6_flowinfo is not compared because it only affects QoS + * and sin6_scope_id is only compared if the address is "link local" + * because "link local" addresses need only be unique to a specific + * link. Conversely, ordinary unicast addresses might have different + * sin6_scope_id. + * + * The caller should ensure both socket addresses are AF_INET6. + */ +static int nfs_sockaddr_cmp_ip6 (const struct sockaddr_in6 * saddr1, + const struct sockaddr_in6 * saddr2) +{ + if (!ipv6_addr_equal(&saddr1->sin6_addr, + &saddr1->sin6_addr)) + return 0; + if (ipv6_addr_scope(&saddr1->sin6_addr) == IPV6_ADDR_SCOPE_LINKLOCAL && + saddr1->sin6_scope_id != saddr2->sin6_scope_id) + return 0; + return saddr1->sin6_port == saddr2->sin6_port; +} + +/* + * Test if two socket addresses represent the same actual socket, + * by comparing (only) relevant fields. + */ +static int nfs_sockaddr_cmp(const struct sockaddr *sa1, + const struct sockaddr *sa2) +{ + if (sa1->sa_family != sa2->sa_family) + return 0; + + switch (sa1->sa_family) { + case AF_INET: + return nfs_sockaddr_cmp_ip4((const struct sockaddr_in *) sa1, + (const struct sockaddr_in *) sa2); + case AF_INET6: + return nfs_sockaddr_cmp_ip6((const struct sockaddr_in6 *) sa1, + (const struct sockaddr_in6 *) sa2); + } + return 0; +} + /* * Find a client by IP address and protocol version * - returns NULL if no such client @@ -344,8 +403,10 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp) static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) { struct nfs_client *clp; + const struct sockaddr *sap = data->addr; list_for_each_entry(clp, &nfs_client_list, cl_share_link) { + const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; /* Don't match clients that failed to initialise properly */ if (clp->cl_cons_state < 0) continue; @@ -358,7 +419,7 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat continue; /* Match the full socket address */ - if (memcmp(&clp->cl_addr, data->addr, sizeof(clp->cl_addr)) != 0) + if (!nfs_sockaddr_cmp(sap, clap)) continue; atomic_inc(&clp->cl_count);