@@ -500,6 +500,16 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err, bool kern)
tcp_rsk(req)->tfo_listener) {
spin_lock_bh(&queue->fastopenq.lock);
if (tcp_rsk(req)->tfo_listener) {
+ if (req->rsk_listener != sk) {
+ /* TFO request was migrated to another listener so
+ * the new listener must be used in reqsk_fastopen_remove()
+ * to hold requests which cause RST.
+ */
+ sock_put(req->rsk_listener);
+ sock_hold(sk);
+ req->rsk_listener = sk;
+ }
+
/* We are still waiting for the final ACK from 3WHS
* so can't free req now. Instead, we set req->sk to
* NULL to signify that the child socket is taken
@@ -954,7 +964,6 @@ static void inet_child_forget(struct sock *sk, struct request_sock *req,
if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->tfo_listener) {
BUG_ON(rcu_access_pointer(tcp_sk(child)->fastopen_rsk) != req);
- BUG_ON(sk != req->rsk_listener);
/* Paranoid, to prevent race condition if
* an inbound pkt destined for child is
@@ -995,6 +1004,7 @@ EXPORT_SYMBOL(inet_csk_reqsk_queue_add);
void inet_csk_reqsk_queue_migrate(struct sock *sk, struct sock *nsk)
{
struct request_sock_queue *old_accept_queue, *new_accept_queue;
+ struct fastopen_queue *old_fastopenq, *new_fastopenq;
old_accept_queue = &inet_csk(sk)->icsk_accept_queue;
new_accept_queue = &inet_csk(nsk)->icsk_accept_queue;
@@ -1019,6 +1029,29 @@ void inet_csk_reqsk_queue_migrate(struct sock *sk, struct sock *nsk)
spin_unlock(&new_accept_queue->rskq_lock);
spin_unlock(&old_accept_queue->rskq_lock);
+
+ old_fastopenq = &old_accept_queue->fastopenq;
+ new_fastopenq = &new_accept_queue->fastopenq;
+
+ spin_lock_bh(&old_fastopenq->lock);
+ spin_lock_bh(&new_fastopenq->lock);
+
+ new_fastopenq->qlen += old_fastopenq->qlen;
+ old_fastopenq->qlen = 0;
+
+ if (old_fastopenq->rskq_rst_head) {
+ if (new_fastopenq->rskq_rst_head)
+ old_fastopenq->rskq_rst_tail->dl_next = new_fastopenq->rskq_rst_head;
+ else
+ old_fastopenq->rskq_rst_tail = new_fastopenq->rskq_rst_tail;
+
+ new_fastopenq->rskq_rst_head = old_fastopenq->rskq_rst_head;
+ old_fastopenq->rskq_rst_head = NULL;
+ old_fastopenq->rskq_rst_tail = NULL;
+ }
+
+ spin_unlock_bh(&new_fastopenq->lock);
+ spin_unlock_bh(&old_fastopenq->lock);
}
EXPORT_SYMBOL(inet_csk_reqsk_queue_migrate);