Messages in this thread Patch in this message | | | From | kdp0101@hpmail ... | Subject | Syncookies implementation for 2.1.42 | Date | Sun, 01 Jun 1997 1:12:22 METDST |
| |
Hi,
I'm looking for alpha testers for a Syncookies implementation for 2.1.42. With this patch my telnet port survies a syn flood attack over a 10Mbit/s ethernet nicely.
Just add the patch to a clean 2.1.42 kernel, configure with CONFIG_SYN_COOKIES and enable the /proc/sys/net/ipv4/tcp_syncookies sysctl:
echo 1 >/proc/sys/net/ipv4/tcp_syncookies
The patch includes some sysctl stuff by Christopher Horn too, because I was too lazy to back it out. I did some changes to the IPv6 code to make it compile again, but haven't tested IPv6. I also included at least one bug fix to against stock 2.1.42: the code didn't check if the ACK bit was really set before checking the packet against the open_request queue.
Please test the patch and send feedback to me. When no errors are found I'll submit it to Dave Miller for inclusion in the standard kernel.
Credits: I stole the secure_tcp_syn_cookie() function from Eric Schenk's implementation for 2.0.30. The basic algorithm is based on Ideas from Dan Bernstein and Eric Schenk.
-Andi
Patch follows:
diff -u --recursive -x .* -x *~ linux-clean/drivers/char/random.c linux/drivers/char/random.c --- linux-clean/drivers/char/random.c Mon May 12 19:35:40 1997 +++ linux/drivers/char/random.c Wed May 28 21:56:28 1997 @@ -1431,7 +1431,7 @@ tmp[9]=daddr; tmp[10]=(sport << 16) + dport; halfMD4Transform(tmp, tmp+4); - + /* * As close as possible to RFC 793, which * suggests using a 250kHz clock. @@ -1447,6 +1447,63 @@ #endif return (seq); } + +#ifdef CONFIG_SYN_COOKIES +/* + * Secure SYN cookie computation. This is the algorithm worked out by + * Dan Bernstein and Eric Schenk. + * + * For linux I implement the 1 minute counter by looking at the jiffies clock. + * The count is passed in as a parameter; + * + */ +__u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr, + __u16 sport, __u16 dport, __u32 sseq, __u32 count) +{ + static int is_init = 0; + static __u32 secret[2][16]; + __u32 tmp[16]; + __u32 seq; + + /* + * Pick two random secret the first time we open a TCP connection. + */ + if (is_init == 0) { + get_random_bytes(&secret[0], sizeof(secret[0])); + get_random_bytes(&secret[1], sizeof(secret[1])); + is_init = 1; + } + + /* + * Compute the secure sequence number. + * The output should be: + * MD5(sec1,saddr,sport,daddr,dport,sec1) + their sequence number + * + (count * 2^24) + * + (MD5(sec2,saddr,sport,daddr,dport,count,sec2) % 2^24). + * Where count increases every minute by 1. + */ + + memcpy(tmp, secret[0], sizeof(tmp)); + tmp[8]=saddr; + tmp[9]=daddr; + tmp[10]=(sport << 16) + dport; + HASH_TRANSFORM(tmp, tmp); + seq = tmp[1]; + + memcpy(tmp, secret[1], sizeof(tmp)); + tmp[8]=saddr; + tmp[9]=daddr; + tmp[10]=(sport << 16) + dport; + tmp[11]=count; /* minute counter */ + HASH_TRANSFORM(tmp, tmp); + + seq += sseq + (count << 24) + (tmp[1] & 0x00ffffff); + + /* Zap lower 3 bits to leave room for the MSS representation */ + return (seq & 0xfffff8); +} +#endif + #ifdef RANDOM_BENCHMARK /* diff -u --recursive -x .* -x *~ linux-clean/include/linux/random.h linux/include/linux/random.h --- linux-clean/include/linux/random.h Thu Jun 6 12:42:15 1996 +++ linux/include/linux/random.h Wed May 28 16:50:14 1997 @@ -55,6 +55,8 @@ extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, __u16 sport, __u16 dport); +extern __u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr, + __u16 sport, __u16 dport, __u32 sseq, __u32 count); #ifndef MODULE extern struct file_operations random_fops, urandom_fops; diff -u --recursive -x .* -x *~ linux-clean/include/linux/sysctl.h linux/include/linux/sysctl.h --- linux-clean/include/linux/sysctl.h Sat May 31 14:10:06 1997 +++ linux/include/linux/sysctl.h Sat May 31 12:22:48 1997 @@ -75,7 +75,6 @@ enum { VM_SWAPCTL=1, /* struct: Set vm swapping control */ - VM_KSWAPD, /* struct: control background pageout */ VM_SWAPOUT, /* int: Background pageout interval */ VM_FREEPG, /* struct: Set free page thresholds */ VM_BDFLUSH, /* struct: Control buffer cache flushing */ @@ -110,6 +109,7 @@ NET_CORE_RMEM_MAX, NET_CORE_WMEM_DEFAULT, NET_CORE_RMEM_DEFAULT, + NET_CORE_DESTROY_DELAY, }; /* /proc/sys/net/ethernet */ @@ -118,12 +118,19 @@ /* /proc/sys/net/unix */ +enum +{ + NET_UNIX_DESTROY_DELAY=1, + NET_UNIX_DELETE_DELAY, +}; + /* /proc/sys/net/ipv4 */ enum { NET_IPV4_ARP_RES_TIME=1, NET_IPV4_ARP_DEAD_RES_TIME, NET_IPV4_ARP_MAX_TRIES, + NET_IPV4_ARP_MAX_PINGS, NET_IPV4_ARP_TIMEOUT, NET_IPV4_ARP_CHECK_INTERVAL, NET_IPV4_ARP_CONFIRM_INTERVAL, @@ -147,9 +154,22 @@ NET_IPV4_ACCEPT_REDIRECTS, NET_IPV4_SECURE_REDIRECTS, NET_IPV4_RFC1620_REDIRECTS, - NET_TCP_SYN_RETRIES, - NET_IPFRAG_HIGH_THRESH, - NET_IPFRAG_LOW_THRESH, + NET_IPV4_TCP_SYN_RETRIES, + NET_IPV4_IPFRAG_HIGH_THRESH, + NET_IPV4_IPFRAG_LOW_THRESH, + NET_IPV4_IPFRAG_TIME, + NET_IPV4_TCP_MAX_KA_PROBES, + NET_IPV4_TCP_KEEPALIVE_TIME, + NET_IPV4_TCP_KEEPALIVE_PROBES, + NET_IPV4_TCP_RETRIES1, + NET_IPV4_TCP_RETRIES2, + NET_IPV4_TCP_MAX_DELAY_ACKS, + NET_IPV4_TCP_FIN_TIMEOUT, + NET_IPV4_IGMP_MAX_HOST_REPORT_DELAY, + NET_IPV4_IGMP_TIMER_SCALE, + NET_IPV4_IGMP_AGE_THRESHOLD, + NET_TCP_SYNCOOKIES, + NET_TCP_ALWAYS_SYNCOOKIE, }; @@ -269,6 +289,8 @@ void *, size_t *); extern int proc_dointvec_minmax(ctl_table *, int, struct file *, void *, size_t *); +extern int proc_dointvec_jiffies(ctl_table *, int, struct file *, + void *, size_t *); extern int do_sysctl (int *name, int nlen, void *oldval, size_t *oldlenp, diff -u --recursive -x .* -x *~ linux-clean/include/net/tcp.h linux/include/net/tcp.h --- linux-clean/include/net/tcp.h Tue May 27 13:52:55 1997 +++ linux/include/net/tcp.h Sun Jun 1 00:32:07 1997 @@ -281,15 +281,17 @@ int (*conn_request) (struct sock *sk, struct sk_buff *skb, - void *opt, - __u32 isn); + void *opt, __u32 isn); struct sock * (*syn_recv_sock) (struct sock *sk, struct sk_buff *skb, - struct open_request *req); + struct open_request *req, + struct dst_entry *dst); +#if 0 __u32 (*init_sequence) (struct sock *sk, struct sk_buff *skb); +#endif struct sock * (*get_sock) (struct sk_buff *skb, struct tcphdr *th); @@ -385,7 +387,8 @@ int len, int nonblock, int flags, int *addr_len); -extern void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp); +extern void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, + int no_fancy); /* * TCP v4 functions exported for the inet6 API @@ -407,7 +410,8 @@ extern struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, - struct open_request *req); + struct open_request *req, + struct dst_entry *dst); extern int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb); @@ -417,6 +421,11 @@ int addr_len); +/* From syncookies.c */ +extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb); +extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, + __u16 *mss); + extern void tcp_read_wakeup(struct sock *); extern void tcp_write_xmit(struct sock *); extern void tcp_time_wait(struct sock *); @@ -521,7 +530,6 @@ { return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base); } - #undef STATE_TRACE diff -u --recursive -x .* -x *~ linux-clean/kernel/sysctl.c linux/kernel/sysctl.c --- linux-clean/kernel/sysctl.c Mon May 12 19:35:44 1997 +++ linux/kernel/sysctl.c Fri May 30 13:05:32 1997 @@ -37,9 +37,7 @@ /* External variables not in a header file. */ extern int panic_timeout; -extern int console_loglevel, default_message_loglevel; -extern int minimum_console_loglevel, default_console_loglevel; -extern int C_A_D, swapout_interval; +extern int console_loglevel, C_A_D, swapout_interval; extern int bdf_prm[], bdflush_min[], bdflush_max[]; extern char binfmt_java_interpreter[], binfmt_java_appletviewer[]; extern int sysctl_overcommit_memory; @@ -184,6 +182,8 @@ static ctl_table vm_table[] = { {VM_SWAPCTL, "swapctl", &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec}, + {VM_SWAPOUT, "swapout_interval", + &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec_jiffies}, {VM_FREEPG, "freepages", &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec}, {VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL, @@ -611,8 +611,8 @@ return 0; } -int proc_dointvec(ctl_table *table, int write, struct file *filp, - void *buffer, size_t *lenp) +static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp, int conv) { int *i, vleft, first=1, len, left, neg, val; #define TMPBUFLEN 20 @@ -655,7 +655,7 @@ } if (*p < '0' || *p > '9') break; - val = simple_strtoul(p, &p, 0); + val = simple_strtoul(p, &p, 0) * conv; len = p-buf; if ((len < left) && *p && !isspace(*p)) break; @@ -668,7 +668,7 @@ p = buf; if (!first) *p++ = '\t'; - sprintf(p, "%d", *i); + sprintf(p, "%d", (*i) / conv); len = strlen(buf); if (len > left) len = left; @@ -702,6 +702,12 @@ return 0; } +int proc_dointvec(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + return do_proc_dointvec(table,write,filp,buffer,lenp,1); +} + int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp) { @@ -798,6 +804,13 @@ *lenp -= left; filp->f_pos += *lenp; return 0; +} + +/* Like proc_dointvec, but converts seconds to jiffies */ +int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + return do_proc_dointvec(table,write,filp,buffer,lenp,HZ); } #else /* CONFIG_PROC_FS */ diff -u --recursive -x .* -x *~ linux-clean/net/core/sock.c linux/net/core/sock.c --- linux-clean/net/core/sock.c Mon May 12 19:35:44 1997 +++ linux/net/core/sock.c Thu May 29 22:09:46 1997 @@ -124,6 +124,8 @@ __u32 sysctl_wmem_default = SK_WMEM_MAX; __u32 sysctl_rmem_default = SK_RMEM_MAX; +int sysctl_core_destroy_delay = SOCK_DESTROY_TIME; + /* * This is meant for all protocols to use and covers goings on * at the socket level. Everything here is generic. @@ -787,7 +789,7 @@ * Someone is using our buffers still.. defer */ init_timer(&sk->timer); - sk->timer.expires=jiffies+10*HZ; + sk->timer.expires=jiffies+sysctl_core_destroy_delay; sk->timer.function=sklist_destroy_timer; sk->timer.data = (unsigned long)sk; add_timer(&sk->timer); diff -u --recursive -x .* -x *~ linux-clean/net/core/sysctl_net_core.c linux/net/core/sysctl_net_core.c --- linux-clean/net/core/sysctl_net_core.c Thu May 15 03:21:31 1997 +++ linux/net/core/sysctl_net_core.c Thu May 29 22:20:03 1997 @@ -13,6 +13,8 @@ extern __u32 sysctl_wmem_default; extern __u32 sysctl_rmem_default; +extern int sysctl_core_destroy_delay; + ctl_table core_table[] = { {NET_CORE_WMEM_MAX, "wmem_max", &sysctl_wmem_max, sizeof(int), 0644, NULL, @@ -26,5 +28,8 @@ {NET_CORE_RMEM_DEFAULT, "rmem_default", &sysctl_rmem_default, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_CORE_DESTROY_DELAY, "destroy_delay", + &sysctl_core_destroy_delay, sizeof(int), 0644, NULL, + &proc_dointvec_jiffies}, { 0 } }; diff -u --recursive -x .* -x *~ linux-clean/net/ipv4/Config.in linux/net/ipv4/Config.in --- linux-clean/net/ipv4/Config.in Fri Feb 7 14:54:55 1997 +++ linux/net/ipv4/Config.in Wed May 28 13:46:10 1997 @@ -31,6 +31,7 @@ bool 'IP: ARP daemon support (EXPERIMENTAL)' CONFIG_ARPD fi fi +bool 'IP: TCP syncookie support' CONFIG_SYN_COOKIES comment '(it is safe to leave these untouched)' bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP tristate 'IP: Reverse ARP' CONFIG_INET_RARP diff -u --recursive -x .* -x *~ linux-clean/net/ipv4/Makefile linux/net/ipv4/Makefile --- linux-clean/net/ipv4/Makefile Fri Mar 21 03:17:13 1997 +++ linux/net/ipv4/Makefile Tue May 27 22:40:14 1997 @@ -52,6 +52,11 @@ endif endif +ifeq ($(CONFIG_SYN_COOKIES),y) +IPV4_OBJS += syncookies.o +# module not supported, because it would be too messy. +endif + ifdef CONFIG_INET O_OBJS := $(IPV4_OBJS) OX_OBJS := $(IPV4X_OBJS) diff -u --recursive -x .* -x *~ linux-clean/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c --- linux-clean/net/ipv4/af_inet.c Mon May 12 19:35:44 1997 +++ linux/net/ipv4/af_inet.c Thu May 29 22:09:46 1997 @@ -114,6 +114,7 @@ #define min(a,b) ((a)<(b)?(a):(b)) +extern int sysctl_core_destroy_delay; extern struct proto packet_prot; extern int raw_get_info(char *, char **, off_t, int, int); extern int snmp_get_info(char *, char **, off_t, int, int); @@ -190,7 +191,7 @@ sk->destroy = 1; sk->ack_backlog = 0; release_sock(sk); - net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME); + net_reset_timer(sk, TIME_DESTROY, sysctl_core_destroy_delay); } void destroy_sock(struct sock *sk) diff -u --recursive -x .* -x *~ linux-clean/net/ipv4/igmp.c linux/net/ipv4/igmp.c --- linux-clean/net/ipv4/igmp.c Mon Mar 31 22:52:32 1997 +++ linux/net/ipv4/igmp.c Thu May 29 22:09:47 1997 @@ -88,6 +88,9 @@ #include <linux/igmp.h> #include <net/checksum.h> +int sysctl_igmp_max_host_report_delay = IGMP_MAX_HOST_REPORT_DELAY; +int sysctl_igmp_timer_scale = IGMP_TIMER_SCALE; +int sysctl_igmp_age_threshold = IGMP_AGE_THRESHOLD; /* * If time expired, change the router type to IGMP_NEW_ROUTER. @@ -133,7 +136,7 @@ return NULL; i->dev = dev; i->type = IGMP_NEW_ROUTER; - i->time = IGMP_AGE_THRESHOLD; + i->time = sysctl_igmp_age_threshold; i->next = ip_router_info_head; ip_router_info_head = i; @@ -229,7 +232,7 @@ int tv; if(im->tm_running) return; - tv=random()%(max_resp_time*HZ/IGMP_TIMER_SCALE); /* Pick a number any number 8) */ + tv=random()%(max_resp_time*HZ/sysctl_igmp_timer_scale); /* Pick a number any number 8) */ im->timer.expires=jiffies+tv; im->tm_running=1; add_timer(&im->timer); @@ -363,7 +366,7 @@ if (group && group != im->multiaddr) continue; if(im->tm_running) { - if(im->timer.expires>jiffies+max_resp_time*HZ/IGMP_TIMER_SCALE) { + if(im->timer.expires>jiffies+max_resp_time*HZ/sysctl_igmp_timer_scale) { igmp_stop_timer(im); igmp_start_timer(im,max_resp_time); } @@ -372,9 +375,9 @@ } } else { mrouter_type=IGMP_OLD_ROUTER; - max_resp_time=IGMP_MAX_HOST_REPORT_DELAY*IGMP_TIMER_SCALE; + max_resp_time=sysctl_igmp_max_host_report_delay*sysctl_igmp_timer_scale; - if(igmp_set_mrouter_info(dev,mrouter_type,IGMP_AGE_THRESHOLD)==NULL) + if(igmp_set_mrouter_info(dev,mrouter_type,sysctl_igmp_age_threshold)==NULL) return; /* diff -u --recursive -x .* -x *~ linux-clean/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c --- linux-clean/net/ipv4/ip_fragment.c Tue May 27 13:47:54 1997 +++ linux/net/ipv4/ip_fragment.c Thu May 29 22:09:47 1997 @@ -41,6 +41,8 @@ int sysctl_ipfrag_high_thresh = 256*1024; int sysctl_ipfrag_low_thresh = 192*1024; +int sysctl_ipfrag_time = IP_FRAG_TIME; + /* Describe an IP fragment. */ struct ipfrag { int offset; /* offset of fragment in IP datagram */ @@ -251,7 +253,7 @@ qp->dev = skb->dev; /* Start a timer for this entry. */ - qp->timer.expires = jiffies + IP_FRAG_TIME; /* about 30 seconds */ + qp->timer.expires = jiffies + sysctl_ipfrag_time; /* about 30 seconds */ qp->timer.data = (unsigned long) qp; /* pointer to queue */ qp->timer.function = ip_expire; /* expire function */ add_timer(&qp->timer); @@ -417,7 +419,7 @@ memcpy(qp->iph, iph, ihl+8); } del_timer(&qp->timer); - qp->timer.expires = jiffies + IP_FRAG_TIME; /* about 30 seconds */ + qp->timer.expires = jiffies + sysctl_ipfrag_time; /* about 30 seconds */ qp->timer.data = (unsigned long) qp; /* pointer to queue */ qp->timer.function = ip_expire; /* expire function */ add_timer(&qp->timer); Only in linux/net/ipv4: syncookies.c diff -u --recursive -x .* -x *~ linux-clean/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c --- linux-clean/net/ipv4/sysctl_net_ipv4.c Tue May 27 13:47:54 1997 +++ linux/net/ipv4/sysctl_net_ipv4.c Sat May 31 16:32:47 1997 @@ -34,10 +34,17 @@ extern int sysctl_arp_check_interval; extern int sysctl_arp_confirm_interval; extern int sysctl_arp_confirm_timeout; +extern int sysctl_arp_max_pings; /* From ip_fragment.c */ extern int sysctl_ipfrag_low_thresh; extern int sysctl_ipfrag_high_thresh; +extern int sysctl_ipfrag_time; + +/* From igmp.c */ +extern int sysctl_igmp_max_host_report_delay; +extern int sysctl_igmp_timer_scale; +extern int sysctl_igmp_age_threshold; extern int sysctl_tcp_cong_avoidance; extern int sysctl_tcp_hoe_retransmits; @@ -45,7 +52,16 @@ extern int sysctl_tcp_tsack; extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; -extern int sysctl_syn_retries; +extern int sysctl_tcp_keepalive_time; +extern int sysctl_tcp_keepalive_probes; +extern int sysctl_tcp_max_ka_probes; +extern int sysctl_tcp_retries1; +extern int sysctl_tcp_retries2; +extern int sysctl_tcp_max_delay_acks; +extern int sysctl_tcp_fin_timeout; +extern int sysctl_tcp_syncookies; +extern int sysctl_tcp_always_syncookie; +extern int sysctl_tcp_syn_retries; extern int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp, void *buffer, size_t *lenp); @@ -82,6 +98,8 @@ &sysctl_arp_dead_res_time, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV4_ARP_MAX_TRIES, "arp_max_tries", &sysctl_arp_max_tries, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_ARP_MAX_PINGS, "arp_max_pings", + &sysctl_arp_max_pings, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV4_ARP_TIMEOUT, "arp_timeout", &sysctl_arp_timeout, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV4_ARP_CHECK_INTERVAL, "arp_check_interval", @@ -149,12 +167,48 @@ {NET_IPV4_RFC1620_REDIRECTS, "ip_rfc1620_redirects", &ipv4_config.rfc1620_redirects, sizeof(int), 0644, NULL, &proc_dointvec}, - {NET_TCP_SYN_RETRIES, "tcp_syn_retries", - &sysctl_syn_retries, sizeof(int), 0644, NULL, &proc_dointvec}, - {NET_IPFRAG_HIGH_THRESH, "ipfrag_high_thresh", - &sysctl_ipfrag_high_thresh, sizeof(int), 0644, NULL, &proc_dointvec}, - {NET_IPFRAG_LOW_THRESH, "ipfrag_low_thresh", - &sysctl_ipfrag_low_thresh, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_TCP_SYN_RETRIES, "tcp_syn_retries", + &sysctl_tcp_syn_retries, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_IPFRAG_HIGH_THRESH, "ipfrag_high_thresh", + &sysctl_ipfrag_high_thresh, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_IPFRAG_LOW_THRESH, "ipfrag_low_thresh", + &sysctl_ipfrag_low_thresh, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_IPFRAG_TIME, "ipfrag_time", + &sysctl_ipfrag_time, sizeof(int), 0644, NULL, &proc_dointvec_jiffies}, + {NET_IPV4_TCP_MAX_KA_PROBES, "tcp_max_ka_probes", + &sysctl_tcp_max_ka_probes, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_TCP_KEEPALIVE_TIME, "tcp_keepalive_time", + &sysctl_tcp_keepalive_time, sizeof(int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_TCP_KEEPALIVE_PROBES, "tcp_keepalive_probes", + &sysctl_tcp_keepalive_probes, sizeof(int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_TCP_RETRIES1, "tcp_retries1", + &sysctl_tcp_retries1, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_TCP_RETRIES2, "tcp_retries2", + &sysctl_tcp_retries2, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_TCP_MAX_DELAY_ACKS, "tcp_max_delay_acks", + &sysctl_tcp_max_delay_acks, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_TCP_FIN_TIMEOUT, "tcp_fin_timeout", + &sysctl_tcp_fin_timeout, sizeof(int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_IGMP_MAX_HOST_REPORT_DELAY, "igmp_max_host_report_delay", + &sysctl_igmp_max_host_report_delay, sizeof(int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_IGMP_TIMER_SCALE, "igmp_timer_scale", + &sysctl_igmp_timer_scale, sizeof(int), 0644, NULL, &proc_dointvec}, +#if 0 + /* This one shouldn't be exposed to the user (too implementation + specific): */ + {NET_IPV4_IGMP_AGE_THRESHOLD, "igmp_age_threshold", + &sysctl_igmp_age_threshold, sizeof(int), 0644, NULL, &proc_dointvec}, +#endif +#ifdef CONFIG_SYN_COOKIES + {NET_TCP_SYNCOOKIES, "tcp_syncookies", + &sysctl_tcp_syncookies, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_TCP_ALWAYS_SYNCOOKIE, "tcp_always_syncookie", + &sysctl_tcp_always_syncookie, sizeof(int), 0644, NULL, &proc_dointvec}, +#endif {0} }; diff -u --recursive -x .* -x *~ linux-clean/net/ipv4/tcp.c linux/net/ipv4/tcp.c --- linux-clean/net/ipv4/tcp.c Thu May 15 00:01:21 1997 +++ linux/net/ipv4/tcp.c Thu May 29 22:09:47 1997 @@ -431,6 +431,8 @@ #include <asm/uaccess.h> +int sysctl_tcp_fin_timeout = TCP_FIN_TIMEOUT; + unsigned long seq_offset; struct tcp_mib tcp_statistics; @@ -1385,7 +1387,7 @@ if(timer_active) add_timer(&sk->timer); else - tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT); + tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout); } return send_fin; @@ -1499,7 +1501,7 @@ if(timer_active) add_timer(&sk->timer); else - tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT); + tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout); } sk->dead = 1; diff -u --recursive -x .* -x *~ linux-clean/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c --- linux-clean/net/ipv4/tcp_input.c Mon May 12 19:35:44 1997 +++ linux/net/ipv4/tcp_input.c Sat May 31 21:09:00 1997 @@ -56,13 +56,15 @@ static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack, u32 seq_rtt); -int sysctl_tcp_cong_avoidance = 0; -int sysctl_tcp_hoe_retransmits = 0; -int sysctl_tcp_sack = 0; -int sysctl_tcp_tsack = 0; -int sysctl_tcp_timestamps = 0; -int sysctl_tcp_window_scaling = 0; - +int sysctl_tcp_cong_avoidance; +int sysctl_tcp_hoe_retransmits; +int sysctl_tcp_sack; +int sysctl_tcp_tsack; +int sysctl_tcp_timestamps; +int sysctl_tcp_window_scaling; +int sysctl_tcp_syncookies; +int sysctl_tcp_always_syncookie; +int sysctl_tcp_max_delay_acks = MAX_DELAY_ACK; static tcp_sys_cong_ctl_t tcp_sys_cong_ctl_f = &tcp_cong_avoid_vanj; @@ -286,7 +288,7 @@ * FIXME: surely this can be more efficient. -- erics */ -void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp) +void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy) { unsigned char *ptr; int length=(th->doff*4)-sizeof(struct tcphdr); @@ -321,21 +323,21 @@ break; case TCPOPT_WINDOW: if(opsize==TCPOLEN_WINDOW && th->syn) - if (sysctl_tcp_window_scaling) { + if (!no_fancy && sysctl_tcp_window_scaling) { tp->wscale_ok = 1; tp->snd_wscale = *(__u8 *)ptr; } break; case TCPOPT_SACK_PERM: if(opsize==TCPOLEN_SACK_PERM && th->syn) - if (sysctl_tcp_sack) + if (sysctl_tcp_sack && !no_fancy) tp->sack_ok = 1; case TCPOPT_TIMESTAMP: if(opsize==TCPOLEN_TIMESTAMP) { /* Cheaper to set again then to * test syn. Optimize this? */ - if (sysctl_tcp_timestamps) + if (sysctl_tcp_timestamps && !no_fancy) tp->tstamp_ok = 1; tp->saw_tstamp = 1; tp->rcv_tsval = ntohl(*(__u32 *)ptr); @@ -343,6 +345,8 @@ } break; case TCPOPT_SACK: + if (no_fancy) + break; tp->sacks = (opsize-2)>>3; if (tp->sacks<<3 == opsize-2) { int i; @@ -383,7 +387,7 @@ return 1; } } - tcp_parse_options(th,tp); + tcp_parse_options(th,tp,0); return 1; } @@ -1080,7 +1084,7 @@ /* A retransmit, 2nd most common case. Force an imediate ack. */ SOCK_DEBUG(sk, "retransmit received: seq %X\n", skb->seq); - tp->delayed_acks = MAX_DELAY_ACK; + tp->delayed_acks = sysctl_tcp_max_delay_acks; kfree_skb(skb, FREE_READ); return; } @@ -1094,7 +1098,7 @@ } /* Ok. This is an out_of_order segment, force an ack. */ - tp->delayed_acks = MAX_DELAY_ACK; + tp->delayed_acks = sysctl_tcp_max_delay_acks; /* Disable header predition. */ tp->pred_flags = 0; @@ -1218,7 +1222,7 @@ return; } - if (tp->delayed_acks >= MAX_DELAY_ACK || tcp_raise_window(sk)) + if (tp->delayed_acks >= sysctl_tcp_max_delay_acks || tcp_raise_window(sk)) tcp_send_ack(sk); else tcp_send_delayed_ack(sk, HZ/2); @@ -1457,13 +1461,11 @@ /* These use the socket TOS.. * might want to be the received TOS */ - if(th->ack) + if(th->ack) return 1; /* send reset */ if(th->syn) { - __u32 isn = tp->af_specific->init_sequence(sk, skb); - - if(tp->af_specific->conn_request(sk, skb, opt, isn) < 0) + if(tp->af_specific->conn_request(sk, skb, opt, 0) < 0) return 1; /* Now we have several options: In theory there is @@ -1529,7 +1531,7 @@ tp->fin_seq = skb->seq; tcp_set_state(sk, TCP_ESTABLISHED); - tcp_parse_options(th,tp); + tcp_parse_options(th,tp,0); /* FIXME: need to make room for SACK still */ if (tp->wscale_ok == 0) { tp->snd_wscale = tp->rcv_wscale = 0; @@ -1572,7 +1574,7 @@ * tcp_connect. */ tcp_set_state(sk, TCP_SYN_RECV); - tcp_parse_options(th,tp); + tcp_parse_options(th,tp,0); if (tp->saw_tstamp) { tp->ts_recent = tp->rcv_tsval; tp->ts_recent_stamp = jiffies; @@ -1614,6 +1616,8 @@ sk->shutdown = SHUTDOWN_MASK; isn = tp->rcv_nxt + 128000; + if (isn == 0) + isn++; sk = tp->af_specific->get_sock(skb, th); @@ -1708,8 +1712,10 @@ tp->snd_wl1 = skb->seq; tp->snd_wl2 = skb->ack_seq; - } else + } else { + printk(KERN_DEBUG "bad ack\n"); return 1; + } break; case TCP_FIN_WAIT1: diff -u --recursive -x .* -x *~ linux-clean/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c --- linux-clean/net/ipv4/tcp_ipv4.c Thu May 15 00:01:21 1997 +++ linux/net/ipv4/tcp_ipv4.c Sat May 31 22:26:05 1997 @@ -30,6 +30,8 @@ * David S. Miller : Change semantics of established hash, * half is devoted to TIME_WAIT sockets * and the rest go in the other half. + * Andi Kleen : Add support for syncookies and fixed + * some bugs. */ #include <linux/config.h> @@ -48,6 +50,8 @@ extern int sysctl_tcp_tsack; extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; +extern int sysctl_tcp_syncookies; +extern int sysctl_tcp_always_syncookie; static void tcp_v4_send_reset(struct sk_buff *skb); @@ -403,7 +407,7 @@ #endif -static __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb) +static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb) { return secure_tcp_sequence_number(sk->saddr, sk->daddr, skb->h.th->dest, @@ -811,6 +815,19 @@ } #endif +#ifdef REQDEBUG +void printreq(char *name, struct open_request *req) +{ + printk(KERN_DEBUG "req: (%s) %p\n",name,req); + printk(KERN_DEBUG " rcv_isn=%u snt_isn=%u mss=%u\n", + req->rcv_isn, req->snt_isn, req->mss); + printk(KERN_DEBUG " window_clamp=%u rcv_wnd=%u retrans=%u\n", + req->window_clamp,req->rcv_wnd,req->retrans); + printk(KERN_DEBUG " opt=%x loc=%x rmt=%x\n", req->af.v4_req.opt, + req->af.v4_req.loc_addr,req->af.v4_req.rmt_addr); +} +#endif + static void tcp_v4_send_synack(struct sock *sk, struct open_request *req) { struct sk_buff * skb; @@ -835,6 +852,8 @@ /* Don't offer more than they did. * This way we don't have to memorize who said what. + * FIXME: maybe this should be changed for better performance + * with syncookies. */ req->mss = min(mss, req->mss); @@ -880,6 +899,10 @@ req->af.v4_req.loc_addr, req->af.v4_req.rmt_addr, csum_partial((char *)th, sizeof(*th)+tmp, skb->csum)); +#ifdef REQDEBUG + printreq("send_synack",req); +#endif + ip_queue_xmit(skb); tcp_statistics.TcpOutSegs++; } @@ -891,17 +914,13 @@ sizeof(struct ip_options) + req->af.v4_req.opt->optlen); } -static struct or_calltable or_ipv4 = { +struct or_calltable or_ipv4 = { tcp_v4_send_synack, tcp_v4_or_free }; -static int tcp_v4_syn_filter(struct sock *sk, struct sk_buff *skb, __u32 saddr) -{ - return 0; -} - -int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 isn) +int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, + __u32 isn) { struct ip_options *opt = (struct ip_options *) ptr; struct tcp_opt tp; @@ -909,6 +928,7 @@ struct tcphdr *th = skb->h.th; __u32 saddr = skb->nh.iph->saddr; __u32 daddr = skb->nh.iph->daddr; + int want_cookie = sysctl_tcp_always_syncookie; /* If the socket is dead, don't accept the connection. */ if (sk->dead) { @@ -917,15 +937,31 @@ return -ENOTCONN; } - if (sk->ack_backlog >= sk->max_ack_backlog || - tcp_v4_syn_filter(sk, skb, saddr)) { - SOCK_DEBUG(sk, "dropping syn ack:%d max:%d\n", sk->ack_backlog, - sk->max_ack_backlog); -#ifdef CONFIG_IP_TCPSF - tcp_v4_random_drop(sk); + if (sk->ack_backlog >= sk->max_ack_backlog || want_cookie) { +#ifdef CONFIG_SYN_COOKIES + if (sysctl_tcp_syncookies) { + static unsigned long warntime; + + if (!want_cookie && (jiffies - warntime > HZ*60)) { + warntime = jiffies; + printk(KERN_INFO + "possible SYN flooding on port %d. Sending cookies.\n", ntohs(skb->h.th->dest)); + } + want_cookie = 1; + } else #endif - tcp_statistics.TcpAttemptFails++; - goto exit; + { + if (want_cookie) { want_cookie=0; goto ok; } + SOCK_DEBUG(sk, "dropping syn ack:%d max:%d\n", sk->ack_backlog, + sk->max_ack_backlog); + tcp_statistics.TcpAttemptFails++; + goto exit; + } + } else { + ok: + if (isn == 0) + isn = tcp_v4_init_sequence(sk, skb); + sk->ack_backlog++; } req = tcp_openreq_alloc(); @@ -934,15 +970,12 @@ goto exit; } - sk->ack_backlog++; - req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ req->rcv_isn = skb->seq; - req->snt_isn = isn; - tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; + tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; tp.in_mss = 536; - tcp_parse_options(th,&tp); + tcp_parse_options(th,&tp, want_cookie); if (tp.saw_tstamp) req->ts_recent = tp.rcv_tsval; req->mss = tp.in_mss; @@ -954,8 +987,20 @@ req->af.v4_req.loc_addr = daddr; req->af.v4_req.rmt_addr = saddr; + /* Note that we ignore the isn passed from the TIME_WAIT + * state here. That's the price we pay for cookies. + */ + if (want_cookie) + isn = cookie_v4_init_sequence(sk, skb, &req->mss); + + req->snt_isn = isn; + /* IPv4 options */ req->af.v4_req.opt = NULL; + + /* This should be nuked -- conn_request is always called + * with opt == NULL (or the option handling should be fixed) -AK + */ if (opt && opt->optlen) { int opt_size = sizeof(struct ip_options) + opt->optlen; @@ -973,9 +1018,15 @@ tcp_v4_send_synack(sk, req); - req->expires = jiffies + TCP_TIMEOUT_INIT; - tcp_inc_slow_timer(TCP_SLT_SYNACK); - tcp_synq_queue(&sk->tp_pinfo.af_tcp, req); + if (want_cookie) { + if (req->af.v4_req.opt) + kfree(req->af.v4_req.opt); + tcp_openreq_free(req); + } else { + req->expires = jiffies + TCP_TIMEOUT_INIT; + tcp_inc_slow_timer(TCP_SLT_SYNACK); + tcp_synq_queue(&sk->tp_pinfo.af_tcp, req); + } sk->data_ready(sk, 0); @@ -985,24 +1036,27 @@ } struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, - struct open_request *req) + struct open_request *req, + struct dst_entry *dst) { struct tcp_opt *newtp; struct sock *newsk; - struct rtable *rt; int snd_mss; newsk = sk_alloc(GFP_ATOMIC); - if (newsk == NULL) - return NULL; + if (newsk == NULL) { + if (dst) + dst_release(dst); + return NULL; + } memcpy(newsk, sk, sizeof(*newsk)); /* Or else we die! -DaveM */ newsk->sklist_next = NULL; - newsk->opt = NULL; - newsk->dst_cache = NULL; + newsk->opt = req->af.v4_req.opt; + skb_queue_head_init(&newsk->write_queue); skb_queue_head_init(&newsk->receive_queue); skb_queue_head_init(&newsk->out_of_order_queue); @@ -1072,17 +1126,21 @@ newsk->rcv_saddr = req->af.v4_req.loc_addr; /* options / mss / route_cache */ - newsk->opt = req->af.v4_req.opt; - if (ip_route_output(&rt, - newsk->opt && newsk->opt->srr ? newsk->opt->faddr : newsk->daddr, - newsk->saddr, newsk->ip_tos, NULL)) { - kfree(newsk); - return NULL; - } - - newsk->dst_cache = &rt->u.dst; - - snd_mss = rt->u.dst.pmtu; + if (dst == NULL) { + struct rtable *rt; + + if (ip_route_output(&rt, + newsk->opt && newsk->opt->srr ? + newsk->opt->faddr : newsk->daddr, + newsk->saddr, newsk->ip_tos, NULL)) { + kfree(newsk); + return NULL; + } + dst = &rt->u.dst; + } + newsk->dst_cache = dst; + + snd_mss = dst->pmtu; /* FIXME: is mtu really the same as snd_mss? */ newsk->mtu = snd_mss; @@ -1124,7 +1182,7 @@ return newsk; } -struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb) +static inline struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); struct open_request *req = tp->syn_wait_queue; @@ -1133,8 +1191,12 @@ * as we checked the user count on tcp_rcv and we're * running from a soft interrupt. */ - if(!req) - return sk; + if(!req) { +#ifdef CONFIG_SYN_COOKIES + sk = cookie_v4_check(sk, skb); +#endif + goto ende; + } while(req) { if (req->af.v4_req.rmt_addr == skb->nh.iph->saddr && @@ -1147,7 +1209,7 @@ * yet accepted()... */ sk = req->sk; - break; + goto ende; } /* Check for syn retransmission */ @@ -1161,20 +1223,25 @@ return NULL; } - sk = tp->af_specific->syn_recv_sock(sk, skb, req); + sk = tp->af_specific->syn_recv_sock(sk, skb, req, NULL); tcp_dec_slow_timer(TCP_SLT_SYNACK); if (sk == NULL) return NULL; req->expires = 0UL; req->sk = sk; - break; + goto ende; } req = req->dl_next; } +#ifdef CONFIG_SYN_COOKIES + sk = cookie_v4_check(sk, skb); +#endif +ende: skb_orphan(skb); - skb_set_owner_r(skb, sk); + if (sk) + skb_set_owner_r(skb, sk); return sk; } @@ -1195,16 +1262,16 @@ goto ok; } - if (sk->state == TCP_LISTEN) { - struct sock *nsk; + if (sk->state == TCP_LISTEN && (skb->h.th->ack || skb->h.th->syn)) { + struct sock *nsk; - /* Find possible connection requests. */ - nsk = tcp_v4_check_req(sk, skb); - if (nsk == NULL) + /* Find possible connection requests. */ + nsk = tcp_v4_check_req(sk, skb); + if (nsk == NULL) goto discard_it; - - release_sock(sk); - lock_sock(nsk); + + release_sock(sk); + lock_sock(nsk); sk = nsk; } @@ -1352,7 +1419,6 @@ tcp_v4_rebuild_header, tcp_v4_conn_request, tcp_v4_syn_recv_sock, - tcp_v4_init_sequence, tcp_v4_get_sock, ip_setsockopt, ip_getsockopt, diff -u --recursive -x .* -x *~ linux-clean/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c --- linux-clean/net/ipv4/tcp_timer.c Thu May 15 00:01:21 1997 +++ linux/net/ipv4/tcp_timer.c Thu May 29 22:40:38 1997 @@ -22,7 +22,11 @@ #include <net/tcp.h> -int sysctl_syn_retries = TCP_SYN_RETRIES; +int sysctl_tcp_syn_retries = TCP_SYN_RETRIES; +int sysctl_tcp_keepalive_time = TCP_KEEPALIVE_TIME; +int sysctl_tcp_keepalive_probes = TCP_KEEPALIVE_PROBES; +int sysctl_tcp_retries1 = TCP_RETR1; +int sysctl_tcp_retries2 = TCP_RETR2; static void tcp_sltimer_handler(unsigned long); static void tcp_syn_recv_timer(unsigned long); @@ -172,7 +176,7 @@ /* Eric, what the heck is this doing?!?! */ tp->retransmits && !(tp->retransmits & 7)) || - (sk->state != TCP_ESTABLISHED && tp->retransmits > TCP_RETR1)) { + (sk->state != TCP_ESTABLISHED && tp->retransmits > sysctl_tcp_retries1)) { /* Attempt to recover if arp has changed (unlikely!) or * a route has shifted (not supported prior to 1.3). */ @@ -180,7 +184,7 @@ } /* Have we tried to SYN too many times (repent repent 8)) */ - if(tp->retransmits > sysctl_syn_retries && sk->state==TCP_SYN_SENT) { + if(tp->retransmits > sysctl_tcp_syn_retries && sk->state==TCP_SYN_SENT) { if(sk->err_soft) sk->err=sk->err_soft; else @@ -198,7 +202,7 @@ } /* Has it gone just too far? */ - if (tp->retransmits > TCP_RETR2) { + if (tp->retransmits > sysctl_tcp_retries2) { if(sk->err_soft) sk->err = sk->err_soft; else @@ -251,7 +255,7 @@ * FIXME: We ought not to do it, Solaris 2.5 actually has fixing * this behaviour in Solaris down as a bug fix. [AC] */ - if (tp->probes_out > TCP_RETR2) { + if (tp->probes_out > sysctl_tcp_retries2) { if(sk->err_soft) sk->err = sk->err_soft; else @@ -281,8 +285,8 @@ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; __u32 elapsed = jiffies - tp->rcv_tstamp; - if (elapsed >= TCP_KEEPALIVE_TIME) { - if (tp->probes_out > TCP_KEEPALIVE_PROBES) { + if (elapsed >= sysctl_tcp_keepalive_time) { + if (tp->probes_out > sysctl_tcp_keepalive_probes) { if(sk->err_soft) sk->err = sk->err_soft; else @@ -316,6 +320,8 @@ */ #define MAX_KA_PROBES 5 +int sysctl_tcp_max_ka_probes = MAX_KA_PROBES; + /* Keepopen's are only valid for "established" TCP's, nicely our listener * hash gets rid of most of the useless testing, so we run through a couple * of the established hash chains each clock tick. -DaveM @@ -341,7 +347,7 @@ while(sk) { if(sk->keepopen) { count += tcp_keepopen_proc(sk); - if(count == MAX_KA_PROBES) + if(count == sysctl_tcp_max_ka_probes) goto out; } sk = sk->next; @@ -455,7 +461,7 @@ break; tcp_synq_unlink(tp, conn); - if (conn->retrans >= TCP_RETR1) { + if (conn->retrans >= sysctl_tcp_retries1) { #ifdef TCP_DEBUG printk(KERN_DEBUG "syn_recv: " "too many retransmits\n"); diff -u --recursive -x .* -x *~ linux-clean/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c --- linux-clean/net/ipv6/tcp_ipv6.c Mon May 12 19:35:45 1997 +++ linux/net/ipv6/tcp_ipv6.c Sun Jun 1 00:36:54 1997 @@ -695,7 +695,7 @@ * Can some kind of merge be done? -- erics */ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, - __u32 isn) + __u32 isn) { struct tcp_opt tp; struct open_request *req; @@ -711,6 +711,9 @@ if (skb->protocol == __constant_htons(ETH_P_IP)) return tcp_v4_conn_request(sk, skb, ptr, isn); + if (isn == 0) + isn = tcp_v6_init_sequence(sk,skb); + /* * There are no SYN attacks on IPv6, yet... */ @@ -735,7 +738,7 @@ req->snt_isn = isn; tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; tp.in_mss = 536; - tcp_parse_options(skb->h.th,&tp); + tcp_parse_options(skb->h.th,&tp,0); if (tp.saw_tstamp) req->ts_recent = tp.rcv_tsval; req->mss = tp.in_mss; @@ -778,10 +781,10 @@ } static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, - struct open_request *req) + struct open_request *req, + struct dst_entry *dst) { struct ipv6_pinfo *np; - struct dst_entry *dst; struct flowi fl; struct tcp_opt *newtp; struct sock *newsk; @@ -791,11 +794,11 @@ * v6 mapped */ - newsk = tcp_v4_syn_recv_sock(sk, skb, req); + newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst); - if (newsk == NULL) + if (newsk == NULL) return NULL; - + np = &newsk->net_pinfo.af_inet6; ipv6_addr_set(&np->daddr, 0, 0, __constant_htonl(0x0000FFFF), @@ -813,8 +816,11 @@ } newsk = sk_alloc(GFP_ATOMIC); - if (newsk == NULL) + if (newsk == NULL) { + if (dst) + dst_release(dst); return NULL; + } memcpy(newsk, sk, sizeof(*newsk)); @@ -902,18 +908,20 @@ ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr); np->oif = req->af.v6_req.dev; - /* - * options / mss / route cache - */ - - fl.proto = IPPROTO_TCP; - fl.nl_u.ip6_u.daddr = &np->daddr; - fl.nl_u.ip6_u.saddr = &np->saddr; - fl.dev = np->oif; - fl.uli_u.ports.dport = newsk->dummy_th.dest; - fl.uli_u.ports.sport = newsk->dummy_th.source; - - dst = ip6_route_output(newsk, &fl); + if (dst == NULL) { + /* + * options / mss / route cache + */ + + fl.proto = IPPROTO_TCP; + fl.nl_u.ip6_u.daddr = &np->daddr; + fl.nl_u.ip6_u.saddr = &np->saddr; + fl.dev = np->oif; + fl.uli_u.ports.dport = newsk->dummy_th.dest; + fl.uli_u.ports.sport = newsk->dummy_th.source; + + dst = ip6_route_output(newsk, &fl); + } ip6_dst_store(newsk, dst); @@ -1051,7 +1059,7 @@ } skb_orphan(skb); - sk = tp->af_specific->syn_recv_sock(sk, skb, req); + sk = tp->af_specific->syn_recv_sock(sk, skb, req, NULL); tcp_dec_slow_timer(TCP_SLT_SYNACK); @@ -1308,7 +1316,6 @@ tcp_v6_rebuild_header, tcp_v6_conn_request, tcp_v6_syn_recv_sock, - tcp_v6_init_sequence, tcp_v6_get_sock, ipv6_setsockopt, ipv6_getsockopt, @@ -1328,7 +1335,6 @@ tcp_v4_rebuild_header, tcp_v6_conn_request, tcp_v6_syn_recv_sock, - tcp_v6_init_sequence, tcp_v6_get_sock, ipv6_setsockopt, ipv6_getsockopt, diff -u --recursive -x .* -x *~ linux-clean/net/unix/af_unix.c linux/net/unix/af_unix.c --- linux-clean/net/unix/af_unix.c Mon May 12 19:35:45 1997 +++ linux/net/unix/af_unix.c Thu May 29 22:09:47 1997 @@ -87,6 +87,8 @@ #define min(a,b) (((a)<(b))?(a):(b)) +int sysctl_unix_delete_delay = HZ; +int sysctl_unix_destroy_delay = 10*HZ; unix_socket *unix_socket_table[UNIX_HASH_SIZE+1]; @@ -240,7 +242,7 @@ * Retry; */ - sk->timer.expires=jiffies+10*HZ; /* No real hurry try it every 10 seconds or so */ + sk->timer.expires=jiffies+sysctl_unix_destroy_delay; /* No real hurry try it every 10 seconds or so */ add_timer(&sk->timer); } @@ -248,7 +250,7 @@ static void unix_delayed_delete(unix_socket *sk) { sk->timer.data=(unsigned long)sk; - sk->timer.expires=jiffies+HZ; /* Normally 1 second after will clean up. After that we try every 10 */ + sk->timer.expires=jiffies+sysctl_unix_delete_delay; /* Normally 1 second after will clean up. After that we try every 10 */ sk->timer.function=unix_destroy_timer; add_timer(&sk->timer); } diff -u --recursive -x .* -x *~ linux-clean/net/unix/sysctl_net_unix.c linux/net/unix/sysctl_net_unix.c --- linux-clean/net/unix/sysctl_net_unix.c Thu Jan 2 14:13:35 1997 +++ linux/net/unix/sysctl_net_unix.c Thu May 29 22:27:21 1997 @@ -14,6 +14,15 @@ #include <linux/mm.h> #include <linux/sysctl.h> +extern int sysctl_unix_destroy_delay; +extern int sysctl_unix_delete_delay; + ctl_table unix_table[] = { + {NET_UNIX_DESTROY_DELAY, "destroy_delay", + &sysctl_unix_destroy_delay, sizeof(int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_UNIX_DELETE_DELAY, "delete_delay", + &sysctl_unix_delete_delay, sizeof(int), 0644, NULL, + &proc_dointvec_jiffies}, {0} }; --- /dev/null Tue Jan 1 05:00:00 1980 +++ linux/net/ipv4/syncookies.c Sat May 31 22:26:11 1997 @@ -0,0 +1,194 @@ +/* + * Syncookies implementation for the Linux kernel + * + * Author: Andi Kleen, <andi@aec.at> + * Based on ideas by D.J.Bernstein and Eric Schenk. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Missing: IPv6 support. + * Some counter so that the Administrator can see when the machine + * is under a syn flood attack. + */ + +#include <linux/config.h> +#if defined(CONFIG_SYN_COOKIES) +#include <linux/tcp.h> +#include <linux/malloc.h> +#include <linux/random.h> +#include <net/tcp.h> + +extern int sysctl_tcp_syncookies; + +static unsigned long tcp_lastsynq_overflow; + +/* + * This table has to be sorted. Only 8 entries are allowed and the + * last entry has to be duplicated. + * XXX generate a better table. + * Unresolved Issues: HIPPI with a 64k MSS is not well supported. + */ +static __u16 const msstab[] = { + 256, + 512, + 536, + 966, + 1024, + 1440, + 1460, + 4312, + 4312 +}; + +static __u32 make_syncookie(struct sk_buff *skb, __u32 counter, __u32 seq) +{ + __u32 z; + + z = secure_tcp_syn_cookie(skb->nh.iph->saddr, skb->nh.iph->daddr, + skb->h.th->source, skb->h.th->dest, + seq, + counter); + +#if 0 + printk(KERN_DEBUG + "msc: z=%u,cnt=%u,seq=%u,sadr=%u,dadr=%u,sp=%u,dp=%u\n", + z,counter,seq, + skb->nh.iph->saddr,skb->nh.iph->daddr, + ntohs(skb->h.th->source), ntohs(skb->h.th->dest)); +#endif + + return z; +} + +/* + * Generate a syncookie. + */ +__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) +{ + int i; + __u32 isn; + __u16 mss = *mssp, *w; + + tcp_lastsynq_overflow = jiffies; + + isn = make_syncookie(skb, (jiffies/HZ) >> 6, ntohl(skb->h.th->seq)); + + /* XXX sort msstab[] by probability? */ + w = msstab; + for (i = 0; i < 8; i++) + if (mss >= *w && mss < *++w) + goto found; + i--; +found: + *mssp = w[-1]; + + isn |= i; + return isn; +} + +#define COUNTER_TRIES 4 + +/* + * Check if a ack sequence number is a valid syncookie. + */ +static inline int cookie_check(struct sk_buff *skb, __u32 cookie) +{ + int mssind; + int i; + __u32 counter; + + /* XXX use tp->rcv_tstamp */ + if ((jiffies - tcp_lastsynq_overflow) > TCP_TIMEOUT_INIT + && tcp_lastsynq_overflow) { + return 0; + } + + mssind = cookie & 7; + cookie &= ~7; + + counter = (jiffies/HZ)>>6; + for (i = 0; i < COUNTER_TRIES; i++) + if (make_syncookie(skb, counter-i, + ntohl(skb->h.th->seq)-1) == cookie) + { + return msstab[mssind]; + } + + return 0; +} + +extern struct or_calltable or_ipv4; + +static inline struct sock * +get_cookie_sock(struct sock *sk, struct sk_buff *skb, struct open_request *req, + struct dst_entry *dst) +{ + struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + + sk = tp->af_specific->syn_recv_sock(sk, skb, req,dst); + req->sk = sk; + tcp_synq_queue(tp, req); + + return sk; +} + +struct sock * +cookie_v4_check(struct sock *sk, struct sk_buff *skb) +{ + __u32 cookie = ntohl(skb->h.th->ack_seq)-1; + struct open_request *req; + int mss; + struct rtable *rt; + + if (!sysctl_tcp_syncookies) + return sk; + if (!skb->h.th->ack) + return sk; + + mss = cookie_check(skb, cookie); + if (mss == 0) + return sk; + + req = tcp_openreq_alloc(); + if (req == NULL) + return NULL; + + memset(req, 0, sizeof (struct open_request)); + req->rcv_isn = htonl(skb->h.th->seq)-1; + req->snt_isn = cookie; + req->mss = mss; + req->rmt_port = skb->h.th->source; + req->af.v4_req.loc_addr = skb->nh.iph->daddr; + req->af.v4_req.rmt_addr = skb->nh.iph->saddr; + req->class = &or_ipv4; /* for savety */ + + /* + * We need to lookup the route here to get at the correct + * window size. We should better make sure that the window size + * hasn't changed since we received the original syn, but I see + * no easy way to do this. Hope this won't break anything. + * + * FIXME: we should check for source routing here (currently + * it would be useless because v4 tcp ignores IP options + * altogether). + */ + if (ip_route_output(&rt, + req->af.v4_req.rmt_addr,req->af.v4_req.loc_addr, + sk->ip_tos, NULL)) { + tcp_openreq_free(req); + return NULL; + } + + /* Try to redo what tcp_v4_send_synack did. */ + req->window_clamp = rt->u.dst.window; + tcp_select_initial_window(sock_rspace(sk)/2,req->mss, + &req->rcv_wnd, &req->window_clamp, + 0, &req->rcv_wscale); + + return get_cookie_sock(sk, skb, req, &rt->u.dst); +} + +#endif
| |