Skip to content

Commit 73319a8

Browse files
Phil Sutterummakynes
authored andcommitted
netfilter: nf_tables: Have a list of nf_hook_ops in nft_hook
Supporting a 1:n relationship between nft_hook and nf_hook_ops is convenient since a chain's or flowtable's nft_hooks may remain in place despite matching interfaces disappearing. This stabilizes ruleset dumps in that regard and opens the possibility to claim newly added interfaces which match the spec. Also it prepares for wildcard interface specs since these will potentially match multiple interfaces. All spots dealing with hook registration are updated to handle a list of multiple nf_hook_ops, but nft_netdev_hook_alloc() only adds a single item for now to retain the old behaviour. The only expected functional change here is how vanishing interfaces are handled: Instead of dropping the respective nft_hook, only the matching nf_hook_ops are dropped. To safely remove individual ops from the list in netdev handlers, an rcu_head is added to struct nf_hook_ops so kfree_rcu() may be used. There is at least nft_flowtable_find_dev() which may be iterating through the list at the same time. Signed-off-by: Phil Sutter <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 91a089d commit 73319a8

File tree

5 files changed

+137
-63
lines changed

5 files changed

+137
-63
lines changed

include/linux/netfilter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ enum nf_hook_ops_type {
9595
};
9696

9797
struct nf_hook_ops {
98+
struct list_head list;
99+
struct rcu_head rcu;
100+
98101
/* User fills in from here down. */
99102
nf_hookfn *hook;
100103
struct net_device *dev;

include/net/netfilter/nf_tables.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1199,7 +1199,7 @@ struct nft_stats {
11991199

12001200
struct nft_hook {
12011201
struct list_head list;
1202-
struct nf_hook_ops ops;
1202+
struct list_head ops_list;
12031203
struct rcu_head rcu;
12041204
char ifname[IFNAMSIZ];
12051205
u8 ifnamelen;

net/netfilter/nf_tables_api.c

Lines changed: 103 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -300,47 +300,72 @@ void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)
300300
static int nft_netdev_register_hooks(struct net *net,
301301
struct list_head *hook_list)
302302
{
303+
struct nf_hook_ops *ops;
303304
struct nft_hook *hook;
304305
int err, j;
305306

306307
j = 0;
307308
list_for_each_entry(hook, hook_list, list) {
308-
err = nf_register_net_hook(net, &hook->ops);
309-
if (err < 0)
310-
goto err_register;
309+
list_for_each_entry(ops, &hook->ops_list, list) {
310+
err = nf_register_net_hook(net, ops);
311+
if (err < 0)
312+
goto err_register;
311313

312-
j++;
314+
j++;
315+
}
313316
}
314317
return 0;
315318

316319
err_register:
317320
list_for_each_entry(hook, hook_list, list) {
318-
if (j-- <= 0)
319-
break;
321+
list_for_each_entry(ops, &hook->ops_list, list) {
322+
if (j-- <= 0)
323+
break;
320324

321-
nf_unregister_net_hook(net, &hook->ops);
325+
nf_unregister_net_hook(net, ops);
326+
}
322327
}
323328
return err;
324329
}
325330

331+
static void nft_netdev_hook_free_ops(struct nft_hook *hook)
332+
{
333+
struct nf_hook_ops *ops, *next;
334+
335+
list_for_each_entry_safe(ops, next, &hook->ops_list, list) {
336+
list_del(&ops->list);
337+
kfree(ops);
338+
}
339+
}
340+
326341
static void nft_netdev_hook_free(struct nft_hook *hook)
327342
{
343+
nft_netdev_hook_free_ops(hook);
328344
kfree(hook);
329345
}
330346

347+
static void __nft_netdev_hook_free_rcu(struct rcu_head *rcu)
348+
{
349+
struct nft_hook *hook = container_of(rcu, struct nft_hook, rcu);
350+
351+
nft_netdev_hook_free(hook);
352+
}
353+
331354
static void nft_netdev_hook_free_rcu(struct nft_hook *hook)
332355
{
333-
kfree_rcu(hook, rcu);
356+
call_rcu(&hook->rcu, __nft_netdev_hook_free_rcu);
334357
}
335358

336359
static void nft_netdev_unregister_hooks(struct net *net,
337360
struct list_head *hook_list,
338361
bool release_netdev)
339362
{
340363
struct nft_hook *hook, *next;
364+
struct nf_hook_ops *ops;
341365

342366
list_for_each_entry_safe(hook, next, hook_list, list) {
343-
nf_unregister_net_hook(net, &hook->ops);
367+
list_for_each_entry(ops, &hook->ops_list, list)
368+
nf_unregister_net_hook(net, ops);
344369
if (release_netdev) {
345370
list_del(&hook->list);
346371
nft_netdev_hook_free_rcu(hook);
@@ -2284,6 +2309,7 @@ void nf_tables_chain_destroy(struct nft_chain *chain)
22842309
static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
22852310
const struct nlattr *attr)
22862311
{
2312+
struct nf_hook_ops *ops;
22872313
struct net_device *dev;
22882314
struct nft_hook *hook;
22892315
int err;
@@ -2293,6 +2319,7 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
22932319
err = -ENOMEM;
22942320
goto err_hook_alloc;
22952321
}
2322+
INIT_LIST_HEAD(&hook->ops_list);
22962323

22972324
err = nla_strscpy(hook->ifname, attr, IFNAMSIZ);
22982325
if (err < 0)
@@ -2309,7 +2336,14 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
23092336
err = -ENOENT;
23102337
goto err_hook_dev;
23112338
}
2312-
hook->ops.dev = dev;
2339+
2340+
ops = kzalloc(sizeof(struct nf_hook_ops), GFP_KERNEL_ACCOUNT);
2341+
if (!ops) {
2342+
err = -ENOMEM;
2343+
goto err_hook_dev;
2344+
}
2345+
ops->dev = dev;
2346+
list_add_tail(&ops->list, &hook->ops_list);
23132347

23142348
return hook;
23152349

@@ -2569,6 +2603,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
25692603
struct nft_chain_hook *hook, u32 flags)
25702604
{
25712605
struct nft_chain *chain;
2606+
struct nf_hook_ops *ops;
25722607
struct nft_hook *h;
25732608

25742609
basechain->type = hook->type;
@@ -2577,8 +2612,10 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
25772612

25782613
if (nft_base_chain_netdev(family, hook->num)) {
25792614
list_splice_init(&hook->list, &basechain->hook_list);
2580-
list_for_each_entry(h, &basechain->hook_list, list)
2581-
nft_basechain_hook_init(&h->ops, family, hook, chain);
2615+
list_for_each_entry(h, &basechain->hook_list, list) {
2616+
list_for_each_entry(ops, &h->ops_list, list)
2617+
nft_basechain_hook_init(ops, family, hook, chain);
2618+
}
25822619
}
25832620
nft_basechain_hook_init(&basechain->ops, family, hook, chain);
25842621

@@ -2797,11 +2834,13 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
27972834

27982835
if (nft_base_chain_netdev(ctx->family, basechain->ops.hooknum)) {
27992836
list_for_each_entry_safe(h, next, &hook.list, list) {
2800-
h->ops.pf = basechain->ops.pf;
2801-
h->ops.hooknum = basechain->ops.hooknum;
2802-
h->ops.priority = basechain->ops.priority;
2803-
h->ops.priv = basechain->ops.priv;
2804-
h->ops.hook = basechain->ops.hook;
2837+
list_for_each_entry(ops, &h->ops_list, list) {
2838+
ops->pf = basechain->ops.pf;
2839+
ops->hooknum = basechain->ops.hooknum;
2840+
ops->priority = basechain->ops.priority;
2841+
ops->priv = basechain->ops.priv;
2842+
ops->hook = basechain->ops.hook;
2843+
}
28052844

28062845
if (nft_hook_list_find(&basechain->hook_list, h)) {
28072846
list_del(&h->list);
@@ -2923,8 +2962,10 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
29232962
err_hooks:
29242963
if (nla[NFTA_CHAIN_HOOK]) {
29252964
list_for_each_entry_safe(h, next, &hook.list, list) {
2926-
if (unregister)
2927-
nf_unregister_net_hook(ctx->net, &h->ops);
2965+
if (unregister) {
2966+
list_for_each_entry(ops, &h->ops_list, list)
2967+
nf_unregister_net_hook(ctx->net, ops);
2968+
}
29282969
list_del(&h->list);
29292970
nft_netdev_hook_free_rcu(h);
29302971
}
@@ -8795,6 +8836,7 @@ static int nft_flowtable_parse_hook(const struct nft_ctx *ctx,
87958836
struct netlink_ext_ack *extack, bool add)
87968837
{
87978838
struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1];
8839+
struct nf_hook_ops *ops;
87988840
struct nft_hook *hook;
87998841
int hooknum, priority;
88008842
int err;
@@ -8849,11 +8891,13 @@ static int nft_flowtable_parse_hook(const struct nft_ctx *ctx,
88498891
}
88508892

88518893
list_for_each_entry(hook, &flowtable_hook->list, list) {
8852-
hook->ops.pf = NFPROTO_NETDEV;
8853-
hook->ops.hooknum = flowtable_hook->num;
8854-
hook->ops.priority = flowtable_hook->priority;
8855-
hook->ops.priv = &flowtable->data;
8856-
hook->ops.hook = flowtable->data.type->hook;
8894+
list_for_each_entry(ops, &hook->ops_list, list) {
8895+
ops->pf = NFPROTO_NETDEV;
8896+
ops->hooknum = flowtable_hook->num;
8897+
ops->priority = flowtable_hook->priority;
8898+
ops->priv = &flowtable->data;
8899+
ops->hook = flowtable->data.type->hook;
8900+
}
88578901
}
88588902

88598903
return err;
@@ -8910,9 +8954,11 @@ static void __nft_unregister_flowtable_net_hooks(struct net *net,
89108954
bool release_netdev)
89118955
{
89128956
struct nft_hook *hook, *next;
8957+
struct nf_hook_ops *ops;
89138958

89148959
list_for_each_entry_safe(hook, next, hook_list, list) {
8915-
nft_unregister_flowtable_ops(net, flowtable, &hook->ops);
8960+
list_for_each_entry(ops, &hook->ops_list, list)
8961+
nft_unregister_flowtable_ops(net, flowtable, ops);
89168962
if (release_netdev) {
89178963
list_del(&hook->list);
89188964
nft_netdev_hook_free_rcu(hook);
@@ -8954,6 +9000,7 @@ static int nft_register_flowtable_net_hooks(struct net *net,
89549000
{
89559001
struct nft_hook *hook, *next;
89569002
struct nft_flowtable *ft;
9003+
struct nf_hook_ops *ops;
89579004
int err, i = 0;
89589005

89599006
list_for_each_entry(hook, hook_list, list) {
@@ -8967,21 +9014,25 @@ static int nft_register_flowtable_net_hooks(struct net *net,
89679014
}
89689015
}
89699016

8970-
err = nft_register_flowtable_ops(net, flowtable, &hook->ops);
8971-
if (err < 0)
8972-
goto err_unregister_net_hooks;
9017+
list_for_each_entry(ops, &hook->ops_list, list) {
9018+
err = nft_register_flowtable_ops(net, flowtable, ops);
9019+
if (err < 0)
9020+
goto err_unregister_net_hooks;
89739021

8974-
i++;
9022+
i++;
9023+
}
89759024
}
89769025

89779026
return 0;
89789027

89799028
err_unregister_net_hooks:
89809029
list_for_each_entry_safe(hook, next, hook_list, list) {
8981-
if (i-- <= 0)
8982-
break;
9030+
list_for_each_entry(ops, &hook->ops_list, list) {
9031+
if (i-- <= 0)
9032+
break;
89839033

8984-
nft_unregister_flowtable_ops(net, flowtable, &hook->ops);
9034+
nft_unregister_flowtable_ops(net, flowtable, ops);
9035+
}
89859036
list_del_rcu(&hook->list);
89869037
nft_netdev_hook_free_rcu(hook);
89879038
}
@@ -9006,6 +9057,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
90069057
const struct nlattr * const *nla = ctx->nla;
90079058
struct nft_flowtable_hook flowtable_hook;
90089059
struct nft_hook *hook, *next;
9060+
struct nf_hook_ops *ops;
90099061
struct nft_trans *trans;
90109062
bool unregister = false;
90119063
u32 flags;
@@ -9063,8 +9115,11 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
90639115

90649116
err_flowtable_update_hook:
90659117
list_for_each_entry_safe(hook, next, &flowtable_hook.list, list) {
9066-
if (unregister)
9067-
nft_unregister_flowtable_ops(ctx->net, flowtable, &hook->ops);
9118+
if (unregister) {
9119+
list_for_each_entry(ops, &hook->ops_list, list)
9120+
nft_unregister_flowtable_ops(ctx->net,
9121+
flowtable, ops);
9122+
}
90689123
list_del_rcu(&hook->list);
90699124
nft_netdev_hook_free_rcu(hook);
90709125
}
@@ -9611,17 +9666,26 @@ static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
96119666
struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
96129667
const struct net_device *dev)
96139668
{
9614-
if (hook->ops.dev == dev)
9615-
return (struct nf_hook_ops *)&hook->ops;
9669+
struct nf_hook_ops *ops;
96169670

9671+
list_for_each_entry(ops, &hook->ops_list, list) {
9672+
if (ops->dev == dev)
9673+
return ops;
9674+
}
96179675
return NULL;
96189676
}
96199677
EXPORT_SYMBOL_GPL(nft_hook_find_ops);
96209678

96219679
struct nf_hook_ops *nft_hook_find_ops_rcu(const struct nft_hook *hook,
96229680
const struct net_device *dev)
96239681
{
9624-
return nft_hook_find_ops(hook, dev);
9682+
struct nf_hook_ops *ops;
9683+
9684+
list_for_each_entry_rcu(ops, &hook->ops_list, list) {
9685+
if (ops->dev == dev)
9686+
return ops;
9687+
}
9688+
return NULL;
96259689
}
96269690
EXPORT_SYMBOL_GPL(nft_hook_find_ops_rcu);
96279691

@@ -9638,8 +9702,8 @@ static void nft_flowtable_event(unsigned long event, struct net_device *dev,
96389702

96399703
/* flow_offload_netdev_event() cleans up entries for us. */
96409704
nft_unregister_flowtable_ops(dev_net(dev), flowtable, ops);
9641-
list_del_rcu(&hook->list);
9642-
kfree_rcu(hook, rcu);
9705+
list_del_rcu(&ops->list);
9706+
kfree_rcu(ops, rcu);
96439707
break;
96449708
}
96459709
}

0 commit comments

Comments
 (0)