tcp: implement RFC 5961 3.2
authorEric Dumazet <edumazet@google.com>
Tue, 17 Jul 2012 08:13:05 +0000 (10:13 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 17 Jul 2012 08:36:20 +0000 (01:36 -0700)
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 <edumazet@google.com>
Cc: Kiran Kumar Kella <kkiran@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/ip-sysctl.txt
include/linux/snmp.h
include/net/tcp.h
net/ipv4/proc.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp_input.c

index e20c17a7d34e70dc914a490fe3667c7574e93483..e1e021594cff4a15a5d2f81d2b0b946184c09e20 100644 (file)
@@ -565,6 +565,11 @@ tcp_limit_output_bytes - INTEGER
        reduce the size of individual GSO packet (64KB being the max)
        Default: 131072
 
+tcp_challenge_ack_limit - INTEGER
+       Limits number of Challenge ACK sent per second, as recommended
+       in RFC 5961 (Improving TCP's Robustness to Blind In-Window Attacks)
+       Default: 100
+
 UDP variables:
 
 udp_mem - vector of 3 INTEGERs: min, pressure, max
index 6e4c5112382895502b53cf2b11b63c53bc690aaa..673e0e928b2bbaaf35a04af3f148cb6703a1e921 100644 (file)
@@ -237,6 +237,7 @@ enum
        LINUX_MIB_TCPOFOQUEUE,                  /* TCPOFOQueue */
        LINUX_MIB_TCPOFODROP,                   /* TCPOFODrop */
        LINUX_MIB_TCPOFOMERGE,                  /* TCPOFOMerge */
+       LINUX_MIB_TCPCHALLENGEACK,              /* TCPChallengeACK */
        __LINUX_MIB_MAX
 };
 
index 439984b9af4949451c0d5f1621c3c5178a8b3af5..85c5090bfe25abef6ee2f4ba88d3c10fe3fa1cca 100644 (file)
@@ -254,6 +254,7 @@ extern int sysctl_tcp_thin_linear_timeouts;
 extern int sysctl_tcp_thin_dupack;
 extern int sysctl_tcp_early_retrans;
 extern int sysctl_tcp_limit_output_bytes;
+extern int sysctl_tcp_challenge_ack_limit;
 
 extern atomic_long_t tcp_memory_allocated;
 extern struct percpu_counter tcp_sockets_allocated;
index dae25e7622cf77f3f82d3d771564238234159a2d..3e8e78f12a38e635f9e0bb4d3174a21ef143156b 100644 (file)
@@ -261,6 +261,7 @@ static const struct snmp_mib snmp4_net_list[] = {
        SNMP_MIB_ITEM("TCPOFOQueue", LINUX_MIB_TCPOFOQUEUE),
        SNMP_MIB_ITEM("TCPOFODrop", LINUX_MIB_TCPOFODROP),
        SNMP_MIB_ITEM("TCPOFOMerge", LINUX_MIB_TCPOFOMERGE),
+       SNMP_MIB_ITEM("TCPChallengeACK", LINUX_MIB_TCPCHALLENGEACK),
        SNMP_MIB_SENTINEL
 };
 
index 70730f7aeafe68ce3e1bc5484cc7b600580992f3..3f6a1e762e9c69ab75f9f61850f6a5b2b288fc17 100644 (file)
@@ -605,6 +605,13 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
+       {
+               .procname       = "tcp_challenge_ack_limit",
+               .data           = &sysctl_tcp_challenge_ack_limit,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
 #ifdef CONFIG_NET_DMA
        {
                .procname       = "tcp_dma_copybreak",
index cc4e12f1f2f77e5a16dfebe06d5acc8f7875ac9c..c841a8990377be4929fb580d9aff26003795961a 100644 (file)
@@ -88,6 +88,9 @@ int sysctl_tcp_app_win __read_mostly = 31;
 int sysctl_tcp_adv_win_scale __read_mostly = 1;
 EXPORT_SYMBOL(sysctl_tcp_adv_win_scale);
 
+/* rfc5961 challenge ack rate limiting */
+int sysctl_tcp_challenge_ack_limit = 100;
+
 int sysctl_tcp_stdurg __read_mostly;
 int sysctl_tcp_rfc1337 __read_mostly;
 int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
@@ -5247,6 +5250,23 @@ out:
 }
 #endif /* CONFIG_NET_DMA */
 
+static void tcp_send_challenge_ack(struct sock *sk)
+{
+       /* unprotected vars, we dont care of overwrites */
+       static u32 challenge_timestamp;
+       static unsigned int challenge_count;
+       u32 now = jiffies / HZ;
+
+       if (now != challenge_timestamp) {
+               challenge_timestamp = now;
+               challenge_count = 0;
+       }
+       if (++challenge_count <= sysctl_tcp_challenge_ack_limit) {
+               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK);
+               tcp_send_ack(sk);
+       }
+}
+
 /* Does PAWS and seqno based validation of an incoming segment, flags will
  * play significant role here.
  */
@@ -5283,7 +5303,16 @@ static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
 
        /* Step 2: check RST bit */
        if (th->rst) {
-               tcp_reset(sk);
+               /* RFC 5961 3.2 :
+                * If sequence number exactly matches RCV.NXT, then
+                *     RESET the connection
+                * else
+                *     Send a challenge ACK
+                */
+               if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt)
+                       tcp_reset(sk);
+               else
+                       tcp_send_challenge_ack(sk);
                goto discard;
        }