Whamcloud - gitweb
LU-10391 lnet: allow creation of IPv6 socket. 05/37705/12
authorMr NeilBrown <neilb@suse.de>
Wed, 22 Jan 2020 23:26:37 +0000 (10:26 +1100)
committerOleg Drokin <green@whamcloud.com>
Fri, 26 Feb 2021 21:07:39 +0000 (21:07 +0000)
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.

Signed-off-by: Mr NeilBrown <neilb@suse.de>
Change-Id: Iaef1c08a9afd578b60f54cc83fd2d6882a021e6b
Reviewed-on: https://review.whamcloud.com/37705
Reviewed-by: Shaun Tancheff <shaun.tancheff@hpe.com>
Reviewed-by: Serguei Smirnov <ssmirnov@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lnet/lnet/lib-socket.c

index 18b49a9..8ea4812 100644 (file)
@@ -34,6 +34,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 */
@@ -184,14 +186,18 @@ static struct socket *
 lnet_sock_create(int interface, struct sockaddr *remaddr,
                 int local_port, struct net *ns)
 {
-       struct socket      *sock;
-       int                 rc;
-       int                 option;
+       struct socket *sock;
+       int rc;
+       int option;
+       int family;
 
+       family = AF_INET6;
+       if (remaddr)
+               family = remaddr->sa_family;
 #ifdef HAVE_SOCK_CREATE_KERN_USE_NET
-       rc = sock_create_kern(ns, PF_INET, SOCK_STREAM, 0, &sock);
+       rc = sock_create_kern(ns, family, SOCK_STREAM, 0, &sock);
 #else
-       rc = sock_create_kern(PF_INET, SOCK_STREAM, 0, &sock);
+       rc = sock_create_kern(family, SOCK_STREAM, 0, &sock);
 #endif
        if (rc) {
                CERROR("Can't create socket: %d\n", rc);
@@ -207,25 +213,43 @@ lnet_sock_create(int interface, struct sockaddr *remaddr,
        }
 
        if (interface >= 0 || local_port != 0) {
-               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) {
@@ -296,7 +320,19 @@ lnet_sock_getaddr(struct socket *sock, bool remote,
                        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);
@@ -318,6 +354,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);
@@ -329,6 +366,13 @@ lnet_sock_listen(int local_port, int backlog, struct net *ns)
                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 == 0)
                return sock;