+++ /dev/null
-From foo@baz Wed Feb 7 11:22:35 PST 2018
-From: Martin KaFai Lau <kafai@fb.com>
-Date: Wed, 24 Jan 2018 23:15:27 -0800
-Subject: ipv6: Fix SO_REUSEPORT UDP socket with implicit sk_ipv6only
-
-From: Martin KaFai Lau <kafai@fb.com>
-
-
-[ Upstream commit 7ece54a60ee2ba7a386308cae73c790bd580589c ]
-
-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 <calvinowens@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 <kafai@fb.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- net/ipv6/af_inet6.c | 11 +++++++----
- 1 file changed, 7 insertions(+), 4 deletions(-)
-
---- a/net/ipv6/af_inet6.c
-+++ b/net/ipv6/af_inet6.c
-@@ -261,6 +261,7 @@ int inet6_bind(struct socket *sock, stru
- struct net *net = sock_net(sk);
- __be32 v4addr = 0;
- unsigned short snum;
-+ bool saved_ipv6only;
- int addr_type = 0;
- int err = 0;
-
-@@ -365,19 +366,21 @@ int inet6_bind(struct socket *sock, stru
- if (!(addr_type & IPV6_ADDR_MULTICAST))
- np->saddr = addr->sin6_addr;
-
-+ saved_ipv6only = sk->sk_ipv6only;
-+ if (addr_type != IPV6_ADDR_ANY && addr_type != IPV6_ADDR_MAPPED)
-+ sk->sk_ipv6only = 1;
-+
- /* Make sure we are allowed to bind here. */
- if ((snum || !inet->bind_address_no_port) &&
- sk->sk_prot->get_port(sk, snum)) {
-+ sk->sk_ipv6only = saved_ipv6only;
- inet_reset_saddr(sk);
- err = -EADDRINUSE;
- goto out;
- }
-
-- if (addr_type != IPV6_ADDR_ANY) {
-+ if (addr_type != IPV6_ADDR_ANY)
- sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
-- if (addr_type != IPV6_ADDR_MAPPED)
-- sk->sk_ipv6only = 1;
-- }
- if (snum)
- sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
- inet->inet_sport = htons(inet->inet_num);