diff options
author | Eric Dumazet <edumazet@google.com> | 2016-02-02 22:31:12 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-02-09 04:15:37 -0500 |
commit | 9cf7490360bf2c46a16b7525f899e4970c5fc144 (patch) | |
tree | 62252375fcdc3d7ce324054262ea30015d3227b5 | |
parent | 44c3d0c1c0a880354e9de5d94175742e2c7c9683 (diff) |
tcp: do not drop syn_recv on all icmp reports
Petr Novopashenniy reported that ICMP redirects on SYN_RECV sockets
were leading to RST.
This is of course incorrect.
A specific list of ICMP messages should be able to drop a SYN_RECV.
For instance, a REDIRECT on SYN_RECV shall be ignored, as we do
not hold a dst per SYN_RECV pseudo request.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=111751
Fixes: 079096f103fa ("tcp/dccp: install syn_recv requests into ehash table")
Reported-by: Petr Novopashenniy <pety@rusnet.ru>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/tcp.h | 2 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 11 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 5 |
3 files changed, 12 insertions, 6 deletions
diff --git a/include/net/tcp.h b/include/net/tcp.h index f6f8f032c73e..ae6468f5c9f3 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h | |||
@@ -447,7 +447,7 @@ const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); | |||
447 | 447 | ||
448 | void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb); | 448 | void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb); |
449 | void tcp_v4_mtu_reduced(struct sock *sk); | 449 | void tcp_v4_mtu_reduced(struct sock *sk); |
450 | void tcp_req_err(struct sock *sk, u32 seq); | 450 | void tcp_req_err(struct sock *sk, u32 seq, bool abort); |
451 | int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb); | 451 | int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb); |
452 | struct sock *tcp_create_openreq_child(const struct sock *sk, | 452 | struct sock *tcp_create_openreq_child(const struct sock *sk, |
453 | struct request_sock *req, | 453 | struct request_sock *req, |
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a4d523709ab3..7f6ff037adaf 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -311,7 +311,7 @@ static void do_redirect(struct sk_buff *skb, struct sock *sk) | |||
311 | 311 | ||
312 | 312 | ||
313 | /* handle ICMP messages on TCP_NEW_SYN_RECV request sockets */ | 313 | /* handle ICMP messages on TCP_NEW_SYN_RECV request sockets */ |
314 | void tcp_req_err(struct sock *sk, u32 seq) | 314 | void tcp_req_err(struct sock *sk, u32 seq, bool abort) |
315 | { | 315 | { |
316 | struct request_sock *req = inet_reqsk(sk); | 316 | struct request_sock *req = inet_reqsk(sk); |
317 | struct net *net = sock_net(sk); | 317 | struct net *net = sock_net(sk); |
@@ -323,7 +323,7 @@ void tcp_req_err(struct sock *sk, u32 seq) | |||
323 | 323 | ||
324 | if (seq != tcp_rsk(req)->snt_isn) { | 324 | if (seq != tcp_rsk(req)->snt_isn) { |
325 | NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); | 325 | NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); |
326 | } else { | 326 | } else if (abort) { |
327 | /* | 327 | /* |
328 | * Still in SYN_RECV, just remove it silently. | 328 | * Still in SYN_RECV, just remove it silently. |
329 | * There is no good way to pass the error to the newly | 329 | * There is no good way to pass the error to the newly |
@@ -383,7 +383,12 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) | |||
383 | } | 383 | } |
384 | seq = ntohl(th->seq); | 384 | seq = ntohl(th->seq); |
385 | if (sk->sk_state == TCP_NEW_SYN_RECV) | 385 | if (sk->sk_state == TCP_NEW_SYN_RECV) |
386 | return tcp_req_err(sk, seq); | 386 | return tcp_req_err(sk, seq, |
387 | type == ICMP_PARAMETERPROB || | ||
388 | type == ICMP_TIME_EXCEEDED || | ||
389 | (type == ICMP_DEST_UNREACH && | ||
390 | (code == ICMP_NET_UNREACH || | ||
391 | code == ICMP_HOST_UNREACH))); | ||
387 | 392 | ||
388 | bh_lock_sock(sk); | 393 | bh_lock_sock(sk); |
389 | /* If too many ICMPs get dropped on busy | 394 | /* If too many ICMPs get dropped on busy |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 006396e31cb0..1a5a70fb8551 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -327,6 +327,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
327 | struct tcp_sock *tp; | 327 | struct tcp_sock *tp; |
328 | __u32 seq, snd_una; | 328 | __u32 seq, snd_una; |
329 | struct sock *sk; | 329 | struct sock *sk; |
330 | bool fatal; | ||
330 | int err; | 331 | int err; |
331 | 332 | ||
332 | sk = __inet6_lookup_established(net, &tcp_hashinfo, | 333 | sk = __inet6_lookup_established(net, &tcp_hashinfo, |
@@ -345,8 +346,9 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
345 | return; | 346 | return; |
346 | } | 347 | } |
347 | seq = ntohl(th->seq); | 348 | seq = ntohl(th->seq); |
349 | fatal = icmpv6_err_convert(type, code, &err); | ||
348 | if (sk->sk_state == TCP_NEW_SYN_RECV) | 350 | if (sk->sk_state == TCP_NEW_SYN_RECV) |
349 | return tcp_req_err(sk, seq); | 351 | return tcp_req_err(sk, seq, fatal); |
350 | 352 | ||
351 | bh_lock_sock(sk); | 353 | bh_lock_sock(sk); |
352 | if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG) | 354 | if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG) |
@@ -400,7 +402,6 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
400 | goto out; | 402 | goto out; |
401 | } | 403 | } |
402 | 404 | ||
403 | icmpv6_err_convert(type, code, &err); | ||
404 | 405 | ||
405 | /* Might be for an request_sock */ | 406 | /* Might be for an request_sock */ |
406 | switch (sk->sk_state) { | 407 | switch (sk->sk_state) { |