Fix for using IPv6 addresses with RDMA:
IPv6 addresses has a scope ID which sometimes is stored in the "sin6_scope_id" field of "struct sockaddr_in6" and sometimes as part of the IPv6 address itself depending on the context. If the scope ID is not in the expected location, the IPv6 address lookups in the so-called GID table will fail. Some code factoring has been made to achieve a clean exit of the "addr_resolve" function via a common "done" label. Sponsored by: Mellanox Technologies Submitted by: Shani Michaeli <shanim@mellanox.com> MFC after: 1 week
This commit is contained in:
parent
892bfb4bb2
commit
150c88d471
@ -109,6 +109,14 @@ int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct ifnet *dev,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(rdma_copy_addr);
|
EXPORT_SYMBOL(rdma_copy_addr);
|
||||||
|
|
||||||
|
#define SCOPE_ID_CACHE(_scope_id, _addr6) do { \
|
||||||
|
(_addr6)->sin6_addr.s6_addr[3] = (_scope_id); \
|
||||||
|
(_addr6)->sin6_scope_id = 0; } while (0)
|
||||||
|
|
||||||
|
#define SCOPE_ID_RESTORE(_scope_id, _addr6) do { \
|
||||||
|
(_addr6)->sin6_scope_id = (_scope_id); \
|
||||||
|
(_addr6)->sin6_addr.s6_addr[3] = 0; } while (0)
|
||||||
|
|
||||||
int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr,
|
int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr,
|
||||||
u16 *vlan_id)
|
u16 *vlan_id)
|
||||||
{
|
{
|
||||||
@ -144,12 +152,18 @@ int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr,
|
|||||||
struct sockaddr_in6 *sin6;
|
struct sockaddr_in6 *sin6;
|
||||||
struct ifaddr *ifa;
|
struct ifaddr *ifa;
|
||||||
in_port_t port;
|
in_port_t port;
|
||||||
|
uint32_t scope_id;
|
||||||
|
|
||||||
sin6 = (struct sockaddr_in6 *)addr;
|
sin6 = (struct sockaddr_in6 *)addr;
|
||||||
port = sin6->sin6_port;
|
port = sin6->sin6_port;
|
||||||
sin6->sin6_port = 0;
|
sin6->sin6_port = 0;
|
||||||
|
scope_id = sin6->sin6_scope_id;
|
||||||
|
if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
|
||||||
|
SCOPE_ID_CACHE(scope_id, sin6);
|
||||||
ifa = ifa_ifwithaddr(addr);
|
ifa = ifa_ifwithaddr(addr);
|
||||||
sin6->sin6_port = port;
|
sin6->sin6_port = port;
|
||||||
|
if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
|
||||||
|
SCOPE_ID_RESTORE(scope_id, sin6);
|
||||||
if (ifa == NULL) {
|
if (ifa == NULL) {
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
break;
|
break;
|
||||||
@ -161,6 +175,8 @@ int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -203,7 +219,12 @@ static int addr_resolve(struct sockaddr *src_in,
|
|||||||
struct ifaddr *ifa;
|
struct ifaddr *ifa;
|
||||||
struct ifnet *ifp;
|
struct ifnet *ifp;
|
||||||
struct rtentry *rte;
|
struct rtentry *rte;
|
||||||
|
#if defined(INET) || defined(INET6)
|
||||||
in_port_t port;
|
in_port_t port;
|
||||||
|
#endif
|
||||||
|
#ifdef INET6
|
||||||
|
uint32_t scope_id;
|
||||||
|
#endif
|
||||||
u_char edst[MAX_ADDR_LEN];
|
u_char edst[MAX_ADDR_LEN];
|
||||||
int multi;
|
int multi;
|
||||||
int bcast;
|
int bcast;
|
||||||
@ -219,6 +240,13 @@ static int addr_resolve(struct sockaddr *src_in,
|
|||||||
sin6 = NULL;
|
sin6 = NULL;
|
||||||
ifp = NULL;
|
ifp = NULL;
|
||||||
rte = NULL;
|
rte = NULL;
|
||||||
|
ifa = NULL;
|
||||||
|
ifp = NULL;
|
||||||
|
memset(edst, 0, sizeof(edst));
|
||||||
|
#ifdef INET6
|
||||||
|
scope_id = -1U;
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (dst_in->sa_family) {
|
switch (dst_in->sa_family) {
|
||||||
#ifdef INET
|
#ifdef INET
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
@ -236,6 +264,22 @@ static int addr_resolve(struct sockaddr *src_in,
|
|||||||
port = sin->sin_port;
|
port = sin->sin_port;
|
||||||
sin->sin_port = 0;
|
sin->sin_port = 0;
|
||||||
memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
|
memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have a source address to use look it
|
||||||
|
* up first and verify that it is a local
|
||||||
|
* interface:
|
||||||
|
*/
|
||||||
|
ifa = ifa_ifwithaddr(src_in);
|
||||||
|
sin->sin_port = port;
|
||||||
|
if (ifa == NULL) {
|
||||||
|
error = ENETUNREACH;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
ifp = ifa->ifa_ifp;
|
||||||
|
ifa_free(ifa);
|
||||||
|
if (bcast || multi)
|
||||||
|
goto mcast;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
@ -244,42 +288,55 @@ static int addr_resolve(struct sockaddr *src_in,
|
|||||||
sin6 = (struct sockaddr_in6 *)dst_in;
|
sin6 = (struct sockaddr_in6 *)dst_in;
|
||||||
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
|
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
|
||||||
multi = 1;
|
multi = 1;
|
||||||
|
if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) {
|
||||||
|
/*
|
||||||
|
* The IB address comparison fails if the
|
||||||
|
* scope ID is set and not part of the addr:
|
||||||
|
*/
|
||||||
|
scope_id = sin6->sin6_scope_id;
|
||||||
|
if (scope_id < 256)
|
||||||
|
SCOPE_ID_CACHE(scope_id, sin6);
|
||||||
|
}
|
||||||
sin6 = (struct sockaddr_in6 *)src_in;
|
sin6 = (struct sockaddr_in6 *)src_in;
|
||||||
if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
|
if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
|
||||||
port = sin6->sin6_port;
|
port = sin6->sin6_port;
|
||||||
sin6->sin6_port = 0;
|
sin6->sin6_port = 0;
|
||||||
} else
|
if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) {
|
||||||
src_in = NULL;
|
if (scope_id < 256)
|
||||||
|
SCOPE_ID_CACHE(scope_id, sin6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have a source address to use look it
|
||||||
|
* up first and verify that it is a local
|
||||||
|
* interface:
|
||||||
|
*/
|
||||||
|
ifa = ifa_ifwithaddr(src_in);
|
||||||
|
sin6->sin6_port = port;
|
||||||
|
if (ifa == NULL) {
|
||||||
|
error = ENETUNREACH;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
ifp = ifa->ifa_ifp;
|
||||||
|
ifa_free(ifa);
|
||||||
|
if (bcast || multi)
|
||||||
|
goto mcast;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
error = EINVAL;
|
||||||
}
|
goto done;
|
||||||
/*
|
|
||||||
* If we have a source address to use look it up first and verify
|
|
||||||
* that it is a local interface.
|
|
||||||
*/
|
|
||||||
if (sin->sin_addr.s_addr != INADDR_ANY) {
|
|
||||||
ifa = ifa_ifwithaddr(src_in);
|
|
||||||
if (sin)
|
|
||||||
sin->sin_port = port;
|
|
||||||
if (sin6)
|
|
||||||
sin6->sin6_port = port;
|
|
||||||
if (ifa == NULL)
|
|
||||||
return -ENETUNREACH;
|
|
||||||
ifp = ifa->ifa_ifp;
|
|
||||||
ifa_free(ifa);
|
|
||||||
if (bcast || multi)
|
|
||||||
goto mcast;
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Make sure the route exists and has a valid link.
|
* Make sure the route exists and has a valid link.
|
||||||
*/
|
*/
|
||||||
rte = rtalloc1(dst_in, 1, 0);
|
rte = rtalloc1(dst_in, 1, 0);
|
||||||
if (rte == NULL || rte->rt_ifp == NULL || !RT_LINK_IS_UP(rte->rt_ifp)) {
|
if (rte == NULL || rte->rt_ifp == NULL || !RT_LINK_IS_UP(rte->rt_ifp)) {
|
||||||
if (rte)
|
if (rte)
|
||||||
RTFREE_LOCKED(rte);
|
RTFREE_LOCKED(rte);
|
||||||
return -EHOSTUNREACH;
|
error = EHOSTUNREACH;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
if (rte->rt_flags & RTF_GATEWAY)
|
if (rte->rt_flags & RTF_GATEWAY)
|
||||||
is_gw = 1;
|
is_gw = 1;
|
||||||
@ -297,7 +354,8 @@ static int addr_resolve(struct sockaddr *src_in,
|
|||||||
RTFREE_LOCKED(rte);
|
RTFREE_LOCKED(rte);
|
||||||
} else if (ifp && ifp != rte->rt_ifp) {
|
} else if (ifp && ifp != rte->rt_ifp) {
|
||||||
RTFREE_LOCKED(rte);
|
RTFREE_LOCKED(rte);
|
||||||
return -ENETUNREACH;
|
error = ENETUNREACH;
|
||||||
|
goto done;
|
||||||
} else {
|
} else {
|
||||||
if (ifp == NULL) {
|
if (ifp == NULL) {
|
||||||
ifp = rte->rt_ifp;
|
ifp = rte->rt_ifp;
|
||||||
@ -305,27 +363,29 @@ static int addr_resolve(struct sockaddr *src_in,
|
|||||||
}
|
}
|
||||||
RT_UNLOCK(rte);
|
RT_UNLOCK(rte);
|
||||||
}
|
}
|
||||||
|
#if defined(INET) || defined(INET6)
|
||||||
mcast:
|
mcast:
|
||||||
if (bcast)
|
#endif
|
||||||
return rdma_copy_addr(addr, ifp, ifp->if_broadcastaddr);
|
if (bcast) {
|
||||||
if (multi) {
|
memcpy(edst, ifp->if_broadcastaddr, ifp->if_addrlen);
|
||||||
|
goto done;
|
||||||
|
} else if (multi) {
|
||||||
struct sockaddr *llsa;
|
struct sockaddr *llsa;
|
||||||
struct sockaddr_dl sdl;
|
struct sockaddr_dl sdl;
|
||||||
|
|
||||||
sdl.sdl_len = sizeof(sdl);
|
sdl.sdl_len = sizeof(sdl);
|
||||||
llsa = (struct sockaddr *)&sdl;
|
llsa = (struct sockaddr *)&sdl;
|
||||||
|
|
||||||
if (ifp->if_resolvemulti == NULL)
|
if (ifp->if_resolvemulti == NULL) {
|
||||||
return -EOPNOTSUPP;
|
error = EOPNOTSUPP;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
error = ifp->if_resolvemulti(ifp, &llsa, dst_in);
|
error = ifp->if_resolvemulti(ifp, &llsa, dst_in);
|
||||||
if (error)
|
if (error == 0) {
|
||||||
return -error;
|
memcpy(edst, LLADDR((struct sockaddr_dl *)llsa),
|
||||||
error = rdma_copy_addr(addr, ifp,
|
ifp->if_addrlen);
|
||||||
LLADDR((struct sockaddr_dl *)llsa));
|
}
|
||||||
if (error == 0)
|
goto done;
|
||||||
memcpy(src_in, ifa->ifa_addr, ip_addr_size(ifa->ifa_addr));
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Resolve the link local address.
|
* Resolve the link local address.
|
||||||
@ -347,12 +407,21 @@ mcast:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
RTFREE(rte);
|
RTFREE(rte);
|
||||||
if (error == 0) {
|
done:
|
||||||
|
if (error == 0)
|
||||||
|
error = -rdma_copy_addr(addr, ifp, edst);
|
||||||
|
if (error == 0)
|
||||||
memcpy(src_in, ifa->ifa_addr, ip_addr_size(ifa->ifa_addr));
|
memcpy(src_in, ifa->ifa_addr, ip_addr_size(ifa->ifa_addr));
|
||||||
return rdma_copy_addr(addr, ifp, edst);
|
#ifdef INET6
|
||||||
|
if (scope_id < 256) {
|
||||||
|
sin6 = (struct sockaddr_in6 *)src_in;
|
||||||
|
SCOPE_ID_RESTORE(scope_id, sin6);
|
||||||
|
sin6 = (struct sockaddr_in6 *)dst_in;
|
||||||
|
SCOPE_ID_RESTORE(scope_id, sin6);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (error == EWOULDBLOCK)
|
if (error == EWOULDBLOCK)
|
||||||
return -ENODATA;
|
error = ENODATA;
|
||||||
return -error;
|
return -error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user