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

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
...@@ -784,6 +784,11 @@ static inline bool ipv6_addr_v4mapped(const struct in6_addr *a) ...@@ -784,6 +784,11 @@ static inline bool ipv6_addr_v4mapped(const struct in6_addr *a)
cpu_to_be32(0x0000ffff))) == 0UL; 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) static inline bool ipv6_addr_v4mapped_loopback(const struct in6_addr *a)
{ {
return ipv6_addr_v4mapped(a) && ipv4_is_loopback(a->s6_addr32[3]); return ipv6_addr_v4mapped(a) && ipv4_is_loopback(a->s6_addr32[3]);
......
...@@ -815,41 +815,45 @@ static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb, ...@@ -815,41 +815,45 @@ static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb,
const struct net *net, unsigned short port, const struct net *net, unsigned short port,
int l3mdev, const struct sock *sk) 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 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; return false;
}
if (sk->sk_family == AF_INET6) if (sk->sk_family == AF_INET6)
return net_eq(ib2_net(tb), net) && tb->port == port && return ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr);
tb->l3mdev == l3mdev &&
ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr);
else
#endif #endif
return net_eq(ib2_net(tb), net) && tb->port == port && return tb->rcv_saddr == sk->sk_rcv_saddr;
tb->l3mdev == l3mdev && tb->rcv_saddr == sk->sk_rcv_saddr;
} }
bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const struct net *net, 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) 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 IS_ENABLED(CONFIG_IPV6)
if (sk->sk_family != tb->family) { if (sk->sk_family != tb->family) {
if (sk->sk_family == AF_INET) if (sk->sk_family == AF_INET)
return net_eq(ib2_net(tb), net) && tb->port == port && return ipv6_addr_any(&tb->v6_rcv_saddr) ||
tb->l3mdev == l3mdev && ipv6_addr_v4mapped_any(&tb->v6_rcv_saddr);
ipv6_addr_any(&tb->v6_rcv_saddr);
return false; return false;
} }
if (sk->sk_family == AF_INET6) if (sk->sk_family == AF_INET6)
return net_eq(ib2_net(tb), net) && tb->port == port && return ipv6_addr_any(&tb->v6_rcv_saddr);
tb->l3mdev == l3mdev &&
ipv6_addr_any(&tb->v6_rcv_saddr);
else
#endif #endif
return net_eq(ib2_net(tb), net) && tb->port == port && return tb->rcv_saddr == 0;
tb->l3mdev == l3mdev && tb->rcv_saddr == 0;
} }
/* The socket's bhash2 hashbucket spinlock must be held when this is called */ /* The socket's bhash2 hashbucket spinlock must be held when this is called */
......
...@@ -6,41 +6,91 @@ ...@@ -6,41 +6,91 @@
#include "../kselftest_harness.h" #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) FIXTURE(bind_wildcard)
{ {
struct sockaddr_in addr4; struct sockaddr_in addr4;
struct sockaddr_in6 addr6; struct sockaddr_in6 addr6;
int expected_errno;
}; };
FIXTURE_VARIANT(bind_wildcard) FIXTURE_VARIANT(bind_wildcard)
{ {
const __u32 addr4_const; const __u32 addr4_const;
const struct in6_addr *addr6_const; const struct in6_addr *addr6_const;
int expected_errno;
}; };
FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_any) FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_any)
{ {
.addr4_const = INADDR_ANY, .addr4_const = INADDR_ANY,
.addr6_const = &in6addr_any, .addr6_const = &in6addr_any,
.expected_errno = EADDRINUSE,
}; };
FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_local) FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_local)
{ {
.addr4_const = INADDR_ANY, .addr4_const = INADDR_ANY,
.addr6_const = &in6addr_loopback, .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) FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_any)
{ {
.addr4_const = INADDR_LOOPBACK, .addr4_const = INADDR_LOOPBACK,
.addr6_const = &in6addr_any, .addr6_const = &in6addr_any,
.expected_errno = EADDRINUSE,
}; };
FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_local) FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_local)
{ {
.addr4_const = INADDR_LOOPBACK, .addr4_const = INADDR_LOOPBACK,
.addr6_const = &in6addr_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) FIXTURE_SETUP(bind_wildcard)
...@@ -52,11 +102,6 @@ FIXTURE_SETUP(bind_wildcard) ...@@ -52,11 +102,6 @@ FIXTURE_SETUP(bind_wildcard)
self->addr6.sin6_family = AF_INET6; self->addr6.sin6_family = AF_INET6;
self->addr6.sin6_port = htons(0); self->addr6.sin6_port = htons(0);
self->addr6.sin6_addr = *variant->addr6_const; 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) FIXTURE_TEARDOWN(bind_wildcard)
...@@ -65,6 +110,7 @@ FIXTURE_TEARDOWN(bind_wildcard) ...@@ -65,6 +110,7 @@ FIXTURE_TEARDOWN(bind_wildcard)
void bind_sockets(struct __test_metadata *_metadata, void bind_sockets(struct __test_metadata *_metadata,
FIXTURE_DATA(bind_wildcard) *self, FIXTURE_DATA(bind_wildcard) *self,
int expected_errno,
struct sockaddr *addr1, socklen_t addrlen1, struct sockaddr *addr1, socklen_t addrlen1,
struct sockaddr *addr2, socklen_t addrlen2) struct sockaddr *addr2, socklen_t addrlen2)
{ {
...@@ -86,9 +132,9 @@ void bind_sockets(struct __test_metadata *_metadata, ...@@ -86,9 +132,9 @@ void bind_sockets(struct __test_metadata *_metadata,
ASSERT_GT(fd[1], 0); ASSERT_GT(fd[1], 0);
ret = bind(fd[1], addr2, addrlen2); ret = bind(fd[1], addr2, addrlen2);
if (self->expected_errno) { if (expected_errno) {
ASSERT_EQ(ret, -1); ASSERT_EQ(ret, -1);
ASSERT_EQ(errno, self->expected_errno); ASSERT_EQ(errno, expected_errno);
} else { } else {
ASSERT_EQ(ret, 0); ASSERT_EQ(ret, 0);
} }
...@@ -99,14 +145,14 @@ void bind_sockets(struct __test_metadata *_metadata, ...@@ -99,14 +145,14 @@ void bind_sockets(struct __test_metadata *_metadata,
TEST_F(bind_wildcard, v4_v6) TEST_F(bind_wildcard, v4_v6)
{ {
bind_sockets(_metadata, self, bind_sockets(_metadata, self, variant->expected_errno,
(struct sockaddr *)&self->addr4, sizeof(self->addr6), (struct sockaddr *)&self->addr4, sizeof(self->addr4),
(struct sockaddr *)&self->addr6, sizeof(self->addr6)); (struct sockaddr *)&self->addr6, sizeof(self->addr6));
} }
TEST_F(bind_wildcard, v6_v4) 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->addr6, sizeof(self->addr6),
(struct sockaddr *)&self->addr4, sizeof(self->addr4)); (struct sockaddr *)&self->addr4, sizeof(self->addr4));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment