tcp: implement RFC 5961 3.2
authorEric Dumazet <edumazet@google.com>
Tue, 17 Jul 2012 08:13:05 +0000 (10:13 +0200)
committerBen Hutchings <ben@decadent.org.uk>
Wed, 16 Jan 2013 01:13:25 +0000 (01:13 +0000)
[ Upstream commit 282f23c6ee343126156dd41218b22ece96d747e3 ]

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>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
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 a4399f5..3b979c6 100644 (file)
@@ -524,6 +524,11 @@ tcp_thin_dupack - BOOLEAN
        Documentation/networking/tcp-thin.txt
        Default: 0
 
        Documentation/networking/tcp-thin.txt
        Default: 0
 
+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
 UDP variables:
 
 udp_mem - vector of 3 INTEGERs: min, pressure, max
index e16557a..6e310bb 100644 (file)
@@ -233,6 +233,7 @@ enum
        LINUX_MIB_TCPTIMEWAITOVERFLOW,          /* TCPTimeWaitOverflow */
        LINUX_MIB_TCPREQQFULLDOCOOKIES,         /* TCPReqQFullDoCookies */
        LINUX_MIB_TCPREQQFULLDROP,              /* TCPReqQFullDrop */
        LINUX_MIB_TCPTIMEWAITOVERFLOW,          /* TCPTimeWaitOverflow */
        LINUX_MIB_TCPREQQFULLDOCOOKIES,         /* TCPReqQFullDoCookies */
        LINUX_MIB_TCPREQQFULLDROP,              /* TCPReqQFullDrop */
+       LINUX_MIB_TCPCHALLENGEACK,              /* TCPChallengeACK */
        __LINUX_MIB_MAX
 };
 
        __LINUX_MIB_MAX
 };
 
index bb18c4d..0768715 100644 (file)
@@ -251,6 +251,7 @@ extern int sysctl_tcp_max_ssthresh;
 extern int sysctl_tcp_cookie_size;
 extern int sysctl_tcp_thin_linear_timeouts;
 extern int sysctl_tcp_thin_dupack;
 extern int sysctl_tcp_cookie_size;
 extern int sysctl_tcp_thin_linear_timeouts;
 extern int sysctl_tcp_thin_dupack;
+extern int sysctl_tcp_challenge_ack_limit;
 
 extern atomic_long_t tcp_memory_allocated;
 extern struct percpu_counter tcp_sockets_allocated;
 
 extern atomic_long_t tcp_memory_allocated;
 extern struct percpu_counter tcp_sockets_allocated;
index 466ea8b..f46c478 100644 (file)
@@ -257,6 +257,7 @@ static const struct snmp_mib snmp4_net_list[] = {
        SNMP_MIB_ITEM("TCPTimeWaitOverflow", LINUX_MIB_TCPTIMEWAITOVERFLOW),
        SNMP_MIB_ITEM("TCPReqQFullDoCookies", LINUX_MIB_TCPREQQFULLDOCOOKIES),
        SNMP_MIB_ITEM("TCPReqQFullDrop", LINUX_MIB_TCPREQQFULLDROP),
        SNMP_MIB_ITEM("TCPTimeWaitOverflow", LINUX_MIB_TCPTIMEWAITOVERFLOW),
        SNMP_MIB_ITEM("TCPReqQFullDoCookies", LINUX_MIB_TCPREQQFULLDOCOOKIES),
        SNMP_MIB_ITEM("TCPReqQFullDrop", LINUX_MIB_TCPREQQFULLDROP),
+       SNMP_MIB_ITEM("TCPChallengeACK", LINUX_MIB_TCPCHALLENGEACK),
        SNMP_MIB_SENTINEL
 };
 
        SNMP_MIB_SENTINEL
 };
 
index 69fd720..5485077 100644 (file)
@@ -552,6 +552,13 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
                .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",
 #ifdef CONFIG_NET_DMA
        {
                .procname       = "tcp_dma_copybreak",
index a08a621..a0d3b88 100644 (file)
@@ -86,6 +86,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);
 
 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;
 int sysctl_tcp_stdurg __read_mostly;
 int sysctl_tcp_rfc1337 __read_mostly;
 int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
@@ -5240,6 +5243,23 @@ out:
 }
 #endif /* CONFIG_NET_DMA */
 
 }
 #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.
  */
 /* Does PAWS and seqno based validation of an incoming segment, flags will
  * play significant role here.
  */
@@ -5276,7 +5296,16 @@ static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
 
        /* Step 2: check RST bit */
        if (th->rst) {
 
        /* 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;
        }
 
                goto discard;
        }