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 <sha...@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;
 }
 
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to