Whamcloud - gitweb
LU-10391 lnet: allow creation of IPv6 socket.
[fs/lustre-release.git] / 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;