Skip to content

Commit fb82865

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nf_tables: make destruction work queue pernet
The call to flush_work before tearing down a table from the netlink notifier was supposed to make sure that all earlier updates (e.g. rule add) that might reference that table have been processed. Unfortunately, flush_work() waits for the last queued instance. This could be an instance that is different from the one that we must wait for. This is because transactions are protected with a pernet mutex, but the work item is global, so holding the transaction mutex doesn't prevent another netns from queueing more work. Make the work item pernet so that flush_work() will wait for all transactions queued from this netns. A welcome side effect is that we no longer need to wait for transaction objects from foreign netns. The gc work queue is still global. This seems to be ok because nft_set structures are reference counted and each container structure owns a reference on the net namespace. The destroy_list is still protected by a global spinlock rather than pernet one but the hold time is very short anyway. v2: call cancel_work_sync before reaping the remaining tables (Pablo). Fixes: 9f6958b ("netfilter: nf_tables: unconditionally flush pending work before notifier") Reported-by: [email protected] Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent df08c94 commit fb82865

File tree

3 files changed

+21
-15
lines changed

3 files changed

+21
-15
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1891,7 +1891,7 @@ void nft_chain_filter_fini(void);
18911891
void __init nft_chain_route_init(void);
18921892
void nft_chain_route_fini(void);
18931893

1894-
void nf_tables_trans_destroy_flush_work(void);
1894+
void nf_tables_trans_destroy_flush_work(struct net *net);
18951895

18961896
int nf_msecs_to_jiffies64(const struct nlattr *nla, u64 *result);
18971897
__be64 nf_jiffies64_to_msecs(u64 input);
@@ -1905,6 +1905,7 @@ static inline int nft_request_module(struct net *net, const char *fmt, ...) { re
19051905
struct nftables_pernet {
19061906
struct list_head tables;
19071907
struct list_head commit_list;
1908+
struct list_head destroy_list;
19081909
struct list_head commit_set_list;
19091910
struct list_head binding_list;
19101911
struct list_head module_list;
@@ -1915,6 +1916,7 @@ struct nftables_pernet {
19151916
unsigned int base_seq;
19161917
unsigned int gc_seq;
19171918
u8 validate_state;
1919+
struct work_struct destroy_work;
19181920
};
19191921

19201922
extern unsigned int nf_tables_net_id;

net/netfilter/nf_tables_api.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ unsigned int nf_tables_net_id __read_mostly;
3434
static LIST_HEAD(nf_tables_expressions);
3535
static LIST_HEAD(nf_tables_objects);
3636
static LIST_HEAD(nf_tables_flowtables);
37-
static LIST_HEAD(nf_tables_destroy_list);
3837
static LIST_HEAD(nf_tables_gc_list);
3938
static DEFINE_SPINLOCK(nf_tables_destroy_list_lock);
4039
static DEFINE_SPINLOCK(nf_tables_gc_list_lock);
@@ -125,7 +124,6 @@ static void nft_validate_state_update(struct nft_table *table, u8 new_validate_s
125124
table->validate_state = new_validate_state;
126125
}
127126
static void nf_tables_trans_destroy_work(struct work_struct *w);
128-
static DECLARE_WORK(trans_destroy_work, nf_tables_trans_destroy_work);
129127

130128
static void nft_trans_gc_work(struct work_struct *work);
131129
static DECLARE_WORK(trans_gc_work, nft_trans_gc_work);
@@ -10006,11 +10004,12 @@ static void nft_commit_release(struct nft_trans *trans)
1000610004

1000710005
static void nf_tables_trans_destroy_work(struct work_struct *w)
1000810006
{
10007+
struct nftables_pernet *nft_net = container_of(w, struct nftables_pernet, destroy_work);
1000910008
struct nft_trans *trans, *next;
1001010009
LIST_HEAD(head);
1001110010

1001210011
spin_lock(&nf_tables_destroy_list_lock);
10013-
list_splice_init(&nf_tables_destroy_list, &head);
10012+
list_splice_init(&nft_net->destroy_list, &head);
1001410013
spin_unlock(&nf_tables_destroy_list_lock);
1001510014

1001610015
if (list_empty(&head))
@@ -10024,9 +10023,11 @@ static void nf_tables_trans_destroy_work(struct work_struct *w)
1002410023
}
1002510024
}
1002610025

10027-
void nf_tables_trans_destroy_flush_work(void)
10026+
void nf_tables_trans_destroy_flush_work(struct net *net)
1002810027
{
10029-
flush_work(&trans_destroy_work);
10028+
struct nftables_pernet *nft_net = nft_pernet(net);
10029+
10030+
flush_work(&nft_net->destroy_work);
1003010031
}
1003110032
EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work);
1003210033

@@ -10484,11 +10485,11 @@ static void nf_tables_commit_release(struct net *net)
1048410485

1048510486
trans->put_net = true;
1048610487
spin_lock(&nf_tables_destroy_list_lock);
10487-
list_splice_tail_init(&nft_net->commit_list, &nf_tables_destroy_list);
10488+
list_splice_tail_init(&nft_net->commit_list, &nft_net->destroy_list);
1048810489
spin_unlock(&nf_tables_destroy_list_lock);
1048910490

1049010491
nf_tables_module_autoload_cleanup(net);
10491-
schedule_work(&trans_destroy_work);
10492+
schedule_work(&nft_net->destroy_work);
1049210493

1049310494
mutex_unlock(&nft_net->commit_mutex);
1049410495
}
@@ -11853,7 +11854,7 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event,
1185311854

1185411855
gc_seq = nft_gc_seq_begin(nft_net);
1185511856

11856-
nf_tables_trans_destroy_flush_work();
11857+
nf_tables_trans_destroy_flush_work(net);
1185711858
again:
1185811859
list_for_each_entry(table, &nft_net->tables, list) {
1185911860
if (nft_table_has_owner(table) &&
@@ -11895,6 +11896,7 @@ static int __net_init nf_tables_init_net(struct net *net)
1189511896

1189611897
INIT_LIST_HEAD(&nft_net->tables);
1189711898
INIT_LIST_HEAD(&nft_net->commit_list);
11899+
INIT_LIST_HEAD(&nft_net->destroy_list);
1189811900
INIT_LIST_HEAD(&nft_net->commit_set_list);
1189911901
INIT_LIST_HEAD(&nft_net->binding_list);
1190011902
INIT_LIST_HEAD(&nft_net->module_list);
@@ -11903,6 +11905,7 @@ static int __net_init nf_tables_init_net(struct net *net)
1190311905
nft_net->base_seq = 1;
1190411906
nft_net->gc_seq = 0;
1190511907
nft_net->validate_state = NFT_VALIDATE_SKIP;
11908+
INIT_WORK(&nft_net->destroy_work, nf_tables_trans_destroy_work);
1190611909

1190711910
return 0;
1190811911
}
@@ -11931,14 +11934,17 @@ static void __net_exit nf_tables_exit_net(struct net *net)
1193111934
if (!list_empty(&nft_net->module_list))
1193211935
nf_tables_module_autoload_cleanup(net);
1193311936

11937+
cancel_work_sync(&nft_net->destroy_work);
1193411938
__nft_release_tables(net);
1193511939

1193611940
nft_gc_seq_end(nft_net, gc_seq);
1193711941

1193811942
mutex_unlock(&nft_net->commit_mutex);
11943+
1193911944
WARN_ON_ONCE(!list_empty(&nft_net->tables));
1194011945
WARN_ON_ONCE(!list_empty(&nft_net->module_list));
1194111946
WARN_ON_ONCE(!list_empty(&nft_net->notify_list));
11947+
WARN_ON_ONCE(!list_empty(&nft_net->destroy_list));
1194211948
}
1194311949

1194411950
static void nf_tables_exit_batch(struct list_head *net_exit_list)
@@ -12029,10 +12035,8 @@ static void __exit nf_tables_module_exit(void)
1202912035
unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
1203012036
nft_chain_filter_fini();
1203112037
nft_chain_route_fini();
12032-
nf_tables_trans_destroy_flush_work();
1203312038
unregister_pernet_subsys(&nf_tables_net_ops);
1203412039
cancel_work_sync(&trans_gc_work);
12035-
cancel_work_sync(&trans_destroy_work);
1203612040
rcu_barrier();
1203712041
rhltable_destroy(&nft_objname_ht);
1203812042
nf_tables_core_module_exit();

net/netfilter/nft_compat.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,15 +228,15 @@ static int nft_parse_compat(const struct nlattr *attr, u16 *proto, bool *inv)
228228
return 0;
229229
}
230230

231-
static void nft_compat_wait_for_destructors(void)
231+
static void nft_compat_wait_for_destructors(struct net *net)
232232
{
233233
/* xtables matches or targets can have side effects, e.g.
234234
* creation/destruction of /proc files.
235235
* The xt ->destroy functions are run asynchronously from
236236
* work queue. If we have pending invocations we thus
237237
* need to wait for those to finish.
238238
*/
239-
nf_tables_trans_destroy_flush_work();
239+
nf_tables_trans_destroy_flush_work(net);
240240
}
241241

242242
static int
@@ -262,7 +262,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
262262

263263
nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
264264

265-
nft_compat_wait_for_destructors();
265+
nft_compat_wait_for_destructors(ctx->net);
266266

267267
ret = xt_check_target(&par, size, proto, inv);
268268
if (ret < 0) {
@@ -515,7 +515,7 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
515515

516516
nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
517517

518-
nft_compat_wait_for_destructors();
518+
nft_compat_wait_for_destructors(ctx->net);
519519

520520
return xt_check_match(&par, size, proto, inv);
521521
}

0 commit comments

Comments
 (0)