Commit 2a526ac9 authored by Rusty Russell's avatar Rusty Russell Committed by David S. Miller

[NETFILTER]: Simplify expect handling

Now we've changed expect handling, we can simplify it significantly.

1) struct ip_conntrack_expect only exists until the connection
   matching it is created.  Now NAT is done directly at the time the
   expectation is matched, we don't need to keep this information
   around.

2) The term 'master' is used everywhere to mean the connection that
   expected this connection.  The 'master' field in the new connection
   points straight to the master connection, and holds a reference.

3) There is no direct link from the connection to the expectations it
   has created: we walk the global list to find them if we need to
   clean them up.  Each expectation holds a reference.

4) The ip_conntrack_expect_tuple_lock is now a proper subset of
   ip_conntrack_lock, so we can eliminate it.

5) Remove flags from helper: the policy of evicting the oldest
   expectation seems to be appropriate for everyone.

6) ip_conntrack_expect_find_get() and ip_conntrack_expect_put() are no
   longer required.

7) Remove reference count from expectations, and don't free when we
   fail ip_conntrack_expect_related(): have user call
   ip_conntrack_expect_free().
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 55d349b2
...@@ -102,33 +102,19 @@ struct ip_conntrack_expect ...@@ -102,33 +102,19 @@ struct ip_conntrack_expect
/* Internal linked list (global expectation list) */ /* Internal linked list (global expectation list) */
struct list_head list; struct list_head list;
/* reference count */ /* We expect this tuple, with the following mask */
atomic_t use; struct ip_conntrack_tuple tuple, mask;
/* expectation list for this master */ /* Function to call after setup and insertion */
struct list_head expected_list; void (*expectfn)(struct ip_conntrack *new,
struct ip_conntrack_expect *this);
/* The conntrack of the master connection */ /* The conntrack of the master connection */
struct ip_conntrack *expectant; struct ip_conntrack *master;
/* The conntrack of the sibling connection, set after
* expectation arrived */
struct ip_conntrack *sibling;
/* Tuple saved for conntrack */
struct ip_conntrack_tuple ct_tuple;
/* Timer function; deletes the expectation. */ /* Timer function; deletes the expectation. */
struct timer_list timeout; struct timer_list timeout;
/* Data filled out by the conntrack helpers follow: */
/* We expect this tuple, with the following mask */
struct ip_conntrack_tuple tuple, mask;
/* Function to call after setup and insertion */
void (*expectfn)(struct ip_conntrack *new);
#ifdef CONFIG_IP_NF_NAT_NEEDED #ifdef CONFIG_IP_NF_NAT_NEEDED
/* This is the original per-proto part, used to map the /* This is the original per-proto part, used to map the
* expected connection the way the recipient expects. */ * expected connection the way the recipient expects. */
...@@ -136,8 +122,6 @@ struct ip_conntrack_expect ...@@ -136,8 +122,6 @@ struct ip_conntrack_expect
/* Direction relative to the master connection. */ /* Direction relative to the master connection. */
enum ip_conntrack_dir dir; enum ip_conntrack_dir dir;
#endif #endif
union ip_conntrack_expect_proto proto;
}; };
struct ip_conntrack_counter struct ip_conntrack_counter
...@@ -164,17 +148,12 @@ struct ip_conntrack ...@@ -164,17 +148,12 @@ struct ip_conntrack
/* Accounting Information (same cache line as other written members) */ /* Accounting Information (same cache line as other written members) */
struct ip_conntrack_counter counters[IP_CT_DIR_MAX]; struct ip_conntrack_counter counters[IP_CT_DIR_MAX];
#endif #endif
/* If we were expected by an expectation, this will be it */
/* If we're expecting another related connection, this will be struct ip_conntrack *master;
in expected linked list */
struct list_head sibling_list;
/* Current number of expected connections */ /* Current number of expected connections */
unsigned int expecting; unsigned int expecting;
/* If we were expected by an expectation, this will be it */
struct ip_conntrack_expect *master;
/* Helper, if any. */ /* Helper, if any. */
struct ip_conntrack_helper *helper; struct ip_conntrack_helper *helper;
...@@ -203,7 +182,7 @@ struct ip_conntrack ...@@ -203,7 +182,7 @@ struct ip_conntrack
}; };
/* get master conntrack via master expectation */ /* get master conntrack via master expectation */
#define master_ct(conntr) (conntr->master ? conntr->master->expectant : NULL) #define master_ct(conntr) (conntr->master)
/* Alter reply tuple (maybe alter helper). */ /* Alter reply tuple (maybe alter helper). */
extern void extern void
...@@ -227,13 +206,6 @@ ip_conntrack_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo) ...@@ -227,13 +206,6 @@ ip_conntrack_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo)
/* decrement reference count on a conntrack */ /* decrement reference count on a conntrack */
extern inline void ip_conntrack_put(struct ip_conntrack *ct); extern inline void ip_conntrack_put(struct ip_conntrack *ct);
/* find unconfirmed expectation based on tuple */
struct ip_conntrack_expect *
ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple);
/* decrement reference count on an expectation */
void ip_conntrack_expect_put(struct ip_conntrack_expect *exp);
/* call to create an explicit dependency on ip_conntrack. */ /* call to create an explicit dependency on ip_conntrack. */
extern void need_ip_conntrack(void); extern void need_ip_conntrack(void);
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
struct ip_conntrack_expect; struct ip_conntrack_expect;
extern unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb, extern unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int matchoff, unsigned int matchoff,
unsigned int matchlen, unsigned int matchlen,
......
...@@ -48,6 +48,5 @@ static inline int ip_conntrack_confirm(struct sk_buff **pskb) ...@@ -48,6 +48,5 @@ static inline int ip_conntrack_confirm(struct sk_buff **pskb)
extern struct list_head *ip_conntrack_hash; extern struct list_head *ip_conntrack_hash;
extern struct list_head ip_conntrack_expect_list; extern struct list_head ip_conntrack_expect_list;
DECLARE_RWLOCK_EXTERN(ip_conntrack_lock); DECLARE_RWLOCK_EXTERN(ip_conntrack_lock);
DECLARE_RWLOCK_EXTERN(ip_conntrack_expect_tuple_lock);
#endif /* _IP_CONNTRACK_CORE_H */ #endif /* _IP_CONNTRACK_CORE_H */
...@@ -34,7 +34,6 @@ struct ip_conntrack_expect; ...@@ -34,7 +34,6 @@ struct ip_conntrack_expect;
/* For NAT to hook in when we find a packet which describes what other /* For NAT to hook in when we find a packet which describes what other
* connection we should expect. */ * connection we should expect. */
extern unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb, extern unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
enum ip_ct_ftp_type type, enum ip_ct_ftp_type type,
unsigned int matchoff, unsigned int matchoff,
......
...@@ -5,15 +5,11 @@ ...@@ -5,15 +5,11 @@
struct module; struct module;
/* Reuse expectation when max_expected reached */
#define IP_CT_HELPER_F_REUSE_EXPECT 0x01
struct ip_conntrack_helper struct ip_conntrack_helper
{ {
struct list_head list; /* Internal use. */ struct list_head list; /* Internal use. */
const char *name; /* name of the module */ const char *name; /* name of the module */
unsigned char flags; /* Flags (see above) */
struct module *me; /* pointer to self */ struct module *me; /* pointer to self */
unsigned int max_expected; /* Maximum number of concurrent unsigned int max_expected; /* Maximum number of concurrent
* expected connections */ * expected connections */
...@@ -39,9 +35,10 @@ extern struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_t ...@@ -39,9 +35,10 @@ extern struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_t
/* Allocate space for an expectation: this is mandatory before calling /* Allocate space for an expectation: this is mandatory before calling
ip_conntrack_expect_related. */ ip_conntrack_expect_related. */
extern struct ip_conntrack_expect *ip_conntrack_expect_alloc(void); extern struct ip_conntrack_expect *ip_conntrack_expect_alloc(void);
extern void ip_conntrack_expect_free(struct ip_conntrack_expect *exp);
/* Add an expected connection: can have more than one per connection */ /* Add an expected connection: can have more than one per connection */
extern int ip_conntrack_expect_related(struct ip_conntrack_expect *exp, extern int ip_conntrack_expect_related(struct ip_conntrack_expect *exp);
struct ip_conntrack *related_to);
extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp); extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp);
#endif /*_IP_CONNTRACK_HELPER_H*/ #endif /*_IP_CONNTRACK_HELPER_H*/
...@@ -20,7 +20,6 @@ struct ip_ct_irc_master { ...@@ -20,7 +20,6 @@ struct ip_ct_irc_master {
#ifdef __KERNEL__ #ifdef __KERNEL__
extern unsigned int (*ip_nat_irc_hook)(struct sk_buff **pskb, extern unsigned int (*ip_nat_irc_hook)(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int matchoff, unsigned int matchoff,
unsigned int matchlen, unsigned int matchlen,
......
...@@ -14,7 +14,6 @@ struct tftphdr { ...@@ -14,7 +14,6 @@ struct tftphdr {
#define TFTP_OPCODE_ERROR 5 #define TFTP_OPCODE_ERROR 5
unsigned int (*ip_nat_tftp_hook)(struct sk_buff **pskb, unsigned int (*ip_nat_tftp_hook)(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
struct ip_conntrack_expect *exp); struct ip_conntrack_expect *exp);
......
...@@ -28,5 +28,6 @@ extern int ip_nat_seq_adjust(struct sk_buff **pskb, ...@@ -28,5 +28,6 @@ extern int ip_nat_seq_adjust(struct sk_buff **pskb,
/* Setup NAT on this expected conntrack so it follows master, but goes /* Setup NAT on this expected conntrack so it follows master, but goes
* to port ct->master->saved_proto. */ * to port ct->master->saved_proto. */
extern void ip_nat_follow_master(struct ip_conntrack *ct); extern void ip_nat_follow_master(struct ip_conntrack *ct,
struct ip_conntrack_expect *this);
#endif #endif
...@@ -45,7 +45,6 @@ static char amanda_buffer[65536]; ...@@ -45,7 +45,6 @@ static char amanda_buffer[65536];
static DECLARE_LOCK(amanda_buffer_lock); static DECLARE_LOCK(amanda_buffer_lock);
unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb, unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int matchoff, unsigned int matchoff,
unsigned int matchlen, unsigned int matchlen,
...@@ -110,6 +109,7 @@ static int help(struct sk_buff **pskb, ...@@ -110,6 +109,7 @@ static int help(struct sk_buff **pskb,
} }
exp->expectfn = NULL; exp->expectfn = NULL;
exp->master = ct;
exp->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; exp->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
exp->tuple.src.u.tcp.port = 0; exp->tuple.src.u.tcp.port = 0;
...@@ -124,12 +124,14 @@ static int help(struct sk_buff **pskb, ...@@ -124,12 +124,14 @@ static int help(struct sk_buff **pskb,
exp->mask.dst.u.tcp.port = 0xFFFF; exp->mask.dst.u.tcp.port = 0xFFFF;
if (ip_nat_amanda_hook) if (ip_nat_amanda_hook)
ret = ip_nat_amanda_hook(pskb, ct, ctinfo, ret = ip_nat_amanda_hook(pskb, ctinfo,
tmp - amanda_buffer, tmp - amanda_buffer,
len, exp); len, exp);
else if (ip_conntrack_expect_related(exp, ct) != 0) else if (ip_conntrack_expect_related(exp) != 0) {
ip_conntrack_expect_free(exp);
ret = NF_DROP; ret = NF_DROP;
} }
}
out: out:
UNLOCK_BH(&amanda_buffer_lock); UNLOCK_BH(&amanda_buffer_lock);
......
...@@ -58,7 +58,6 @@ ...@@ -58,7 +58,6 @@
#endif #endif
DECLARE_RWLOCK(ip_conntrack_lock); DECLARE_RWLOCK(ip_conntrack_lock);
DECLARE_RWLOCK(ip_conntrack_expect_tuple_lock);
/* ip_conntrack_standalone needs this */ /* ip_conntrack_standalone needs this */
atomic_t ip_conntrack_count = ATOMIC_INIT(0); atomic_t ip_conntrack_count = ATOMIC_INIT(0);
...@@ -136,129 +135,70 @@ ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse, ...@@ -136,129 +135,70 @@ ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse,
/* ip_conntrack_expect helper functions */ /* ip_conntrack_expect helper functions */
static void destroy_expect(struct ip_conntrack_expect *exp)
/* Compare tuple parts depending on mask. */
static inline int expect_cmp(const struct ip_conntrack_expect *i,
const struct ip_conntrack_tuple *tuple)
{
MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock);
return ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask);
}
static void
destroy_expect(struct ip_conntrack_expect *exp)
{ {
DEBUGP("destroy_expect(%p) use=%d\n", exp, atomic_read(&exp->use)); ip_conntrack_put(exp->master);
IP_NF_ASSERT(atomic_read(&exp->use) == 0);
IP_NF_ASSERT(!timer_pending(&exp->timeout)); IP_NF_ASSERT(!timer_pending(&exp->timeout));
kmem_cache_free(ip_conntrack_expect_cachep, exp); kmem_cache_free(ip_conntrack_expect_cachep, exp);
CONNTRACK_STAT_INC(expect_delete); CONNTRACK_STAT_INC(expect_delete);
} }
inline void ip_conntrack_expect_put(struct ip_conntrack_expect *exp) static void unlink_expect(struct ip_conntrack_expect *exp)
{
IP_NF_ASSERT(exp);
if (atomic_dec_and_test(&exp->use)) {
/* usage count dropped to zero */
destroy_expect(exp);
}
}
static inline struct ip_conntrack_expect *
__ip_ct_expect_find(const struct ip_conntrack_tuple *tuple)
{
MUST_BE_READ_LOCKED(&ip_conntrack_lock);
MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock);
return LIST_FIND(&ip_conntrack_expect_list, expect_cmp,
struct ip_conntrack_expect *, tuple);
}
/* Find a expectation corresponding to a tuple. */
struct ip_conntrack_expect *
ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple)
{ {
struct ip_conntrack_expect *exp; MUST_BE_WRITE_LOCKED(&ip_conntrack_lock);
list_del(&exp->list);
READ_LOCK(&ip_conntrack_lock); /* Logically in destroy_expect, but we hold the lock here. */
READ_LOCK(&ip_conntrack_expect_tuple_lock); exp->master->expecting--;
exp = __ip_ct_expect_find(tuple);
if (exp)
atomic_inc(&exp->use);
READ_UNLOCK(&ip_conntrack_expect_tuple_lock);
READ_UNLOCK(&ip_conntrack_lock);
return exp;
} }
/* remove one specific expectation from all lists and drop refcount, static void expectation_timed_out(unsigned long ul_expect)
* does _NOT_ delete the timer. */
static void __unexpect_related(struct ip_conntrack_expect *expect)
{ {
DEBUGP("unexpect_related(%p)\n", expect); struct ip_conntrack_expect *exp = (void *)ul_expect;
MUST_BE_WRITE_LOCKED(&ip_conntrack_lock);
/* we're not allowed to unexpect a confirmed expectation! */ WRITE_LOCK(&ip_conntrack_lock);
IP_NF_ASSERT(!expect->sibling); unlink_expect(exp);
WRITE_UNLOCK(&ip_conntrack_lock);
/* delete from global and local lists */ destroy_expect(exp);
list_del(&expect->list);
list_del(&expect->expected_list);
/* decrement expect-count of master conntrack */
if (expect->expectant)
expect->expectant->expecting--;
ip_conntrack_expect_put(expect);
} }
/* remove one specific expecatation from all lists, drop refcount /* If an expectation for this connection is found, it gets delete from
* and expire timer. * global list then returned. */
* This function can _NOT_ be called for confirmed expects! */ static struct ip_conntrack_expect *
static void unexpect_related(struct ip_conntrack_expect *expect) find_expectation(const struct ip_conntrack_tuple *tuple)
{ {
IP_NF_ASSERT(expect->expectant); struct ip_conntrack_expect *i;
IP_NF_ASSERT(expect->expectant->helper);
/* if we are supposed to have a timer, but we can't delete
* it: race condition. __unexpect_related will
* be calledd by timeout function */
if (expect->expectant->helper->timeout
&& !del_timer(&expect->timeout))
return;
__unexpect_related(expect); list_for_each_entry(i, &ip_conntrack_expect_list, list) {
/* If master is not in hash table yet (ie. packet hasn't left
this machine yet), how can other end know about expected?
Hence these are not the droids you are looking for (if
master ct never got confirmed, we'd hold a reference to it
and weird things would happen to future packets). */
if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)
&& is_confirmed(i->master)
&& (!i->timeout.function || del_timer(&i->timeout))) {
unlink_expect(i);
return i;
}
}
return NULL;
} }
/* delete all unconfirmed expectations for this conntrack */ /* delete all expectations for this conntrack */
static void remove_expectations(struct ip_conntrack *ct, int drop_refcount) static void remove_expectations(struct ip_conntrack *ct)
{ {
struct list_head *exp_entry, *next; struct ip_conntrack_expect *i, *tmp;
struct ip_conntrack_expect *exp;
DEBUGP("remove_expectations(%p)\n", ct); /* Optimization: most connection never expect any others. */
if (ct->expecting == 0)
list_for_each_safe(exp_entry, next, &ct->sibling_list) { return;
exp = list_entry(exp_entry, struct ip_conntrack_expect,
expected_list);
/* we skip established expectations, as we want to delete list_for_each_entry_safe(i, tmp, &ip_conntrack_expect_list, list) {
* the un-established ones only */ if (i->master == ct
if (exp->sibling) { && (!i->timeout.function || del_timer(&i->timeout))) {
DEBUGP("remove_expectations: skipping established %p of %p\n", exp->sibling, ct); unlink_expect(i);
if (drop_refcount) { destroy_expect(i);
/* Indicate that this expectations parent is dead */
ip_conntrack_put(exp->expectant);
exp->expectant = NULL;
} }
continue;
}
IP_NF_ASSERT(list_inlist(&ip_conntrack_expect_list, exp));
IP_NF_ASSERT(exp->expectant == ct);
/* delete expectation from global and private lists */
unexpect_related(exp);
} }
} }
...@@ -275,14 +215,14 @@ clean_from_lists(struct ip_conntrack *ct) ...@@ -275,14 +215,14 @@ clean_from_lists(struct ip_conntrack *ct)
LIST_DELETE(&ip_conntrack_hash[ho], &ct->tuplehash[IP_CT_DIR_ORIGINAL]); LIST_DELETE(&ip_conntrack_hash[ho], &ct->tuplehash[IP_CT_DIR_ORIGINAL]);
LIST_DELETE(&ip_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]); LIST_DELETE(&ip_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]);
/* Destroy all un-established, pending expectations */ /* Destroy all pending expectations */
remove_expectations(ct, 1); remove_expectations(ct);
} }
static void static void
destroy_conntrack(struct nf_conntrack *nfct) destroy_conntrack(struct nf_conntrack *nfct)
{ {
struct ip_conntrack *ct = (struct ip_conntrack *)nfct, *master = NULL; struct ip_conntrack *ct = (struct ip_conntrack *)nfct;
struct ip_conntrack_protocol *proto; struct ip_conntrack_protocol *proto;
DEBUGP("destroy_conntrack(%p)\n", ct); DEBUGP("destroy_conntrack(%p)\n", ct);
...@@ -304,8 +244,7 @@ destroy_conntrack(struct nf_conntrack *nfct) ...@@ -304,8 +244,7 @@ destroy_conntrack(struct nf_conntrack *nfct)
* except TFTP can create an expectation on the first packet, * except TFTP can create an expectation on the first packet,
* before connection is in the list, so we need to clean here, * before connection is in the list, so we need to clean here,
* too. */ * too. */
if (ct->expecting) remove_expectations(ct);
remove_expectations(ct, 1);
/* We overload first tuple to link into unconfirmed list. */ /* We overload first tuple to link into unconfirmed list. */
if (!is_confirmed(ct)) { if (!is_confirmed(ct)) {
...@@ -313,21 +252,11 @@ destroy_conntrack(struct nf_conntrack *nfct) ...@@ -313,21 +252,11 @@ destroy_conntrack(struct nf_conntrack *nfct)
list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list); list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list);
} }
/* Delete our master expectation */
if (ct->master) {
if (ct->master->expectant) {
/* can't call __unexpect_related here,
* since it would screw up expect_list */
list_del(&ct->master->expected_list);
master = ct->master->expectant;
}
kmem_cache_free(ip_conntrack_expect_cachep, ct->master);
}
CONNTRACK_STAT_INC(delete); CONNTRACK_STAT_INC(delete);
WRITE_UNLOCK(&ip_conntrack_lock); WRITE_UNLOCK(&ip_conntrack_lock);
if (master) if (ct->master)
ip_conntrack_put(master); ip_conntrack_put(ct->master);
DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct);
kmem_cache_free(ip_conntrack_cachep, ct); kmem_cache_free(ip_conntrack_cachep, ct);
...@@ -529,7 +458,7 @@ init_conntrack(const struct ip_conntrack_tuple *tuple, ...@@ -529,7 +458,7 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
struct ip_conntrack *conntrack; struct ip_conntrack *conntrack;
struct ip_conntrack_tuple repl_tuple; struct ip_conntrack_tuple repl_tuple;
size_t hash; size_t hash;
struct ip_conntrack_expect *expected; struct ip_conntrack_expect *exp;
if (!ip_conntrack_hash_rnd_initted) { if (!ip_conntrack_hash_rnd_initted) {
get_random_bytes(&ip_conntrack_hash_rnd, 4); get_random_bytes(&ip_conntrack_hash_rnd, 4);
...@@ -577,73 +506,39 @@ init_conntrack(const struct ip_conntrack_tuple *tuple, ...@@ -577,73 +506,39 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
conntrack->timeout.data = (unsigned long)conntrack; conntrack->timeout.data = (unsigned long)conntrack;
conntrack->timeout.function = death_by_timeout; conntrack->timeout.function = death_by_timeout;
INIT_LIST_HEAD(&conntrack->sibling_list);
WRITE_LOCK(&ip_conntrack_lock); WRITE_LOCK(&ip_conntrack_lock);
/* Need finding and deleting of expected ONLY if we win race */ exp = find_expectation(tuple);
READ_LOCK(&ip_conntrack_expect_tuple_lock);
expected = LIST_FIND(&ip_conntrack_expect_list, expect_cmp,
struct ip_conntrack_expect *, tuple);
READ_UNLOCK(&ip_conntrack_expect_tuple_lock);
if (expected) {
/* If master is not in hash table yet (ie. packet hasn't left
this machine yet), how can other end know about expected?
Hence these are not the droids you are looking for (if
master ct never got confirmed, we'd hold a reference to it
and weird things would happen to future packets). */
if (!is_confirmed(expected->expectant)) {
conntrack->helper = ip_ct_find_helper(&repl_tuple);
goto end;
}
/* Expectation is dying... */
if (expected->expectant->helper->timeout
&& !del_timer(&expected->timeout))
goto end;
if (exp) {
DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n",
conntrack, expected); conntrack, exp);
/* Welcome, Mr. Bond. We've been expecting you... */ /* Welcome, Mr. Bond. We've been expecting you... */
IP_NF_ASSERT(expected->expectant);
__set_bit(IPS_EXPECTED_BIT, &conntrack->status); __set_bit(IPS_EXPECTED_BIT, &conntrack->status);
conntrack->master = expected; conntrack->master = exp->master;
expected->sibling = conntrack;
#if CONFIG_IP_NF_CONNTRACK_MARK #if CONFIG_IP_NF_CONNTRACK_MARK
conntrack->mark = expected->expectant->mark; conntrack->mark = exp->master->mark;
#endif #endif
LIST_DELETE(&ip_conntrack_expect_list, expected); nf_conntrack_get(&conntrack->master->ct_general);
expected->expectant->expecting--;
nf_conntrack_get(&master_ct(conntrack)->ct_general);
/* this is a braindead... --pablo */
atomic_inc(&ip_conntrack_count);
/* Overload tuple linked list to put us in unconfirmed list. */
list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list,
&unconfirmed);
WRITE_UNLOCK(&ip_conntrack_lock);
if (expected->expectfn)
expected->expectfn(conntrack);
CONNTRACK_STAT_INC(expect_new); CONNTRACK_STAT_INC(expect_new);
goto ret;
} else { } else {
conntrack->helper = ip_ct_find_helper(&repl_tuple); conntrack->helper = ip_ct_find_helper(&repl_tuple);
CONNTRACK_STAT_INC(new); CONNTRACK_STAT_INC(new);
} }
end:
/* Overload tuple linked list to put us in unconfirmed list. */ /* Overload tuple linked list to put us in unconfirmed list. */
list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list, &unconfirmed); list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list, &unconfirmed);
atomic_inc(&ip_conntrack_count); atomic_inc(&ip_conntrack_count);
WRITE_UNLOCK(&ip_conntrack_lock); WRITE_UNLOCK(&ip_conntrack_lock);
ret: return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]; if (exp) {
if (exp->expectfn)
exp->expectfn(conntrack, exp);
destroy_expect(exp);
}
return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL];
} }
/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */ /* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
...@@ -795,55 +690,50 @@ int invert_tuplepr(struct ip_conntrack_tuple *inverse, ...@@ -795,55 +690,50 @@ int invert_tuplepr(struct ip_conntrack_tuple *inverse,
ip_ct_find_proto(orig->dst.protonum)); ip_ct_find_proto(orig->dst.protonum));
} }
static inline int resent_expect(const struct ip_conntrack_expect *i,
const struct ip_conntrack_tuple *tuple,
const struct ip_conntrack_tuple *mask)
{
DEBUGP("resent_expect\n");
DEBUGP(" tuple: "); DUMP_TUPLE(&i->tuple);
DEBUGP("ct_tuple: "); DUMP_TUPLE(&i->ct_tuple);
DEBUGP("test tuple: "); DUMP_TUPLE(tuple);
return (((i->ct_tuple.dst.protonum == 0 && ip_ct_tuple_equal(&i->tuple, tuple))
|| (i->ct_tuple.dst.protonum && ip_ct_tuple_equal(&i->ct_tuple, tuple)))
&& ip_ct_tuple_equal(&i->mask, mask));
}
/* Would two expected things clash? */ /* Would two expected things clash? */
static inline int expect_clash(const struct ip_conntrack_expect *i, static inline int expect_clash(const struct ip_conntrack_expect *a,
const struct ip_conntrack_tuple *tuple, const struct ip_conntrack_expect *b)
const struct ip_conntrack_tuple *mask)
{ {
/* Part covered by intersection of masks must be unequal, /* Part covered by intersection of masks must be unequal,
otherwise they clash */ otherwise they clash */
struct ip_conntrack_tuple intersect_mask struct ip_conntrack_tuple intersect_mask
= { { i->mask.src.ip & mask->src.ip, = { { a->mask.src.ip & b->mask.src.ip,
{ i->mask.src.u.all & mask->src.u.all } }, { a->mask.src.u.all & b->mask.src.u.all } },
{ i->mask.dst.ip & mask->dst.ip, { a->mask.dst.ip & b->mask.dst.ip,
{ i->mask.dst.u.all & mask->dst.u.all }, { a->mask.dst.u.all & b->mask.dst.u.all },
i->mask.dst.protonum & mask->dst.protonum } }; a->mask.dst.protonum & b->mask.dst.protonum } };
return ip_ct_tuple_mask_cmp(&i->tuple, tuple, &intersect_mask); return ip_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
} }
inline void ip_conntrack_unexpect_related(struct ip_conntrack_expect *expect) static inline int expect_matches(const struct ip_conntrack_expect *a,
const struct ip_conntrack_expect *b)
{ {
WRITE_LOCK(&ip_conntrack_lock); return a->master == b->master
unexpect_related(expect); && ip_ct_tuple_equal(&a->tuple, &b->tuple)
WRITE_UNLOCK(&ip_conntrack_lock); && ip_ct_tuple_equal(&a->mask, &b->mask);
} }
static void expectation_timed_out(unsigned long ul_expect) /* Generally a bad idea to call this: could have matched already. */
void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp)
{ {
struct ip_conntrack_expect *expect = (void *) ul_expect; struct ip_conntrack_expect *i;
DEBUGP("expectation %p timed out\n", expect);
WRITE_LOCK(&ip_conntrack_lock); WRITE_LOCK(&ip_conntrack_lock);
__unexpect_related(expect); /* choose the the oldest expectation to evict */
list_for_each_entry_reverse(i, &ip_conntrack_expect_list, list) {
if (expect_matches(i, exp)
&& (!i->timeout.function || del_timer(&i->timeout))) {
unlink_expect(i);
WRITE_UNLOCK(&ip_conntrack_lock);
destroy_expect(i);
return;
}
}
WRITE_UNLOCK(&ip_conntrack_lock); WRITE_UNLOCK(&ip_conntrack_lock);
} }
struct ip_conntrack_expect * struct ip_conntrack_expect *ip_conntrack_expect_alloc(void)
ip_conntrack_expect_alloc(void)
{ {
struct ip_conntrack_expect *new; struct ip_conntrack_expect *new;
...@@ -852,135 +742,97 @@ ip_conntrack_expect_alloc(void) ...@@ -852,135 +742,97 @@ ip_conntrack_expect_alloc(void)
DEBUGP("expect_related: OOM allocating expect\n"); DEBUGP("expect_related: OOM allocating expect\n");
return NULL; return NULL;
} }
new->master = NULL;
return new;
}
/* tuple_cmp compares whole union, we have to initialized cleanly */ void ip_conntrack_expect_free(struct ip_conntrack_expect *expect)
memset(new, 0, sizeof(struct ip_conntrack_expect)); {
atomic_set(&new->use, 1); kmem_cache_free(ip_conntrack_expect_cachep, expect);
}
return new; static void ip_conntrack_expect_insert(struct ip_conntrack_expect *exp)
{
atomic_inc(&exp->master->ct_general.use);
exp->master->expecting++;
list_add(&exp->list, &ip_conntrack_expect_list);
if (exp->master->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;
add_timer(&exp->timeout);
} else
exp->timeout.function = NULL;
CONNTRACK_STAT_INC(expect_create);
} }
static void /* Race with expectations being used means we could have none to find; OK. */
ip_conntrack_expect_insert(struct ip_conntrack_expect *new, static void evict_oldest_expect(struct ip_conntrack *master)
struct ip_conntrack *related_to)
{ {
DEBUGP("new expectation %p of conntrack %p\n", new, related_to); struct ip_conntrack_expect *i;
new->expectant = related_to;
new->sibling = NULL; list_for_each_entry_reverse(i, &ip_conntrack_expect_list, list) {
if (i->master == master) {
/* add to expected list for this connection */ if (!i->timeout.function || del_timer(&i->timeout)) {
list_add_tail(&new->expected_list, &related_to->sibling_list); unlink_expect(i);
/* add to global list of expectations */ destroy_expect(i);
list_prepend(&ip_conntrack_expect_list, &new->list); }
/* add and start timer if required */ break;
if (related_to->helper->timeout) { }
init_timer(&new->timeout);
new->timeout.data = (unsigned long)new;
new->timeout.function = expectation_timed_out;
new->timeout.expires = jiffies +
related_to->helper->timeout * HZ;
add_timer(&new->timeout);
} }
related_to->expecting++;
} }
/* Add a related connection. */ static inline int refresh_timer(struct ip_conntrack_expect *i)
int ip_conntrack_expect_related(struct ip_conntrack_expect *expect,
struct ip_conntrack *related_to)
{ {
struct ip_conntrack_expect *old; if (!i->timeout.function)
int ret = 0; return 1;
WRITE_LOCK(&ip_conntrack_lock); if (!del_timer(&i->timeout))
/* Because of the write lock, no reader can walk the lists, return 0;
* so there is no need to use the tuple lock too */
i->timeout.expires = jiffies + i->master->helper->timeout*HZ;
add_timer(&i->timeout);
return 1;
}
int ip_conntrack_expect_related(struct ip_conntrack_expect *expect)
{
struct ip_conntrack_expect *i;
int ret;
DEBUGP("ip_conntrack_expect_related %p\n", related_to); DEBUGP("ip_conntrack_expect_related %p\n", related_to);
DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple);
DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); DEBUGP("mask: "); DUMP_TUPLE(&expect->mask);
old = LIST_FIND(&ip_conntrack_expect_list, resent_expect, WRITE_LOCK(&ip_conntrack_lock);
struct ip_conntrack_expect *, &expect->tuple, list_for_each_entry(i, &ip_conntrack_expect_list, list) {
&expect->mask); if (expect_matches(i, expect)) {
if (old && old->expectant == related_to) { /* Refresh timer: if it's dying, ignore.. */
/* Helper private data may contain offsets but no pointers if (refresh_timer(i)) {
pointing into the payload - otherwise we should have to copy ret = 0;
the data filled out by the helper over the old one */ /* We don't need the one they've given us. */
DEBUGP("expect_related: resent packet\n"); ip_conntrack_expect_free(expect);
if (related_to->helper->timeout) {
if (!del_timer(&old->timeout)) {
/* expectation is dying. Fall through */
goto out; goto out;
} else {
old->timeout.expires = jiffies +
related_to->helper->timeout * HZ;
add_timer(&old->timeout);
} }
} else if (expect_clash(i, expect)) {
ret = -EBUSY;
goto out;
} }
WRITE_UNLOCK(&ip_conntrack_lock);
ip_conntrack_expect_put(expect);
return -EEXIST;
} else if (related_to->helper->max_expected &&
related_to->expecting >= related_to->helper->max_expected) {
/* old == NULL */
if (!(related_to->helper->flags &
IP_CT_HELPER_F_REUSE_EXPECT)) {
WRITE_UNLOCK(&ip_conntrack_lock);
if (net_ratelimit())
printk(KERN_WARNING
"ip_conntrack: max number of expected "
"connections %i of %s reached for "
"%u.%u.%u.%u->%u.%u.%u.%u\n",
related_to->helper->max_expected,
related_to->helper->name,
NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip));
ip_conntrack_expect_put(expect);
return -EPERM;
}
DEBUGP("ip_conntrack: max number of expected "
"connections %i of %s reached for "
"%u.%u.%u.%u->%u.%u.%u.%u, reusing\n",
related_to->helper->max_expected,
related_to->helper->name,
NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip));
/* choose the the oldest expectation to evict */
list_for_each_entry(old, &related_to->sibling_list,
expected_list)
if (old->sibling == NULL)
break;
/* We cannot fail since related_to->expecting is the number
* of unconfirmed expectations */
IP_NF_ASSERT(old && old->sibling == NULL);
/* newnat14 does not reuse the real allocated memory
* structures but rather unexpects the old and
* allocates a new. unexpect_related will decrement
* related_to->expecting.
*/
unexpect_related(old);
ret = -EPERM;
} else if (LIST_FIND(&ip_conntrack_expect_list, expect_clash,
struct ip_conntrack_expect *, &expect->tuple,
&expect->mask)) {
WRITE_UNLOCK(&ip_conntrack_lock);
DEBUGP("expect_related: busy!\n");
ip_conntrack_expect_put(expect);
return -EBUSY;
} }
out: ip_conntrack_expect_insert(expect, related_to); /* Will be over limit? */
if (expect->master->helper->max_expected &&
expect->master->expecting >= expect->master->helper->max_expected)
evict_oldest_expect(expect->master);
ip_conntrack_expect_insert(expect);
ret = 0;
out:
WRITE_UNLOCK(&ip_conntrack_lock); WRITE_UNLOCK(&ip_conntrack_lock);
CONNTRACK_STAT_INC(expect_create);
return ret; return ret;
} }
...@@ -997,7 +849,7 @@ void ip_conntrack_alter_reply(struct ip_conntrack *conntrack, ...@@ -997,7 +849,7 @@ void ip_conntrack_alter_reply(struct ip_conntrack *conntrack,
DUMP_TUPLE(newreply); DUMP_TUPLE(newreply);
conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply;
if (!conntrack->master && list_empty(&conntrack->sibling_list)) if (!conntrack->master && conntrack->expecting == 0)
conntrack->helper = ip_ct_find_helper(newreply); conntrack->helper = ip_ct_find_helper(newreply);
WRITE_UNLOCK(&ip_conntrack_lock); WRITE_UNLOCK(&ip_conntrack_lock);
} }
...@@ -1014,23 +866,30 @@ int ip_conntrack_helper_register(struct ip_conntrack_helper *me) ...@@ -1014,23 +866,30 @@ int ip_conntrack_helper_register(struct ip_conntrack_helper *me)
static inline int unhelp(struct ip_conntrack_tuple_hash *i, static inline int unhelp(struct ip_conntrack_tuple_hash *i,
const struct ip_conntrack_helper *me) const struct ip_conntrack_helper *me)
{ {
if (i->ctrack->helper == me) { if (i->ctrack->helper == me)
/* Get rid of any expected. */
remove_expectations(i->ctrack, 0);
/* And *then* set helper to NULL */
i->ctrack->helper = NULL; i->ctrack->helper = NULL;
}
return 0; return 0;
} }
void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me) void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me)
{ {
unsigned int i; unsigned int i;
struct ip_conntrack_expect *exp, *tmp;
/* Need write lock here, to delete helper. */ /* Need write lock here, to delete helper. */
WRITE_LOCK(&ip_conntrack_lock); WRITE_LOCK(&ip_conntrack_lock);
LIST_DELETE(&helpers, me); LIST_DELETE(&helpers, me);
/* Get rid of expectations */
list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) {
if (exp->master->helper == me) {
if (!exp->timeout.function
|| del_timer(&exp->timeout)) {
unlink_expect(exp);
destroy_expect(exp);
}
}
}
/* Get rid of expecteds, set helpers to NULL. */ /* Get rid of expecteds, set helpers to NULL. */
LIST_FIND_W(&unconfirmed, unhelp, struct ip_conntrack_tuple_hash*, me); LIST_FIND_W(&unconfirmed, unhelp, struct ip_conntrack_tuple_hash*, me);
for (i = 0; i < ip_conntrack_htable_size; i++) for (i = 0; i < ip_conntrack_htable_size; i++)
......
...@@ -40,7 +40,6 @@ static int loose; ...@@ -40,7 +40,6 @@ static int loose;
module_param(loose, int, 0600); module_param(loose, int, 0600);
unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb, unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
enum ip_ct_ftp_type type, enum ip_ct_ftp_type type,
unsigned int matchoff, unsigned int matchoff,
...@@ -407,7 +406,7 @@ static int help(struct sk_buff **pskb, ...@@ -407,7 +406,7 @@ static int help(struct sk_buff **pskb,
networks, or the packet filter itself). */ networks, or the packet filter itself). */
if (!loose) { if (!loose) {
ret = NF_ACCEPT; ret = NF_ACCEPT;
ip_conntrack_expect_put(exp); ip_conntrack_expect_free(exp);
goto out_update_nl; goto out_update_nl;
} }
exp->tuple.dst.ip = htonl((array[0] << 24) | (array[1] << 16) exp->tuple.dst.ip = htonl((array[0] << 24) | (array[1] << 16)
...@@ -423,17 +422,19 @@ static int help(struct sk_buff **pskb, ...@@ -423,17 +422,19 @@ static int help(struct sk_buff **pskb,
{ 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
exp->expectfn = NULL; exp->expectfn = NULL;
exp->master = ct;
/* Now, NAT might want to mangle the packet, and register the /* Now, NAT might want to mangle the packet, and register the
* (possibly changed) expectation itself. */ * (possibly changed) expectation itself. */
if (ip_nat_ftp_hook) if (ip_nat_ftp_hook)
ret = ip_nat_ftp_hook(pskb, ct, ctinfo, search[i].ftptype, ret = ip_nat_ftp_hook(pskb, ctinfo, search[i].ftptype,
matchoff, matchlen, exp, &seq); matchoff, matchlen, exp, &seq);
else { else {
/* Can't expect this? Best to drop packet now. */ /* Can't expect this? Best to drop packet now. */
if (ip_conntrack_expect_related(exp, ct) != 0) if (ip_conntrack_expect_related(exp) != 0) {
ip_conntrack_expect_free(exp);
ret = NF_DROP; ret = NF_DROP;
else } else
ret = NF_ACCEPT; ret = NF_ACCEPT;
} }
...@@ -476,7 +477,6 @@ static int __init init(void) ...@@ -476,7 +477,6 @@ static int __init init(void)
ftp[i].mask.dst.protonum = 0xFFFF; ftp[i].mask.dst.protonum = 0xFFFF;
ftp[i].max_expected = 1; ftp[i].max_expected = 1;
ftp[i].timeout = 0; ftp[i].timeout = 0;
ftp[i].flags = IP_CT_HELPER_F_REUSE_EXPECT;
ftp[i].me = ip_conntrack_ftp; ftp[i].me = ip_conntrack_ftp;
ftp[i].help = help; ftp[i].help = help;
......
...@@ -44,7 +44,6 @@ static char irc_buffer[65536]; ...@@ -44,7 +44,6 @@ static char irc_buffer[65536];
static DECLARE_LOCK(irc_buffer_lock); static DECLARE_LOCK(irc_buffer_lock);
unsigned int (*ip_nat_irc_hook)(struct sk_buff **pskb, unsigned int (*ip_nat_irc_hook)(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int matchoff, unsigned int matchoff,
unsigned int matchlen, unsigned int matchlen,
...@@ -220,13 +219,16 @@ static int help(struct sk_buff **pskb, ...@@ -220,13 +219,16 @@ static int help(struct sk_buff **pskb,
{ { 0, { 0 } }, { { 0, { 0 } },
{ 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
exp->expectfn = NULL; exp->expectfn = NULL;
exp->master = ct;
if (ip_nat_irc_hook) if (ip_nat_irc_hook)
ret = ip_nat_irc_hook(pskb, ct, ctinfo, ret = ip_nat_irc_hook(pskb, ctinfo,
addr_beg_p - ib_ptr, addr_beg_p - ib_ptr,
addr_end_p - addr_beg_p, addr_end_p - addr_beg_p,
exp); exp);
else if (ip_conntrack_expect_related(exp, ct) != 0) else if (ip_conntrack_expect_related(exp) != 0) {
ip_conntrack_expect_free(exp);
ret = NF_DROP; ret = NF_DROP;
}
goto out; goto out;
} /* for .. NUM_DCCPROTO */ } /* for .. NUM_DCCPROTO */
} /* while data < ... */ } /* while data < ... */
......
...@@ -200,7 +200,6 @@ static void *exp_seq_start(struct seq_file *s, loff_t *pos) ...@@ -200,7 +200,6 @@ static void *exp_seq_start(struct seq_file *s, loff_t *pos)
/* strange seq_file api calls stop even if we fail, /* strange seq_file api calls stop even if we fail,
* thus we need to grab lock since stop unlocks */ * thus we need to grab lock since stop unlocks */
READ_LOCK(&ip_conntrack_lock); READ_LOCK(&ip_conntrack_lock);
READ_LOCK(&ip_conntrack_expect_tuple_lock);
if (list_empty(e)) if (list_empty(e))
return NULL; return NULL;
...@@ -227,7 +226,6 @@ static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos) ...@@ -227,7 +226,6 @@ static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos)
static void exp_seq_stop(struct seq_file *s, void *v) static void exp_seq_stop(struct seq_file *s, void *v)
{ {
READ_UNLOCK(&ip_conntrack_expect_tuple_lock);
READ_UNLOCK(&ip_conntrack_lock); READ_UNLOCK(&ip_conntrack_lock);
} }
...@@ -235,14 +233,13 @@ static int exp_seq_show(struct seq_file *s, void *v) ...@@ -235,14 +233,13 @@ static int exp_seq_show(struct seq_file *s, void *v)
{ {
struct ip_conntrack_expect *expect = v; struct ip_conntrack_expect *expect = v;
if (expect->expectant->helper->timeout) if (expect->timeout.function)
seq_printf(s, "%lu ", timer_pending(&expect->timeout) seq_printf(s, "%lu ", timer_pending(&expect->timeout)
? (expect->timeout.expires - jiffies)/HZ : 0); ? (expect->timeout.expires - jiffies)/HZ : 0);
else else
seq_printf(s, "- "); seq_printf(s, "- ");
seq_printf(s, "use=%u proto=%u ", atomic_read(&expect->use), seq_printf(s, "proto=%u ", expect->tuple.dst.protonum);
expect->tuple.dst.protonum);
print_tuple(s, &expect->tuple, print_tuple(s, &expect->tuple,
ip_ct_find_proto(expect->tuple.dst.protonum)); ip_ct_find_proto(expect->tuple.dst.protonum));
...@@ -910,14 +907,12 @@ EXPORT_SYMBOL(ip_ct_protos); ...@@ -910,14 +907,12 @@ EXPORT_SYMBOL(ip_ct_protos);
EXPORT_SYMBOL(ip_ct_find_proto); EXPORT_SYMBOL(ip_ct_find_proto);
EXPORT_SYMBOL(ip_ct_find_helper); EXPORT_SYMBOL(ip_ct_find_helper);
EXPORT_SYMBOL(ip_conntrack_expect_alloc); EXPORT_SYMBOL(ip_conntrack_expect_alloc);
EXPORT_SYMBOL(ip_conntrack_expect_free);
EXPORT_SYMBOL(ip_conntrack_expect_related); EXPORT_SYMBOL(ip_conntrack_expect_related);
EXPORT_SYMBOL(ip_conntrack_unexpect_related); EXPORT_SYMBOL(ip_conntrack_unexpect_related);
EXPORT_SYMBOL_GPL(ip_conntrack_expect_find_get);
EXPORT_SYMBOL_GPL(ip_conntrack_expect_put);
EXPORT_SYMBOL(ip_conntrack_tuple_taken); EXPORT_SYMBOL(ip_conntrack_tuple_taken);
EXPORT_SYMBOL(ip_ct_gather_frags); EXPORT_SYMBOL(ip_ct_gather_frags);
EXPORT_SYMBOL(ip_conntrack_htable_size); EXPORT_SYMBOL(ip_conntrack_htable_size);
EXPORT_SYMBOL(ip_conntrack_expect_list);
EXPORT_SYMBOL(ip_conntrack_lock); EXPORT_SYMBOL(ip_conntrack_lock);
EXPORT_SYMBOL(ip_conntrack_hash); EXPORT_SYMBOL(ip_conntrack_hash);
EXPORT_SYMBOL(ip_conntrack_untracked); EXPORT_SYMBOL(ip_conntrack_untracked);
......
...@@ -39,7 +39,6 @@ MODULE_PARM_DESC(ports, "port numbers of tftp servers"); ...@@ -39,7 +39,6 @@ MODULE_PARM_DESC(ports, "port numbers of tftp servers");
#endif #endif
unsigned int (*ip_nat_tftp_hook)(struct sk_buff **pskb, unsigned int (*ip_nat_tftp_hook)(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
struct ip_conntrack_expect *exp); struct ip_conntrack_expect *exp);
EXPORT_SYMBOL_GPL(ip_nat_tftp_hook); EXPORT_SYMBOL_GPL(ip_nat_tftp_hook);
...@@ -76,14 +75,17 @@ static int tftp_help(struct sk_buff **pskb, ...@@ -76,14 +75,17 @@ static int tftp_help(struct sk_buff **pskb,
exp->mask.dst.u.udp.port = 0xffff; exp->mask.dst.u.udp.port = 0xffff;
exp->mask.dst.protonum = 0xffff; exp->mask.dst.protonum = 0xffff;
exp->expectfn = NULL; exp->expectfn = NULL;
exp->master = ct;
DEBUGP("expect: "); DEBUGP("expect: ");
DUMP_TUPLE(&exp->tuple); DUMP_TUPLE(&exp->tuple);
DUMP_TUPLE(&exp->mask); DUMP_TUPLE(&exp->mask);
if (ip_nat_tftp_hook) if (ip_nat_tftp_hook)
ret = ip_nat_tftp_hook(pskb, ct, ctinfo, exp); ret = ip_nat_tftp_hook(pskb, ctinfo, exp);
else if (ip_conntrack_expect_related(exp, ct) != 0) else if (ip_conntrack_expect_related(exp) != 0) {
ip_conntrack_expect_free(exp);
ret = NF_DROP; ret = NF_DROP;
}
break; break;
case TFTP_OPCODE_DATA: case TFTP_OPCODE_DATA:
case TFTP_OPCODE_ACK: case TFTP_OPCODE_ACK:
......
...@@ -32,7 +32,6 @@ MODULE_DESCRIPTION("Amanda NAT helper"); ...@@ -32,7 +32,6 @@ MODULE_DESCRIPTION("Amanda NAT helper");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static unsigned int help(struct sk_buff **pskb, static unsigned int help(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int matchoff, unsigned int matchoff,
unsigned int matchlen, unsigned int matchlen,
...@@ -52,22 +51,18 @@ static unsigned int help(struct sk_buff **pskb, ...@@ -52,22 +51,18 @@ static unsigned int help(struct sk_buff **pskb,
/* Try to get same port: if not, try to change it. */ /* Try to get same port: if not, try to change it. */
for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
int err;
exp->tuple.dst.u.tcp.port = htons(port); exp->tuple.dst.u.tcp.port = htons(port);
atomic_inc(&exp->use); if (ip_conntrack_expect_related(exp) == 0)
err = ip_conntrack_expect_related(exp, ct);
/* Success, or retransmit. */
if (!err || err == -EEXIST)
break; break;
} }
if (port == 0) { if (port == 0) {
ip_conntrack_expect_put(exp); ip_conntrack_expect_free(exp);
return NF_DROP; return NF_DROP;
} }
sprintf(buffer, "%u", port); sprintf(buffer, "%u", port);
ret = ip_nat_mangle_udp_packet(pskb, ct, ctinfo, ret = ip_nat_mangle_udp_packet(pskb, exp->master, ctinfo,
matchoff, matchlen, matchoff, matchlen,
buffer, strlen(buffer)); buffer, strlen(buffer));
if (ret != NF_ACCEPT) if (ret != NF_ACCEPT)
......
...@@ -113,7 +113,6 @@ static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t, ...@@ -113,7 +113,6 @@ static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
/* So, this packet has hit the connection tracking matching code. /* So, this packet has hit the connection tracking matching code.
Mangle it, and change the expectation to match the new version. */ Mangle it, and change the expectation to match the new version. */
static unsigned int ip_nat_ftp(struct sk_buff **pskb, static unsigned int ip_nat_ftp(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
enum ip_ct_ftp_type type, enum ip_ct_ftp_type type,
unsigned int matchoff, unsigned int matchoff,
...@@ -124,6 +123,7 @@ static unsigned int ip_nat_ftp(struct sk_buff **pskb, ...@@ -124,6 +123,7 @@ static unsigned int ip_nat_ftp(struct sk_buff **pskb,
u_int32_t newip; u_int32_t newip;
u_int16_t port; u_int16_t port;
int dir = CTINFO2DIR(ctinfo); int dir = CTINFO2DIR(ctinfo);
struct ip_conntrack *ct = exp->master;
DEBUGP("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen); DEBUGP("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen);
...@@ -138,17 +138,13 @@ static unsigned int ip_nat_ftp(struct sk_buff **pskb, ...@@ -138,17 +138,13 @@ static unsigned int ip_nat_ftp(struct sk_buff **pskb,
/* Try to get same port: if not, try to change it. */ /* Try to get same port: if not, try to change it. */
for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
int err;
exp->tuple.dst.u.tcp.port = htons(port); exp->tuple.dst.u.tcp.port = htons(port);
atomic_inc(&exp->use); if (ip_conntrack_expect_related(exp) == 0)
err = ip_conntrack_expect_related(exp, ct);
/* Success, or retransmit. */
if (!err || err == -EEXIST)
break; break;
} }
if (port == 0) { if (port == 0) {
ip_conntrack_expect_put(exp); ip_conntrack_expect_free(exp);
return NF_DROP; return NF_DROP;
} }
......
...@@ -438,12 +438,13 @@ static void ip_nat_copy_manip(struct ip_nat_info *master, ...@@ -438,12 +438,13 @@ static void ip_nat_copy_manip(struct ip_nat_info *master,
/* Setup NAT on this expected conntrack so it follows master. */ /* Setup NAT on this expected conntrack so it follows master. */
/* If we fail to get a free NAT slot, we'll get dropped on confirm */ /* If we fail to get a free NAT slot, we'll get dropped on confirm */
void ip_nat_follow_master(struct ip_conntrack *ct) void ip_nat_follow_master(struct ip_conntrack *ct,
struct ip_conntrack_expect *this)
{ {
struct ip_nat_info *master = &ct->master->expectant->nat.info; struct ip_nat_info *master = &ct->master->nat.info;
/* This must be a fresh one. */ /* This must be a fresh one. */
BUG_ON(ct->nat.info.initialized); BUG_ON(ct->nat.info.initialized);
ip_nat_copy_manip(master, ct->master, ct); ip_nat_copy_manip(master, this, ct);
} }
...@@ -37,7 +37,6 @@ MODULE_LICENSE("GPL"); ...@@ -37,7 +37,6 @@ MODULE_LICENSE("GPL");
/* FIXME: Time out? --RR */ /* FIXME: Time out? --RR */
static unsigned int help(struct sk_buff **pskb, static unsigned int help(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int matchoff, unsigned int matchoff,
unsigned int matchlen, unsigned int matchlen,
...@@ -63,18 +62,13 @@ static unsigned int help(struct sk_buff **pskb, ...@@ -63,18 +62,13 @@ static unsigned int help(struct sk_buff **pskb,
/* Try to get same port: if not, try to change it. */ /* Try to get same port: if not, try to change it. */
for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
int err;
exp->tuple.dst.u.tcp.port = htons(port); exp->tuple.dst.u.tcp.port = htons(port);
atomic_inc(&exp->use); if (ip_conntrack_expect_related(exp) == 0)
err = ip_conntrack_expect_related(exp, ct);
/* Success, or retransmit. */
if (!err || err == -EEXIST)
break; break;
} }
if (port == 0) { if (port == 0) {
ip_conntrack_expect_put(exp); ip_conntrack_expect_free(exp);
return NF_DROP; return NF_DROP;
} }
...@@ -95,7 +89,7 @@ static unsigned int help(struct sk_buff **pskb, ...@@ -95,7 +89,7 @@ static unsigned int help(struct sk_buff **pskb,
DEBUGP("ip_nat_irc: Inserting '%s' == %u.%u.%u.%u, port %u\n", DEBUGP("ip_nat_irc: Inserting '%s' == %u.%u.%u.%u, port %u\n",
buffer, NIPQUAD(exp->tuple.src.ip), port); buffer, NIPQUAD(exp->tuple.src.ip), port);
ret = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, ret = ip_nat_mangle_tcp_packet(pskb, exp->master, ctinfo,
matchoff, matchlen, buffer, matchoff, matchlen, buffer,
strlen(buffer)); strlen(buffer));
if (ret != NF_ACCEPT) if (ret != NF_ACCEPT)
......
...@@ -39,15 +39,14 @@ MODULE_DESCRIPTION("tftp NAT helper"); ...@@ -39,15 +39,14 @@ MODULE_DESCRIPTION("tftp NAT helper");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static unsigned int help(struct sk_buff **pskb, static unsigned int help(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
struct ip_conntrack_expect *exp) struct ip_conntrack_expect *exp)
{ {
exp->saved_proto.udp.port = exp->tuple.dst.u.tcp.port; exp->saved_proto.udp.port = exp->tuple.dst.u.tcp.port;
exp->dir = IP_CT_DIR_REPLY; exp->dir = IP_CT_DIR_REPLY;
exp->expectfn = ip_nat_follow_master; exp->expectfn = ip_nat_follow_master;
if (ip_conntrack_expect_related(exp, ct) != 0) { if (ip_conntrack_expect_related(exp) != 0) {
ip_conntrack_expect_put(exp); ip_conntrack_expect_free(exp);
return NF_DROP; return NF_DROP;
} }
return NF_ACCEPT; return NF_ACCEPT;
......
...@@ -38,7 +38,6 @@ match(const struct sk_buff *skb, ...@@ -38,7 +38,6 @@ match(const struct sk_buff *skb,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_helper_info *info = matchinfo; const struct ipt_helper_info *info = matchinfo;
struct ip_conntrack_expect *exp;
struct ip_conntrack *ct; struct ip_conntrack *ct;
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
int ret = info->invert; int ret = info->invert;
...@@ -54,28 +53,21 @@ match(const struct sk_buff *skb, ...@@ -54,28 +53,21 @@ match(const struct sk_buff *skb,
return ret; return ret;
} }
exp = ct->master;
READ_LOCK(&ip_conntrack_lock); READ_LOCK(&ip_conntrack_lock);
if (!exp->expectant) { if (!ct->master->helper) {
DEBUGP("ipt_helper: expectation %p without expectant !?!\n",
exp);
goto out_unlock;
}
if (!exp->expectant->helper) {
DEBUGP("ipt_helper: master ct %p has no helper\n", DEBUGP("ipt_helper: master ct %p has no helper\n",
exp->expectant); exp->expectant);
goto out_unlock; goto out_unlock;
} }
DEBUGP("master's name = %s , info->name = %s\n", DEBUGP("master's name = %s , info->name = %s\n",
exp->expectant->helper->name, info->name); ct->master->helper->name, info->name);
if (info->name[0] == '\0') if (info->name[0] == '\0')
ret ^= 1; ret ^= 1;
else else
ret ^= !strncmp(exp->expectant->helper->name, info->name, ret ^= !strncmp(ct->master->helper->name, info->name,
strlen(exp->expectant->helper->name)); strlen(ct->master->helper->name));
out_unlock: out_unlock:
READ_UNLOCK(&ip_conntrack_lock); READ_UNLOCK(&ip_conntrack_lock);
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment