Skip to content

Commit

Permalink
net: ipv6: keep sk status consistent after datagram connect failure
Browse files Browse the repository at this point in the history
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: 85cb73f ("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>
  • Loading branch information
Paolo Abeni authored and davem330 committed Mar 12, 2018
1 parent b747594 commit 2f987a7
Showing 1 changed file with 14 additions and 7 deletions.
21 changes: 14 additions & 7 deletions net/ipv6/datagram.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,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))
Expand Down Expand Up @@ -238,9 +240,13 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,
}
}

/* 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;

/*
Expand All @@ -250,11 +256,12 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,

err = ip6_datagram_dst_update(sk, true);
if (err) {
/* Reset daddr and dport so that udp_v6_early_demux()
* fails to find this socket
/* Restore the socket peer info, to keep it consistent with
* the old socket state
*/
memset(&sk->sk_v6_daddr, 0, sizeof(sk->sk_v6_daddr));
inet->inet_dport = 0;
sk->sk_v6_daddr = old_daddr;
np->flow_label = old_fl6_flowlabel;
inet->inet_dport = old_dport;
goto out;
}

Expand Down

0 comments on commit 2f987a7

Please sign in to comment.