#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 */
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);
}
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) {
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);
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);
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;