diff options
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 13 |
1 files changed, 12 insertions, 1 deletions
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 412139f4eccd..883df0ad5bfe 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -1451,6 +1451,7 @@ process: | |||
1451 | 1451 | ||
1452 | if (sk->sk_state == TCP_NEW_SYN_RECV) { | 1452 | if (sk->sk_state == TCP_NEW_SYN_RECV) { |
1453 | struct request_sock *req = inet_reqsk(sk); | 1453 | struct request_sock *req = inet_reqsk(sk); |
1454 | bool req_stolen = false; | ||
1454 | struct sock *nsk; | 1455 | struct sock *nsk; |
1455 | 1456 | ||
1456 | sk = req->rsk_listener; | 1457 | sk = req->rsk_listener; |
@@ -1470,10 +1471,20 @@ process: | |||
1470 | th = (const struct tcphdr *)skb->data; | 1471 | th = (const struct tcphdr *)skb->data; |
1471 | hdr = ipv6_hdr(skb); | 1472 | hdr = ipv6_hdr(skb); |
1472 | tcp_v6_fill_cb(skb, hdr, th); | 1473 | tcp_v6_fill_cb(skb, hdr, th); |
1473 | nsk = tcp_check_req(sk, skb, req, false); | 1474 | nsk = tcp_check_req(sk, skb, req, false, &req_stolen); |
1474 | } | 1475 | } |
1475 | if (!nsk) { | 1476 | if (!nsk) { |
1476 | reqsk_put(req); | 1477 | reqsk_put(req); |
1478 | if (req_stolen) { | ||
1479 | /* Another cpu got exclusive access to req | ||
1480 | * and created a full blown socket. | ||
1481 | * Try to feed this packet to this socket | ||
1482 | * instead of discarding it. | ||
1483 | */ | ||
1484 | tcp_v6_restore_cb(skb); | ||
1485 | sock_put(sk); | ||
1486 | goto lookup; | ||
1487 | } | ||
1477 | goto discard_and_relse; | 1488 | goto discard_and_relse; |
1478 | } | 1489 | } |
1479 | if (nsk == sk) { | 1490 | if (nsk == sk) { |