Commit 039b40ee authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso

netfilter: nf_queue: only call synchronize_net twice if nf_queue is active

nf_unregister_net_hook(s) can avoid a second call to synchronize_net,
provided there is no nfqueue active in that net namespace (which is
the common case).

This also gets rid of the extra arg to nf_queue_nf_hook_drop(), normally
this gets called during netns cleanup so no packets should be queued.

For the rare case of base chain being unregistered or module removal
while nfqueue is in use the extra hiccup due to the packet drops isn't
a big deal.
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent c83fa196
...@@ -24,8 +24,7 @@ struct nf_queue_entry { ...@@ -24,8 +24,7 @@ struct nf_queue_entry {
struct nf_queue_handler { struct nf_queue_handler {
int (*outfn)(struct nf_queue_entry *entry, int (*outfn)(struct nf_queue_entry *entry,
unsigned int queuenum); unsigned int queuenum);
void (*nf_hook_drop)(struct net *net, unsigned int (*nf_hook_drop)(struct net *net);
const struct nf_hook_entry *hooks);
}; };
void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh); void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh);
......
...@@ -162,13 +162,16 @@ __nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) ...@@ -162,13 +162,16 @@ __nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
{ {
struct nf_hook_entry *p = __nf_unregister_net_hook(net, reg); struct nf_hook_entry *p = __nf_unregister_net_hook(net, reg);
unsigned int nfq;
if (!p) if (!p)
return; return;
synchronize_net(); synchronize_net();
nf_queue_nf_hook_drop(net, p);
/* other cpu might still process nfqueue verdict that used reg */ /* other cpu might still process nfqueue verdict that used reg */
nfq = nf_queue_nf_hook_drop(net);
if (nfq)
synchronize_net(); synchronize_net();
kfree(p); kfree(p);
} }
...@@ -198,7 +201,7 @@ void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, ...@@ -198,7 +201,7 @@ void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg,
unsigned int hookcount) unsigned int hookcount)
{ {
struct nf_hook_entry *to_free[16]; struct nf_hook_entry *to_free[16];
unsigned int i, n; unsigned int i, n, nfq;
do { do {
n = min_t(unsigned int, hookcount, ARRAY_SIZE(to_free)); n = min_t(unsigned int, hookcount, ARRAY_SIZE(to_free));
...@@ -208,11 +211,11 @@ void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, ...@@ -208,11 +211,11 @@ void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg,
synchronize_net(); synchronize_net();
for (i = 0; i < n; i++) { /* need 2nd synchronize_net() if nfqueue is used, skb
if (to_free[i]) * can get reinjected right before nf_queue_hook_drop()
nf_queue_nf_hook_drop(net, to_free[i]); */
} nfq = nf_queue_nf_hook_drop(net);
if (nfq)
synchronize_net(); synchronize_net();
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
/* nf_queue.c */ /* nf_queue.c */
int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
struct nf_hook_entry **entryp, unsigned int verdict); struct nf_hook_entry **entryp, unsigned int verdict);
void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_entry *entry); unsigned int nf_queue_nf_hook_drop(struct net *net);
int __init netfilter_queue_init(void); int __init netfilter_queue_init(void);
/* nf_log.c */ /* nf_log.c */
......
...@@ -96,15 +96,18 @@ void nf_queue_entry_get_refs(struct nf_queue_entry *entry) ...@@ -96,15 +96,18 @@ void nf_queue_entry_get_refs(struct nf_queue_entry *entry)
} }
EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs); EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_entry *entry) unsigned int nf_queue_nf_hook_drop(struct net *net)
{ {
const struct nf_queue_handler *qh; const struct nf_queue_handler *qh;
unsigned int count = 0;
rcu_read_lock(); rcu_read_lock();
qh = rcu_dereference(net->nf.queue_handler); qh = rcu_dereference(net->nf.queue_handler);
if (qh) if (qh)
qh->nf_hook_drop(net, entry); count = qh->nf_hook_drop(net);
rcu_read_unlock(); rcu_read_unlock();
return count;
} }
static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
......
...@@ -922,16 +922,10 @@ static struct notifier_block nfqnl_dev_notifier = { ...@@ -922,16 +922,10 @@ static struct notifier_block nfqnl_dev_notifier = {
.notifier_call = nfqnl_rcv_dev_event, .notifier_call = nfqnl_rcv_dev_event,
}; };
static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long entry_ptr) static unsigned int nfqnl_nf_hook_drop(struct net *net)
{
return rcu_access_pointer(entry->hook) ==
(struct nf_hook_entry *)entry_ptr;
}
static void nfqnl_nf_hook_drop(struct net *net,
const struct nf_hook_entry *hook)
{ {
struct nfnl_queue_net *q = nfnl_queue_pernet(net); struct nfnl_queue_net *q = nfnl_queue_pernet(net);
unsigned int instances = 0;
int i; int i;
rcu_read_lock(); rcu_read_lock();
...@@ -939,10 +933,14 @@ static void nfqnl_nf_hook_drop(struct net *net, ...@@ -939,10 +933,14 @@ static void nfqnl_nf_hook_drop(struct net *net,
struct nfqnl_instance *inst; struct nfqnl_instance *inst;
struct hlist_head *head = &q->instance_table[i]; struct hlist_head *head = &q->instance_table[i];
hlist_for_each_entry_rcu(inst, head, hlist) hlist_for_each_entry_rcu(inst, head, hlist) {
nfqnl_flush(inst, nf_hook_cmp, (unsigned long)hook); nfqnl_flush(inst, NULL, 0);
instances++;
}
} }
rcu_read_unlock(); rcu_read_unlock();
return instances;
} }
static int static int
......
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