Merge tag 'fixes-non-critical-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / net / ipv4 / tcp_minisocks.c
index 63d2680..dd11ac7 100644 (file)
@@ -58,6 +58,25 @@ static bool tcp_in_window(u32 seq, u32 end_seq, u32 s_win, u32 e_win)
        return seq == e_win && seq == end_seq;
 }
 
+static enum tcp_tw_status
+tcp_timewait_check_oow_rate_limit(struct inet_timewait_sock *tw,
+                                 const struct sk_buff *skb, int mib_idx)
+{
+       struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
+
+       if (!tcp_oow_rate_limited(twsk_net(tw), skb, mib_idx,
+                                 &tcptw->tw_last_oow_ack_time)) {
+               /* Send ACK. Note, we do not put the bucket,
+                * it will be released by caller.
+                */
+               return TCP_TW_ACK;
+       }
+
+       /* We are rate-limiting, so just release the tw sock and drop skb. */
+       inet_twsk_put(tw);
+       return TCP_TW_SUCCESS;
+}
+
 /*
  * * Main purpose of TIME-WAIT state is to close connection gracefully,
  *   when one of ends sits in LAST-ACK or CLOSING retransmitting FIN
@@ -116,7 +135,8 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
                    !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
                                   tcptw->tw_rcv_nxt,
                                   tcptw->tw_rcv_nxt + tcptw->tw_rcv_wnd))
-                       return TCP_TW_ACK;
+                       return tcp_timewait_check_oow_rate_limit(
+                               tw, skb, LINUX_MIB_TCPACKSKIPPEDFINWAIT2);
 
                if (th->rst)
                        goto kill;
@@ -250,10 +270,8 @@ kill:
                        inet_twsk_schedule(tw, &tcp_death_row, TCP_TIMEWAIT_LEN,
                                           TCP_TIMEWAIT_LEN);
 
-               /* Send ACK. Note, we do not put the bucket,
-                * it will be released by caller.
-                */
-               return TCP_TW_ACK;
+               return tcp_timewait_check_oow_rate_limit(
+                       tw, skb, LINUX_MIB_TCPACKSKIPPEDTIMEWAIT);
        }
        inet_twsk_put(tw);
        return TCP_TW_SUCCESS;
@@ -289,6 +307,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
                tcptw->tw_ts_recent     = tp->rx_opt.ts_recent;
                tcptw->tw_ts_recent_stamp = tp->rx_opt.ts_recent_stamp;
                tcptw->tw_ts_offset     = tp->tsoffset;
+               tcptw->tw_last_oow_ack_time = 0;
 
 #if IS_ENABLED(CONFIG_IPV6)
                if (tw->tw_family == PF_INET6) {
@@ -399,6 +418,32 @@ static void tcp_ecn_openreq_child(struct tcp_sock *tp,
        tp->ecn_flags = inet_rsk(req)->ecn_ok ? TCP_ECN_OK : 0;
 }
 
+void tcp_ca_openreq_child(struct sock *sk, const struct dst_entry *dst)
+{
+       struct inet_connection_sock *icsk = inet_csk(sk);
+       u32 ca_key = dst_metric(dst, RTAX_CC_ALGO);
+       bool ca_got_dst = false;
+
+       if (ca_key != TCP_CA_UNSPEC) {
+               const struct tcp_congestion_ops *ca;
+
+               rcu_read_lock();
+               ca = tcp_ca_find_key(ca_key);
+               if (likely(ca && try_module_get(ca->owner))) {
+                       icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
+                       icsk->icsk_ca_ops = ca;
+                       ca_got_dst = true;
+               }
+               rcu_read_unlock();
+       }
+
+       if (!ca_got_dst && !try_module_get(icsk->icsk_ca_ops->owner))
+               tcp_assign_congestion_control(sk);
+
+       tcp_set_ca_state(sk, TCP_CA_Open);
+}
+EXPORT_SYMBOL_GPL(tcp_ca_openreq_child);
+
 /* This is not only more efficient than what we used to do, it eliminates
  * a lot of code duplication between IPv4/IPv6 SYN recv processing. -DaveM
  *
@@ -441,6 +486,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                tcp_enable_early_retrans(newtp);
                newtp->tlp_high_seq = 0;
                newtp->lsndtime = treq->snt_synack;
+               newtp->last_oow_ack_time = 0;
                newtp->total_retrans = req->num_retrans;
 
                /* So many TCP implementations out there (incorrectly) count the
@@ -451,10 +497,6 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                newtp->snd_cwnd = TCP_INIT_CWND;
                newtp->snd_cwnd_cnt = 0;
 
-               if (!try_module_get(newicsk->icsk_ca_ops->owner))
-                       tcp_assign_congestion_control(newsk);
-
-               tcp_set_ca_state(newsk, TCP_CA_Open);
                tcp_init_xmit_timers(newsk);
                __skb_queue_head_init(&newtp->out_of_order_queue);
                newtp->write_seq = newtp->pushed_seq = treq->snt_isn + 1;
@@ -583,7 +625,11 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
                 * Reset timer after retransmitting SYNACK, similar to
                 * the idea of fast retransmit in recovery.
                 */
-               if (!inet_rtx_syn_ack(sk, req))
+               if (!tcp_oow_rate_limited(sock_net(sk), skb,
+                                         LINUX_MIB_TCPACKSKIPPEDSYNRECV,
+                                         &tcp_rsk(req)->last_oow_ack_time) &&
+
+                   !inet_rtx_syn_ack(sk, req))
                        req->expires = min(TCP_TIMEOUT_INIT << req->num_timeout,
                                           TCP_RTO_MAX) + jiffies;
                return NULL;