aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2018-02-06 07:22:47 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2018-02-06 18:58:57 -0500
commitb408c5b04f82fe4e20bceb8e4f219453d4f21f02 (patch)
treecd33d1bcfebc1bc0bdddef0379b57eb28ef20131
parentc0ea1bcb39352b57ac5c4b6da8acd65bddeee2c5 (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.h2
-rw-r--r--net/ipv4/netfilter/nf_flow_table_ipv4.c1
-rw-r--r--net/ipv6/netfilter/nf_flow_table_ipv6.c1
-rw-r--r--net/netfilter/nf_flow_table.c25
-rw-r--r--net/netfilter/nf_flow_table_inet.c1
-rw-r--r--net/netfilter/nf_tables_api.c9
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
99void nf_flow_table_cleanup(struct net *net, struct net_device *dev); 100void nf_flow_table_cleanup(struct net *net, struct net_device *dev);
100 101
102void nf_flow_table_free(struct nf_flowtable *flow_table);
101void nf_flow_offload_work_gc(struct work_struct *work); 103void nf_flow_offload_work_gc(struct work_struct *work);
102extern const struct rhashtable_params nf_flow_offload_rhash_params; 104extern 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
235void nf_flow_offload_work_gc(struct work_struct *work) 235static 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)
270out: 267out:
271 rhashtable_walk_stop(&hti); 268 rhashtable_walk_stop(&hti);
272 rhashtable_walk_exit(&hti); 269 rhashtable_walk_exit(&hti);
273schedule: 270
271 return 1;
272}
273
274void 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}
276EXPORT_SYMBOL_GPL(nf_flow_offload_work_gc); 282EXPORT_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}
450EXPORT_SYMBOL_GPL(nf_flow_table_cleanup); 456EXPORT_SYMBOL_GPL(nf_flow_table_cleanup);
451 457
458void 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}
463EXPORT_SYMBOL_GPL(nf_flow_table_free);
464
452MODULE_LICENSE("GPL"); 465MODULE_LICENSE("GPL");
453MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); 466MODULE_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
5402static void nft_flowtable_destroy(void *ptr, void *arg)
5403{
5404 kfree(ptr);
5405}
5406
5407static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable) 5402static 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