From e4fa181abf1032198ad278cf9f0560e45158c9bc Mon Sep 17 00:00:00 2001 From: Mr NeilBrown Date: Thu, 23 Jan 2020 10:26:37 +1100 Subject: [PATCH] LU-10391 lnet: allow creation of IPv6 socket. 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 Change-Id: Iaef1c08a9afd578b60f54cc83fd2d6882a021e6b Reviewed-on: https://review.whamcloud.com/37705 Reviewed-by: Shaun Tancheff Reviewed-by: Serguei Smirnov Tested-by: jenkins Tested-by: Maloo Reviewed-by: Oleg Drokin --- lnet/lnet/lib-socket.c | 92 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/lnet/lnet/lib-socket.c b/lnet/lnet/lib-socket.c index 18b49a9..8ea4812 100644 --- a/lnet/lnet/lib-socket.c +++ b/lnet/lnet/lib-socket.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include /* 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; -- 1.8.3.1