linux/net/netfilter/nf_tables_offload.c
<<
>>
Prefs
   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
  10static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions)
  11{
  12        struct nft_flow_rule *flow;
  13
  14        flow = kzalloc(sizeof(struct nft_flow_rule), GFP_KERNEL);
  15        if (!flow)
  16                return NULL;
  17
  18        flow->rule = flow_rule_alloc(num_actions);
  19        if (!flow->rule) {
  20                kfree(flow);
  21                return NULL;
  22        }
  23
  24        flow->rule->match.dissector     = &flow->match.dissector;
  25        flow->rule->match.mask          = &flow->match.mask;
  26        flow->rule->match.key           = &flow->match.key;
  27
  28        return flow;
  29}
  30
  31struct nft_flow_rule *nft_flow_rule_create(const struct nft_rule *rule)
  32{
  33        struct nft_offload_ctx ctx = {
  34                .dep    = {
  35                        .type   = NFT_OFFLOAD_DEP_UNSPEC,
  36                },
  37        };
  38        struct nft_flow_rule *flow;
  39        int num_actions = 0, err;
  40        struct nft_expr *expr;
  41
  42        expr = nft_expr_first(rule);
  43        while (expr->ops && expr != nft_expr_last(rule)) {
  44                if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION)
  45                        num_actions++;
  46
  47                expr = nft_expr_next(expr);
  48        }
  49
  50        flow = nft_flow_rule_alloc(num_actions);
  51        if (!flow)
  52                return ERR_PTR(-ENOMEM);
  53
  54        expr = nft_expr_first(rule);
  55        while (expr->ops && expr != nft_expr_last(rule)) {
  56                if (!expr->ops->offload) {
  57                        err = -EOPNOTSUPP;
  58                        goto err_out;
  59                }
  60                err = expr->ops->offload(&ctx, flow, expr);
  61                if (err < 0)
  62                        goto err_out;
  63
  64                expr = nft_expr_next(expr);
  65        }
  66        flow->proto = ctx.dep.l3num;
  67
  68        return flow;
  69err_out:
  70        nft_flow_rule_destroy(flow);
  71
  72        return ERR_PTR(err);
  73}
  74
  75void nft_flow_rule_destroy(struct nft_flow_rule *flow)
  76{
  77        kfree(flow->rule);
  78        kfree(flow);
  79}
  80
  81void nft_offload_set_dependency(struct nft_offload_ctx *ctx,
  82                                enum nft_offload_dep_type type)
  83{
  84        ctx->dep.type = type;
  85}
  86
  87void nft_offload_update_dependency(struct nft_offload_ctx *ctx,
  88                                   const void *data, u32 len)
  89{
  90        switch (ctx->dep.type) {
  91        case NFT_OFFLOAD_DEP_NETWORK:
  92                WARN_ON(len != sizeof(__u16));
  93                memcpy(&ctx->dep.l3num, data, sizeof(__u16));
  94                break;
  95        case NFT_OFFLOAD_DEP_TRANSPORT:
  96                WARN_ON(len != sizeof(__u8));
  97                memcpy(&ctx->dep.protonum, data, sizeof(__u8));
  98                break;
  99        default:
 100                break;
 101        }
 102        ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
 103}
 104
 105static void nft_flow_offload_common_init(struct flow_cls_common_offload *common,
 106                                         __be16 proto, int priority,
 107                                         struct netlink_ext_ack *extack)
 108{
 109        common->protocol = proto;
 110        common->prio = priority;
 111        common->extack = extack;
 112}
 113
 114static int nft_setup_cb_call(struct nft_base_chain *basechain,
 115                             enum tc_setup_type type, void *type_data)
 116{
 117        struct flow_block_cb *block_cb;
 118        int err;
 119
 120        list_for_each_entry(block_cb, &basechain->flow_block.cb_list, list) {
 121                err = block_cb->cb(type, type_data, block_cb->cb_priv);
 122                if (err < 0)
 123                        return err;
 124        }
 125        return 0;
 126}
 127
 128int nft_chain_offload_priority(struct nft_base_chain *basechain)
 129{
 130        if (basechain->ops.priority <= 0 ||
 131            basechain->ops.priority > USHRT_MAX)
 132                return -1;
 133
 134        return 0;
 135}
 136
 137static int nft_flow_offload_rule(struct nft_trans *trans,
 138                                 enum flow_cls_command command)
 139{
 140        struct nft_flow_rule *flow = nft_trans_flow_rule(trans);
 141        struct nft_rule *rule = nft_trans_rule(trans);
 142        struct flow_cls_offload cls_flow = {};
 143        struct nft_base_chain *basechain;
 144        struct netlink_ext_ack extack;
 145        __be16 proto = ETH_P_ALL;
 146
 147        if (!nft_is_base_chain(trans->ctx.chain))
 148                return -EOPNOTSUPP;
 149
 150        basechain = nft_base_chain(trans->ctx.chain);
 151
 152        if (flow)
 153                proto = flow->proto;
 154
 155        nft_flow_offload_common_init(&cls_flow.common, proto,
 156                                     basechain->ops.priority, &extack);
 157        cls_flow.command = command;
 158        cls_flow.cookie = (unsigned long) rule;
 159        if (flow)
 160                cls_flow.rule = flow->rule;
 161
 162        return nft_setup_cb_call(basechain, TC_SETUP_CLSFLOWER, &cls_flow);
 163}
 164
 165static int nft_flow_offload_bind(struct flow_block_offload *bo,
 166                                 struct nft_base_chain *basechain)
 167{
 168        list_splice(&bo->cb_list, &basechain->flow_block.cb_list);
 169        return 0;
 170}
 171
 172static int nft_flow_offload_unbind(struct flow_block_offload *bo,
 173                                   struct nft_base_chain *basechain)
 174{
 175        struct flow_block_cb *block_cb, *next;
 176
 177        list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
 178                list_del(&block_cb->list);
 179                flow_block_cb_free(block_cb);
 180        }
 181
 182        return 0;
 183}
 184
 185#define FLOW_SETUP_BLOCK TC_SETUP_BLOCK
 186
 187static int nft_flow_offload_chain(struct nft_trans *trans,
 188                                  enum flow_block_command cmd)
 189{
 190        struct nft_chain *chain = trans->ctx.chain;
 191        struct netlink_ext_ack extack = {};
 192        struct flow_block_offload bo = {};
 193        struct nft_base_chain *basechain;
 194        struct net_device *dev;
 195        int err;
 196
 197        if (!nft_is_base_chain(chain))
 198                return -EOPNOTSUPP;
 199
 200        basechain = nft_base_chain(chain);
 201        dev = basechain->ops.dev;
 202        if (!dev || !dev->netdev_ops->ndo_setup_tc)
 203                return -EOPNOTSUPP;
 204
 205        /* Only default policy to accept is supported for now. */
 206        if (cmd == FLOW_BLOCK_BIND &&
 207            nft_trans_chain_policy(trans) != -1 &&
 208            nft_trans_chain_policy(trans) != NF_ACCEPT)
 209                return -EOPNOTSUPP;
 210
 211        bo.command = cmd;
 212        bo.block = &basechain->flow_block;
 213        bo.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
 214        bo.extack = &extack;
 215        INIT_LIST_HEAD(&bo.cb_list);
 216
 217        err = dev->netdev_ops->ndo_setup_tc(dev, FLOW_SETUP_BLOCK, &bo);
 218        if (err < 0)
 219                return err;
 220
 221        switch (cmd) {
 222        case FLOW_BLOCK_BIND:
 223                err = nft_flow_offload_bind(&bo, basechain);
 224                break;
 225        case FLOW_BLOCK_UNBIND:
 226                err = nft_flow_offload_unbind(&bo, basechain);
 227                break;
 228        }
 229
 230        return err;
 231}
 232
 233int nft_flow_rule_offload_commit(struct net *net)
 234{
 235        struct nft_trans *trans;
 236        int err = 0;
 237
 238        list_for_each_entry(trans, &net->nft.commit_list, list) {
 239                if (trans->ctx.family != NFPROTO_NETDEV)
 240                        continue;
 241
 242                switch (trans->msg_type) {
 243                case NFT_MSG_NEWCHAIN:
 244                        if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
 245                                continue;
 246
 247                        err = nft_flow_offload_chain(trans, FLOW_BLOCK_BIND);
 248                        break;
 249                case NFT_MSG_DELCHAIN:
 250                        if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
 251                                continue;
 252
 253                        err = nft_flow_offload_chain(trans, FLOW_BLOCK_UNBIND);
 254                        break;
 255                case NFT_MSG_NEWRULE:
 256                        if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
 257                                continue;
 258
 259                        if (trans->ctx.flags & NLM_F_REPLACE ||
 260                            !(trans->ctx.flags & NLM_F_APPEND))
 261                                return -EOPNOTSUPP;
 262
 263                        err = nft_flow_offload_rule(trans, FLOW_CLS_REPLACE);
 264                        nft_flow_rule_destroy(nft_trans_flow_rule(trans));
 265                        break;
 266                case NFT_MSG_DELRULE:
 267                        if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
 268                                continue;
 269
 270                        err = nft_flow_offload_rule(trans, FLOW_CLS_DESTROY);
 271                        break;
 272                }
 273
 274                if (err)
 275                        return err;
 276        }
 277
 278        return err;
 279}
 280