diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2005-07-21 13:14:46 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2005-07-21 13:14:46 -0700 |
commit | 4acdbdbe5089c06d5e0c7e96783fcc4414ded00a (patch) | |
tree | 77629aef70bd92983518b6f5dd13c70a222c4cbb /net/ipv4/netfilter/ip_conntrack_core.c | |
parent | 4aa49d130df9209707a97786a55a3f584b7345e9 (diff) | |
download | op-kernel-dev-4acdbdbe5089c06d5e0c7e96783fcc4414ded00a.zip op-kernel-dev-4acdbdbe5089c06d5e0c7e96783fcc4414ded00a.tar.gz |
[NETFILTER]: ip_conntrack_expect_related must not free expectation
If a connection tracking helper tells us to expect a connection, and
we're already expecting that connection, we simply free the one they
gave us and return success.
The problem is that NAT helpers (eg. FTP) have to allocate the
expectation first (to see what port is available) then rewrite the
packet. If that rewrite fails, they try to remove the expectation,
but it was freed in ip_conntrack_expect_related.
This is one example of a larger problem: having registered the
expectation, the pointer is no longer ours to use. Reference counting
is needed for ctnetlink anyway, so introduce it now.
To have a single "put" path, we need to grab the reference to the
connection on creation, rather than open-coding it in the caller.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/netfilter/ip_conntrack_core.c')
-rw-r--r-- | net/ipv4/netfilter/ip_conntrack_core.c | 40 |
1 files changed, 18 insertions, 22 deletions
diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c index 4b78ebe..14af55c 100644 --- a/net/ipv4/netfilter/ip_conntrack_core.c +++ b/net/ipv4/netfilter/ip_conntrack_core.c @@ -137,19 +137,12 @@ ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse, /* ip_conntrack_expect helper functions */ -static void destroy_expect(struct ip_conntrack_expect *exp) -{ - ip_conntrack_put(exp->master); - IP_NF_ASSERT(!timer_pending(&exp->timeout)); - kmem_cache_free(ip_conntrack_expect_cachep, exp); - CONNTRACK_STAT_INC(expect_delete); -} - static void unlink_expect(struct ip_conntrack_expect *exp) { ASSERT_WRITE_LOCK(&ip_conntrack_lock); + IP_NF_ASSERT(!timer_pending(&exp->timeout)); list_del(&exp->list); - /* Logically in destroy_expect, but we hold the lock here. */ + CONNTRACK_STAT_INC(expect_delete); exp->master->expecting--; } @@ -160,7 +153,7 @@ static void expectation_timed_out(unsigned long ul_expect) write_lock_bh(&ip_conntrack_lock); unlink_expect(exp); write_unlock_bh(&ip_conntrack_lock); - destroy_expect(exp); + ip_conntrack_expect_put(exp); } /* If an expectation for this connection is found, it gets delete from @@ -198,7 +191,7 @@ static void remove_expectations(struct ip_conntrack *ct) list_for_each_entry_safe(i, tmp, &ip_conntrack_expect_list, list) { if (i->master == ct && del_timer(&i->timeout)) { unlink_expect(i); - destroy_expect(i); + ip_conntrack_expect_put(i); } } } @@ -537,7 +530,7 @@ init_conntrack(const struct ip_conntrack_tuple *tuple, if (exp) { if (exp->expectfn) exp->expectfn(conntrack, exp); - destroy_expect(exp); + ip_conntrack_expect_put(exp); } return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]; @@ -729,14 +722,14 @@ void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp) if (expect_matches(i, exp) && del_timer(&i->timeout)) { unlink_expect(i); write_unlock_bh(&ip_conntrack_lock); - destroy_expect(i); + ip_conntrack_expect_put(i); return; } } write_unlock_bh(&ip_conntrack_lock); } -struct ip_conntrack_expect *ip_conntrack_expect_alloc(void) +struct ip_conntrack_expect *ip_conntrack_expect_alloc(struct ip_conntrack *me) { struct ip_conntrack_expect *new; @@ -745,18 +738,23 @@ struct ip_conntrack_expect *ip_conntrack_expect_alloc(void) DEBUGP("expect_related: OOM allocating expect\n"); return NULL; } - new->master = NULL; + new->master = me; + atomic_inc(&new->master->ct_general.use); + atomic_set(&new->use, 1); return new; } -void ip_conntrack_expect_free(struct ip_conntrack_expect *expect) +void ip_conntrack_expect_put(struct ip_conntrack_expect *exp) { - kmem_cache_free(ip_conntrack_expect_cachep, expect); + if (atomic_dec_and_test(&exp->use)) { + ip_conntrack_put(exp->master); + kmem_cache_free(ip_conntrack_expect_cachep, exp); + } } static void ip_conntrack_expect_insert(struct ip_conntrack_expect *exp) { - atomic_inc(&exp->master->ct_general.use); + atomic_inc(&exp->use); exp->master->expecting++; list_add(&exp->list, &ip_conntrack_expect_list); @@ -778,7 +776,7 @@ static void evict_oldest_expect(struct ip_conntrack *master) if (i->master == master) { if (del_timer(&i->timeout)) { unlink_expect(i); - destroy_expect(i); + ip_conntrack_expect_put(i); } break; } @@ -810,8 +808,6 @@ int ip_conntrack_expect_related(struct ip_conntrack_expect *expect) /* Refresh timer: if it's dying, ignore.. */ if (refresh_timer(i)) { ret = 0; - /* We don't need the one they've given us. */ - ip_conntrack_expect_free(expect); goto out; } } else if (expect_clash(i, expect)) { @@ -881,7 +877,7 @@ void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me) list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) { if (exp->master->helper == me && del_timer(&exp->timeout)) { unlink_expect(exp); - destroy_expect(exp); + ip_conntrack_expect_put(exp); } } /* Get rid of expecteds, set helpers to NULL. */ |