| From 8719f4fa4bfd377eb9952677e7e49d6d54e16883 Mon Sep 17 00:00:00 2001 |
| From: Parav Pandit <parav@mellanox.com> |
| Date: Wed, 10 Apr 2019 11:23:04 +0300 |
| Subject: RDMA/cma: Consider scope_id while binding to ipv6 ll address |
| |
| [ Upstream commit 5d7ed2f27bbd482fd29e6b2e204b1a1ee8a0b268 ] |
| |
| 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 listener 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 this, 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> |
| Signed-off-by: Jason Gunthorpe <jgg@mellanox.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/infiniband/core/cma.c | 25 +++++++++++++++++++------ |
| 1 file changed, 19 insertions(+), 6 deletions(-) |
| |
| diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c |
| index 68c997be24293..c54da16df0beb 100644 |
| --- a/drivers/infiniband/core/cma.c |
| +++ b/drivers/infiniband/core/cma.c |
| @@ -1173,18 +1173,31 @@ 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) |
| { |
| if (src->sa_family != dst->sa_family) |
| return -1; |
| |
| switch (src->sa_family) { |
| case AF_INET: |
| - 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); |
| + return ((struct sockaddr_in *)src)->sin_addr.s_addr != |
| + ((struct sockaddr_in *)dst)->sin_addr.s_addr; |
| + case AF_INET6: { |
| + struct sockaddr_in6 *src_addr6 = (struct sockaddr_in6 *)src; |
| + struct sockaddr_in6 *dst_addr6 = (struct sockaddr_in6 *)dst; |
| + bool link_local; |
| + |
| + if (ipv6_addr_cmp(&src_addr6->sin6_addr, |
| + &dst_addr6->sin6_addr)) |
| + return 1; |
| + link_local = ipv6_addr_type(&dst_addr6->sin6_addr) & |
| + IPV6_ADDR_LINKLOCAL; |
| + /* Link local must 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); |
| -- |
| 2.20.1 |
| |