Skip to content

Commit

Permalink
Merge branch 'tcp-bind-fixes'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
davem330 committed Sep 13, 2023
2 parents 8cdd9f1 + 8637d8e commit ab6c4ec
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 27 deletions.
5 changes: 5 additions & 0 deletions include/net/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
36 changes: 20 additions & 16 deletions net/ipv4/inet_hashtables.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
68 changes: 57 additions & 11 deletions tools/testing/selftests/net/bind_wildcard.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
{
Expand All @@ -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);
}
Expand All @@ -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));
}
Expand Down

0 comments on commit ab6c4ec

Please sign in to comment.