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        priv->sreg = nft_parse_register(tb[NFTA_LOOKUP_SREG]);
  77        err = nft_validate_register_load(priv->sreg, set->klen);
  78        if (err < 0)
  79                return err;
  80
  81        if (tb[NFTA_LOOKUP_FLAGS]) {
  82                flags = ntohl(nla_get_be32(tb[NFTA_LOOKUP_FLAGS]));
  83
  84                if (flags & ~NFT_LOOKUP_F_INV)
  85                        return -EINVAL;
  86
  87                if (flags & NFT_LOOKUP_F_INV) {
  88                        if (set->flags & NFT_SET_MAP)
  89                                return -EINVAL;
  90                        priv->invert = true;
  91                }
  92        }
  93
  94        if (tb[NFTA_LOOKUP_DREG] != NULL) {
  95                if (priv->invert)
  96                        return -EINVAL;
  97                if (!(set->flags & NFT_SET_MAP))
  98                        return -EINVAL;
  99
 100                priv->dreg = nft_parse_register(tb[NFTA_LOOKUP_DREG]);
 101                err = nft_validate_register_store(ctx, priv->dreg, NULL,
 102                                                  set->dtype, set->dlen);
 103                if (err < 0)
 104                        return err;
 105        } else if (set->flags & NFT_SET_MAP)
 106                return -EINVAL;
 107
 108        priv->binding.flags = set->flags & NFT_SET_MAP;
 109
 110        err = nf_tables_bind_set(ctx, set, &priv->binding);
 111        if (err < 0)
 112                return err;
 113
 114        priv->set = set;
 115        return 0;
 116}
 117
 118static void nft_lookup_deactivate(const struct nft_ctx *ctx,
 119                                  const struct nft_expr *expr,
 120                                  enum nft_trans_phase phase)
 121{
 122        struct nft_lookup *priv = nft_expr_priv(expr);
 123
 124        nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
 125}
 126
 127static void nft_lookup_activate(const struct nft_ctx *ctx,
 128                                const struct nft_expr *expr)
 129{
 130        struct nft_lookup *priv = nft_expr_priv(expr);
 131
 132        priv->set->use++;
 133}
 134
 135static void nft_lookup_destroy(const struct nft_ctx *ctx,
 136                               const struct nft_expr *expr)
 137{
 138        struct nft_lookup *priv = nft_expr_priv(expr);
 139
 140        nf_tables_destroy_set(ctx, priv->set);
 141}
 142
 143static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr)
 144{
 145        const struct nft_lookup *priv = nft_expr_priv(expr);
 146        u32 flags = priv->invert ? NFT_LOOKUP_F_INV : 0;
 147
 148        if (nla_put_string(skb, NFTA_LOOKUP_SET, priv->set->name))
 149                goto nla_put_failure;
 150        if (nft_dump_register(skb, NFTA_LOOKUP_SREG, priv->sreg))
 151                goto nla_put_failure;
 152        if (priv->set->flags & NFT_SET_MAP)
 153                if (nft_dump_register(skb, NFTA_LOOKUP_DREG, priv->dreg))
 154                        goto nla_put_failure;
 155        if (nla_put_be32(skb, NFTA_LOOKUP_FLAGS, htonl(flags)))
 156                goto nla_put_failure;
 157        return 0;
 158
 159nla_put_failure:
 160        return -1;
 161}
 162
 163static int nft_lookup_validate_setelem(const struct nft_ctx *ctx,
 164                                       struct nft_set *set,
 165                                       const struct nft_set_iter *iter,
 166                                       struct nft_set_elem *elem)
 167{
 168        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
 169        struct nft_ctx *pctx = (struct nft_ctx *)ctx;
 170        const struct nft_data *data;
 171        int err;
 172
 173        if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
 174            *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
 175                return 0;
 176
 177        data = nft_set_ext_data(ext);
 178        switch (data->verdict.code) {
 179        case NFT_JUMP:
 180        case NFT_GOTO:
 181                pctx->level++;
 182                err = nft_chain_validate(ctx, data->verdict.chain);
 183                if (err < 0)
 184                        return err;
 185                pctx->level--;
 186                break;
 187        default:
 188                break;
 189        }
 190
 191        return 0;
 192}
 193
 194static int nft_lookup_validate(const struct nft_ctx *ctx,
 195                               const struct nft_expr *expr,
 196                               const struct nft_data **d)
 197{
 198        const struct nft_lookup *priv = nft_expr_priv(expr);
 199        struct nft_set_iter iter;
 200
 201        if (!(priv->set->flags & NFT_SET_MAP) ||
 202            priv->set->dtype != NFT_DATA_VERDICT)
 203                return 0;
 204
 205        iter.genmask    = nft_genmask_next(ctx->net);
 206        iter.skip       = 0;
 207        iter.count      = 0;
 208        iter.err        = 0;
 209        iter.fn         = nft_lookup_validate_setelem;
 210
 211        priv->set->ops->walk(ctx, priv->set, &iter);
 212        if (iter.err < 0)
 213                return iter.err;
 214
 215        return 0;
 216}
 217
 218static const struct nft_expr_ops nft_lookup_ops = {
 219        .type           = &nft_lookup_type,
 220        .size           = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
 221        .eval           = nft_lookup_eval,
 222        .init           = nft_lookup_init,
 223        .activate       = nft_lookup_activate,
 224        .deactivate     = nft_lookup_deactivate,
 225        .destroy        = nft_lookup_destroy,
 226        .dump           = nft_lookup_dump,
 227        .validate       = nft_lookup_validate,
 228};
 229
 230struct nft_expr_type nft_lookup_type __read_mostly = {
 231        .name           = "lookup",
 232        .ops            = &nft_lookup_ops,
 233        .policy         = nft_lookup_policy,
 234        .maxattr        = NFTA_LOOKUP_MAX,
 235        .owner          = THIS_MODULE,
 236};
 237