Name: Use ct_extend for helper pointer Status: Tested lightly under nfsim Signed-off-by: Rusty Russell Most connections don't have a connection tracking helper. So that pointer is a good candidate to put in ct_extend. Index: linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/ip_nat_core.c =================================================================== --- linux-2.6.10-bk14-Netfilter.orig/net/ipv4/netfilter/ip_nat_core.c 2005-01-12 23:29:00.605426112 +1100 +++ linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/ip_nat_core.c 2005-01-12 23:43:00.822693672 +1100 @@ -283,7 +283,8 @@ /* Alter conntrack table so will recognize replies. */ invert_tuplepr(&reply, &new_tuple); - ip_conntrack_alter_reply(conntrack, &reply); + if (ip_conntrack_alter_reply(conntrack, &reply) != 0) + return NF_DROP; /* Non-atomic: we own this at the moment. */ if (maniptype == IP_NAT_MANIP_SRC) Index: linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/ip_conntrack_standalone.c =================================================================== --- linux-2.6.10-bk14-Netfilter.orig/net/ipv4/netfilter/ip_conntrack_standalone.c 2005-01-12 23:41:09.571606408 +1100 +++ linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/ip_conntrack_standalone.c 2005-01-12 23:42:01.974639936 +1100 @@ -385,11 +385,17 @@ /* This is where we call the helper: as the packet goes out. */ ct = ip_conntrack_get(*pskb, &ctinfo); - if (ct && ct->helper) { - unsigned int ret; - ret = ct->helper->help(pskb, ct, ctinfo); - if (ret != NF_ACCEPT) - return ret; + if (ct) { + struct ip_conntrack_helper **helpp; + + /* Don't need lock: ct not in hash yet. */ + helpp = ct_extend_find(ct->ext, CTE_CT_HELPER); + if (helpp) { + unsigned int ret; + ret = (*helpp)->help(pskb, ct, ctinfo); + if (ret != NF_ACCEPT) + return ret; + } } /* We've seen it coming out the other side: confirm it */ Index: linux-2.6.10-bk14-Netfilter/include/linux/netfilter_ipv4/ip_nat.h =================================================================== --- linux-2.6.10-bk14-Netfilter.orig/include/linux/netfilter_ipv4/ip_nat.h 2005-01-12 23:29:00.641420640 +1100 +++ linux-2.6.10-bk14-Netfilter/include/linux/netfilter_ipv4/ip_nat.h 2005-01-12 23:43:00.823693520 +1100 @@ -57,9 +57,6 @@ { struct list_head bysource; - /* Helper (NULL if none). */ - struct ip_nat_helper *helper; - struct ip_nat_seq seq[IP_CT_DIR_MAX]; }; Index: linux-2.6.10-bk14-Netfilter/include/linux/netfilter_ipv4/ip_conntrack.h =================================================================== --- linux-2.6.10-bk14-Netfilter.orig/include/linux/netfilter_ipv4/ip_conntrack.h 2005-01-12 23:41:09.573606104 +1100 +++ linux-2.6.10-bk14-Netfilter/include/linux/netfilter_ipv4/ip_conntrack.h 2005-01-12 23:42:02.014633856 +1100 @@ -172,9 +172,6 @@ /* Current number of expected connections */ unsigned int expecting; - /* Helper, if any. */ - struct ip_conntrack_helper *helper; - /* Storage reserved for other modules: */ union ip_conntrack_proto proto; @@ -202,8 +199,8 @@ /* get master conntrack via master expectation */ #define master_ct(conntr) (conntr->master) -/* Alter reply tuple (maybe alter helper). */ -extern void +/* Alter reply tuple (maybe alter helper). Returns 0 or -errno. */ +extern int ip_conntrack_alter_reply(struct ip_conntrack *conntrack, const struct ip_conntrack_tuple *newreply); Index: linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/ipt_helper.c =================================================================== --- linux-2.6.10-bk14-Netfilter.orig/net/ipv4/netfilter/ipt_helper.c 2005-01-12 23:29:00.605426112 +1100 +++ linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/ipt_helper.c 2005-01-12 23:42:02.015633704 +1100 @@ -18,6 +18,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Martin Josefsson "); @@ -40,6 +41,7 @@ const struct ipt_helper_info *info = matchinfo; struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; + struct ip_conntrack_helper **helpp; int ret = info->invert; ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); @@ -54,20 +56,21 @@ } READ_LOCK(&ip_conntrack_lock); - if (!ct->master->helper) { + helpp = ct_extend_find(ct->master->ext, CTE_CT_HELPER); + if (!helpp) { DEBUGP("ipt_helper: master ct %p has no helper\n", exp->expectant); goto out_unlock; } DEBUGP("master's name = %s , info->name = %s\n", - ct->master->helper->name, info->name); + (*helpp)->name, info->name); if (info->name[0] == '\0') ret ^= 1; else - ret ^= !strncmp(ct->master->helper->name, info->name, - strlen(ct->master->helper->name)); + ret ^= !strncmp((*helpp)->name, info->name, + strlen((*helpp)->name)); out_unlock: READ_UNLOCK(&ip_conntrack_lock); return ret; Index: linux-2.6.10-bk14-Netfilter/include/linux/netfilter_ipv4/ct_extend.h =================================================================== --- linux-2.6.10-bk14-Netfilter.orig/include/linux/netfilter_ipv4/ct_extend.h 2005-01-12 23:41:58.520165096 +1100 +++ linux-2.6.10-bk14-Netfilter/include/linux/netfilter_ipv4/ct_extend.h 2005-01-12 23:43:16.845257872 +1100 @@ -7,12 +7,14 @@ CTE_MASQ, CTE_FTP_CONN, CTE_MARK, + CTE_CT_HELPER, CTE_MAX, } __attribute__((packed)); #define CTE_MASQ_TYPE char /* Actually char[IFNAMSIZ] */ #define CTE_FTP_CONN_TYPE struct ip_ct_ftp_master #define CTE_MARK_TYPE unsigned long +#define CTE_CT_HELPER_TYPE struct ip_conntrack_helper * /* Extensions: optional stuff which isn't permanently in struct. */ struct ct_extend { Index: linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/ip_conntrack_core.c =================================================================== --- linux-2.6.10-bk14-Netfilter.orig/net/ipv4/netfilter/ip_conntrack_core.c 2005-01-12 23:41:09.578605344 +1100 +++ linux-2.6.10-bk14-Netfilter/net/ipv4/netfilter/ip_conntrack_core.c 2005-01-12 23:42:02.018633248 +1100 @@ -574,8 +574,19 @@ nf_conntrack_get(&conntrack->master->ct_general); CONNTRACK_STAT_INC(expect_new); } else { - conntrack->helper = ip_ct_find_helper(&repl_tuple); + struct ip_conntrack_helper *helper, **helpp; + helper = ip_ct_find_helper(&repl_tuple); + if (helper) { + helpp = ct_extend_add(&conntrack->ext, CTE_CT_HELPER, + GFP_ATOMIC); + if (!helpp) { + kmem_cache_free(ip_conntrack_cachep,conntrack); + WRITE_UNLOCK(&ip_conntrack_lock); + return NULL; + } + *helpp = helper; + } CONNTRACK_STAT_INC(new); } @@ -805,18 +816,19 @@ kmem_cache_free(ip_conntrack_expect_cachep, expect); } -static void ip_conntrack_expect_insert(struct ip_conntrack_expect *exp) +static void ip_conntrack_expect_insert(struct ip_conntrack_expect *exp, + struct ip_conntrack_helper *helper) { atomic_inc(&exp->master->ct_general.use); exp->master->expecting++; list_add(&exp->list, &ip_conntrack_expect_list); - if (exp->master->helper->timeout) { + if (helper->timeout) { init_timer(&exp->timeout); exp->timeout.data = (unsigned long)exp; exp->timeout.function = expectation_timed_out; exp->timeout.expires - = jiffies + exp->master->helper->timeout * HZ; + = jiffies + helper->timeout * HZ; add_timer(&exp->timeout); } else exp->timeout.function = NULL; @@ -840,12 +852,13 @@ } } -static inline int refresh_timer(struct ip_conntrack_expect *i) +static inline int refresh_timer(struct ip_conntrack_expect *i, + struct ip_conntrack_helper *helper) { if (!del_timer(&i->timeout)) return 0; - i->timeout.expires = jiffies + i->master->helper->timeout*HZ; + i->timeout.expires = jiffies + helper->timeout*HZ; add_timer(&i->timeout); return 1; } @@ -854,16 +867,20 @@ { struct ip_conntrack_expect *i; int ret; + struct ip_conntrack_helper **helpp; DEBUGP("ip_conntrack_expect_related %p\n", related_to); DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); WRITE_LOCK(&ip_conntrack_lock); + helpp = ct_extend_find(expect->master->ext, CTE_CT_HELPER); + BUG_ON(!helpp || !*helpp); + list_for_each_entry(i, &ip_conntrack_expect_list, list) { if (expect_matches(i, expect)) { /* Refresh timer: if it's dying, ignore.. */ - if (refresh_timer(i)) { + if (refresh_timer(i, *helpp)) { ret = 0; /* We don't need the one they've given us. */ ip_conntrack_expect_free(expect); @@ -876,11 +893,11 @@ } /* Will be over limit? */ - if (expect->master->helper->max_expected && - expect->master->expecting >= expect->master->helper->max_expected) + if ((*helpp)->max_expected && + expect->master->expecting >= (*helpp)->max_expected) evict_oldest_expect(expect->master); - ip_conntrack_expect_insert(expect); + ip_conntrack_expect_insert(expect, *helpp); ret = 0; out: WRITE_UNLOCK(&ip_conntrack_lock); @@ -889,10 +906,11 @@ /* Alter reply tuple (maybe alter helper). This is for NAT, and is implicitly racy: see __ip_conntrack_confirm */ -void ip_conntrack_alter_reply(struct ip_conntrack *conntrack, - const struct ip_conntrack_tuple *newreply) +int ip_conntrack_alter_reply(struct ip_conntrack *conntrack, + const struct ip_conntrack_tuple *newreply) { - WRITE_LOCK(&ip_conntrack_lock); + struct ip_conntrack_helper *helper, **helpp; + /* Should be unconfirmed, so not in hash table yet */ IP_NF_ASSERT(!is_confirmed(conntrack)); @@ -900,9 +918,25 @@ DUMP_TUPLE(newreply); conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; - if (!conntrack->master && conntrack->expecting == 0) - conntrack->helper = ip_ct_find_helper(newreply); + if (conntrack->master || conntrack->expecting) + return 0; + + WRITE_LOCK(&ip_conntrack_lock); + helper = ip_ct_find_helper(newreply); + if (helper) { + helpp = ct_extend_find(conntrack->ext, CTE_CT_HELPER); + if (!helpp) { + helpp = ct_extend_add(&conntrack->ext, CTE_CT_HELPER, + GFP_ATOMIC); + if (!helpp) { + READ_UNLOCK(&ip_conntrack_lock); + return -ENOMEM; + } + } + *helpp = helper; + } WRITE_UNLOCK(&ip_conntrack_lock); + return 0; } int ip_conntrack_helper_register(struct ip_conntrack_helper *me) @@ -918,8 +952,13 @@ static inline int unhelp(struct ip_conntrack_tuple_hash *i, const struct ip_conntrack_helper *me) { - if (tuplehash_to_ctrack(i)->helper == me) - tuplehash_to_ctrack(i)->helper = NULL; + struct ip_conntrack_helper **helpp; + helpp = ct_extend_find(tuplehash_to_ctrack(i)->ext, CTE_CT_HELPER); + + if (helpp && *helpp == me) + tuplehash_to_ctrack(i)->ext + = ct_extend_del(tuplehash_to_ctrack(i)->ext, + CTE_CT_HELPER); return 0; } @@ -934,7 +973,10 @@ /* Get rid of expectations */ list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) { - if (exp->master->helper == me && del_timer(&exp->timeout)) { + struct ip_conntrack_helper **helpp; + helpp = ct_extend_find(exp->master->ext, CTE_CT_HELPER); + + if (*helpp == me && del_timer(&exp->timeout)) { unlink_expect(exp); destroy_expect(exp); } @@ -1178,6 +1220,13 @@ * ip_conntrack_htable_size)); } +static struct ct_extend_type helper_extend = +{ + .len = sizeof(struct ip_conntrack_helper *), + .align = __alignof__(struct ip_conntrack_helper *), + .type = CTE_CT_HELPER, +}; + /* Mishearing the voices in his head, our hero wonders how he's supposed to kill the mall. */ void ip_conntrack_cleanup(void) @@ -1196,6 +1245,7 @@ } unregister_cte_mark(); + unregister_ct_extend_type(&helper_extend); kmem_cache_destroy(ip_conntrack_cachep); kmem_cache_destroy(ip_conntrack_expect_cachep); free_conntrack_hash(); @@ -1293,6 +1343,7 @@ /* - and look it like as a confirmed connection */ set_bit(IPS_CONFIRMED_BIT, &ip_conntrack_untracked.status); + register_ct_extend_type(&helper_extend); register_cte_mark(); return ret;