Commit a22a2965 authored by Joshua Murphy's avatar Joshua Murphy Committed by Dominique Martinet
Browse files

net/9p/fd: support ipv6 for trans=tcp



Allows specifying an IPv6 address when mounting a remote 9p file system.

Signed-off-by: default avatarJoshua Murphy <joshuamurphy@posteo.net>
Message-ID: <20250118192122.327-2-joshuamurphy@posteo.net>
Signed-off-by: default avatarDominique Martinet <asmadeus@codewreck.org>
parent ffd294d3
Loading
Loading
Loading
Loading
+24 −32
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/in.h>
#include <linux/in6.h>
#include <linux/module.h>
#include <linux/net.h>
#include <linux/ipv6.h>
@@ -954,64 +955,55 @@ static void p9_fd_close(struct p9_client *client)
	kfree(ts);
}

/*
 * stolen from NFS - maybe should be made a generic function?
 */
static inline int valid_ipaddr4(const char *buf)
{
	int rc, count, in[4];

	rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]);
	if (rc != 4)
		return -EINVAL;
	for (count = 0; count < 4; count++) {
		if (in[count] > 255)
			return -EINVAL;
	}
	return 0;
}

static int p9_bind_privport(struct socket *sock)
{
	struct sockaddr_in cl;
	struct sockaddr_storage stor = { 0 };
	int port, err = -EINVAL;

	memset(&cl, 0, sizeof(cl));
	cl.sin_family = AF_INET;
	cl.sin_addr.s_addr = htonl(INADDR_ANY);
	stor.ss_family = sock->ops->family;
	if (stor.ss_family == AF_INET)
		((struct sockaddr_in *)&stor)->sin_addr.s_addr = htonl(INADDR_ANY);
	else
		((struct sockaddr_in6 *)&stor)->sin6_addr = in6addr_any;
	for (port = p9_ipport_resv_max; port >= p9_ipport_resv_min; port--) {
		cl.sin_port = htons((ushort)port);
		err = kernel_bind(sock, (struct sockaddr *)&cl, sizeof(cl));
		if (stor.ss_family == AF_INET)
			((struct sockaddr_in *)&stor)->sin_port = htons((ushort)port);
		else
			((struct sockaddr_in6 *)&stor)->sin6_port = htons((ushort)port);
		err = kernel_bind(sock, (struct sockaddr *)&stor, sizeof(stor));
		if (err != -EADDRINUSE)
			break;
	}
	return err;
}


static int
p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
{
	int err;
	char port_str[6];
	struct socket *csocket;
	struct sockaddr_in sin_server;
	struct sockaddr_storage stor = { 0 };
	struct p9_fd_opts opts;

	err = parse_opts(args, &opts);
	if (err < 0)
		return err;

	if (addr == NULL || valid_ipaddr4(addr) < 0)
	if (!addr)
		return -EINVAL;

	sprintf(port_str, "%u", opts.port);
	err = inet_pton_with_scope(current->nsproxy->net_ns, AF_UNSPEC, addr,
				   port_str, &stor);
	if (err < 0)
		return err;

	csocket = NULL;

	client->trans_opts.tcp.port = opts.port;
	client->trans_opts.tcp.privport = opts.privport;
	sin_server.sin_family = AF_INET;
	sin_server.sin_addr.s_addr = in_aton(addr);
	sin_server.sin_port = htons(opts.port);
	err = __sock_create(current->nsproxy->net_ns, PF_INET,
	err = __sock_create(current->nsproxy->net_ns, stor.ss_family,
			    SOCK_STREAM, IPPROTO_TCP, &csocket, 1);
	if (err) {
		pr_err("%s (%d): problem creating socket\n",
@@ -1030,8 +1022,8 @@ p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
	}

	err = READ_ONCE(csocket->ops)->connect(csocket,
				    (struct sockaddr *)&sin_server,
				    sizeof(struct sockaddr_in), 0);
					       (struct sockaddr *)&stor,
					       sizeof(stor), 0);
	if (err < 0) {
		pr_err("%s (%d): problem connecting socket to %s\n",
		       __func__, task_pid_nr(current), addr);