diff options
-rw-r--r-- | include/linux/tcp.h | 4 | ||||
-rw-r--r-- | net/ipv4/tcp_minisocks.c | 29 |
2 files changed, 28 insertions, 5 deletions
diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 66d85a80a1ec..1a7adb411647 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h | |||
@@ -342,6 +342,10 @@ struct tcp_timewait_sock { | |||
342 | u32 tw_rcv_wnd; | 342 | u32 tw_rcv_wnd; |
343 | u32 tw_ts_offset; | 343 | u32 tw_ts_offset; |
344 | u32 tw_ts_recent; | 344 | u32 tw_ts_recent; |
345 | |||
346 | /* The time we sent the last out-of-window ACK: */ | ||
347 | u32 tw_last_oow_ack_time; | ||
348 | |||
345 | long tw_ts_recent_stamp; | 349 | long tw_ts_recent_stamp; |
346 | #ifdef CONFIG_TCP_MD5SIG | 350 | #ifdef CONFIG_TCP_MD5SIG |
347 | struct tcp_md5sig_key *tw_md5_key; | 351 | struct tcp_md5sig_key *tw_md5_key; |
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 98a840561ec8..dd11ac7798c6 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c | |||
@@ -58,6 +58,25 @@ static bool tcp_in_window(u32 seq, u32 end_seq, u32 s_win, u32 e_win) | |||
58 | return seq == e_win && seq == end_seq; | 58 | return seq == e_win && seq == end_seq; |
59 | } | 59 | } |
60 | 60 | ||
61 | static enum tcp_tw_status | ||
62 | tcp_timewait_check_oow_rate_limit(struct inet_timewait_sock *tw, | ||
63 | const struct sk_buff *skb, int mib_idx) | ||
64 | { | ||
65 | struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); | ||
66 | |||
67 | if (!tcp_oow_rate_limited(twsk_net(tw), skb, mib_idx, | ||
68 | &tcptw->tw_last_oow_ack_time)) { | ||
69 | /* Send ACK. Note, we do not put the bucket, | ||
70 | * it will be released by caller. | ||
71 | */ | ||
72 | return TCP_TW_ACK; | ||
73 | } | ||
74 | |||
75 | /* We are rate-limiting, so just release the tw sock and drop skb. */ | ||
76 | inet_twsk_put(tw); | ||
77 | return TCP_TW_SUCCESS; | ||
78 | } | ||
79 | |||
61 | /* | 80 | /* |
62 | * * Main purpose of TIME-WAIT state is to close connection gracefully, | 81 | * * Main purpose of TIME-WAIT state is to close connection gracefully, |
63 | * when one of ends sits in LAST-ACK or CLOSING retransmitting FIN | 82 | * 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, | |||
116 | !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq, | 135 | !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq, |
117 | tcptw->tw_rcv_nxt, | 136 | tcptw->tw_rcv_nxt, |
118 | tcptw->tw_rcv_nxt + tcptw->tw_rcv_wnd)) | 137 | tcptw->tw_rcv_nxt + tcptw->tw_rcv_wnd)) |
119 | return TCP_TW_ACK; | 138 | return tcp_timewait_check_oow_rate_limit( |
139 | tw, skb, LINUX_MIB_TCPACKSKIPPEDFINWAIT2); | ||
120 | 140 | ||
121 | if (th->rst) | 141 | if (th->rst) |
122 | goto kill; | 142 | goto kill; |
@@ -250,10 +270,8 @@ kill: | |||
250 | inet_twsk_schedule(tw, &tcp_death_row, TCP_TIMEWAIT_LEN, | 270 | inet_twsk_schedule(tw, &tcp_death_row, TCP_TIMEWAIT_LEN, |
251 | TCP_TIMEWAIT_LEN); | 271 | TCP_TIMEWAIT_LEN); |
252 | 272 | ||
253 | /* Send ACK. Note, we do not put the bucket, | 273 | return tcp_timewait_check_oow_rate_limit( |
254 | * it will be released by caller. | 274 | tw, skb, LINUX_MIB_TCPACKSKIPPEDTIMEWAIT); |
255 | */ | ||
256 | return TCP_TW_ACK; | ||
257 | } | 275 | } |
258 | inet_twsk_put(tw); | 276 | inet_twsk_put(tw); |
259 | return TCP_TW_SUCCESS; | 277 | return TCP_TW_SUCCESS; |
@@ -289,6 +307,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) | |||
289 | tcptw->tw_ts_recent = tp->rx_opt.ts_recent; | 307 | tcptw->tw_ts_recent = tp->rx_opt.ts_recent; |
290 | tcptw->tw_ts_recent_stamp = tp->rx_opt.ts_recent_stamp; | 308 | tcptw->tw_ts_recent_stamp = tp->rx_opt.ts_recent_stamp; |
291 | tcptw->tw_ts_offset = tp->tsoffset; | 309 | tcptw->tw_ts_offset = tp->tsoffset; |
310 | tcptw->tw_last_oow_ack_time = 0; | ||
292 | 311 | ||
293 | #if IS_ENABLED(CONFIG_IPV6) | 312 | #if IS_ENABLED(CONFIG_IPV6) |
294 | if (tw->tw_family == PF_INET6) { | 313 | if (tw->tw_family == PF_INET6) { |