From 06cdb6349c1f3fd439398dbc04ce4c696f0a41ab Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 18 Jan 2011 15:28:38 +0100 Subject: netfilter: nfnetlink_queue: do not free skb on error Move free responsibility from nf_queue to caller. This enables more flexible error handling; we can now accept the skb instead of freeing it. Signed-off-by: Florian Westphal Signed-off-by: Patrick McHardy --- net/netfilter/core.c | 7 +++++-- net/netfilter/nf_queue.c | 17 ++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'net/netfilter') diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 91d66d2f8cd9..0c5b796ef527 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -181,8 +181,11 @@ next_hook: } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { ret = nf_queue(skb, elem, pf, hook, indev, outdev, okfn, verdict >> NF_VERDICT_BITS); - if (ret == -ECANCELED) - goto next_hook; + if (ret < 0) { + if (ret == -ECANCELED) + goto next_hook; + kfree_skb(skb); + } ret = 0; } rcu_read_unlock(); diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index ad25c7e726bc..5c4b730a2e68 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -163,9 +163,8 @@ static int __nf_queue(struct sk_buff *skb, /* If it's going away, ignore hook. */ if (!try_module_get(entry->elem->owner)) { - rcu_read_unlock(); - kfree(entry); - return -ECANCELED; + status = -ECANCELED; + goto err_unlock; } /* Bump dev refs so they don't vanish while packet is out */ if (indev) @@ -198,7 +197,6 @@ static int __nf_queue(struct sk_buff *skb, err_unlock: rcu_read_unlock(); err: - kfree_skb(skb); kfree(entry); return status; } @@ -229,7 +227,6 @@ int nf_queue(struct sk_buff *skb, } segs = skb_gso_segment(skb, 0); - kfree_skb(skb); /* Does not use PTR_ERR to limit the number of error codes that can be * returned by nf_queue. For instance, callers rely on -ECANCELED to mean * 'ignore this hook'. @@ -253,8 +250,11 @@ int nf_queue(struct sk_buff *skb, segs = nskb; } while (segs); + /* also free orig skb if only some segments were queued */ if (unlikely(err && queued)) err = 0; + if (err == 0) + kfree_skb(skb); return err; } @@ -300,8 +300,11 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) err = __nf_queue(skb, elem, entry->pf, entry->hook, entry->indev, entry->outdev, entry->okfn, verdict >> NF_VERDICT_BITS); - if (err == -ECANCELED) - goto next_hook; + if (err < 0) { + if (err == -ECANCELED) + goto next_hook; + kfree_skb(skb); + } break; case NF_STOLEN: default: -- cgit v1.2.3-59-g8ed1b