Skip to content

Commit 282f23c

Browse files
edumazetdavem330
authored andcommitted
tcp: implement RFC 5961 3.2
Implement the RFC 5691 mitigation against Blind Reset attack using RST bit. Idea is to validate incoming RST sequence, to match RCV.NXT value, instead of previouly accepted window : (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) If sequence is in window but not an exact match, send a "challenge ACK", so that the other part can resend an RST with the appropriate sequence. Add a new sysctl, tcp_challenge_ack_limit, to limit number of challenge ACK sent per second. Add a new SNMP counter to count number of challenge acks sent. (netstat -s | grep TCPChallengeACK) Signed-off-by: Eric Dumazet <[email protected]> Cc: Kiran Kumar Kella <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent a858d64 commit 282f23c

File tree

6 files changed

+45
-1
lines changed

6 files changed

+45
-1
lines changed

Documentation/networking/ip-sysctl.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,11 @@ tcp_limit_output_bytes - INTEGER
565565
reduce the size of individual GSO packet (64KB being the max)
566566
Default: 131072
567567

568+
tcp_challenge_ack_limit - INTEGER
569+
Limits number of Challenge ACK sent per second, as recommended
570+
in RFC 5961 (Improving TCP's Robustness to Blind In-Window Attacks)
571+
Default: 100
572+
568573
UDP variables:
569574

570575
udp_mem - vector of 3 INTEGERs: min, pressure, max

include/linux/snmp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ enum
237237
LINUX_MIB_TCPOFOQUEUE, /* TCPOFOQueue */
238238
LINUX_MIB_TCPOFODROP, /* TCPOFODrop */
239239
LINUX_MIB_TCPOFOMERGE, /* TCPOFOMerge */
240+
LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */
240241
__LINUX_MIB_MAX
241242
};
242243

include/net/tcp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ extern int sysctl_tcp_thin_linear_timeouts;
254254
extern int sysctl_tcp_thin_dupack;
255255
extern int sysctl_tcp_early_retrans;
256256
extern int sysctl_tcp_limit_output_bytes;
257+
extern int sysctl_tcp_challenge_ack_limit;
257258

258259
extern atomic_long_t tcp_memory_allocated;
259260
extern struct percpu_counter tcp_sockets_allocated;

net/ipv4/proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ static const struct snmp_mib snmp4_net_list[] = {
261261
SNMP_MIB_ITEM("TCPOFOQueue", LINUX_MIB_TCPOFOQUEUE),
262262
SNMP_MIB_ITEM("TCPOFODrop", LINUX_MIB_TCPOFODROP),
263263
SNMP_MIB_ITEM("TCPOFOMerge", LINUX_MIB_TCPOFOMERGE),
264+
SNMP_MIB_ITEM("TCPChallengeACK", LINUX_MIB_TCPCHALLENGEACK),
264265
SNMP_MIB_SENTINEL
265266
};
266267

net/ipv4/sysctl_net_ipv4.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,13 @@ static struct ctl_table ipv4_table[] = {
605605
.mode = 0644,
606606
.proc_handler = proc_dointvec
607607
},
608+
{
609+
.procname = "tcp_challenge_ack_limit",
610+
.data = &sysctl_tcp_challenge_ack_limit,
611+
.maxlen = sizeof(int),
612+
.mode = 0644,
613+
.proc_handler = proc_dointvec
614+
},
608615
#ifdef CONFIG_NET_DMA
609616
{
610617
.procname = "tcp_dma_copybreak",

net/ipv4/tcp_input.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ int sysctl_tcp_app_win __read_mostly = 31;
8888
int sysctl_tcp_adv_win_scale __read_mostly = 1;
8989
EXPORT_SYMBOL(sysctl_tcp_adv_win_scale);
9090

91+
/* rfc5961 challenge ack rate limiting */
92+
int sysctl_tcp_challenge_ack_limit = 100;
93+
9194
int sysctl_tcp_stdurg __read_mostly;
9295
int sysctl_tcp_rfc1337 __read_mostly;
9396
int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
@@ -5247,6 +5250,23 @@ static bool tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb,
52475250
}
52485251
#endif /* CONFIG_NET_DMA */
52495252

5253+
static void tcp_send_challenge_ack(struct sock *sk)
5254+
{
5255+
/* unprotected vars, we dont care of overwrites */
5256+
static u32 challenge_timestamp;
5257+
static unsigned int challenge_count;
5258+
u32 now = jiffies / HZ;
5259+
5260+
if (now != challenge_timestamp) {
5261+
challenge_timestamp = now;
5262+
challenge_count = 0;
5263+
}
5264+
if (++challenge_count <= sysctl_tcp_challenge_ack_limit) {
5265+
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK);
5266+
tcp_send_ack(sk);
5267+
}
5268+
}
5269+
52505270
/* Does PAWS and seqno based validation of an incoming segment, flags will
52515271
* play significant role here.
52525272
*/
@@ -5283,7 +5303,16 @@ static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
52835303

52845304
/* Step 2: check RST bit */
52855305
if (th->rst) {
5286-
tcp_reset(sk);
5306+
/* RFC 5961 3.2 :
5307+
* If sequence number exactly matches RCV.NXT, then
5308+
* RESET the connection
5309+
* else
5310+
* Send a challenge ACK
5311+
*/
5312+
if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt)
5313+
tcp_reset(sk);
5314+
else
5315+
tcp_send_challenge_ack(sk);
52875316
goto discard;
52885317
}
52895318

0 commit comments

Comments
 (0)