diff -E -b -x '*o' -x '*.cmd' -x '*.mod*c' -urN include/asm/socket.h /usr/src/linux/include/asm/socket.h --- include/asm/socket.h 2005-10-09 23:21:40.000000000 +0200 +++ /usr/src/linux/include/asm/socket.h 2005-10-11 18:31:51.000000000 +0200 @@ -53,4 +53,6 @@ #define SO_PEERSEC 31 +#define SO_ACCEPTFILTER 32 + #endif /* _ASM_SOCKET_H */ --- include/net/accf.h 1970-01-01 01:00:00.000000000 +0100 +++ /usr/src/linux/include/net/accf.h 2005-10-15 18:16:20.000000000 +0200 @@ -0,0 +1,14 @@ +#ifndef _ACCF_H +#define _ACCF_H + +#include +#include + +extern void accf_data_handler(struct sock *sk); +extern void accf_http_handler(struct sock *sk); + +extern void sk_accf_install(struct sock *sk, void (*func)(struct sock *), + void *data); +extern void sk_accf_uninstall(struct sock *sk); + +#endif diff -E -b -x '*o' -x '*.cmd' -x '*.mod*c' -urN include/net/sock.h /usr/src/linux/include/net/sock.h --- include/net/sock.h 2005-10-09 23:21:42.000000000 +0200 +++ /usr/src/linux/include/net/sock.h 2005-10-11 17:56:27.000000000 +0200 @@ -248,6 +248,9 @@ int (*sk_backlog_rcv)(struct sock *sk, struct sk_buff *skb); void (*sk_destruct)(struct sock *sk); + void (*sk_accf_func)(struct sock *sk); + void *sk_accf_data; + struct sock *sk_accf_parent; }; /* diff -E -b -x '*o' -x '*.cmd' -x '*.mod*c' -urN net/core/sock.c /usr/src/linux/net/core/sock.c --- net/core/sock.c 2005-10-09 23:21:58.000000000 +0200 +++ /usr/src/linux/net/core/sock.c 2005-10-15 18:15:48.000000000 +0200 @@ -128,6 +128,8 @@ #include #endif +#include + /* Take into consideration the size of the struct sk_buff overhead in the * determination of these values, since that is non-constant across * platforms. This makes socket queueing behavior and performance @@ -440,6 +442,13 @@ ret = -ENONET; break; + case SO_ACCEPTFILTER: + spin_lock_bh(&sk->sk_lock.slock); + /* XXX have it select the right filter */ + sk_accf_install(sk, accf_http_handler, (void *)10); + spin_unlock_bh(&sk->sk_lock.slock); + break; + /* We implement the SO_SNDLOWAT etc to not be settable (1003.1g 5.3) */ default: @@ -1118,7 +1127,9 @@ static void sock_def_readable(struct sock *sk, int len) { read_lock(&sk->sk_callback_lock); - if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) + if (sk->sk_accf_func != NULL) + sk->sk_accf_func(sk); + else if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) wake_up_interruptible(sk->sk_sleep); sk_wake_async(sk,1,POLL_IN); read_unlock(&sk->sk_callback_lock); diff -E -b -x '*o' -x '*.cmd' -x '*.mod*c' -urN net/ipv4/accf.c /usr/src/linux/net/ipv4/accf.c --- net/ipv4/accf.c 1970-01-01 01:00:00.000000000 +0100 +++ /usr/src/linux/net/ipv4/accf.c 2005-10-17 04:20:14.000000000 +0200 @@ -0,0 +1,135 @@ +#include +#include + +#include + +static int skb_strcmp(struct sk_buff *skb, int offset, char *cmp, struct sk_buff **newskb, int *newoff); +static int skb_findnext(struct sk_buff *skb, int offset, char c, struct sk_buff **newskb, int *newoff); + +static int skb_strcmp(struct sk_buff *skb, int offset, char *cmp, struct sk_buff **newskb, int *newoff) +{ + struct sk_buff *s; + +#if 1 + int i, j; + i = 0; + skb_queue_walk(skb->list, s) { + printk(KERN_INFO "packet %d (l:%d h:0x%p d:0x%p 0x%p): '", i, + s->len, s->head, s->data, s); + for (j = 0; j < s->len; j++) + printk("%c", *(s->data + j)); + printk("'\n"); + i++; + } +#endif + for (s = skb; s != (struct sk_buff *)s->list; s = s->next) { + for (; offset < s->len; offset++, cmp++) { + if (*cmp == 0) + goto good; + else if (*cmp != *(s->data + offset)) { + printk(KERN_INFO "cmp %c data %c(0x%p) o:%d\n", *cmp, *(s->data + offset), s->data + offset, offset); + return 1; + } + } + offset = 0; + } + if (*cmp != 0) + return 1; +good: + *newskb = s; + *newoff = offset; + return 0; +} + +static int skb_findnext(struct sk_buff *skb, int offset, char c, struct sk_buff **newskb, int *newoff) +{ + struct sk_buff *s; + int done = 0; + + for (s = skb; s != (struct sk_buff *)s->list; s = s->next) { + for (; offset < s->len; offset++) { + if (done) { + *newskb = s; + *newoff = offset; + return 0; + } + if (*(s->data + offset) == c) + done = 1; /* Go to the next character */ + } + offset = 0; + } + return 1; +} + +void accf_http_handler(struct sock *sk) +{ + int err, i; + struct sk_buff *skb; + + skb = sk->sk_receive_queue.next; + if (skb_queue_empty(&sk->sk_receive_queue) || skb->len == 0) + return; + + err = 1; + switch (*skb->data) { + case 'H': + err = skb_strcmp(skb, 1, "EAD ", &skb, &i); + case 'G': + err = skb_strcmp(skb, 1, "ET ", &skb , &i); + } + + if (err) + goto bad; + printk(KERN_INFO "found method. 0x%p[%d]\n", skb, i); + + err = skb_findnext(skb, i, ' ', &skb, &i); + if (err) + goto bad; + printk(KERN_INFO "found space. i 0x%p[%d]\n", skb, i); + + err = skb_strcmp(skb, i, "HTTP/1.1", &skb, &i); + if (err) + goto bad; + + printk(KERN_INFO "%s: waking up parent\n", __func__); + sk->sk_state_change(sk->sk_accf_parent); + sk_accf_uninstall(sk); + return; +bad: + return; +} + +void accf_data_handler(struct sock *sk) +{ + struct sk_buff *skb; + int received = 0; + skb_queue_walk(&sk->sk_receive_queue, skb) { + /* XXX Should we discard certain packets? */ + received += skb->len; + } + + if (received >= (int)sk->sk_accf_data) { + printk(KERN_INFO "%s: waking up parent (%d)\n", __func__, received); + sk->sk_state_change(sk->sk_accf_parent); + sk_accf_uninstall(sk); + } +} + +void sk_accf_install(struct sock *sk, void (*func)(struct sock *), void *data) +{ + sk->sk_accf_func = func; + sk->sk_accf_data = data; + sk->sk_accf_parent = sk; +} + +void sk_accf_uninstall(struct sock *sk) +{ + sk->sk_accf_func = NULL; + sk->sk_accf_data = NULL; + sk->sk_accf_parent = NULL; +} + +EXPORT_SYMBOL(accf_data_handler); +EXPORT_SYMBOL(accf_http_handler); +EXPORT_SYMBOL(sk_accf_install); +EXPORT_SYMBOL(sk_accf_uninstall); diff -E -b -x '*o' -x '*.cmd' -x '*.mod*c' -urN net/ipv4/Makefile /usr/src/linux/net/ipv4/Makefile --- net/ipv4/Makefile 2005-10-11 18:09:02.000000000 +0200 +++ /usr/src/linux/net/ipv4/Makefile 2005-10-11 20:02:27.000000000 +0200 @@ -7,7 +7,8 @@ ip_output.o ip_sockglue.o \ tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o tcp_minisocks.o \ datagram.o raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o \ - sysctl_net_ipv4.o fib_frontend.o fib_semantics.o fib_hash.o + sysctl_net_ipv4.o fib_frontend.o fib_semantics.o fib_hash.o \ + accf.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o diff -E -b -x '*o' -x '*.cmd' -x '*.mod*c' -urN net/ipv4/tcp_minisocks.c /usr/src/linux/net/ipv4/tcp_minisocks.c --- net/ipv4/tcp_minisocks.c 2005-10-09 23:22:05.000000000 +0200 +++ /usr/src/linux/net/ipv4/tcp_minisocks.c 2005-10-15 18:19:19.000000000 +0200 @@ -1055,7 +1055,8 @@ ret = tcp_rcv_state_process(child, skb, skb->h.th, skb->len); /* Wakeup parent, send SIGIO */ - if (state == TCP_SYN_RECV && child->sk_state != state) + if (!parent->sk_accf_func && + state == TCP_SYN_RECV && child->sk_state != state) parent->sk_data_ready(parent, 0); } else { /* Alas, it is possible again, because we do lookup