summaryrefslogtreecommitdiffstats
path: root/sys/ofed
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2016-04-21 16:33:42 +0000
committerhselasky <hselasky@FreeBSD.org>2016-04-21 16:33:42 +0000
commitc16ba233cb310029accfb9c6863483878a9ab7af (patch)
tree72a57052ef60a9be59f8ccf86717e3261db918c7 /sys/ofed
parentadad00107e8dfa088fb9af32271cf95ef7f9b29f (diff)
downloadFreeBSD-src-c16ba233cb310029accfb9c6863483878a9ab7af.zip
FreeBSD-src-c16ba233cb310029accfb9c6863483878a9ab7af.tar.gz
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
Diffstat (limited to 'sys/ofed')
-rw-r--r--sys/ofed/drivers/infiniband/core/addr.c147
1 files changed, 108 insertions, 39 deletions
diff --git a/sys/ofed/drivers/infiniband/core/addr.c b/sys/ofed/drivers/infiniband/core/addr.c
index 7992bae..76884d1 100644
--- a/sys/ofed/drivers/infiniband/core/addr.c
+++ b/sys/ofed/drivers/infiniband/core/addr.c
@@ -109,6 +109,14 @@ int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct ifnet *dev,
}
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,
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 ifaddr *ifa;
in_port_t port;
+ uint32_t scope_id;
sin6 = (struct sockaddr_in6 *)addr;
port = sin6->sin6_port;
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);
sin6->sin6_port = port;
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ SCOPE_ID_RESTORE(scope_id, sin6);
if (ifa == NULL) {
ret = -ENODEV;
break;
@@ -161,6 +175,8 @@ int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr,
break;
}
#endif
+ default:
+ break;
}
return ret;
}
@@ -203,7 +219,12 @@ static int addr_resolve(struct sockaddr *src_in,
struct ifaddr *ifa;
struct ifnet *ifp;
struct rtentry *rte;
+#if defined(INET) || defined(INET6)
in_port_t port;
+#endif
+#ifdef INET6
+ uint32_t scope_id;
+#endif
u_char edst[MAX_ADDR_LEN];
int multi;
int bcast;
@@ -219,6 +240,13 @@ static int addr_resolve(struct sockaddr *src_in,
sin6 = NULL;
ifp = NULL;
rte = NULL;
+ ifa = NULL;
+ ifp = NULL;
+ memset(edst, 0, sizeof(edst));
+#ifdef INET6
+ scope_id = -1U;
+#endif
+
switch (dst_in->sa_family) {
#ifdef INET
case AF_INET:
@@ -236,6 +264,22 @@ static int addr_resolve(struct sockaddr *src_in,
port = sin->sin_port;
sin->sin_port = 0;
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;
#endif
@@ -244,42 +288,55 @@ static int addr_resolve(struct sockaddr *src_in,
sin6 = (struct sockaddr_in6 *)dst_in;
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
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;
if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
port = sin6->sin6_port;
sin6->sin6_port = 0;
- } else
- src_in = NULL;
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) {
+ 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;
#endif
default:
- return -EINVAL;
- }
- /*
- * 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;
+ error = EINVAL;
+ goto done;
}
/*
* Make sure the route exists and has a valid link.
*/
rte = rtalloc1(dst_in, 1, 0);
if (rte == NULL || rte->rt_ifp == NULL || !RT_LINK_IS_UP(rte->rt_ifp)) {
- if (rte)
+ if (rte)
RTFREE_LOCKED(rte);
- return -EHOSTUNREACH;
+ error = EHOSTUNREACH;
+ goto done;
}
if (rte->rt_flags & RTF_GATEWAY)
is_gw = 1;
@@ -297,7 +354,8 @@ static int addr_resolve(struct sockaddr *src_in,
RTFREE_LOCKED(rte);
} else if (ifp && ifp != rte->rt_ifp) {
RTFREE_LOCKED(rte);
- return -ENETUNREACH;
+ error = ENETUNREACH;
+ goto done;
} else {
if (ifp == NULL) {
ifp = rte->rt_ifp;
@@ -305,27 +363,29 @@ static int addr_resolve(struct sockaddr *src_in,
}
RT_UNLOCK(rte);
}
+#if defined(INET) || defined(INET6)
mcast:
- if (bcast)
- return rdma_copy_addr(addr, ifp, ifp->if_broadcastaddr);
- if (multi) {
+#endif
+ if (bcast) {
+ memcpy(edst, ifp->if_broadcastaddr, ifp->if_addrlen);
+ goto done;
+ } else if (multi) {
struct sockaddr *llsa;
struct sockaddr_dl sdl;
sdl.sdl_len = sizeof(sdl);
llsa = (struct sockaddr *)&sdl;
- if (ifp->if_resolvemulti == NULL)
- return -EOPNOTSUPP;
-
+ if (ifp->if_resolvemulti == NULL) {
+ error = EOPNOTSUPP;
+ goto done;
+ }
error = ifp->if_resolvemulti(ifp, &llsa, dst_in);
- if (error)
- return -error;
- error = rdma_copy_addr(addr, ifp,
- LLADDR((struct sockaddr_dl *)llsa));
- if (error == 0)
- memcpy(src_in, ifa->ifa_addr, ip_addr_size(ifa->ifa_addr));
- return error;
+ if (error == 0) {
+ memcpy(edst, LLADDR((struct sockaddr_dl *)llsa),
+ ifp->if_addrlen);
+ }
+ goto done;
}
/*
* Resolve the link local address.
@@ -347,12 +407,21 @@ mcast:
break;
}
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));
- 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)
- return -ENODATA;
+ error = ENODATA;
return -error;
}
OpenPOWER on IntegriCloud