0001
0002 #include <linux/init.h>
0003 #include <linux/module.h>
0004 #include <linux/netfilter.h>
0005 #include <net/flow_offload.h>
0006 #include <net/netfilter/nf_tables.h>
0007 #include <net/netfilter/nf_tables_offload.h>
0008 #include <net/pkt_cls.h>
0009
0010 static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions)
0011 {
0012 struct nft_flow_rule *flow;
0013
0014 flow = kzalloc(sizeof(struct nft_flow_rule), GFP_KERNEL);
0015 if (!flow)
0016 return NULL;
0017
0018 flow->rule = flow_rule_alloc(num_actions);
0019 if (!flow->rule) {
0020 kfree(flow);
0021 return NULL;
0022 }
0023
0024 flow->rule->match.dissector = &flow->match.dissector;
0025 flow->rule->match.mask = &flow->match.mask;
0026 flow->rule->match.key = &flow->match.key;
0027
0028 return flow;
0029 }
0030
0031 void nft_flow_rule_set_addr_type(struct nft_flow_rule *flow,
0032 enum flow_dissector_key_id addr_type)
0033 {
0034 struct nft_flow_match *match = &flow->match;
0035 struct nft_flow_key *mask = &match->mask;
0036 struct nft_flow_key *key = &match->key;
0037
0038 if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL))
0039 return;
0040
0041 key->control.addr_type = addr_type;
0042 mask->control.addr_type = 0xffff;
0043 match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
0044 match->dissector.offset[FLOW_DISSECTOR_KEY_CONTROL] =
0045 offsetof(struct nft_flow_key, control);
0046 }
0047
0048 struct nft_offload_ethertype {
0049 __be16 value;
0050 __be16 mask;
0051 };
0052
0053 static void nft_flow_rule_transfer_vlan(struct nft_offload_ctx *ctx,
0054 struct nft_flow_rule *flow)
0055 {
0056 struct nft_flow_match *match = &flow->match;
0057 struct nft_offload_ethertype ethertype = {
0058 .value = match->key.basic.n_proto,
0059 .mask = match->mask.basic.n_proto,
0060 };
0061
0062 if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_VLAN) &&
0063 (match->key.vlan.vlan_tpid == htons(ETH_P_8021Q) ||
0064 match->key.vlan.vlan_tpid == htons(ETH_P_8021AD))) {
0065 match->key.basic.n_proto = match->key.cvlan.vlan_tpid;
0066 match->mask.basic.n_proto = match->mask.cvlan.vlan_tpid;
0067 match->key.cvlan.vlan_tpid = match->key.vlan.vlan_tpid;
0068 match->mask.cvlan.vlan_tpid = match->mask.vlan.vlan_tpid;
0069 match->key.vlan.vlan_tpid = ethertype.value;
0070 match->mask.vlan.vlan_tpid = ethertype.mask;
0071 match->dissector.offset[FLOW_DISSECTOR_KEY_CVLAN] =
0072 offsetof(struct nft_flow_key, cvlan);
0073 match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CVLAN);
0074 } else if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_BASIC) &&
0075 (match->key.basic.n_proto == htons(ETH_P_8021Q) ||
0076 match->key.basic.n_proto == htons(ETH_P_8021AD))) {
0077 match->key.basic.n_proto = match->key.vlan.vlan_tpid;
0078 match->mask.basic.n_proto = match->mask.vlan.vlan_tpid;
0079 match->key.vlan.vlan_tpid = ethertype.value;
0080 match->mask.vlan.vlan_tpid = ethertype.mask;
0081 match->dissector.offset[FLOW_DISSECTOR_KEY_VLAN] =
0082 offsetof(struct nft_flow_key, vlan);
0083 match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN);
0084 }
0085 }
0086
0087 struct nft_flow_rule *nft_flow_rule_create(struct net *net,
0088 const struct nft_rule *rule)
0089 {
0090 struct nft_offload_ctx *ctx;
0091 struct nft_flow_rule *flow;
0092 int num_actions = 0, err;
0093 struct nft_expr *expr;
0094
0095 expr = nft_expr_first(rule);
0096 while (nft_expr_more(rule, expr)) {
0097 if (expr->ops->offload_action &&
0098 expr->ops->offload_action(expr))
0099 num_actions++;
0100
0101 expr = nft_expr_next(expr);
0102 }
0103
0104 if (num_actions == 0)
0105 return ERR_PTR(-EOPNOTSUPP);
0106
0107 flow = nft_flow_rule_alloc(num_actions);
0108 if (!flow)
0109 return ERR_PTR(-ENOMEM);
0110
0111 expr = nft_expr_first(rule);
0112
0113 ctx = kzalloc(sizeof(struct nft_offload_ctx), GFP_KERNEL);
0114 if (!ctx) {
0115 err = -ENOMEM;
0116 goto err_out;
0117 }
0118 ctx->net = net;
0119 ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
0120
0121 while (nft_expr_more(rule, expr)) {
0122 if (!expr->ops->offload) {
0123 err = -EOPNOTSUPP;
0124 goto err_out;
0125 }
0126 err = expr->ops->offload(ctx, flow, expr);
0127 if (err < 0)
0128 goto err_out;
0129
0130 expr = nft_expr_next(expr);
0131 }
0132 nft_flow_rule_transfer_vlan(ctx, flow);
0133
0134 flow->proto = ctx->dep.l3num;
0135 kfree(ctx);
0136
0137 return flow;
0138 err_out:
0139 kfree(ctx);
0140 nft_flow_rule_destroy(flow);
0141
0142 return ERR_PTR(err);
0143 }
0144
0145 void nft_flow_rule_destroy(struct nft_flow_rule *flow)
0146 {
0147 struct flow_action_entry *entry;
0148 int i;
0149
0150 flow_action_for_each(i, entry, &flow->rule->action) {
0151 switch (entry->id) {
0152 case FLOW_ACTION_REDIRECT:
0153 case FLOW_ACTION_MIRRED:
0154 dev_put(entry->dev);
0155 break;
0156 default:
0157 break;
0158 }
0159 }
0160 kfree(flow->rule);
0161 kfree(flow);
0162 }
0163
0164 void nft_offload_set_dependency(struct nft_offload_ctx *ctx,
0165 enum nft_offload_dep_type type)
0166 {
0167 ctx->dep.type = type;
0168 }
0169
0170 void nft_offload_update_dependency(struct nft_offload_ctx *ctx,
0171 const void *data, u32 len)
0172 {
0173 switch (ctx->dep.type) {
0174 case NFT_OFFLOAD_DEP_NETWORK:
0175 WARN_ON(len != sizeof(__u16));
0176 memcpy(&ctx->dep.l3num, data, sizeof(__u16));
0177 break;
0178 case NFT_OFFLOAD_DEP_TRANSPORT:
0179 WARN_ON(len != sizeof(__u8));
0180 memcpy(&ctx->dep.protonum, data, sizeof(__u8));
0181 break;
0182 default:
0183 break;
0184 }
0185 ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
0186 }
0187
0188 static void nft_flow_offload_common_init(struct flow_cls_common_offload *common,
0189 __be16 proto, int priority,
0190 struct netlink_ext_ack *extack)
0191 {
0192 common->protocol = proto;
0193 common->prio = priority;
0194 common->extack = extack;
0195 }
0196
0197 static int nft_setup_cb_call(enum tc_setup_type type, void *type_data,
0198 struct list_head *cb_list)
0199 {
0200 struct flow_block_cb *block_cb;
0201 int err;
0202
0203 list_for_each_entry(block_cb, cb_list, list) {
0204 err = block_cb->cb(type, type_data, block_cb->cb_priv);
0205 if (err < 0)
0206 return err;
0207 }
0208 return 0;
0209 }
0210
0211 static int nft_chain_offload_priority(const struct nft_base_chain *basechain)
0212 {
0213 if (basechain->ops.priority <= 0 ||
0214 basechain->ops.priority > USHRT_MAX)
0215 return -1;
0216
0217 return 0;
0218 }
0219
0220 bool nft_chain_offload_support(const struct nft_base_chain *basechain)
0221 {
0222 struct net_device *dev;
0223 struct nft_hook *hook;
0224
0225 if (nft_chain_offload_priority(basechain) < 0)
0226 return false;
0227
0228 list_for_each_entry(hook, &basechain->hook_list, list) {
0229 if (hook->ops.pf != NFPROTO_NETDEV ||
0230 hook->ops.hooknum != NF_NETDEV_INGRESS)
0231 return false;
0232
0233 dev = hook->ops.dev;
0234 if (!dev->netdev_ops->ndo_setup_tc && !flow_indr_dev_exists())
0235 return false;
0236 }
0237
0238 return true;
0239 }
0240
0241 static void nft_flow_cls_offload_setup(struct flow_cls_offload *cls_flow,
0242 const struct nft_base_chain *basechain,
0243 const struct nft_rule *rule,
0244 const struct nft_flow_rule *flow,
0245 struct netlink_ext_ack *extack,
0246 enum flow_cls_command command)
0247 {
0248 __be16 proto = ETH_P_ALL;
0249
0250 memset(cls_flow, 0, sizeof(*cls_flow));
0251
0252 if (flow)
0253 proto = flow->proto;
0254
0255 nft_flow_offload_common_init(&cls_flow->common, proto,
0256 basechain->ops.priority, extack);
0257 cls_flow->command = command;
0258 cls_flow->cookie = (unsigned long) rule;
0259 if (flow)
0260 cls_flow->rule = flow->rule;
0261 }
0262
0263 static int nft_flow_offload_cmd(const struct nft_chain *chain,
0264 const struct nft_rule *rule,
0265 struct nft_flow_rule *flow,
0266 enum flow_cls_command command,
0267 struct flow_cls_offload *cls_flow)
0268 {
0269 struct netlink_ext_ack extack = {};
0270 struct nft_base_chain *basechain;
0271
0272 if (!nft_is_base_chain(chain))
0273 return -EOPNOTSUPP;
0274
0275 basechain = nft_base_chain(chain);
0276 nft_flow_cls_offload_setup(cls_flow, basechain, rule, flow, &extack,
0277 command);
0278
0279 return nft_setup_cb_call(TC_SETUP_CLSFLOWER, cls_flow,
0280 &basechain->flow_block.cb_list);
0281 }
0282
0283 static int nft_flow_offload_rule(const struct nft_chain *chain,
0284 struct nft_rule *rule,
0285 struct nft_flow_rule *flow,
0286 enum flow_cls_command command)
0287 {
0288 struct flow_cls_offload cls_flow;
0289
0290 return nft_flow_offload_cmd(chain, rule, flow, command, &cls_flow);
0291 }
0292
0293 int nft_flow_rule_stats(const struct nft_chain *chain,
0294 const struct nft_rule *rule)
0295 {
0296 struct flow_cls_offload cls_flow = {};
0297 struct nft_expr *expr, *next;
0298 int err;
0299
0300 err = nft_flow_offload_cmd(chain, rule, NULL, FLOW_CLS_STATS,
0301 &cls_flow);
0302 if (err < 0)
0303 return err;
0304
0305 nft_rule_for_each_expr(expr, next, rule) {
0306 if (expr->ops->offload_stats)
0307 expr->ops->offload_stats(expr, &cls_flow.stats);
0308 }
0309
0310 return 0;
0311 }
0312
0313 static int nft_flow_offload_bind(struct flow_block_offload *bo,
0314 struct nft_base_chain *basechain)
0315 {
0316 list_splice(&bo->cb_list, &basechain->flow_block.cb_list);
0317 return 0;
0318 }
0319
0320 static int nft_flow_offload_unbind(struct flow_block_offload *bo,
0321 struct nft_base_chain *basechain)
0322 {
0323 struct flow_block_cb *block_cb, *next;
0324 struct flow_cls_offload cls_flow;
0325 struct netlink_ext_ack extack;
0326 struct nft_chain *chain;
0327 struct nft_rule *rule;
0328
0329 chain = &basechain->chain;
0330 list_for_each_entry(rule, &chain->rules, list) {
0331 memset(&extack, 0, sizeof(extack));
0332 nft_flow_cls_offload_setup(&cls_flow, basechain, rule, NULL,
0333 &extack, FLOW_CLS_DESTROY);
0334 nft_setup_cb_call(TC_SETUP_CLSFLOWER, &cls_flow, &bo->cb_list);
0335 }
0336
0337 list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
0338 list_del(&block_cb->list);
0339 flow_block_cb_free(block_cb);
0340 }
0341
0342 return 0;
0343 }
0344
0345 static int nft_block_setup(struct nft_base_chain *basechain,
0346 struct flow_block_offload *bo,
0347 enum flow_block_command cmd)
0348 {
0349 int err;
0350
0351 switch (cmd) {
0352 case FLOW_BLOCK_BIND:
0353 err = nft_flow_offload_bind(bo, basechain);
0354 break;
0355 case FLOW_BLOCK_UNBIND:
0356 err = nft_flow_offload_unbind(bo, basechain);
0357 break;
0358 default:
0359 WARN_ON_ONCE(1);
0360 err = -EOPNOTSUPP;
0361 }
0362
0363 return err;
0364 }
0365
0366 static void nft_flow_block_offload_init(struct flow_block_offload *bo,
0367 struct net *net,
0368 enum flow_block_command cmd,
0369 struct nft_base_chain *basechain,
0370 struct netlink_ext_ack *extack)
0371 {
0372 memset(bo, 0, sizeof(*bo));
0373 bo->net = net;
0374 bo->block = &basechain->flow_block;
0375 bo->command = cmd;
0376 bo->binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
0377 bo->extack = extack;
0378 bo->cb_list_head = &basechain->flow_block.cb_list;
0379 INIT_LIST_HEAD(&bo->cb_list);
0380 }
0381
0382 static int nft_block_offload_cmd(struct nft_base_chain *chain,
0383 struct net_device *dev,
0384 enum flow_block_command cmd)
0385 {
0386 struct netlink_ext_ack extack = {};
0387 struct flow_block_offload bo;
0388 int err;
0389
0390 nft_flow_block_offload_init(&bo, dev_net(dev), cmd, chain, &extack);
0391
0392 err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
0393 if (err < 0)
0394 return err;
0395
0396 return nft_block_setup(chain, &bo, cmd);
0397 }
0398
0399 static void nft_indr_block_cleanup(struct flow_block_cb *block_cb)
0400 {
0401 struct nft_base_chain *basechain = block_cb->indr.data;
0402 struct net_device *dev = block_cb->indr.dev;
0403 struct netlink_ext_ack extack = {};
0404 struct nftables_pernet *nft_net;
0405 struct net *net = dev_net(dev);
0406 struct flow_block_offload bo;
0407
0408 nft_flow_block_offload_init(&bo, dev_net(dev), FLOW_BLOCK_UNBIND,
0409 basechain, &extack);
0410 nft_net = nft_pernet(net);
0411 mutex_lock(&nft_net->commit_mutex);
0412 list_del(&block_cb->driver_list);
0413 list_move(&block_cb->list, &bo.cb_list);
0414 nft_flow_offload_unbind(&bo, basechain);
0415 mutex_unlock(&nft_net->commit_mutex);
0416 }
0417
0418 static int nft_indr_block_offload_cmd(struct nft_base_chain *basechain,
0419 struct net_device *dev,
0420 enum flow_block_command cmd)
0421 {
0422 struct netlink_ext_ack extack = {};
0423 struct flow_block_offload bo;
0424 int err;
0425
0426 nft_flow_block_offload_init(&bo, dev_net(dev), cmd, basechain, &extack);
0427
0428 err = flow_indr_dev_setup_offload(dev, NULL, TC_SETUP_BLOCK, basechain, &bo,
0429 nft_indr_block_cleanup);
0430 if (err < 0)
0431 return err;
0432
0433 if (list_empty(&bo.cb_list))
0434 return -EOPNOTSUPP;
0435
0436 return nft_block_setup(basechain, &bo, cmd);
0437 }
0438
0439 static int nft_chain_offload_cmd(struct nft_base_chain *basechain,
0440 struct net_device *dev,
0441 enum flow_block_command cmd)
0442 {
0443 int err;
0444
0445 if (dev->netdev_ops->ndo_setup_tc)
0446 err = nft_block_offload_cmd(basechain, dev, cmd);
0447 else
0448 err = nft_indr_block_offload_cmd(basechain, dev, cmd);
0449
0450 return err;
0451 }
0452
0453 static int nft_flow_block_chain(struct nft_base_chain *basechain,
0454 const struct net_device *this_dev,
0455 enum flow_block_command cmd)
0456 {
0457 struct net_device *dev;
0458 struct nft_hook *hook;
0459 int err, i = 0;
0460
0461 list_for_each_entry(hook, &basechain->hook_list, list) {
0462 dev = hook->ops.dev;
0463 if (this_dev && this_dev != dev)
0464 continue;
0465
0466 err = nft_chain_offload_cmd(basechain, dev, cmd);
0467 if (err < 0 && cmd == FLOW_BLOCK_BIND) {
0468 if (!this_dev)
0469 goto err_flow_block;
0470
0471 return err;
0472 }
0473 i++;
0474 }
0475
0476 return 0;
0477
0478 err_flow_block:
0479 list_for_each_entry(hook, &basechain->hook_list, list) {
0480 if (i-- <= 0)
0481 break;
0482
0483 dev = hook->ops.dev;
0484 nft_chain_offload_cmd(basechain, dev, FLOW_BLOCK_UNBIND);
0485 }
0486 return err;
0487 }
0488
0489 static int nft_flow_offload_chain(struct nft_chain *chain, u8 *ppolicy,
0490 enum flow_block_command cmd)
0491 {
0492 struct nft_base_chain *basechain;
0493 u8 policy;
0494
0495 if (!nft_is_base_chain(chain))
0496 return -EOPNOTSUPP;
0497
0498 basechain = nft_base_chain(chain);
0499 policy = ppolicy ? *ppolicy : basechain->policy;
0500
0501
0502 if (cmd == FLOW_BLOCK_BIND && policy == NF_DROP)
0503 return -EOPNOTSUPP;
0504
0505 return nft_flow_block_chain(basechain, NULL, cmd);
0506 }
0507
0508 static void nft_flow_rule_offload_abort(struct net *net,
0509 struct nft_trans *trans)
0510 {
0511 struct nftables_pernet *nft_net = nft_pernet(net);
0512 int err = 0;
0513
0514 list_for_each_entry_continue_reverse(trans, &nft_net->commit_list, list) {
0515 if (trans->ctx.family != NFPROTO_NETDEV)
0516 continue;
0517
0518 switch (trans->msg_type) {
0519 case NFT_MSG_NEWCHAIN:
0520 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) ||
0521 nft_trans_chain_update(trans))
0522 continue;
0523
0524 err = nft_flow_offload_chain(trans->ctx.chain, NULL,
0525 FLOW_BLOCK_UNBIND);
0526 break;
0527 case NFT_MSG_DELCHAIN:
0528 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
0529 continue;
0530
0531 err = nft_flow_offload_chain(trans->ctx.chain, NULL,
0532 FLOW_BLOCK_BIND);
0533 break;
0534 case NFT_MSG_NEWRULE:
0535 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
0536 continue;
0537
0538 err = nft_flow_offload_rule(trans->ctx.chain,
0539 nft_trans_rule(trans),
0540 NULL, FLOW_CLS_DESTROY);
0541 break;
0542 case NFT_MSG_DELRULE:
0543 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
0544 continue;
0545
0546 err = nft_flow_offload_rule(trans->ctx.chain,
0547 nft_trans_rule(trans),
0548 nft_trans_flow_rule(trans),
0549 FLOW_CLS_REPLACE);
0550 break;
0551 }
0552
0553 if (WARN_ON_ONCE(err))
0554 break;
0555 }
0556 }
0557
0558 int nft_flow_rule_offload_commit(struct net *net)
0559 {
0560 struct nftables_pernet *nft_net = nft_pernet(net);
0561 struct nft_trans *trans;
0562 int err = 0;
0563 u8 policy;
0564
0565 list_for_each_entry(trans, &nft_net->commit_list, list) {
0566 if (trans->ctx.family != NFPROTO_NETDEV)
0567 continue;
0568
0569 switch (trans->msg_type) {
0570 case NFT_MSG_NEWCHAIN:
0571 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) ||
0572 nft_trans_chain_update(trans))
0573 continue;
0574
0575 policy = nft_trans_chain_policy(trans);
0576 err = nft_flow_offload_chain(trans->ctx.chain, &policy,
0577 FLOW_BLOCK_BIND);
0578 break;
0579 case NFT_MSG_DELCHAIN:
0580 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
0581 continue;
0582
0583 policy = nft_trans_chain_policy(trans);
0584 err = nft_flow_offload_chain(trans->ctx.chain, &policy,
0585 FLOW_BLOCK_UNBIND);
0586 break;
0587 case NFT_MSG_NEWRULE:
0588 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
0589 continue;
0590
0591 if (trans->ctx.flags & NLM_F_REPLACE ||
0592 !(trans->ctx.flags & NLM_F_APPEND)) {
0593 err = -EOPNOTSUPP;
0594 break;
0595 }
0596 err = nft_flow_offload_rule(trans->ctx.chain,
0597 nft_trans_rule(trans),
0598 nft_trans_flow_rule(trans),
0599 FLOW_CLS_REPLACE);
0600 break;
0601 case NFT_MSG_DELRULE:
0602 if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
0603 continue;
0604
0605 err = nft_flow_offload_rule(trans->ctx.chain,
0606 nft_trans_rule(trans),
0607 NULL, FLOW_CLS_DESTROY);
0608 break;
0609 }
0610
0611 if (err) {
0612 nft_flow_rule_offload_abort(net, trans);
0613 break;
0614 }
0615 }
0616
0617 return err;
0618 }
0619
0620 static struct nft_chain *__nft_offload_get_chain(const struct nftables_pernet *nft_net,
0621 struct net_device *dev)
0622 {
0623 struct nft_base_chain *basechain;
0624 struct nft_hook *hook, *found;
0625 const struct nft_table *table;
0626 struct nft_chain *chain;
0627
0628 list_for_each_entry(table, &nft_net->tables, list) {
0629 if (table->family != NFPROTO_NETDEV)
0630 continue;
0631
0632 list_for_each_entry(chain, &table->chains, list) {
0633 if (!nft_is_base_chain(chain) ||
0634 !(chain->flags & NFT_CHAIN_HW_OFFLOAD))
0635 continue;
0636
0637 found = NULL;
0638 basechain = nft_base_chain(chain);
0639 list_for_each_entry(hook, &basechain->hook_list, list) {
0640 if (hook->ops.dev != dev)
0641 continue;
0642
0643 found = hook;
0644 break;
0645 }
0646 if (!found)
0647 continue;
0648
0649 return chain;
0650 }
0651 }
0652
0653 return NULL;
0654 }
0655
0656 static int nft_offload_netdev_event(struct notifier_block *this,
0657 unsigned long event, void *ptr)
0658 {
0659 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
0660 struct nftables_pernet *nft_net;
0661 struct net *net = dev_net(dev);
0662 struct nft_chain *chain;
0663
0664 if (event != NETDEV_UNREGISTER)
0665 return NOTIFY_DONE;
0666
0667 nft_net = nft_pernet(net);
0668 mutex_lock(&nft_net->commit_mutex);
0669 chain = __nft_offload_get_chain(nft_net, dev);
0670 if (chain)
0671 nft_flow_block_chain(nft_base_chain(chain), dev,
0672 FLOW_BLOCK_UNBIND);
0673
0674 mutex_unlock(&nft_net->commit_mutex);
0675
0676 return NOTIFY_DONE;
0677 }
0678
0679 static struct notifier_block nft_offload_netdev_notifier = {
0680 .notifier_call = nft_offload_netdev_event,
0681 };
0682
0683 int nft_offload_init(void)
0684 {
0685 return register_netdevice_notifier(&nft_offload_netdev_notifier);
0686 }
0687
0688 void nft_offload_exit(void)
0689 {
0690 unregister_netdevice_notifier(&nft_offload_netdev_notifier);
0691 }