aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);