aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorIan Dall <ian@beware.dropbear.id.au>2009-03-10 20:33:22 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2009-03-10 20:33:22 -0400
commitd7371c41b0cda782256b1df759df4e8d4724584c (patch)
treec5807f0cb163db7bb4a03cdd6c986d8fa40545e0 /fs/nfs
parenta71ee337b31271e701f689d544b6153b75609bc5 (diff)
Bug 11061, NFS mounts dropped
Addresses: http://bugzilla.kernel.org/show_bug.cgi?id=11061 sockaddr structures can't be reliably compared using memcmp() because there are padding bytes in the structure which can't be guaranteed to be the same even when the sockaddr structures refer to the same socket. Instead compare all the relevant fields. In the case of IPv6 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. Signed-off-by: Ian Dall <ian@beware.dropbear.id.au> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/client.c63
1 files changed, 62 insertions, 1 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 9b728f3565a1..06654b831d19 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -273,6 +273,65 @@ static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
273#endif 273#endif
274 274
275/* 275/*
276 * Test if two ip4 socket addresses refer to the same socket, by
277 * comparing relevant fields. The padding bytes specifically, are
278 * not compared.
279 *
280 * The caller should ensure both socket addresses are AF_INET.
281 */
282static int nfs_sockaddr_cmp_ip4(const struct sockaddr_in * saddr1,
283 const struct sockaddr_in * saddr2)
284{
285 if (saddr1->sin_addr.s_addr != saddr2->sin_addr.s_addr)
286 return 0;
287 return saddr1->sin_port == saddr2->sin_port;
288}
289
290/*
291 * Test if two ip6 socket addresses refer to the same socket by
292 * comparing relevant fields. The padding bytes specifically, are not
293 * compared. sin6_flowinfo is not compared because it only affects QoS
294 * and sin6_scope_id is only compared if the address is "link local"
295 * because "link local" addresses need only be unique to a specific
296 * link. Conversely, ordinary unicast addresses might have different
297 * sin6_scope_id.
298 *
299 * The caller should ensure both socket addresses are AF_INET6.
300 */
301static int nfs_sockaddr_cmp_ip6 (const struct sockaddr_in6 * saddr1,
302 const struct sockaddr_in6 * saddr2)
303{
304 if (!ipv6_addr_equal(&saddr1->sin6_addr,
305 &saddr1->sin6_addr))
306 return 0;
307 if (ipv6_addr_scope(&saddr1->sin6_addr) == IPV6_ADDR_SCOPE_LINKLOCAL &&
308 saddr1->sin6_scope_id != saddr2->sin6_scope_id)
309 return 0;
310 return saddr1->sin6_port == saddr2->sin6_port;
311}
312
313/*
314 * Test if two socket addresses represent the same actual socket,
315 * by comparing (only) relevant fields.
316 */
317static int nfs_sockaddr_cmp(const struct sockaddr *sa1,
318 const struct sockaddr *sa2)
319{
320 if (sa1->sa_family != sa2->sa_family)
321 return 0;
322
323 switch (sa1->sa_family) {
324 case AF_INET:
325 return nfs_sockaddr_cmp_ip4((const struct sockaddr_in *) sa1,
326 (const struct sockaddr_in *) sa2);
327 case AF_INET6:
328 return nfs_sockaddr_cmp_ip6((const struct sockaddr_in6 *) sa1,
329 (const struct sockaddr_in6 *) sa2);
330 }
331 return 0;
332}
333
334/*
276 * Find a client by IP address and protocol version 335 * Find a client by IP address and protocol version
277 * - returns NULL if no such client 336 * - returns NULL if no such client
278 */ 337 */
@@ -344,8 +403,10 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
344static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 403static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data)
345{ 404{
346 struct nfs_client *clp; 405 struct nfs_client *clp;
406 const struct sockaddr *sap = data->addr;
347 407
348 list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 408 list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
409 const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
349 /* Don't match clients that failed to initialise properly */ 410 /* Don't match clients that failed to initialise properly */
350 if (clp->cl_cons_state < 0) 411 if (clp->cl_cons_state < 0)
351 continue; 412 continue;
@@ -358,7 +419,7 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
358 continue; 419 continue;
359 420
360 /* Match the full socket address */ 421 /* Match the full socket address */
361 if (memcmp(&clp->cl_addr, data->addr, sizeof(clp->cl_addr)) != 0) 422 if (!nfs_sockaddr_cmp(sap, clap))
362 continue; 423 continue;
363 424
364 atomic_inc(&clp->cl_count); 425 atomic_inc(&clp->cl_count);