diff options
-rw-r--r-- | fs/nfs/client.c | 63 |
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 | */ | ||
282 | static 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 | */ | ||
301 | static 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 | */ | ||
317 | static 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) | |||
344 | static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) | 403 | static 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); |