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 
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to