Index: linux-2.6.10-rc3-bk16-Netfilter/include/linux/skb_extend.h =================================================================== --- linux-2.6.10-rc3-bk16-Netfilter.orig/include/linux/skb_extend.h 2004-12-24 14:27:33.530132712 +1100 +++ linux-2.6.10-rc3-bk16-Netfilter/include/linux/skb_extend.h 2004-12-24 14:45:12.788101024 +1100 @@ -5,6 +5,7 @@ enum skb_ext_type { SKB_EXT_IPCONN, + SKB_EXT_IPCONN_EXP, SKB_EXT_MAX, } __attribute__((packed)); Index: linux-2.6.10-rc3-bk16-Netfilter/include/linux/netfilter_ipv4/ip_conntrack.h =================================================================== --- linux-2.6.10-rc3-bk16-Netfilter.orig/include/linux/netfilter_ipv4/ip_conntrack.h 2004-12-24 14:27:27.531044712 +1100 +++ linux-2.6.10-rc3-bk16-Netfilter/include/linux/netfilter_ipv4/ip_conntrack.h 2004-12-24 15:05:04.454940128 +1100 @@ -1,5 +1,7 @@ #ifndef _IP_CONNTRACK_H #define _IP_CONNTRACK_H +#include + /* Connection state tracking for netfilter. This is separated from, but required by, the NAT layer; it can also be used by an iptables extension. */ @@ -110,9 +112,6 @@ /* Function to call after setup and insertion */ void (*expectfn)(struct ip_conntrack *new); - /* The conntrack of the master connection */ - struct ip_conntrack *expectant; - #ifdef CONFIG_IP_NF_NAT_NEEDED /* This is the original per-proto part, used to map the * expected connection the way the recipient expects. */ @@ -142,21 +141,20 @@ /* Timer function; drops refcnt when it goes off. */ struct timer_list timeout; + /* These are my tuples; original and reply */ + struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; + #ifdef CONFIG_IP_NF_CT_ACCT /* Accounting Information (same cache line as other written members) */ struct ip_conntrack_counter counters[IP_CT_DIR_MAX]; #endif - /* If we're expecting another related connection, this will be - in expected linked list */ - struct list_head sibling_list; - + /* Linked list of us in expecting list. */ + struct skb_extend *ext; + /* Current number of expected connections */ unsigned int expecting; - /* If we were expected by an expectation, this will be it */ - struct ip_conntrack_expect *master; - /* Helper, if any. */ struct ip_conntrack_helper *helper; @@ -179,10 +177,6 @@ #if defined(CONFIG_IP_NF_CONNTRACK_MARK) unsigned long mark; #endif - - /* Traversed often, so hopefully in different cacheline to top */ - /* These are my tuples; original and reply */ - struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; }; /* get master conntrack via master expectation */ Index: linux-2.6.10-rc3-bk16-Netfilter/net/ipv4/netfilter/ip_conntrack_core.c =================================================================== --- linux-2.6.10-rc3-bk16-Netfilter.orig/net/ipv4/netfilter/ip_conntrack_core.c 2004-12-24 14:27:27.537043800 +1100 +++ linux-2.6.10-rc3-bk16-Netfilter/net/ipv4/netfilter/ip_conntrack_core.c 2004-12-24 15:04:16.124287504 +1100 @@ -210,31 +210,47 @@ __unexpect_related(expect); } +/* We have one of these if we're expecting anything. */ +struct expectations +{ + unsigned int num; + struct ip_conntrack_expect_internal *exp[0]; +}; + /* delete all unconfirmed expectations for this conntrack */ -static void remove_expectations(struct ip_conntrack *ct, int drop_refcount) +static void remove_expectations(struct ip_conntrack *ct) { - struct ip_conntrack_expect_internal *exp, *next + struct expectations *exp; + unsigned int i; DEBUGP("remove_expectations(%p)\n", ct); + exp = skb_extend_find(ct->ext, SKBE_IPCT_EXP); + if (exp) { + for (i = 0; i < exp->num; i++) + unexpect_related(exp[i]); + skb_extend_destroy(exp); + } +} - list_for_each_entry_safe(exp, next, &ct->sibling_list, expected_list) { - /* we skip established expectations, as we want to delete - * the un-established ones only */ - if (exp->sibling) { - DEBUGP("remove_expectations: skipping established %p of %p\n", exp->sibling, ct); - if (drop_refcount) { - /* Indicate that this expectations parent is dead */ - ip_conntrack_put(exp->expect.expectant); - exp->expect.expectant = NULL; - } - continue; - } +/* We have one of these for the connections we were expecting, now realized. */ +struct children +{ + unsigned int num; + struct ip_conntrack *child[0]; +}; - IP_NF_ASSERT(list_inlist(&ip_conntrack_expect_list, exp)); - IP_NF_ASSERT(exp->expectant == ct); +static void disown_children(struct ip_conntrack *ct) +{ + struct children *children; + unsigned int i; - /* delete expectation from global and private lists */ - unexpect_related(exp); + children = skb_extend_find(ct->ext, SKBE_IPCT_CHILDREN); + if (children) { + /* We are no longer your parent. */ + for (i = 0; i < children->num; i++) + children[i]->ext = skb_extend_del(children[i]->ext, + SKBE_IPCT_PARENT); + skb_extend_destroy(children); } } @@ -250,15 +266,12 @@ hr = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); LIST_DELETE(&ip_conntrack_hash[ho], &ct->tuplehash[IP_CT_DIR_ORIGINAL]); LIST_DELETE(&ip_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]); - - /* Destroy all un-established, pending expectations */ - remove_expectations(ct, 1); } static void destroy_conntrack(struct ip_conntrack *ct) { - struct ip_conntrack *master = NULL; + struct ip_conntrack **parent; struct ip_conntrack_protocol *proto; DEBUGP("destroy_conntrack(%p)\n", ct); @@ -276,29 +289,18 @@ ip_conntrack_destroyed(ct); WRITE_LOCK(&ip_conntrack_lock); - /* Make sure don't leave any orphaned expectations lying around */ - if (ct->expecting) - remove_expectations(ct, 1); + /* These should have been cleaned when we were removed from lists, + or never have existed. */ + BUG_ON(skb_extend_find(ct->ext, SKBE_IPCT_EXP)); + BUG_ON(skb_extend_find(ct->ext, SKBE_IPCT_CHILDREN)); /* Delete our master expectation */ - if (ct->master) { - struct ip_conntrack_expect_internal *exp - = container_of(ct->master, - struct ip_conntrack_expect_internal, - expect); - if (ct->master->expectant) { - /* can't call __unexpect_related here, - * since it would screw up expect_list */ - list_del(&exp->expected_list); - master = ct->master->expectant; - } - kmem_cache_free(ip_conntrack_expect_cachep, exp); - } + parent = skb_extend_find(ct->ext, SKBE_IPCT_PARENT); CONNTRACK_STAT_INC(delete); WRITE_UNLOCK(&ip_conntrack_lock); - if (master) - ip_conntrack_put(master); + if (parent) + ip_conntrack_put(*parent); DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); kmem_cache_free(ip_conntrack_cachep, ct); @@ -314,6 +316,8 @@ * Otherwise we can get spurious warnings. */ CONNTRACK_STAT_INC(delete_list); clean_from_lists(ct); + remove_expectations(ct); + disown_children(ct); WRITE_UNLOCK(&ip_conntrack_lock); ip_conntrack_put(ct); }