Commit ab6c4ec8 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'tcp-bind-fixes'

Kuniyuki Iwashima says:

====================
tcp: Fix bind() regression for v4-mapped-v6 address

Since bhash2 was introduced, bind() is broken in two cases related
to v4-mapped-v6 address.

This series fixes the regression and adds test to cover the cases.

Changes:
  v2:
    * Added patch 1 to factorise duplicated comparison (Eric Dumazet)

  v1: https://lore.kernel.org/netdev/20230911165106.39384-1-kuniyu@amazon.com/


====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8cdd9f1a 8637d8e8
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -784,6 +784,11 @@ static inline bool ipv6_addr_v4mapped(const struct in6_addr *a)
					cpu_to_be32(0x0000ffff))) == 0UL;
}

static inline bool ipv6_addr_v4mapped_any(const struct in6_addr *a)
{
	return ipv6_addr_v4mapped(a) && ipv4_is_zeronet(a->s6_addr32[3]);
}

static inline bool ipv6_addr_v4mapped_loopback(const struct in6_addr *a)
{
	return ipv6_addr_v4mapped(a) && ipv4_is_loopback(a->s6_addr32[3]);
+20 −16
Original line number Diff line number Diff line
@@ -815,41 +815,45 @@ static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb,
				    const struct net *net, unsigned short port,
				    int l3mdev, const struct sock *sk)
{
	if (!net_eq(ib2_net(tb), net) || tb->port != port ||
	    tb->l3mdev != l3mdev)
		return false;

#if IS_ENABLED(CONFIG_IPV6)
	if (sk->sk_family != tb->family)
	if (sk->sk_family != tb->family) {
		if (sk->sk_family == AF_INET)
			return ipv6_addr_v4mapped(&tb->v6_rcv_saddr) &&
				tb->v6_rcv_saddr.s6_addr32[3] == sk->sk_rcv_saddr;

		return false;
	}

	if (sk->sk_family == AF_INET6)
		return net_eq(ib2_net(tb), net) && tb->port == port &&
			tb->l3mdev == l3mdev &&
			ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr);
	else
		return ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr);
#endif
		return net_eq(ib2_net(tb), net) && tb->port == port &&
			tb->l3mdev == l3mdev && tb->rcv_saddr == sk->sk_rcv_saddr;
	return tb->rcv_saddr == sk->sk_rcv_saddr;
}

bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const struct net *net,
				      unsigned short port, int l3mdev, const struct sock *sk)
{
	if (!net_eq(ib2_net(tb), net) || tb->port != port ||
	    tb->l3mdev != l3mdev)
		return false;

#if IS_ENABLED(CONFIG_IPV6)
	if (sk->sk_family != tb->family) {
		if (sk->sk_family == AF_INET)
			return net_eq(ib2_net(tb), net) && tb->port == port &&
				tb->l3mdev == l3mdev &&
				ipv6_addr_any(&tb->v6_rcv_saddr);
			return ipv6_addr_any(&tb->v6_rcv_saddr) ||
				ipv6_addr_v4mapped_any(&tb->v6_rcv_saddr);

		return false;
	}

	if (sk->sk_family == AF_INET6)
		return net_eq(ib2_net(tb), net) && tb->port == port &&
			tb->l3mdev == l3mdev &&
			ipv6_addr_any(&tb->v6_rcv_saddr);
	else
		return ipv6_addr_any(&tb->v6_rcv_saddr);
#endif
		return net_eq(ib2_net(tb), net) && tb->port == port &&
			tb->l3mdev == l3mdev && tb->rcv_saddr == 0;
	return tb->rcv_saddr == 0;
}

/* The socket's bhash2 hashbucket spinlock must be held when this is called */
+57 −11
Original line number Diff line number Diff line
@@ -6,41 +6,91 @@

#include "../kselftest_harness.h"

struct in6_addr in6addr_v4mapped_any = {
	.s6_addr = {
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 255, 255,
		0, 0, 0, 0
	}
};

struct in6_addr in6addr_v4mapped_loopback = {
	.s6_addr = {
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 255, 255,
		127, 0, 0, 1
	}
};

FIXTURE(bind_wildcard)
{
	struct sockaddr_in addr4;
	struct sockaddr_in6 addr6;
	int expected_errno;
};

FIXTURE_VARIANT(bind_wildcard)
{
	const __u32 addr4_const;
	const struct in6_addr *addr6_const;
	int expected_errno;
};

FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_any)
{
	.addr4_const = INADDR_ANY,
	.addr6_const = &in6addr_any,
	.expected_errno = EADDRINUSE,
};

FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_local)
{
	.addr4_const = INADDR_ANY,
	.addr6_const = &in6addr_loopback,
	.expected_errno = 0,
};

FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_v4mapped_any)
{
	.addr4_const = INADDR_ANY,
	.addr6_const = &in6addr_v4mapped_any,
	.expected_errno = EADDRINUSE,
};

FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_v4mapped_local)
{
	.addr4_const = INADDR_ANY,
	.addr6_const = &in6addr_v4mapped_loopback,
	.expected_errno = EADDRINUSE,
};

FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_any)
{
	.addr4_const = INADDR_LOOPBACK,
	.addr6_const = &in6addr_any,
	.expected_errno = EADDRINUSE,
};

FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_local)
{
	.addr4_const = INADDR_LOOPBACK,
	.addr6_const = &in6addr_loopback,
	.expected_errno = 0,
};

FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_v4mapped_any)
{
	.addr4_const = INADDR_LOOPBACK,
	.addr6_const = &in6addr_v4mapped_any,
	.expected_errno = EADDRINUSE,
};

FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_v4mapped_local)
{
	.addr4_const = INADDR_LOOPBACK,
	.addr6_const = &in6addr_v4mapped_loopback,
	.expected_errno = EADDRINUSE,
};

FIXTURE_SETUP(bind_wildcard)
@@ -52,11 +102,6 @@ FIXTURE_SETUP(bind_wildcard)
	self->addr6.sin6_family = AF_INET6;
	self->addr6.sin6_port = htons(0);
	self->addr6.sin6_addr = *variant->addr6_const;

	if (variant->addr6_const == &in6addr_any)
		self->expected_errno = EADDRINUSE;
	else
		self->expected_errno = 0;
}

FIXTURE_TEARDOWN(bind_wildcard)
@@ -65,6 +110,7 @@ FIXTURE_TEARDOWN(bind_wildcard)

void bind_sockets(struct __test_metadata *_metadata,
		  FIXTURE_DATA(bind_wildcard) *self,
		  int expected_errno,
		  struct sockaddr *addr1, socklen_t addrlen1,
		  struct sockaddr *addr2, socklen_t addrlen2)
{
@@ -86,9 +132,9 @@ void bind_sockets(struct __test_metadata *_metadata,
	ASSERT_GT(fd[1], 0);

	ret = bind(fd[1], addr2, addrlen2);
	if (self->expected_errno) {
	if (expected_errno) {
		ASSERT_EQ(ret, -1);
		ASSERT_EQ(errno, self->expected_errno);
		ASSERT_EQ(errno, expected_errno);
	} else {
		ASSERT_EQ(ret, 0);
	}
@@ -99,14 +145,14 @@ void bind_sockets(struct __test_metadata *_metadata,

TEST_F(bind_wildcard, v4_v6)
{
	bind_sockets(_metadata, self,
		     (struct sockaddr *)&self->addr4, sizeof(self->addr6),
	bind_sockets(_metadata, self, variant->expected_errno,
		     (struct sockaddr *)&self->addr4, sizeof(self->addr4),
		     (struct sockaddr *)&self->addr6, sizeof(self->addr6));
}

TEST_F(bind_wildcard, v6_v4)
{
	bind_sockets(_metadata, self,
	bind_sockets(_metadata, self, variant->expected_errno,
		     (struct sockaddr *)&self->addr6, sizeof(self->addr6),
		     (struct sockaddr *)&self->addr4, sizeof(self->addr4));
}