greetings! this is my first creation of a patch for the linux kernel. if you have time, could you please take a look at it and give me some feedback. this patch creates a syn_cache for caching TCP options when syn_cookies are in use (by default, all TCP options are lost when using syncookies). any feedback on the implementation of this cache would also be appreciated. if anybody's interested, i have also written a paper on this project. jensen diff -Naur linux-2.6.11.11/include/net/tcp.h linux_new-2.6.11.11/include/net/tcp.h --- linux-2.6.11.11/include/net/tcp.h 2005-05-27 05:06:46.000000000 +0000 +++ linux_new-2.6.11.11/include/net/tcp.h 2006-03-15 07:21:39.000000000 +0000 @@ -669,6 +669,32 @@ } af; }; +/* added struct for caching syn_options */ +struct syn_opt { + struct hlist_node hentry; + __u32 isn_key; + unsigned long expires; + __u8 snd_wscale : 4, + tstamp_ok : 1, + sack_ok : 1, + wscale_ok : 1; +}; + +struct syn_hash_bucket { + rwlock_t lock; + struct hlist_head chain; + __u8 size; +}; + +extern struct syn_hash_bucket *syn_hasht; +extern struct timer_list synhashtimer; + +/* + * change these values to increase (or decrease) the SYNHASH size + */ +#define SYNHASH_SIZE 512 +#define SYNHASH_BUCKET 30 + /* SLAB cache for open requests. */ extern kmem_cache_t *tcp_openreq_cachep; @@ -681,6 +707,12 @@ tcp_openreq_fastfree(req); } +/* SLAB cache for syn_opt. */ +extern kmem_cache_t *syn_opt_cachep; + +#define syn_opt_alloc() kmem_cache_alloc(syn_opt_cachep, SLAB_ATOMIC) +#define syn_opt_fastfree(syn_req) kmem_cache_free(syn_opt_cachep, syn_req) + #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #define TCP_INET_FAMILY(fam) ((fam) == AF_INET) #else diff -Naur linux-2.6.11.11/net/ipv4/syncookies.c linux_new-2.6.11.11/net/ipv4/syncookies.c --- linux-2.6.11.11/net/ipv4/syncookies.c 2005-05-27 05:06:46.000000000 +0000 +++ linux_new-2.6.11.11/net/ipv4/syncookies.c 2006-03-15 07:22:34.000000000 +0000 @@ -19,6 +19,7 @@ #include <linux/random.h> #include <linux/kernel.h> #include <net/tcp.h> +#include <linux/list.h> extern int sysctl_tcp_syncookies; @@ -121,6 +122,9 @@ int mss; struct rtable *rt; __u8 rcv_wscale; + struct syn_opt *tmp, *found; + struct hlist_node *pos; + int n; // key for hash table if (!sysctl_tcp_syncookies || !skb->h.th->ack) goto out; @@ -162,11 +166,38 @@ } } - req->snd_wscale = req->rcv_wscale = req->tstamp_ok = 0; - req->wscale_ok = req->sack_ok = 0; + /* look up cached syn options in hash table */ + n = cookie % SYNHASH_SIZE; + read_lock(&syn_hasht[n].lock); + hlist_for_each_entry(tmp, pos, &syn_hasht[n].chain, hentry) { + if (cookie == tmp->isn_key) { + if (!(time_after(jiffies, tmp->expires))) { + found = tmp; + break; + } + // FOUND COOKIE, BUT EXPIRED + else { + found = NULL; + break; + } + } + } + read_unlock(&syn_hasht[n].lock); + + /* must check if found exists. may have expired */ + if (found) { + req->snd_wscale = found->snd_wscale; + req->tstamp_ok = found->tstamp_ok; + req->wscale_ok = found->wscale_ok; + req->sack_ok = found->sack_ok; + } + else { + req->snd_wscale = req->rcv_wscale = req->tstamp_ok = 0; + req->wscale_ok = req->sack_ok = 0; + } req->expires = 0UL; req->retrans = 0; - + /* * We need to lookup the route here to get at the correct * window size. We should better make sure that the window size @@ -194,8 +225,10 @@ req->window_clamp = dst_metric(&rt->u.dst, RTAX_WINDOW); tcp_select_initial_window(tcp_full_space(sk), req->mss, &req->rcv_wnd, &req->window_clamp, - 0, &rcv_wscale); + req->wscale_ok, &rcv_wscale); + /* BTW win scale with syncookies is 0 by definition */ + /* this is not true with syn_cache */ req->rcv_wscale = rcv_wscale; ret = get_cookie_sock(sk, skb, req, &rt->u.dst); diff -Naur linux-2.6.11.11/net/ipv4/tcp.c linux_new-2.6.11.11/net/ipv4/tcp.c --- linux-2.6.11.11/net/ipv4/tcp.c 2005-05-27 05:06:46.000000000 +0000 +++ linux_new-2.6.11.11/net/ipv4/tcp.c 2006-03-15 07:21:59.000000000 +0000 @@ -257,6 +257,7 @@ #include <linux/fs.h> #include <linux/random.h> #include <linux/bootmem.h> +#include <linux/list.h> #include <net/icmp.h> #include <net/tcp.h> @@ -272,9 +273,13 @@ DEFINE_SNMP_STAT(struct tcp_mib, tcp_statistics); kmem_cache_t *tcp_openreq_cachep; +kmem_cache_t *syn_opt_cachep; kmem_cache_t *tcp_bucket_cachep; kmem_cache_t *tcp_timewait_cachep; +struct syn_hash_bucket *syn_hasht; +struct timer_list synhashtimer; + atomic_t tcp_orphan_count = ATOMIC_INIT(0); int sysctl_tcp_mem[3]; @@ -2261,6 +2266,31 @@ } __setup("thash_entries=", set_thash_entries); +/* + * synhash_expired() + * deletes the global hash table once the "newest" syn_opt has expired. + */ +static void synhash_expired() +{ + int i; + struct syn_opt *tmp; + struct hlist_node *node, *pos; + + for (i = 0; i < SYNHASH_SIZE; i++) { + write_lock(&syn_hasht[i].lock); + pos = (&syn_hasht[i].chain)->first; + while (pos) { + node = pos; + pos = node->next; + tmp = hlist_entry(node, typeof(*tmp), hentry); + hlist_del(node); + syn_opt_fastfree(tmp); + syn_hasht[i].size--; + } + write_unlock(&syn_hasht[i].lock); + } +} + void __init tcp_init(void) { struct sk_buff *skb = NULL; @@ -2274,6 +2304,7 @@ sizeof(struct open_request), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!tcp_openreq_cachep) panic("tcp_init: Cannot alloc open_request cache."); @@ -2365,6 +2396,29 @@ printk(KERN_INFO "TCP: Hash tables configured " "(established %d bind %d)\n", tcp_ehash_size << 1, tcp_bhash_size); + + syn_opt_cachep = kmem_cache_create("syn_opt", sizeof(struct syn_opt), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + + if (!syn_opt_cachep) + panic("tcp_init: Cannot alloc syn_opt cache."); + + syn_hasht = (struct syn_hash_bucket *)kmalloc + (sizeof(struct syn_hash_bucket) * SYNHASH_SIZE, + GFP_KERNEL); + + /* syn hash table initialization */ + for (i=0; i < SYNHASH_SIZE; i++) { + rwlock_init(&syn_hasht[i].lock); + INIT_HLIST_HEAD(&syn_hasht[i].chain); + syn_hasht[i].size = 0; + } + + /* initialize synhash timer */ + init_timer(&synhashtimer); + synhashtimer.function = &synhash_expired; + } EXPORT_SYMBOL(tcp_accept); @@ -2374,6 +2428,7 @@ EXPORT_SYMBOL(tcp_getsockopt); EXPORT_SYMBOL(tcp_ioctl); EXPORT_SYMBOL(tcp_openreq_cachep); +EXPORT_SYMBOL(syn_opt_cachep); EXPORT_SYMBOL(tcp_poll); EXPORT_SYMBOL(tcp_read_sock); EXPORT_SYMBOL(tcp_recvmsg); diff -Naur linux-2.6.11.11/net/ipv4/tcp_ipv4.c linux_new-2.6.11.11/net/ipv4/tcp_ipv4.c --- linux-2.6.11.11/net/ipv4/tcp_ipv4.c 2005-05-27 05:06:46.000000000 +0000 +++ linux_new-2.6.11.11/net/ipv4/tcp_ipv4.c 2006-03-15 07:22:06.000000000 +0000 @@ -62,6 +62,7 @@ #include <linux/jhash.h> #include <linux/init.h> #include <linux/times.h> +#include <linux/list.h> #include <net/icmp.h> #include <net/tcp.h> @@ -909,7 +910,6 @@ tcp_synq_added(sk); } - /* * This routine does path mtu discovery as defined in RFC1191. */ @@ -1399,6 +1399,13 @@ __u32 daddr = skb->nh.iph->daddr; __u32 isn = TCP_SKB_CB(skb)->when; struct dst_entry *dst = NULL; + + /* added code for syn_cache */ + struct syn_opt *req_opt, *tmp; + struct hlist_node *node, *pos; + int n; + /* end added code */ + #ifdef CONFIG_SYN_COOKIES int want_cookie = 0; #else @@ -1441,7 +1448,15 @@ tcp_parse_options(skb, &tmp_opt, 0); + /* added code */ if (want_cookie) { + /* allocate syn_opt and insert TCP options */ + req_opt = syn_opt_alloc(); + INIT_HLIST_NODE(&(req_opt->hentry)); + req_opt->tstamp_ok = tmp_opt.saw_tstamp; + req_opt->wscale_ok = tmp_opt.wscale_ok; + req_opt->sack_ok = tmp_opt.sack_ok; + req_opt->snd_wscale = tmp_opt.snd_wscale; tcp_clear_options(&tmp_opt); tmp_opt.saw_tstamp = 0; } @@ -1523,6 +1538,53 @@ } req->snt_isn = isn; + /* added code for caching TCP options */ + if (want_cookie) { + /* TCP options for syn/ack */ + req->tstamp_ok = req_opt->tstamp_ok; + req->wscale_ok = req_opt->wscale_ok; + req->sack_ok = req_opt->sack_ok; + req->snd_wscale = req_opt->snd_wscale; + + req_opt->isn_key = isn; + req_opt->expires = jiffies + TCP_TIMEOUT_INIT; + n = isn % SYNHASH_SIZE; + + write_lock(&syn_hasht[n].lock); + if (syn_hasht[n].size >= SYNHASH_BUCKET) { + /* + * look for expired syn_opt structs while + * deleting tail + */ + pos = (&syn_hasht[n].chain)->first; + while (pos) { + node = pos; + pos = node->next; + tmp = hlist_entry(node, typeof(*tmp), hentry); + if (tmp->expires <= jiffies) { + hlist_del(node); + syn_opt_fastfree(tmp); + syn_hasht[n].size--; + } + } + + /* check if tail NOT already deleted */ + if (node->next != LIST_POISON1) { + hlist_del(node); + syn_opt_fastfree(tmp); + syn_hasht[n].size--; + } + + } + hlist_add_head(&(req_opt->hentry), &syn_hasht[n].chain); + syn_hasht[n].size++; + write_unlock(&syn_hasht[n].lock); + + mod_timer(&synhashtimer, (jiffies + (2 * TCP_TIMEOUT_INIT))); + + } + /* end added code for caching TCP options */ + if (tcp_v4_send_synack(sk, req, dst)) goto drop_and_free; __________________________________________________ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com - : send the line "unsubscribe linux-net" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html