summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2018-03-12 14:54:23 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-01-09 13:35:49 +0100
commitc49f30b2979bfc8701620e598558f29a48e07234 (patch)
tree159124419714470177d2aa360ad96095f56dd643
parent25d264a3cfa3484adb52f3fb22cbc56e0975e511 (diff)
downloadlinux-stable-c49f30b2979bfc8701620e598558f29a48e07234.tar.gz
linux-stable-c49f30b2979bfc8701620e598558f29a48e07234.tar.bz2
linux-stable-c49f30b2979bfc8701620e598558f29a48e07234.zip
net: ipv6: keep sk status consistent after datagram connect failure
commit 2f987a76a97773beafbc615b9c4d8fe79129a7f4 upstream. On unsuccesful ip6_datagram_connect(), if the failure is caused by ip6_datagram_dst_update(), the sk peer information are cleared, but the sk->sk_state is preserved. If the socket was already in an established status, the overall sk status is inconsistent and fouls later checks in datagram code. Fix this saving the old peer information and restoring them in case of failure. This also aligns ipv6 datagram connect() behavior with ipv4. v1 -> v2: - added missing Fixes tag Fixes: 85cb73ff9b74 ("net: ipv6: reset daddr and dport in sk if connect() fails") Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net> Cc: Suren Baghdasaryan <surenb@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--net/ipv6/datagram.c21
1 files changed, 17 insertions, 4 deletions
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 58929622de0e..fa4987827cda 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -145,10 +145,12 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
struct inet_sock *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
- struct in6_addr *daddr;
+ struct in6_addr *daddr, old_daddr;
+ __be32 fl6_flowlabel = 0;
+ __be32 old_fl6_flowlabel;
+ __be32 old_dport;
int addr_type;
int err;
- __be32 fl6_flowlabel = 0;
if (usin->sin6_family == AF_INET) {
if (__ipv6_only_sock(sk))
@@ -238,9 +240,13 @@ ipv4_connected:
}
}
+ /* save the current peer information before updating it */
+ old_daddr = sk->sk_v6_daddr;
+ old_fl6_flowlabel = np->flow_label;
+ old_dport = inet->inet_dport;
+
sk->sk_v6_daddr = *daddr;
np->flow_label = fl6_flowlabel;
-
inet->inet_dport = usin->sin6_port;
/*
@@ -249,8 +255,15 @@ ipv4_connected:
*/
err = ip6_datagram_dst_update(sk, true);
- if (err)
+ if (err) {
+ /* Restore the socket peer info, to keep it consistent with
+ * the old socket state
+ */
+ sk->sk_v6_daddr = old_daddr;
+ np->flow_label = old_fl6_flowlabel;
+ inet->inet_dport = old_dport;
goto out;
+ }
sk->sk_state = TCP_ESTABLISHED;
sk_set_txhash(sk);