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>
10 static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions)
12 struct nft_flow_rule *flow;
14 flow = kzalloc(sizeof(struct nft_flow_rule), GFP_KERNEL);
18 flow->rule = flow_rule_alloc(num_actions);
24 flow->rule->match.dissector = &flow->match.dissector;
25 flow->rule->match.mask = &flow->match.mask;
26 flow->rule->match.key = &flow->match.key;
31 struct nft_flow_rule *nft_flow_rule_create(struct net *net,
32 const struct nft_rule *rule)
34 struct nft_offload_ctx *ctx;
35 struct nft_flow_rule *flow;
36 int num_actions = 0, err;
37 struct nft_expr *expr;
39 expr = nft_expr_first(rule);
40 while (expr->ops && expr != nft_expr_last(rule)) {
41 if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION)
44 expr = nft_expr_next(expr);
47 flow = nft_flow_rule_alloc(num_actions);
49 return ERR_PTR(-ENOMEM);
51 expr = nft_expr_first(rule);
53 ctx = kzalloc(sizeof(struct nft_offload_ctx), GFP_KERNEL);
59 ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
61 while (expr->ops && expr != nft_expr_last(rule)) {
62 if (!expr->ops->offload) {
66 err = expr->ops->offload(ctx, flow, expr);
70 expr = nft_expr_next(expr);
72 flow->proto = ctx->dep.l3num;
78 nft_flow_rule_destroy(flow);
83 void nft_flow_rule_destroy(struct nft_flow_rule *flow)
85 struct flow_action_entry *entry;
88 flow_action_for_each(i, entry, &flow->rule->action) {
90 case FLOW_ACTION_REDIRECT:
91 case FLOW_ACTION_MIRRED:
102 void nft_offload_set_dependency(struct nft_offload_ctx *ctx,
103 enum nft_offload_dep_type type)
105 ctx->dep.type = type;
108 void nft_offload_update_dependency(struct nft_offload_ctx *ctx,
109 const void *data, u32 len)
111 switch (ctx->dep.type) {
112 case NFT_OFFLOAD_DEP_NETWORK:
113 WARN_ON(len != sizeof(__u16));
114 memcpy(&ctx->dep.l3num, data, sizeof(__u16));
116 case NFT_OFFLOAD_DEP_TRANSPORT:
117 WARN_ON(len != sizeof(__u8));
118 memcpy(&ctx->dep.protonum, data, sizeof(__u8));
123 ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
126 static void nft_flow_offload_common_init(struct flow_cls_common_offload *common,
127 __be16 proto, int priority,
128 struct netlink_ext_ack *extack)
130 common->protocol = proto;
131 common->prio = priority;
132 common->extack = extack;
135 static int nft_setup_cb_call(enum tc_setup_type type, void *type_data,
136 struct list_head *cb_list)
138 struct flow_block_cb *block_cb;
141 list_for_each_entry(block_cb, cb_list, list) {
142 err = block_cb->cb(type, type_data, block_cb->cb_priv);
149 int nft_chain_offload_priority(struct nft_base_chain *basechain)
151 if (basechain->ops.priority <= 0 ||
152 basechain->ops.priority > USHRT_MAX)
158 static void nft_flow_cls_offload_setup(struct flow_cls_offload *cls_flow,
159 const struct nft_base_chain *basechain,
160 const struct nft_rule *rule,
161 const struct nft_flow_rule *flow,
162 struct netlink_ext_ack *extack,
163 enum flow_cls_command command)
165 __be16 proto = ETH_P_ALL;
167 memset(cls_flow, 0, sizeof(*cls_flow));
172 nft_flow_offload_common_init(&cls_flow->common, proto,
173 basechain->ops.priority, extack);
174 cls_flow->command = command;
175 cls_flow->cookie = (unsigned long) rule;
177 cls_flow->rule = flow->rule;
180 static int nft_flow_offload_rule(struct nft_chain *chain,
181 struct nft_rule *rule,
182 struct nft_flow_rule *flow,
183 enum flow_cls_command command)
185 struct netlink_ext_ack extack = {};
186 struct flow_cls_offload cls_flow;
187 struct nft_base_chain *basechain;
189 if (!nft_is_base_chain(chain))
192 basechain = nft_base_chain(chain);
193 nft_flow_cls_offload_setup(&cls_flow, basechain, rule, flow, &extack,
196 return nft_setup_cb_call(TC_SETUP_CLSFLOWER, &cls_flow,
197 &basechain->flow_block.cb_list);
200 static int nft_flow_offload_bind(struct flow_block_offload *bo,
201 struct nft_base_chain *basechain)
203 list_splice(&bo->cb_list, &basechain->flow_block.cb_list);
207 static int nft_flow_offload_unbind(struct flow_block_offload *bo,
208 struct nft_base_chain *basechain)
210 struct flow_block_cb *block_cb, *next;
211 struct flow_cls_offload cls_flow;
212 struct netlink_ext_ack extack;
213 struct nft_chain *chain;
214 struct nft_rule *rule;
216 chain = &basechain->chain;
217 list_for_each_entry(rule, &chain->rules, list) {
218 memset(&extack, 0, sizeof(extack));
219 nft_flow_cls_offload_setup(&cls_flow, basechain, rule, NULL,
220 &extack, FLOW_CLS_DESTROY);
221 nft_setup_cb_call(TC_SETUP_CLSFLOWER, &cls_flow, &bo->cb_list);
224 list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
225 list_del(&block_cb->list);
226 flow_block_cb_free(block_cb);
232 static int nft_block_setup(struct nft_base_chain *basechain,
233 struct flow_block_offload *bo,
234 enum flow_block_command cmd)
239 case FLOW_BLOCK_BIND:
240 err = nft_flow_offload_bind(bo, basechain);
242 case FLOW_BLOCK_UNBIND:
243 err = nft_flow_offload_unbind(bo, basechain);
253 static void nft_flow_block_offload_init(struct flow_block_offload *bo,
255 enum flow_block_command cmd,
256 struct nft_base_chain *basechain,
257 struct netlink_ext_ack *extack)
259 memset(bo, 0, sizeof(*bo));
261 bo->block = &basechain->flow_block;
263 bo->binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
265 INIT_LIST_HEAD(&bo->cb_list);
268 static int nft_block_offload_cmd(struct nft_base_chain *chain,
269 struct net_device *dev,
270 enum flow_block_command cmd)
272 struct netlink_ext_ack extack = {};
273 struct flow_block_offload bo;
276 nft_flow_block_offload_init(&bo, dev_net(dev), cmd, chain, &extack);
278 err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
282 return nft_block_setup(chain, &bo, cmd);
285 static void nft_indr_block_ing_cmd(struct net_device *dev,
286 struct nft_base_chain *chain,
287 flow_indr_block_bind_cb_t *cb,
289 enum flow_block_command cmd)
291 struct netlink_ext_ack extack = {};
292 struct flow_block_offload bo;
297 nft_flow_block_offload_init(&bo, dev_net(dev), cmd, chain, &extack);
299 cb(dev, cb_priv, TC_SETUP_BLOCK, &bo);
301 nft_block_setup(chain, &bo, cmd);
304 static int nft_indr_block_offload_cmd(struct nft_base_chain *chain,
305 struct net_device *dev,
306 enum flow_block_command cmd)
308 struct netlink_ext_ack extack = {};
309 struct flow_block_offload bo;
311 nft_flow_block_offload_init(&bo, dev_net(dev), cmd, chain, &extack);
313 flow_indr_block_call(dev, &bo, cmd);
315 if (list_empty(&bo.cb_list))
318 return nft_block_setup(chain, &bo, cmd);
321 #define FLOW_SETUP_BLOCK TC_SETUP_BLOCK
323 static int nft_chain_offload_cmd(struct nft_base_chain *basechain,
324 struct net_device *dev,
325 enum flow_block_command cmd)
329 if (dev->netdev_ops->ndo_setup_tc)
330 err = nft_block_offload_cmd(basechain, dev, cmd);
332 err = nft_indr_block_offload_cmd(basechain, dev, cmd);
337 static int nft_flow_block_chain(struct nft_base_chain *basechain,
338 const struct net_device *this_dev,
339 enum flow_block_command cmd)
341 struct net_device *dev;
342 struct nft_hook *hook;
345 list_for_each_entry(hook, &basechain->hook_list, list) {
347 if (this_dev && this_dev != dev)
350 err = nft_chain_offload_cmd(basechain, dev, cmd);
351 if (err < 0 && cmd == FLOW_BLOCK_BIND) {
363 list_for_each_entry(hook, &basechain->hook_list, list) {
368 nft_chain_offload_cmd(basechain, dev, FLOW_BLOCK_UNBIND);
373 static int nft_flow_offload_chain(struct nft_chain *chain, u8 *ppolicy,
374 enum flow_block_command cmd)
376 struct nft_base_chain *basechain;
379 if (!nft_is_base_chain(chain))
382 basechain = nft_base_chain(chain);
383 policy = ppolicy ? *ppolicy : basechain->policy;
385 /* Only default policy to accept is supported for now. */
386 if (cmd == FLOW_BLOCK_BIND && policy == NF_DROP)
389 return nft_flow_block_chain(basechain, NULL, cmd);
392 static void nft_flow_rule_offload_abort(struct net *net,
393 struct nft_trans *trans)
397 list_for_each_entry_continue_reverse(trans, &net->nft.commit_list, list) {
398 if (trans->ctx.family != NFPROTO_NETDEV)
401 switch (trans->msg_type) {
402 case NFT_MSG_NEWCHAIN:
403 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) ||
404 nft_trans_chain_update(trans))
407 err = nft_flow_offload_chain(trans->ctx.chain, NULL,
410 case NFT_MSG_DELCHAIN:
411 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
414 err = nft_flow_offload_chain(trans->ctx.chain, NULL,
417 case NFT_MSG_NEWRULE:
418 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
421 err = nft_flow_offload_rule(trans->ctx.chain,
422 nft_trans_rule(trans),
423 NULL, FLOW_CLS_DESTROY);
425 case NFT_MSG_DELRULE:
426 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
429 err = nft_flow_offload_rule(trans->ctx.chain,
430 nft_trans_rule(trans),
431 nft_trans_flow_rule(trans),
436 if (WARN_ON_ONCE(err))
441 int nft_flow_rule_offload_commit(struct net *net)
443 struct nft_trans *trans;
447 list_for_each_entry(trans, &net->nft.commit_list, list) {
448 if (trans->ctx.family != NFPROTO_NETDEV)
451 switch (trans->msg_type) {
452 case NFT_MSG_NEWCHAIN:
453 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) ||
454 nft_trans_chain_update(trans))
457 policy = nft_trans_chain_policy(trans);
458 err = nft_flow_offload_chain(trans->ctx.chain, &policy,
461 case NFT_MSG_DELCHAIN:
462 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
465 policy = nft_trans_chain_policy(trans);
466 err = nft_flow_offload_chain(trans->ctx.chain, &policy,
469 case NFT_MSG_NEWRULE:
470 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
473 if (trans->ctx.flags & NLM_F_REPLACE ||
474 !(trans->ctx.flags & NLM_F_APPEND)) {
478 err = nft_flow_offload_rule(trans->ctx.chain,
479 nft_trans_rule(trans),
480 nft_trans_flow_rule(trans),
483 case NFT_MSG_DELRULE:
484 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
487 err = nft_flow_offload_rule(trans->ctx.chain,
488 nft_trans_rule(trans),
489 NULL, FLOW_CLS_DESTROY);
494 nft_flow_rule_offload_abort(net, trans);
499 list_for_each_entry(trans, &net->nft.commit_list, list) {
500 if (trans->ctx.family != NFPROTO_NETDEV)
503 switch (trans->msg_type) {
504 case NFT_MSG_NEWRULE:
505 case NFT_MSG_DELRULE:
506 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
509 nft_flow_rule_destroy(nft_trans_flow_rule(trans));
519 static struct nft_chain *__nft_offload_get_chain(struct net_device *dev)
521 struct nft_base_chain *basechain;
522 struct net *net = dev_net(dev);
523 struct nft_hook *hook, *found;
524 const struct nft_table *table;
525 struct nft_chain *chain;
527 list_for_each_entry(table, &net->nft.tables, list) {
528 if (table->family != NFPROTO_NETDEV)
531 list_for_each_entry(chain, &table->chains, list) {
532 if (!nft_is_base_chain(chain) ||
533 !(chain->flags & NFT_CHAIN_HW_OFFLOAD))
537 basechain = nft_base_chain(chain);
538 list_for_each_entry(hook, &basechain->hook_list, list) {
539 if (hook->ops.dev != dev)
555 static void nft_indr_block_cb(struct net_device *dev,
556 flow_indr_block_bind_cb_t *cb, void *cb_priv,
557 enum flow_block_command cmd)
559 struct net *net = dev_net(dev);
560 struct nft_chain *chain;
562 mutex_lock(&net->nft.commit_mutex);
563 chain = __nft_offload_get_chain(dev);
565 struct nft_base_chain *basechain;
567 basechain = nft_base_chain(chain);
568 nft_indr_block_ing_cmd(dev, basechain, cb, cb_priv, cmd);
570 mutex_unlock(&net->nft.commit_mutex);
573 static int nft_offload_netdev_event(struct notifier_block *this,
574 unsigned long event, void *ptr)
576 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
577 struct net *net = dev_net(dev);
578 struct nft_chain *chain;
580 mutex_lock(&net->nft.commit_mutex);
581 chain = __nft_offload_get_chain(dev);
583 nft_flow_block_chain(nft_base_chain(chain), dev,
586 mutex_unlock(&net->nft.commit_mutex);
591 static struct flow_indr_block_entry block_ing_entry = {
592 .cb = nft_indr_block_cb,
593 .list = LIST_HEAD_INIT(block_ing_entry.list),
596 static struct notifier_block nft_offload_netdev_notifier = {
597 .notifier_call = nft_offload_netdev_event,
600 int nft_offload_init(void)
604 err = register_netdevice_notifier(&nft_offload_netdev_notifier);
608 flow_indr_add_block_cb(&block_ing_entry);
613 void nft_offload_exit(void)
615 flow_indr_del_block_cb(&block_ing_entry);
616 unregister_netdevice_notifier(&nft_offload_netdev_notifier);