patch for caching TCP options with syncookies

From: jensen galan
Date: Wed Mar 15 2006 - 03:15:29 EST


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 linux-net" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html