Skip site navigation (1)Skip section navigation (2)
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>