On Thu, Jan 25, 2018 at 2:15 AM, Martin KaFai Lau <ka...@fb.com> wrote: > If a sk_v6_rcv_saddr is !IPV6_ADDR_ANY and !IPV6_ADDR_MAPPED, it > implicitly implies it is an ipv6only socket. However, in inet6_bind(), > this addr_type checking and setting sk->sk_ipv6only to 1 are only done > after sk->sk_prot->get_port(sk, snum) has been completed successfully. > > This inconsistency between sk_v6_rcv_saddr and sk_ipv6only confuses > the 'get_port()'. > > In particular, when binding SO_REUSEPORT UDP sockets, > udp_reuseport_add_sock(sk,...) is called. udp_reuseport_add_sock() > checks "ipv6_only_sock(sk2) == ipv6_only_sock(sk)" before adding sk to > sk2->sk_reuseport_cb. In this case, ipv6_only_sock(sk2) could be > 1 while ipv6_only_sock(sk) is still 0 here. The end result is, > reuseport_alloc(sk) is called instead of adding sk to the existing > sk2->sk_reuseport_cb. > > It can be reproduced by binding two SO_REUSEPORT UDP sockets on an > IPv6 address (!ANY and !MAPPED). Only one of the socket will > receive packet. > > The fix is to set the implicit sk_ipv6only before calling get_port(). > The original sk_ipv6only has to be saved such that it can be restored > in case get_port() failed. The situation is similar to the > inet_reset_saddr(sk) after get_port() has failed. > > Thanks to Calvin Owens <calvinow...@fb.com> who created an easy > reproduction which leads to a fix. > > Fixes: e32ea7e74727 ("soreuseport: fast reuseport UDP socket selection") > Signed-off-by: Martin KaFai Lau <ka...@fb.com>
Wow, good catch! Acked-by: Craig Gallek <kr...@google.com>