patch for caching TCP options with syncookies

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux 802.1Q VLAN]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Git]     [Bugtraq]     [Yosemite News and Information]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux PCI]     [Linux Admin]     [Samba]

  Powered by Linux