]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - net/netfilter/nf_tables_offload.c
netfilter: nftables: counter hardware offload support
[mirror_ubuntu-jammy-kernel.git] / net / netfilter / nf_tables_offload.c
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #include <linux/init.h>
3 #include <linux/module.h>
4 #include <linux/netfilter.h>
5 #include <net/flow_offload.h>
6 #include <net/netfilter/nf_tables.h>
7 #include <net/netfilter/nf_tables_offload.h>
8 #include <net/pkt_cls.h>
9
10 extern unsigned int nf_tables_net_id;
11
12 static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions)
13 {
14 struct nft_flow_rule *flow;
15
16 flow = kzalloc(sizeof(struct nft_flow_rule), GFP_KERNEL);
17 if (!flow)
18 return NULL;
19
20 flow->rule = flow_rule_alloc(num_actions);
21 if (!flow->rule) {
22 kfree(flow);
23 return NULL;
24 }
25
26 flow->rule->match.dissector = &flow->match.dissector;
27 flow->rule->match.mask = &flow->match.mask;
28 flow->rule->match.key = &flow->match.key;
29
30 return flow;
31 }
32
33 void nft_flow_rule_set_addr_type(struct nft_flow_rule *flow,
34 enum flow_dissector_key_id addr_type)
35 {
36 struct nft_flow_match *match = &flow->match;
37 struct nft_flow_key *mask = &match->mask;
38 struct nft_flow_key *key = &match->key;
39
40 if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL))
41 return;
42
43 key->control.addr_type = addr_type;
44 mask->control.addr_type = 0xffff;
45 match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
46 match->dissector.offset[FLOW_DISSECTOR_KEY_CONTROL] =
47 offsetof(struct nft_flow_key, control);
48 }
49
50 struct nft_offload_ethertype {
51 __be16 value;
52 __be16 mask;
53 };
54
55 static void nft_flow_rule_transfer_vlan(struct nft_offload_ctx *ctx,
56 struct nft_flow_rule *flow)
57 {
58 struct nft_flow_match *match = &flow->match;
59 struct nft_offload_ethertype ethertype;
60
61 if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL) &&
62 match->key.basic.n_proto != htons(ETH_P_8021Q) &&
63 match->key.basic.n_proto != htons(ETH_P_8021AD))
64 return;
65
66 ethertype.value = match->key.basic.n_proto;
67 ethertype.mask = match->mask.basic.n_proto;
68
69 if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_VLAN) &&
70 (match->key.vlan.vlan_tpid == htons(ETH_P_8021Q) ||
71 match->key.vlan.vlan_tpid == htons(ETH_P_8021AD))) {
72 match->key.basic.n_proto = match->key.cvlan.vlan_tpid;
73 match->mask.basic.n_proto = match->mask.cvlan.vlan_tpid;
74 match->key.cvlan.vlan_tpid = match->key.vlan.vlan_tpid;
75 match->mask.cvlan.vlan_tpid = match->mask.vlan.vlan_tpid;
76 match->key.vlan.vlan_tpid = ethertype.value;
77 match->mask.vlan.vlan_tpid = ethertype.mask;
78 match->dissector.offset[FLOW_DISSECTOR_KEY_CVLAN] =
79 offsetof(struct nft_flow_key, cvlan);
80 match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CVLAN);
81 } else {
82 match->key.basic.n_proto = match->key.vlan.vlan_tpid;
83 match->mask.basic.n_proto = match->mask.vlan.vlan_tpid;
84 match->key.vlan.vlan_tpid = ethertype.value;
85 match->mask.vlan.vlan_tpid = ethertype.mask;
86 match->dissector.offset[FLOW_DISSECTOR_KEY_VLAN] =
87 offsetof(struct nft_flow_key, vlan);
88 match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN);
89 }
90 }
91
92 struct nft_flow_rule *nft_flow_rule_create(struct net *net,
93 const struct nft_rule *rule)
94 {
95 struct nft_offload_ctx *ctx;
96 struct nft_flow_rule *flow;
97 int num_actions = 0, err;
98 struct nft_expr *expr;
99
100 expr = nft_expr_first(rule);
101 while (nft_expr_more(rule, expr)) {
102 if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION)
103 num_actions++;
104
105 expr = nft_expr_next(expr);
106 }
107
108 if (num_actions == 0)
109 return ERR_PTR(-EOPNOTSUPP);
110
111 flow = nft_flow_rule_alloc(num_actions);
112 if (!flow)
113 return ERR_PTR(-ENOMEM);
114
115 expr = nft_expr_first(rule);
116
117 ctx = kzalloc(sizeof(struct nft_offload_ctx), GFP_KERNEL);
118 if (!ctx) {
119 err = -ENOMEM;
120 goto err_out;
121 }
122 ctx->net = net;
123 ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
124
125 while (nft_expr_more(rule, expr)) {
126 if (!expr->ops->offload) {
127 err = -EOPNOTSUPP;
128 goto err_out;
129 }
130 err = expr->ops->offload(ctx, flow, expr);
131 if (err < 0)
132 goto err_out;
133
134 expr = nft_expr_next(expr);
135 }
136 nft_flow_rule_transfer_vlan(ctx, flow);
137
138 flow->proto = ctx->dep.l3num;
139 kfree(ctx);
140
141 return flow;
142 err_out:
143 kfree(ctx);
144 nft_flow_rule_destroy(flow);
145
146 return ERR_PTR(err);
147 }
148
149 void nft_flow_rule_destroy(struct nft_flow_rule *flow)
150 {
151 struct flow_action_entry *entry;
152 int i;
153
154 flow_action_for_each(i, entry, &flow->rule->action) {
155 switch (entry->id) {
156 case FLOW_ACTION_REDIRECT:
157 case FLOW_ACTION_MIRRED:
158 dev_put(entry->dev);
159 break;
160 default:
161 break;
162 }
163 }
164 kfree(flow->rule);
165 kfree(flow);
166 }
167
168 void nft_offload_set_dependency(struct nft_offload_ctx *ctx,
169 enum nft_offload_dep_type type)
170 {
171 ctx->dep.type = type;
172 }
173
174 void nft_offload_update_dependency(struct nft_offload_ctx *ctx,
175 const void *data, u32 len)
176 {
177 switch (ctx->dep.type) {
178 case NFT_OFFLOAD_DEP_NETWORK:
179 WARN_ON(len != sizeof(__u16));
180 memcpy(&ctx->dep.l3num, data, sizeof(__u16));
181 break;
182 case NFT_OFFLOAD_DEP_TRANSPORT:
183 WARN_ON(len != sizeof(__u8));
184 memcpy(&ctx->dep.protonum, data, sizeof(__u8));
185 break;
186 default:
187 break;
188 }
189 ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
190 }
191
192 static void nft_flow_offload_common_init(struct flow_cls_common_offload *common,
193 __be16 proto, int priority,
194 struct netlink_ext_ack *extack)
195 {
196 common->protocol = proto;
197 common->prio = priority;
198 common->extack = extack;
199 }
200
201 static int nft_setup_cb_call(enum tc_setup_type type, void *type_data,
202 struct list_head *cb_list)
203 {
204 struct flow_block_cb *block_cb;
205 int err;
206
207 list_for_each_entry(block_cb, cb_list, list) {
208 err = block_cb->cb(type, type_data, block_cb->cb_priv);
209 if (err < 0)
210 return err;
211 }
212 return 0;
213 }
214
215 int nft_chain_offload_priority(struct nft_base_chain *basechain)
216 {
217 if (basechain->ops.priority <= 0 ||
218 basechain->ops.priority > USHRT_MAX)
219 return -1;
220
221 return 0;
222 }
223
224 static void nft_flow_cls_offload_setup(struct flow_cls_offload *cls_flow,
225 const struct nft_base_chain *basechain,
226 const struct nft_rule *rule,
227 const struct nft_flow_rule *flow,
228 struct netlink_ext_ack *extack,
229 enum flow_cls_command command)
230 {
231 __be16 proto = ETH_P_ALL;
232
233 memset(cls_flow, 0, sizeof(*cls_flow));
234
235 if (flow)
236 proto = flow->proto;
237
238 nft_flow_offload_common_init(&cls_flow->common, proto,
239 basechain->ops.priority, extack);
240 cls_flow->command = command;
241 cls_flow->cookie = (unsigned long) rule;
242 if (flow)
243 cls_flow->rule = flow->rule;
244 }
245
246 static int nft_flow_offload_cmd(const struct nft_chain *chain,
247 const struct nft_rule *rule,
248 struct nft_flow_rule *flow,
249 enum flow_cls_command command,
250 struct flow_cls_offload *cls_flow)
251 {
252 struct netlink_ext_ack extack = {};
253 struct nft_base_chain *basechain;
254
255 if (!nft_is_base_chain(chain))
256 return -EOPNOTSUPP;
257
258 basechain = nft_base_chain(chain);
259 nft_flow_cls_offload_setup(cls_flow, basechain, rule, flow, &extack,
260 command);
261
262 return nft_setup_cb_call(TC_SETUP_CLSFLOWER, cls_flow,
263 &basechain->flow_block.cb_list);
264 }
265
266 static int nft_flow_offload_rule(const struct nft_chain *chain,
267 struct nft_rule *rule,
268 struct nft_flow_rule *flow,
269 enum flow_cls_command command)
270 {
271 struct flow_cls_offload cls_flow;
272
273 return nft_flow_offload_cmd(chain, rule, flow, command, &cls_flow);
274 }
275
276 int nft_flow_rule_stats(const struct nft_chain *chain,
277 const struct nft_rule *rule)
278 {
279 struct flow_cls_offload cls_flow = {};
280 struct nft_expr *expr, *next;
281 int err;
282
283 err = nft_flow_offload_cmd(chain, rule, NULL, FLOW_CLS_STATS,
284 &cls_flow);
285 if (err < 0)
286 return err;
287
288 nft_rule_for_each_expr(expr, next, rule) {
289 if (expr->ops->offload_stats)
290 expr->ops->offload_stats(expr, &cls_flow.stats);
291 }
292
293 return 0;
294 }
295
296 static int nft_flow_offload_bind(struct flow_block_offload *bo,
297 struct nft_base_chain *basechain)
298 {
299 list_splice(&bo->cb_list, &basechain->flow_block.cb_list);
300 return 0;
301 }
302
303 static int nft_flow_offload_unbind(struct flow_block_offload *bo,
304 struct nft_base_chain *basechain)
305 {
306 struct flow_block_cb *block_cb, *next;
307 struct flow_cls_offload cls_flow;
308 struct netlink_ext_ack extack;
309 struct nft_chain *chain;
310 struct nft_rule *rule;
311
312 chain = &basechain->chain;
313 list_for_each_entry(rule, &chain->rules, list) {
314 memset(&extack, 0, sizeof(extack));
315 nft_flow_cls_offload_setup(&cls_flow, basechain, rule, NULL,
316 &extack, FLOW_CLS_DESTROY);
317 nft_setup_cb_call(TC_SETUP_CLSFLOWER, &cls_flow, &bo->cb_list);
318 }
319
320 list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
321 list_del(&block_cb->list);
322 flow_block_cb_free(block_cb);
323 }
324
325 return 0;
326 }
327
328 static int nft_block_setup(struct nft_base_chain *basechain,
329 struct flow_block_offload *bo,
330 enum flow_block_command cmd)
331 {
332 int err;
333
334 switch (cmd) {
335 case FLOW_BLOCK_BIND:
336 err = nft_flow_offload_bind(bo, basechain);
337 break;
338 case FLOW_BLOCK_UNBIND:
339 err = nft_flow_offload_unbind(bo, basechain);
340 break;
341 default:
342 WARN_ON_ONCE(1);
343 err = -EOPNOTSUPP;
344 }
345
346 return err;
347 }
348
349 static void nft_flow_block_offload_init(struct flow_block_offload *bo,
350 struct net *net,
351 enum flow_block_command cmd,
352 struct nft_base_chain *basechain,
353 struct netlink_ext_ack *extack)
354 {
355 memset(bo, 0, sizeof(*bo));
356 bo->net = net;
357 bo->block = &basechain->flow_block;
358 bo->command = cmd;
359 bo->binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
360 bo->extack = extack;
361 INIT_LIST_HEAD(&bo->cb_list);
362 }
363
364 static int nft_block_offload_cmd(struct nft_base_chain *chain,
365 struct net_device *dev,
366 enum flow_block_command cmd)
367 {
368 struct netlink_ext_ack extack = {};
369 struct flow_block_offload bo;
370 int err;
371
372 nft_flow_block_offload_init(&bo, dev_net(dev), cmd, chain, &extack);
373
374 err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
375 if (err < 0)
376 return err;
377
378 return nft_block_setup(chain, &bo, cmd);
379 }
380
381 static void nft_indr_block_cleanup(struct flow_block_cb *block_cb)
382 {
383 struct nft_base_chain *basechain = block_cb->indr.data;
384 struct net_device *dev = block_cb->indr.dev;
385 struct netlink_ext_ack extack = {};
386 struct nftables_pernet *nft_net;
387 struct net *net = dev_net(dev);
388 struct flow_block_offload bo;
389
390 nft_flow_block_offload_init(&bo, dev_net(dev), FLOW_BLOCK_UNBIND,
391 basechain, &extack);
392 nft_net = net_generic(net, nf_tables_net_id);
393 mutex_lock(&nft_net->commit_mutex);
394 list_del(&block_cb->driver_list);
395 list_move(&block_cb->list, &bo.cb_list);
396 nft_flow_offload_unbind(&bo, basechain);
397 mutex_unlock(&nft_net->commit_mutex);
398 }
399
400 static int nft_indr_block_offload_cmd(struct nft_base_chain *basechain,
401 struct net_device *dev,
402 enum flow_block_command cmd)
403 {
404 struct netlink_ext_ack extack = {};
405 struct flow_block_offload bo;
406 int err;
407
408 nft_flow_block_offload_init(&bo, dev_net(dev), cmd, basechain, &extack);
409
410 err = flow_indr_dev_setup_offload(dev, NULL, TC_SETUP_BLOCK, basechain, &bo,
411 nft_indr_block_cleanup);
412 if (err < 0)
413 return err;
414
415 if (list_empty(&bo.cb_list))
416 return -EOPNOTSUPP;
417
418 return nft_block_setup(basechain, &bo, cmd);
419 }
420
421 static int nft_chain_offload_cmd(struct nft_base_chain *basechain,
422 struct net_device *dev,
423 enum flow_block_command cmd)
424 {
425 int err;
426
427 if (dev->netdev_ops->ndo_setup_tc)
428 err = nft_block_offload_cmd(basechain, dev, cmd);
429 else
430 err = nft_indr_block_offload_cmd(basechain, dev, cmd);
431
432 return err;
433 }
434
435 static int nft_flow_block_chain(struct nft_base_chain *basechain,
436 const struct net_device *this_dev,
437 enum flow_block_command cmd)
438 {
439 struct net_device *dev;
440 struct nft_hook *hook;
441 int err, i = 0;
442
443 list_for_each_entry(hook, &basechain->hook_list, list) {
444 dev = hook->ops.dev;
445 if (this_dev && this_dev != dev)
446 continue;
447
448 err = nft_chain_offload_cmd(basechain, dev, cmd);
449 if (err < 0 && cmd == FLOW_BLOCK_BIND) {
450 if (!this_dev)
451 goto err_flow_block;
452
453 return err;
454 }
455 i++;
456 }
457
458 return 0;
459
460 err_flow_block:
461 list_for_each_entry(hook, &basechain->hook_list, list) {
462 if (i-- <= 0)
463 break;
464
465 dev = hook->ops.dev;
466 nft_chain_offload_cmd(basechain, dev, FLOW_BLOCK_UNBIND);
467 }
468 return err;
469 }
470
471 static int nft_flow_offload_chain(struct nft_chain *chain, u8 *ppolicy,
472 enum flow_block_command cmd)
473 {
474 struct nft_base_chain *basechain;
475 u8 policy;
476
477 if (!nft_is_base_chain(chain))
478 return -EOPNOTSUPP;
479
480 basechain = nft_base_chain(chain);
481 policy = ppolicy ? *ppolicy : basechain->policy;
482
483 /* Only default policy to accept is supported for now. */
484 if (cmd == FLOW_BLOCK_BIND && policy == NF_DROP)
485 return -EOPNOTSUPP;
486
487 return nft_flow_block_chain(basechain, NULL, cmd);
488 }
489
490 static void nft_flow_rule_offload_abort(struct net *net,
491 struct nft_trans *trans)
492 {
493 struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
494 int err = 0;
495
496 list_for_each_entry_continue_reverse(trans, &nft_net->commit_list, list) {
497 if (trans->ctx.family != NFPROTO_NETDEV)
498 continue;
499
500 switch (trans->msg_type) {
501 case NFT_MSG_NEWCHAIN:
502 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) ||
503 nft_trans_chain_update(trans))
504 continue;
505
506 err = nft_flow_offload_chain(trans->ctx.chain, NULL,
507 FLOW_BLOCK_UNBIND);
508 break;
509 case NFT_MSG_DELCHAIN:
510 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
511 continue;
512
513 err = nft_flow_offload_chain(trans->ctx.chain, NULL,
514 FLOW_BLOCK_BIND);
515 break;
516 case NFT_MSG_NEWRULE:
517 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
518 continue;
519
520 err = nft_flow_offload_rule(trans->ctx.chain,
521 nft_trans_rule(trans),
522 NULL, FLOW_CLS_DESTROY);
523 break;
524 case NFT_MSG_DELRULE:
525 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
526 continue;
527
528 err = nft_flow_offload_rule(trans->ctx.chain,
529 nft_trans_rule(trans),
530 nft_trans_flow_rule(trans),
531 FLOW_CLS_REPLACE);
532 break;
533 }
534
535 if (WARN_ON_ONCE(err))
536 break;
537 }
538 }
539
540 int nft_flow_rule_offload_commit(struct net *net)
541 {
542 struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
543 struct nft_trans *trans;
544 int err = 0;
545 u8 policy;
546
547 list_for_each_entry(trans, &nft_net->commit_list, list) {
548 if (trans->ctx.family != NFPROTO_NETDEV)
549 continue;
550
551 switch (trans->msg_type) {
552 case NFT_MSG_NEWCHAIN:
553 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) ||
554 nft_trans_chain_update(trans))
555 continue;
556
557 policy = nft_trans_chain_policy(trans);
558 err = nft_flow_offload_chain(trans->ctx.chain, &policy,
559 FLOW_BLOCK_BIND);
560 break;
561 case NFT_MSG_DELCHAIN:
562 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
563 continue;
564
565 policy = nft_trans_chain_policy(trans);
566 err = nft_flow_offload_chain(trans->ctx.chain, &policy,
567 FLOW_BLOCK_UNBIND);
568 break;
569 case NFT_MSG_NEWRULE:
570 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
571 continue;
572
573 if (trans->ctx.flags & NLM_F_REPLACE ||
574 !(trans->ctx.flags & NLM_F_APPEND)) {
575 err = -EOPNOTSUPP;
576 break;
577 }
578 err = nft_flow_offload_rule(trans->ctx.chain,
579 nft_trans_rule(trans),
580 nft_trans_flow_rule(trans),
581 FLOW_CLS_REPLACE);
582 break;
583 case NFT_MSG_DELRULE:
584 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
585 continue;
586
587 err = nft_flow_offload_rule(trans->ctx.chain,
588 nft_trans_rule(trans),
589 NULL, FLOW_CLS_DESTROY);
590 break;
591 }
592
593 if (err) {
594 nft_flow_rule_offload_abort(net, trans);
595 break;
596 }
597 }
598
599 list_for_each_entry(trans, &nft_net->commit_list, list) {
600 if (trans->ctx.family != NFPROTO_NETDEV)
601 continue;
602
603 switch (trans->msg_type) {
604 case NFT_MSG_NEWRULE:
605 case NFT_MSG_DELRULE:
606 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
607 continue;
608
609 nft_flow_rule_destroy(nft_trans_flow_rule(trans));
610 break;
611 default:
612 break;
613 }
614 }
615
616 return err;
617 }
618
619 static struct nft_chain *__nft_offload_get_chain(const struct nftables_pernet *nft_net,
620 struct net_device *dev)
621 {
622 struct nft_base_chain *basechain;
623 struct nft_hook *hook, *found;
624 const struct nft_table *table;
625 struct nft_chain *chain;
626
627 list_for_each_entry(table, &nft_net->tables, list) {
628 if (table->family != NFPROTO_NETDEV)
629 continue;
630
631 list_for_each_entry(chain, &table->chains, list) {
632 if (!nft_is_base_chain(chain) ||
633 !(chain->flags & NFT_CHAIN_HW_OFFLOAD))
634 continue;
635
636 found = NULL;
637 basechain = nft_base_chain(chain);
638 list_for_each_entry(hook, &basechain->hook_list, list) {
639 if (hook->ops.dev != dev)
640 continue;
641
642 found = hook;
643 break;
644 }
645 if (!found)
646 continue;
647
648 return chain;
649 }
650 }
651
652 return NULL;
653 }
654
655 static int nft_offload_netdev_event(struct notifier_block *this,
656 unsigned long event, void *ptr)
657 {
658 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
659 struct nftables_pernet *nft_net;
660 struct net *net = dev_net(dev);
661 struct nft_chain *chain;
662
663 if (event != NETDEV_UNREGISTER)
664 return NOTIFY_DONE;
665
666 nft_net = net_generic(net, nf_tables_net_id);
667 mutex_lock(&nft_net->commit_mutex);
668 chain = __nft_offload_get_chain(nft_net, dev);
669 if (chain)
670 nft_flow_block_chain(nft_base_chain(chain), dev,
671 FLOW_BLOCK_UNBIND);
672
673 mutex_unlock(&nft_net->commit_mutex);
674
675 return NOTIFY_DONE;
676 }
677
678 static struct notifier_block nft_offload_netdev_notifier = {
679 .notifier_call = nft_offload_netdev_event,
680 };
681
682 int nft_offload_init(void)
683 {
684 return register_netdevice_notifier(&nft_offload_netdev_notifier);
685 }
686
687 void nft_offload_exit(void)
688 {
689 unregister_netdevice_notifier(&nft_offload_netdev_notifier);
690 }