diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2018-02-06 07:22:47 -0500 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2018-02-06 18:58:57 -0500 |
commit | b408c5b04f82fe4e20bceb8e4f219453d4f21f02 (patch) | |
tree | cd33d1bcfebc1bc0bdddef0379b57eb28ef20131 | |
parent | c0ea1bcb39352b57ac5c4b6da8acd65bddeee2c5 (diff) |
netfilter: nf_tables: fix flowtable free
Every flow_offload entry is added into the table twice. Because of this,
rhashtable_free_and_destroy can't be used, since it would call kfree for
each flow_offload object twice.
This patch cleans up the flowtable via nf_flow_table_iterate() to
schedule removal of entries by setting on the dying bit, then there is
an explicitly invocation of the garbage collector to release resources.
Based on patch from Felix Fietkau.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | include/net/netfilter/nf_flow_table.h | 2 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_flow_table_ipv4.c | 1 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_flow_table_ipv6.c | 1 | ||||
-rw-r--r-- | net/netfilter/nf_flow_table.c | 25 | ||||
-rw-r--r-- | net/netfilter/nf_flow_table_inet.c | 1 | ||||
-rw-r--r-- | net/netfilter/nf_tables_api.c | 9 |
6 files changed, 26 insertions, 13 deletions
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index ed49cd169ecf..020ae903066f 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h | |||
@@ -14,6 +14,7 @@ struct nf_flowtable_type { | |||
14 | struct list_head list; | 14 | struct list_head list; |
15 | int family; | 15 | int family; |
16 | void (*gc)(struct work_struct *work); | 16 | void (*gc)(struct work_struct *work); |
17 | void (*free)(struct nf_flowtable *ft); | ||
17 | const struct rhashtable_params *params; | 18 | const struct rhashtable_params *params; |
18 | nf_hookfn *hook; | 19 | nf_hookfn *hook; |
19 | struct module *owner; | 20 | struct module *owner; |
@@ -98,6 +99,7 @@ int nf_flow_table_iterate(struct nf_flowtable *flow_table, | |||
98 | 99 | ||
99 | void nf_flow_table_cleanup(struct net *net, struct net_device *dev); | 100 | void nf_flow_table_cleanup(struct net *net, struct net_device *dev); |
100 | 101 | ||
102 | void nf_flow_table_free(struct nf_flowtable *flow_table); | ||
101 | void nf_flow_offload_work_gc(struct work_struct *work); | 103 | void nf_flow_offload_work_gc(struct work_struct *work); |
102 | extern const struct rhashtable_params nf_flow_offload_rhash_params; | 104 | extern const struct rhashtable_params nf_flow_offload_rhash_params; |
103 | 105 | ||
diff --git a/net/ipv4/netfilter/nf_flow_table_ipv4.c b/net/ipv4/netfilter/nf_flow_table_ipv4.c index b2d01eb25f2c..25d2975da156 100644 --- a/net/ipv4/netfilter/nf_flow_table_ipv4.c +++ b/net/ipv4/netfilter/nf_flow_table_ipv4.c | |||
@@ -260,6 +260,7 @@ static struct nf_flowtable_type flowtable_ipv4 = { | |||
260 | .family = NFPROTO_IPV4, | 260 | .family = NFPROTO_IPV4, |
261 | .params = &nf_flow_offload_rhash_params, | 261 | .params = &nf_flow_offload_rhash_params, |
262 | .gc = nf_flow_offload_work_gc, | 262 | .gc = nf_flow_offload_work_gc, |
263 | .free = nf_flow_table_free, | ||
263 | .hook = nf_flow_offload_ip_hook, | 264 | .hook = nf_flow_offload_ip_hook, |
264 | .owner = THIS_MODULE, | 265 | .owner = THIS_MODULE, |
265 | }; | 266 | }; |
diff --git a/net/ipv6/netfilter/nf_flow_table_ipv6.c b/net/ipv6/netfilter/nf_flow_table_ipv6.c index fff21602875a..d346705d6ee6 100644 --- a/net/ipv6/netfilter/nf_flow_table_ipv6.c +++ b/net/ipv6/netfilter/nf_flow_table_ipv6.c | |||
@@ -253,6 +253,7 @@ static struct nf_flowtable_type flowtable_ipv6 = { | |||
253 | .family = NFPROTO_IPV6, | 253 | .family = NFPROTO_IPV6, |
254 | .params = &nf_flow_offload_rhash_params, | 254 | .params = &nf_flow_offload_rhash_params, |
255 | .gc = nf_flow_offload_work_gc, | 255 | .gc = nf_flow_offload_work_gc, |
256 | .free = nf_flow_table_free, | ||
256 | .hook = nf_flow_offload_ipv6_hook, | 257 | .hook = nf_flow_offload_ipv6_hook, |
257 | .owner = THIS_MODULE, | 258 | .owner = THIS_MODULE, |
258 | }; | 259 | }; |
diff --git a/net/netfilter/nf_flow_table.c b/net/netfilter/nf_flow_table.c index 04c08f6b9015..c17f1af42daa 100644 --- a/net/netfilter/nf_flow_table.c +++ b/net/netfilter/nf_flow_table.c | |||
@@ -232,19 +232,16 @@ static inline bool nf_flow_is_dying(const struct flow_offload *flow) | |||
232 | return flow->flags & FLOW_OFFLOAD_DYING; | 232 | return flow->flags & FLOW_OFFLOAD_DYING; |
233 | } | 233 | } |
234 | 234 | ||
235 | void nf_flow_offload_work_gc(struct work_struct *work) | 235 | static int nf_flow_offload_gc_step(struct nf_flowtable *flow_table) |
236 | { | 236 | { |
237 | struct flow_offload_tuple_rhash *tuplehash; | 237 | struct flow_offload_tuple_rhash *tuplehash; |
238 | struct nf_flowtable *flow_table; | ||
239 | struct rhashtable_iter hti; | 238 | struct rhashtable_iter hti; |
240 | struct flow_offload *flow; | 239 | struct flow_offload *flow; |
241 | int err; | 240 | int err; |
242 | 241 | ||
243 | flow_table = container_of(work, struct nf_flowtable, gc_work.work); | ||
244 | |||
245 | err = rhashtable_walk_init(&flow_table->rhashtable, &hti, GFP_KERNEL); | 242 | err = rhashtable_walk_init(&flow_table->rhashtable, &hti, GFP_KERNEL); |
246 | if (err) | 243 | if (err) |
247 | goto schedule; | 244 | return 0; |
248 | 245 | ||
249 | rhashtable_walk_start(&hti); | 246 | rhashtable_walk_start(&hti); |
250 | 247 | ||
@@ -270,7 +267,16 @@ void nf_flow_offload_work_gc(struct work_struct *work) | |||
270 | out: | 267 | out: |
271 | rhashtable_walk_stop(&hti); | 268 | rhashtable_walk_stop(&hti); |
272 | rhashtable_walk_exit(&hti); | 269 | rhashtable_walk_exit(&hti); |
273 | schedule: | 270 | |
271 | return 1; | ||
272 | } | ||
273 | |||
274 | void nf_flow_offload_work_gc(struct work_struct *work) | ||
275 | { | ||
276 | struct nf_flowtable *flow_table; | ||
277 | |||
278 | flow_table = container_of(work, struct nf_flowtable, gc_work.work); | ||
279 | nf_flow_offload_gc_step(flow_table); | ||
274 | queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ); | 280 | queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ); |
275 | } | 281 | } |
276 | EXPORT_SYMBOL_GPL(nf_flow_offload_work_gc); | 282 | EXPORT_SYMBOL_GPL(nf_flow_offload_work_gc); |
@@ -449,5 +455,12 @@ void nf_flow_table_cleanup(struct net *net, struct net_device *dev) | |||
449 | } | 455 | } |
450 | EXPORT_SYMBOL_GPL(nf_flow_table_cleanup); | 456 | EXPORT_SYMBOL_GPL(nf_flow_table_cleanup); |
451 | 457 | ||
458 | void nf_flow_table_free(struct nf_flowtable *flow_table) | ||
459 | { | ||
460 | nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL); | ||
461 | WARN_ON(!nf_flow_offload_gc_step(flow_table)); | ||
462 | } | ||
463 | EXPORT_SYMBOL_GPL(nf_flow_table_free); | ||
464 | |||
452 | MODULE_LICENSE("GPL"); | 465 | MODULE_LICENSE("GPL"); |
453 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); | 466 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); |
diff --git a/net/netfilter/nf_flow_table_inet.c b/net/netfilter/nf_flow_table_inet.c index 281209aeba8f..375a1881d93d 100644 --- a/net/netfilter/nf_flow_table_inet.c +++ b/net/netfilter/nf_flow_table_inet.c | |||
@@ -24,6 +24,7 @@ static struct nf_flowtable_type flowtable_inet = { | |||
24 | .family = NFPROTO_INET, | 24 | .family = NFPROTO_INET, |
25 | .params = &nf_flow_offload_rhash_params, | 25 | .params = &nf_flow_offload_rhash_params, |
26 | .gc = nf_flow_offload_work_gc, | 26 | .gc = nf_flow_offload_work_gc, |
27 | .free = nf_flow_table_free, | ||
27 | .hook = nf_flow_offload_inet_hook, | 28 | .hook = nf_flow_offload_inet_hook, |
28 | .owner = THIS_MODULE, | 29 | .owner = THIS_MODULE, |
29 | }; | 30 | }; |
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 07dd1fac78a8..8b9fe30de0cd 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c | |||
@@ -5399,17 +5399,12 @@ err: | |||
5399 | nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS); | 5399 | nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS); |
5400 | } | 5400 | } |
5401 | 5401 | ||
5402 | static void nft_flowtable_destroy(void *ptr, void *arg) | ||
5403 | { | ||
5404 | kfree(ptr); | ||
5405 | } | ||
5406 | |||
5407 | static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable) | 5402 | static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable) |
5408 | { | 5403 | { |
5409 | cancel_delayed_work_sync(&flowtable->data.gc_work); | 5404 | cancel_delayed_work_sync(&flowtable->data.gc_work); |
5410 | kfree(flowtable->name); | 5405 | kfree(flowtable->name); |
5411 | rhashtable_free_and_destroy(&flowtable->data.rhashtable, | 5406 | flowtable->data.type->free(&flowtable->data); |
5412 | nft_flowtable_destroy, NULL); | 5407 | rhashtable_destroy(&flowtable->data.rhashtable); |
5413 | module_put(flowtable->data.type->owner); | 5408 | module_put(flowtable->data.type->owner); |
5414 | } | 5409 | } |
5415 | 5410 | ||