Date: Thu, 21 Apr 2016 16:33:42 +0000 (UTC) From: Hans Petter Selasky <hselasky@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r298419 - head/sys/ofed/drivers/infiniband/core Message-ID: <201604211633.u3LGXgmY046047@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: hselasky Date: Thu Apr 21 16:33:42 2016 New Revision: 298419 URL: https://svnweb.freebsd.org/changeset/base/298419 Log: 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 Modified: head/sys/ofed/drivers/infiniband/core/addr.c Modified: head/sys/ofed/drivers/infiniband/core/addr.c ============================================================================== --- head/sys/ofed/drivers/infiniband/core/addr.c Thu Apr 21 16:32:48 2016 (r298418) +++ head/sys/ofed/drivers/infiniband/core/addr.c Thu Apr 21 16:33:42 2016 (r298419) @@ -109,6 +109,14 @@ int rdma_copy_addr(struct rdma_dev_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, u16 *vlan_id) { @@ -144,12 +152,18 @@ int rdma_translate_ip(struct sockaddr *a 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 *a break; } #endif + default: + break; } return ret; } @@ -203,7 +219,12 @@ static int addr_resolve(struct sockaddr 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 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 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 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 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 } 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; }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201604211633.u3LGXgmY046047>