diff -ur --exclude='*.o' linux-2.4.24/include/linux/sysctl.h linux-2.4.24-new/include/linux/sysctl.h --- linux-2.4.24/include/linux/sysctl.h Fri Nov 28 13:26:21 2003 +++ linux-2.4.24-new/include/linux/sysctl.h Tue Feb 3 17:14:56 2004 @@ -311,6 +311,9 @@ NET_TCP_FRTO=92, NET_TCP_LOW_LATENCY=93, NET_IPV4_IPFRAG_SECRET_INTERVAL=94, + NET_TCP_OPTACK_ENABLED=95, + NET_TCP_OPTACK_MIN=96, + NET_TCP_OPTACK_MAX=97 }; enum { diff -ur --exclude='*.o' linux-2.4.24/include/net/sock.h linux-2.4.24-new/include/net/sock.h --- linux-2.4.24/include/net/sock.h Fri Nov 28 13:26:21 2003 +++ linux-2.4.24-new/include/net/sock.h Mon Feb 2 16:31:16 2004 @@ -37,6 +37,7 @@ #include #include #include /* struct sockaddr_in */ +#include #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) #include /* struct sockaddr_in6 */ @@ -109,6 +110,8 @@ #include #include +#define OPTACK_MODE_COUNTDOWN 0 +#define OPTACK_MODE_SKIP 1 /* The AF_UNIX specific socket options */ struct unix_opt { @@ -432,6 +435,10 @@ __u32 frto_highmark; /* snd_nxt when RTO occurred */ unsigned long last_synq_overflow; + + __u32 optack_data; + char optack_mode; + }; diff -ur --exclude='*.o' linux-2.4.24/include/net/tcp.h linux-2.4.24-new/include/net/tcp.h --- linux-2.4.24/include/net/tcp.h Fri Nov 28 13:26:21 2003 +++ linux-2.4.24-new/include/net/tcp.h Tue Feb 3 18:06:42 2004 @@ -463,6 +463,9 @@ extern int sysctl_tcp_tw_reuse; extern int sysctl_tcp_frto; extern int sysctl_tcp_low_latency; +extern int sysctl_tcp_optack_enabled; +extern int sysctl_tcp_optack_min; +extern int sysctl_tcp_optack_max; extern atomic_t tcp_memory_allocated; extern atomic_t tcp_sockets_allocated; @@ -1863,4 +1866,7 @@ TCP_ADD_STATS_USER(TcpMaxConn, -1); } +__u32 optack_init_cntr(void); +__inline__ void update_send_head(struct sock *sk, struct tcp_opt *tp, struct sk_buff *skb); + #endif /* _TCP_H */ diff -ur --exclude='*.o' linux-2.4.24/net/ipv4/sysctl_net_ipv4.c linux-2.4.24-new/net/ipv4/sysctl_net_ipv4.c --- linux-2.4.24/net/ipv4/sysctl_net_ipv4.c Fri Jun 13 10:51:39 2003 +++ linux-2.4.24-new/net/ipv4/sysctl_net_ipv4.c Tue Feb 3 17:21:09 2004 @@ -229,6 +229,12 @@ {NET_IPV4_IPFRAG_SECRET_INTERVAL, "ipfrag_secret_interval", &sysctl_ipfrag_secret_interval, sizeof(int), 0644, NULL, &proc_dointvec_jiffies, &sysctl_jiffies}, + {NET_TCP_OPTACK_ENABLED, "tcp_optack_enabled", + &sysctl_tcp_optack_enabled, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_TCP_OPTACK_MIN, "tcp_optack_min", + &sysctl_tcp_optack_min, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_TCP_OPTACK_MAX, "tcp_optack_max", + &sysctl_tcp_optack_max, sizeof(int), 0644, NULL, &proc_dointvec}, {0} }; diff -ur --exclude='*.o' linux-2.4.24/net/ipv4/tcp.c linux-2.4.24-new/net/ipv4/tcp.c --- linux-2.4.24/net/ipv4/tcp.c Mon Aug 25 07:44:44 2003 +++ linux-2.4.24-new/net/ipv4/tcp.c Tue Feb 3 18:07:04 2004 @@ -274,6 +274,10 @@ int sysctl_tcp_wmem[3] = { 4*1024, 16*1024, 128*1024 }; int sysctl_tcp_rmem[3] = { 4*1024, 87380, 87380*2 }; +int sysctl_tcp_optack_enabled = 1; /* Optack fix enabled by default */ +int sysctl_tcp_optack_min = 100; +int sysctl_tcp_optack_max = 200; + atomic_t tcp_memory_allocated; /* Current allocated memory. */ atomic_t tcp_sockets_allocated; /* Current number of TCP sockets. */ @@ -2646,3 +2650,15 @@ (void) tcp_mib_init(); tcpdiag_init(); } + +/* Return a random value between and + * , inclusive. Used for initializing + * the countdown timer for optack attack detection. + */ +__u32 optack_init_cntr(void) +{ + __u32 bytes; + + get_random_bytes(&bytes, 4); + return sysctl_tcp_optack_min + (bytes % (sysctl_tcp_optack_max - sysctl_tcp_optack_min + 1)); +} diff -ur --exclude='*.o' linux-2.4.24/net/ipv4/tcp_input.c linux-2.4.24-new/net/ipv4/tcp_input.c --- linux-2.4.24/net/ipv4/tcp_input.c Fri Nov 28 13:26:21 2003 +++ linux-2.4.24-new/net/ipv4/tcp_input.c Tue Feb 3 18:10:04 2004 @@ -2026,6 +2026,30 @@ u32 prior_in_flight; int prior_packets; + if(!sysctl_tcp_optack_enabled) + goto normal_tcp; + + if (tp->optack_mode == OPTACK_MODE_COUNTDOWN) { + if (tp->optack_data > 0) + tp->optack_data--; + else if(tp->send_head != NULL) { /* Skip next segment */ + tp->optack_data = tp->snd_nxt; + update_send_head(sk, tp, tp->send_head); + tp->optack_mode = OPTACK_MODE_SKIP; + } + } else if (tp->optack_mode == OPTACK_MODE_SKIP) { + if (ack > tp->optack_data) { /* Got malicious ACK */ + printk(KERN_CRIT "Optack attack detected..." + "sending RST\n"); + tcp_send_active_reset(sk, GFP_ATOMIC); + tcp_done(sk); + return 0; + } else if (ack == tp->optack_data) { /* Passed test, reset counter */ + tp->optack_mode = OPTACK_MODE_COUNTDOWN; + tp->optack_data = optack_init_cntr(); + } + } + normal_tcp: /* If the ack is newer than sent or older than previous acks * then we can probably ignore it. */ @@ -3919,6 +3941,9 @@ tp->snd_una = TCP_SKB_CB(skb)->ack_seq; tp->snd_wnd = ntohs(th->window) << tp->snd_wscale; + tp->optack_data = optack_init_cntr(); + tp->optack_mode = OPTACK_MODE_COUNTDOWN; + tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(skb)->seq); /* tcp_ack considers this ACK as duplicate diff -ur --exclude='*.o' linux-2.4.24/net/ipv4/tcp_output.c linux-2.4.24-new/net/ipv4/tcp_output.c --- linux-2.4.24/net/ipv4/tcp_output.c Fri Nov 28 13:26:21 2003 +++ linux-2.4.24-new/net/ipv4/tcp_output.c Mon Feb 2 13:37:50 2004 @@ -44,7 +44,7 @@ /* People can turn this off for buggy TCP's found in printers etc. */ int sysctl_tcp_retrans_collapse = 1; -static __inline__ +__inline__ void update_send_head(struct sock *sk, struct tcp_opt *tp, struct sk_buff *skb) { tp->send_head = skb->next; @@ -1201,6 +1201,9 @@ tp->rcv_wup = 0; tp->copied_seq = 0; + tp->optack_mode = OPTACK_MODE_COUNTDOWN; + tp->optack_data = optack_init_cntr(); + tp->rto = TCP_TIMEOUT_INIT; tp->retransmits = 0; tcp_clear_retrans(tp);