diff mbox series

[rdma-next,2/2] RDMA/cma: Consider scope_id while binding to ipv6 ll address

Message ID 20190410082304.26829-3-leon@kernel.org (mailing list archive)
State Accepted
Delegated to: Jason Gunthorpe
Headers show
Series Support RoCE IPv6 link local vlan GIDs | expand

Commit Message

Leon Romanovsky April 10, 2019, 8:23 a.m. UTC
From: Parav Pandit <parav@mellanox.com>

When two netdev have same link local addresses (such as vlan and non
vlan), two rdma cm listen id should be able to bind to following
different addresses.

listener-1: addr=lla, scope_id=A, port=X
listener-2: addr=lla, scope_id=B, port=X

However while comparing the addresses only addr and port are considered,
due to which 2nd listener fails to listen.

In below example of two listeners, 2nd listner is failing with address
in use error.

$ rping -sv -a fe80::268a:7ff:feb3:d113%ens2f1 -p 4545&

$ rping -sv -a fe80::268a:7ff:feb3:d113%ens2f1.200 -p 4545
rdma_bind_addr: Address already in use

To overcome, consider the scope_ids as well which forms the accurate
IPv6 link local address.

Signed-off-by: Parav Pandit <parav@mellanox.com>
Reviewed-by: Daniel Jurgens <danielj@mellanox.com>
Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
---
 drivers/infiniband/core/cma.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)
diff mbox series

Patch

diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index efa091cad141..adf99e446ab1 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -1177,8 +1177,13 @@  static inline bool cma_any_addr(const struct sockaddr *addr)
 	return cma_zero_addr(addr) || cma_loopback_addr(addr);
 }
 
-static int cma_addr_cmp(struct sockaddr *src, struct sockaddr *dst)
+static int cma_addr_cmp(const struct sockaddr *src, const struct sockaddr *dst)
 {
+	struct sockaddr_in6 *src_addr6 = (struct sockaddr_in6 *)src;
+	struct sockaddr_in6 *dst_addr6 = (struct sockaddr_in6 *)dst;
+	bool link_local;
+	int diff_addr;
+
 	if (src->sa_family != dst->sa_family)
 		return -1;
 
@@ -1187,8 +1192,17 @@  static int cma_addr_cmp(struct sockaddr *src, struct sockaddr *dst)
 		return ((struct sockaddr_in *) src)->sin_addr.s_addr !=
 		       ((struct sockaddr_in *) dst)->sin_addr.s_addr;
 	case AF_INET6:
-		return ipv6_addr_cmp(&((struct sockaddr_in6 *) src)->sin6_addr,
-				     &((struct sockaddr_in6 *) dst)->sin6_addr);
+		diff_addr = ipv6_addr_cmp(&src_addr6->sin6_addr,
+					  &dst_addr6->sin6_addr);
+		/* if addresses differ, return right away that they differ */
+		if (diff_addr)
+			return 1;
+		link_local = ipv6_addr_type(&dst_addr6->sin6_addr) &
+					    IPV6_ADDR_LINKLOCAL;
+		/* link local must have to match their scope_ids */
+		return link_local ?
+			(src_addr6->sin6_scope_id !=
+			 dst_addr6->sin6_scope_id) : 0;
 	default:
 		return ib_addr_cmp(&((struct sockaddr_ib *) src)->sib_addr,
 				   &((struct sockaddr_ib *) dst)->sib_addr);