diff mbox series

[02/29] lnet: allow creation of IPv6 socket.

Message ID 1619381316-7719-3-git-send-email-jsimmons@infradead.org (mailing list archive)
State New, archived
Headers show
Series lustre: Update to OpenSFS tree as of April 25, 2020 | expand

Commit Message

James Simmons April 25, 2021, 8:08 p.m. UTC
From: Mr NeilBrown <neilb@suse.de>

With this patch, lnet_sock_create() can make IPv6 sockets.  If an
interface and destination is given, completely different code is
needed to bind the local address.

When no interface or destination is given, an IPv6 socket is always
created, so lnet_acceptor() needs to request that IPv4 connections are
accepted as well, and lnet_sock_getaddr() needs to detect mapped v4
addresses and present them as true v4 addresses.

WC-bug-id: https://jira.whamcloud.com/browse/LU-10391
Lustre-commit: e4fa181abf103219 ("LU-10391 lnet: allow creation of IPv6 socket.")
Signed-off-by: Mr NeilBrown <neilb@suse.de>
Reviewed-on: https://review.whamcloud.com/37705
Reviewed-by: Shaun Tancheff <shaun.tancheff@hpe.com>
Reviewed-by: Serguei Smirnov <ssmirnov@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Signed-off-by: James Simmons <jsimmons@infradead.org>
---
 net/lnet/lnet/lib-socket.c | 85 +++++++++++++++++++++++++++++++++++-----------
 1 file changed, 65 insertions(+), 20 deletions(-)
diff mbox series

Patch

diff --git a/net/lnet/lnet/lib-socket.c b/net/lnet/lnet/lib-socket.c
index 5c5366f..eb6559c 100644
--- a/net/lnet/lnet/lib-socket.c
+++ b/net/lnet/lnet/lib-socket.c
@@ -35,6 +35,8 @@ 
 #include <linux/if.h>
 #include <linux/in.h>
 #include <linux/net.h>
+#include <net/addrconf.h>
+#include <net/ipv6.h>
 #include <linux/file.h>
 #include <linux/pagemap.h>
 /* For sys_open & sys_close */
@@ -141,7 +143,7 @@ 
 }
 EXPORT_SYMBOL(lnet_sock_read);
 
-int choose_ipv4_src(__u32 *ret, int interface, __u32 dst_ipaddr, struct net *ns)
+int choose_ipv4_src(u32 *ret, int interface, u32 dst_ipaddr, struct net *ns)
 {
 	struct net_device *dev;
 	struct in_device *in_dev;
@@ -181,8 +183,12 @@  int choose_ipv4_src(__u32 *ret, int interface, __u32 dst_ipaddr, struct net *ns)
 	struct socket *sock;
 	int rc;
 	int option;
+	int family;
 
-	rc = sock_create_kern(ns, PF_INET, SOCK_STREAM, 0, &sock);
+	family = AF_INET6;
+	if (remaddr)
+		family = remaddr->sa_family;
+	rc = sock_create_kern(ns, family, SOCK_STREAM, 0, &sock);
 	if (rc) {
 		CERROR("Can't create socket: %d\n", rc);
 		return ERR_PTR(rc);
@@ -197,25 +203,44 @@  int choose_ipv4_src(__u32 *ret, int interface, __u32 dst_ipaddr, struct net *ns)
 	}
 
 	if (interface >= 0 || local_port) {
-		struct sockaddr_in locaddr = {};
-
-		locaddr.sin_family = AF_INET;
-		locaddr.sin_addr.s_addr = INADDR_ANY;
-		if (interface >= 0) {
-			struct sockaddr_in *sin = (void *)remaddr;
-			u32 ip;
-
-			rc = choose_ipv4_src(&ip,
-					     interface,
-					     ntohl(sin->sin_addr.s_addr),
-					     ns);
-			if (rc)
-				goto failed;
-			locaddr.sin_addr.s_addr = htonl(ip);
+		struct sockaddr_storage locaddr = {};
+		struct sockaddr_in *sin = (void *)&locaddr;
+		struct sockaddr_in6 *sin6 = (void *)&locaddr;
+
+		switch (family) {
+		case AF_INET:
+			sin->sin_family = AF_INET;
+			sin->sin_addr.s_addr = INADDR_ANY;
+			if (interface >= 0 && remaddr) {
+				struct sockaddr_in *rem = (void *)remaddr;
+				u32 ip;
+
+				rc = choose_ipv4_src(&ip,
+						     interface,
+						     ntohl(rem->sin_addr.s_addr),
+						     ns);
+				if (rc)
+					goto failed;
+				sin->sin_addr.s_addr = htonl(ip);
+			}
+			sin->sin_port = htons(local_port);
+			break;
+		case AF_INET6:
+			sin6->sin6_family = AF_INET6;
+			sin6->sin6_addr = in6addr_any;
+			if (interface >= 0 && remaddr) {
+				struct sockaddr_in6 *rem = (void *)remaddr;
+
+				ipv6_dev_get_saddr(ns,
+						   dev_get_by_index(ns,
+								    interface),
+						   &rem->sin6_addr, 0,
+						   &sin6->sin6_addr);
+			}
+			sin6->sin6_port = htons(local_port);
+			break;
 		}
 
-		locaddr.sin_port = htons(local_port);
-
 		rc = kernel_bind(sock, (struct sockaddr *)&locaddr,
 				 sizeof(locaddr));
 		if (rc == -EADDRINUSE) {
@@ -281,7 +306,19 @@  int choose_ipv4_src(__u32 *ret, int interface, __u32 dst_ipaddr, struct net *ns)
 		       rc, remote ? "peer" : "local");
 		return rc;
 	}
-
+	if (peer->ss_family == AF_INET6) {
+		struct sockaddr_in6 *in6 = (void *)peer;
+		struct sockaddr_in *in = (void *)peer;
+		short port = in6->sin6_port;
+
+		if (ipv6_addr_v4mapped(&in6->sin6_addr)) {
+			/* Pretend it is a v4 socket */
+			memset(in, 0, sizeof(*in));
+			in->sin_family = AF_INET;
+			in->sin_port = port;
+			memcpy(&in->sin_addr, &in6->sin6_addr.s6_addr32[3], 4);
+		}
+	}
 	return 0;
 }
 EXPORT_SYMBOL(lnet_sock_getaddr);
@@ -303,6 +340,7 @@  struct socket *
 lnet_sock_listen(int local_port, int backlog, struct net *ns)
 {
 	struct socket *sock;
+	int val = 0;
 	int rc;
 
 	sock = lnet_sock_create(-1, NULL, local_port, ns);
@@ -314,6 +352,13 @@  struct socket *
 		return ERR_PTR(rc);
 	}
 
+	/* Make sure we get both IPv4 and IPv6 connections.
+	 * This is the default, but it can be overridden so
+	 * we force it back.
+	 */
+	kernel_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
+			  (char *)&val, sizeof(val));
+
 	rc = kernel_listen(sock, backlog);
 	if (!rc)
 		return sock;