diff options
author | Evgeniy Polyakov <johnpol@2ka.mipt.ru> | 2007-11-20 07:27:35 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-11-20 07:27:35 -0500 |
commit | 1f305323ff5b9ddc1a4346d36072bcdb58f3f68a (patch) | |
tree | aa4497ece78e8fe53113d6d39bca8a2159d88c7d | |
parent | 0a06ea87185531705e4417e3a051f81b64f210c1 (diff) |
[NETFILTER]: Fix kernel panic with REDIRECT target.
When connection tracking entry (nf_conn) is about to copy itself it can
have some of its extension users (like nat) as being already freed and
thus not required to be copied.
Actually looking at this function I suspect it was copied from
nf_nat_setup_info() and thus bug was introduced.
Report and testing from David <david@unsolicited.net>.
[ Patrick McHardy states:
I now understand whats happening:
- new connection is allocated without helper
- connection is REDIRECTed to localhost
- nf_nat_setup_info adds NAT extension, but doesn't initialize it yet
- nf_conntrack_alter_reply performs a helper lookup based on the
new tuple, finds the SIP helper and allocates a helper extension,
causing reallocation because of too little space
- nf_nat_move_storage is called with the uninitialized nat extension
So your fix is entirely correct, thanks a lot :) ]
Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Acked-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv4/netfilter/nf_nat_core.c | 5 |
1 files changed, 1 insertions, 4 deletions
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index 70e7997ea284..86b465b176ba 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c | |||
@@ -607,13 +607,10 @@ static void nf_nat_move_storage(struct nf_conn *conntrack, void *old) | |||
607 | struct nf_conn_nat *new_nat = nf_ct_ext_find(conntrack, NF_CT_EXT_NAT); | 607 | struct nf_conn_nat *new_nat = nf_ct_ext_find(conntrack, NF_CT_EXT_NAT); |
608 | struct nf_conn_nat *old_nat = (struct nf_conn_nat *)old; | 608 | struct nf_conn_nat *old_nat = (struct nf_conn_nat *)old; |
609 | struct nf_conn *ct = old_nat->ct; | 609 | struct nf_conn *ct = old_nat->ct; |
610 | unsigned int srchash; | ||
611 | 610 | ||
612 | if (!(ct->status & IPS_NAT_DONE_MASK)) | 611 | if (!ct || !(ct->status & IPS_NAT_DONE_MASK)) |
613 | return; | 612 | return; |
614 | 613 | ||
615 | srchash = hash_by_src(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); | ||
616 | |||
617 | write_lock_bh(&nf_nat_lock); | 614 | write_lock_bh(&nf_nat_lock); |
618 | hlist_replace_rcu(&old_nat->bysource, &new_nat->bysource); | 615 | hlist_replace_rcu(&old_nat->bysource, &new_nat->bysource); |
619 | new_nat->ct = ct; | 616 | new_nat->ct = ct; |