linux/net/netfilter/nft_lookup.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
   4 *
   5 * Development of this code funded by Astaro AG (http://www.astaro.com/)
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/init.h>
  10#include <linux/list.h>
  11#include <linux/rbtree.h>
  12#include <linux/netlink.h>
  13#include <linux/netfilter.h>
  14#include <linux/netfilter/nf_tables.h>
  15#include <net/netfilter/nf_tables.h>
  16#include <net/netfilter/nf_tables_core.h>
  17
  18struct nft_lookup {
  19        struct nft_set                  *set;
  20        enum nft_registers              sreg:8;
  21        enum nft_registers              dreg:8;
  22        bool                            invert;
  23        struct nft_set_binding          binding;
  24};
  25
  26void nft_lookup_eval(const struct nft_expr *expr,
  27                     struct nft_regs *regs,
  28                     const struct nft_pktinfo *pkt)
  29{
  30        const struct nft_lookup *priv = nft_expr_priv(expr);
  31        const struct nft_set *set = priv->set;
  32        const struct nft_set_ext *ext;
  33        bool found;
  34
  35        found = set->ops->lookup(nft_net(pkt), set, &regs->data[priv->sreg],
  36                                 &ext) ^ priv->invert;
  37        if (!found) {
  38                regs->verdict.code = NFT_BREAK;
  39                return;
  40        }
  41
  42        if (set->flags & NFT_SET_MAP)
  43                nft_data_copy(&regs->data[priv->dreg],
  44                              nft_set_ext_data(ext), set->dlen);
  45
  46}
  47
  48static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = {
  49        [NFTA_LOOKUP_SET]       = { .type = NLA_STRING,
  50                                    .len = NFT_SET_MAXNAMELEN - 1 },
  51        [NFTA_LOOKUP_SET_ID]    = { .type = NLA_U32 },
  52        [NFTA_LOOKUP_SREG]      = { .type = NLA_U32 },
  53        [NFTA_LOOKUP_DREG]      = { .type = NLA_U32 },
  54        [NFTA_LOOKUP_FLAGS]     = { .type = NLA_U32 },
  55};
  56
  57static int nft_lookup_init(const struct nft_ctx *ctx,
  58                           const struct nft_expr *expr,
  59                           const struct nlattr * const tb[])
  60{
  61        struct nft_lookup *priv = nft_expr_priv(expr);
  62        u8 genmask = nft_genmask_next(ctx->net);
  63        struct nft_set *set;
  64        u32 flags;
  65        int err;
  66
  67        if (tb[NFTA_LOOKUP_SET] == NULL ||
  68            tb[NFTA_LOOKUP_SREG] == NULL)
  69                return -EINVAL;
  70
  71        set = nft_set_lookup_global(ctx->net, ctx->table, tb[NFTA_LOOKUP_SET],
  72                                    tb[NFTA_LOOKUP_SET_ID], genmask);
  73        if (IS_ERR(set))
  74                return PTR_ERR(set);
  75
  76        if (set->flags & NFT_SET_EVAL)
  77                return -EOPNOTSUPP;
  78
  79        priv->sreg = nft_parse_register(tb[NFTA_LOOKUP_SREG]);
  80        err = nft_validate_register_load(priv->sreg, set->klen);
  81        if (err < 0)
  82                return err;
  83
  84        if (tb[NFTA_LOOKUP_FLAGS]) {
  85                flags = ntohl(nla_get_be32(tb[NFTA_LOOKUP_FLAGS]));
  86
  87                if (flags & ~NFT_LOOKUP_F_INV)
  88                        return -EINVAL;
  89
  90                if (flags & NFT_LOOKUP_F_INV) {
  91                        if (set->flags & NFT_SET_MAP)
  92                                return -EINVAL;
  93                        priv->invert = true;
  94                }
  95        }
  96
  97        if (tb[NFTA_LOOKUP_DREG] != NULL) {
  98                if (priv->invert)
  99                        return -EINVAL;
 100                if (!(set->flags & NFT_SET_MAP))
 101                        return -EINVAL;
 102
 103                priv->dreg = nft_parse_register(tb[NFTA_LOOKUP_DREG]);
 104                err = nft_validate_register_store(ctx, priv->dreg, NULL,
 105                                                  set->dtype, set->dlen);
 106                if (err < 0)
 107                        return err;
 108        } else if (set->flags & NFT_SET_MAP)
 109                return -EINVAL;
 110
 111        priv->binding.flags = set->flags & NFT_SET_MAP;
 112
 113        err = nf_tables_bind_set(ctx, set, &priv->binding);
 114        if (err < 0)
 115                return err;
 116
 117        priv->set = set;
 118        return 0;
 119}
 120
 121static void nft_lookup_deactivate(const struct nft_ctx *ctx,
 122                                  const struct nft_expr *expr,
 123                                  enum nft_trans_phase phase)
 124{
 125        struct nft_lookup *priv = nft_expr_priv(expr);
 126
 127        nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
 128}
 129
 130static void nft_lookup_activate(const struct nft_ctx *ctx,
 131                                const struct nft_expr *expr)
 132{
 133        struct nft_lookup *priv = nft_expr_priv(expr);
 134
 135        priv->set->use++;
 136}
 137
 138static void nft_lookup_destroy(const struct nft_ctx *ctx,
 139                               const struct nft_expr *expr)
 140{
 141        struct nft_lookup *priv = nft_expr_priv(expr);
 142
 143        nf_tables_destroy_set(ctx, priv->set);
 144}
 145
 146static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr)
 147{
 148        const struct nft_lookup *priv = nft_expr_priv(expr);
 149        u32 flags = priv->invert ? NFT_LOOKUP_F_INV : 0;
 150
 151        if (nla_put_string(skb, NFTA_LOOKUP_SET, priv->set->name))
 152                goto nla_put_failure;
 153        if (nft_dump_register(skb, NFTA_LOOKUP_SREG, priv->sreg))
 154                goto nla_put_failure;
 155        if (priv->set->flags & NFT_SET_MAP)
 156                if (nft_dump_register(skb, NFTA_LOOKUP_DREG, priv->dreg))
 157                        goto nla_put_failure;
 158        if (nla_put_be32(skb, NFTA_LOOKUP_FLAGS, htonl(flags)))
 159                goto nla_put_failure;
 160        return 0;
 161
 162nla_put_failure:
 163        return -1;
 164}
 165
 166static int nft_lookup_validate_setelem(const struct nft_ctx *ctx,
 167                                       struct nft_set *set,
 168                                       const struct nft_set_iter *iter,
 169                                       struct nft_set_elem *elem)
 170{
 171        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
 172        struct nft_ctx *pctx = (struct nft_ctx *)ctx;
 173        const struct nft_data *data;
 174        int err;
 175
 176        if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
 177            *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
 178                return 0;
 179
 180        data = nft_set_ext_data(ext);
 181        switch (data->verdict.code) {
 182        case NFT_JUMP:
 183        case NFT_GOTO:
 184                pctx->level++;
 185                err = nft_chain_validate(ctx, data->verdict.chain);
 186                if (err < 0)
 187                        return err;
 188                pctx->level--;
 189                break;
 190        default:
 191                break;
 192        }
 193
 194        return 0;
 195}
 196
 197static int nft_lookup_validate(const struct nft_ctx *ctx,
 198                               const struct nft_expr *expr,
 199                               const struct nft_data **d)
 200{
 201        const struct nft_lookup *priv = nft_expr_priv(expr);
 202        struct nft_set_iter iter;
 203
 204        if (!(priv->set->flags & NFT_SET_MAP) ||
 205            priv->set->dtype != NFT_DATA_VERDICT)
 206                return 0;
 207
 208        iter.genmask    = nft_genmask_next(ctx->net);
 209        iter.skip       = 0;
 210        iter.count      = 0;
 211        iter.err        = 0;
 212        iter.fn         = nft_lookup_validate_setelem;
 213
 214        priv->set->ops->walk(ctx, priv->set, &iter);
 215        if (iter.err < 0)
 216                return iter.err;
 217
 218        return 0;
 219}
 220
 221static const struct nft_expr_ops nft_lookup_ops = {
 222        .type           = &nft_lookup_type,
 223        .size           = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
 224        .eval           = nft_lookup_eval,
 225        .init           = nft_lookup_init,
 226        .activate       = nft_lookup_activate,
 227        .deactivate     = nft_lookup_deactivate,
 228        .destroy        = nft_lookup_destroy,
 229        .dump           = nft_lookup_dump,
 230        .validate       = nft_lookup_validate,
 231};
 232
 233struct nft_expr_type nft_lookup_type __read_mostly = {
 234        .name           = "lookup",
 235        .ops            = &nft_lookup_ops,
 236        .policy         = nft_lookup_policy,
 237        .maxattr        = NFTA_LOOKUP_MAX,
 238        .owner          = THIS_MODULE,
 239};
 240