linux/net/netfilter/nft_cmp.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * Development of this code funded by Astaro AG (http://www.astaro.com/)
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/init.h>
  13#include <linux/module.h>
  14#include <linux/netlink.h>
  15#include <linux/netfilter.h>
  16#include <linux/netfilter/nf_tables.h>
  17#include <net/netfilter/nf_tables_core.h>
  18#include <net/netfilter/nf_tables.h>
  19
  20struct nft_cmp_expr {
  21        struct nft_data         data;
  22        enum nft_registers      sreg:8;
  23        u8                      len;
  24        enum nft_cmp_ops        op:8;
  25};
  26
  27static void nft_cmp_eval(const struct nft_expr *expr,
  28                         struct nft_data data[NFT_REG_MAX + 1],
  29                         const struct nft_pktinfo *pkt)
  30{
  31        const struct nft_cmp_expr *priv = nft_expr_priv(expr);
  32        int d;
  33
  34        d = nft_data_cmp(&data[priv->sreg], &priv->data, priv->len);
  35        switch (priv->op) {
  36        case NFT_CMP_EQ:
  37                if (d != 0)
  38                        goto mismatch;
  39                break;
  40        case NFT_CMP_NEQ:
  41                if (d == 0)
  42                        goto mismatch;
  43                break;
  44        case NFT_CMP_LT:
  45                if (d == 0)
  46                        goto mismatch;
  47        case NFT_CMP_LTE:
  48                if (d > 0)
  49                        goto mismatch;
  50                break;
  51        case NFT_CMP_GT:
  52                if (d == 0)
  53                        goto mismatch;
  54        case NFT_CMP_GTE:
  55                if (d < 0)
  56                        goto mismatch;
  57                break;
  58        }
  59        return;
  60
  61mismatch:
  62        data[NFT_REG_VERDICT].verdict = NFT_BREAK;
  63}
  64
  65static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = {
  66        [NFTA_CMP_SREG]         = { .type = NLA_U32 },
  67        [NFTA_CMP_OP]           = { .type = NLA_U32 },
  68        [NFTA_CMP_DATA]         = { .type = NLA_NESTED },
  69};
  70
  71static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
  72                        const struct nlattr * const tb[])
  73{
  74        struct nft_cmp_expr *priv = nft_expr_priv(expr);
  75        struct nft_data_desc desc;
  76        int err;
  77
  78        priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
  79        priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
  80
  81        err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
  82        BUG_ON(err < 0);
  83
  84        priv->len = desc.len;
  85        return 0;
  86}
  87
  88static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr)
  89{
  90        const struct nft_cmp_expr *priv = nft_expr_priv(expr);
  91
  92        if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg)))
  93                goto nla_put_failure;
  94        if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op)))
  95                goto nla_put_failure;
  96
  97        if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data,
  98                          NFT_DATA_VALUE, priv->len) < 0)
  99                goto nla_put_failure;
 100        return 0;
 101
 102nla_put_failure:
 103        return -1;
 104}
 105
 106static struct nft_expr_type nft_cmp_type;
 107static const struct nft_expr_ops nft_cmp_ops = {
 108        .type           = &nft_cmp_type,
 109        .size           = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)),
 110        .eval           = nft_cmp_eval,
 111        .init           = nft_cmp_init,
 112        .dump           = nft_cmp_dump,
 113};
 114
 115static int nft_cmp_fast_init(const struct nft_ctx *ctx,
 116                             const struct nft_expr *expr,
 117                             const struct nlattr * const tb[])
 118{
 119        struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
 120        struct nft_data_desc desc;
 121        struct nft_data data;
 122        u32 mask;
 123        int err;
 124
 125        priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
 126
 127        err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
 128        BUG_ON(err < 0);
 129        desc.len *= BITS_PER_BYTE;
 130
 131        mask = nft_cmp_fast_mask(desc.len);
 132        priv->data = data.data[0] & mask;
 133        priv->len  = desc.len;
 134        return 0;
 135}
 136
 137static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr)
 138{
 139        const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
 140        struct nft_data data;
 141
 142        if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg)))
 143                goto nla_put_failure;
 144        if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ)))
 145                goto nla_put_failure;
 146
 147        data.data[0] = priv->data;
 148        if (nft_data_dump(skb, NFTA_CMP_DATA, &data,
 149                          NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0)
 150                goto nla_put_failure;
 151        return 0;
 152
 153nla_put_failure:
 154        return -1;
 155}
 156
 157const struct nft_expr_ops nft_cmp_fast_ops = {
 158        .type           = &nft_cmp_type,
 159        .size           = NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)),
 160        .eval           = NULL, /* inlined */
 161        .init           = nft_cmp_fast_init,
 162        .dump           = nft_cmp_fast_dump,
 163};
 164
 165static const struct nft_expr_ops *
 166nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
 167{
 168        struct nft_data_desc desc;
 169        struct nft_data data;
 170        enum nft_registers sreg;
 171        enum nft_cmp_ops op;
 172        int err;
 173
 174        if (tb[NFTA_CMP_SREG] == NULL ||
 175            tb[NFTA_CMP_OP] == NULL ||
 176            tb[NFTA_CMP_DATA] == NULL)
 177                return ERR_PTR(-EINVAL);
 178
 179        sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
 180        err = nft_validate_input_register(sreg);
 181        if (err < 0)
 182                return ERR_PTR(err);
 183
 184        op = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
 185        switch (op) {
 186        case NFT_CMP_EQ:
 187        case NFT_CMP_NEQ:
 188        case NFT_CMP_LT:
 189        case NFT_CMP_LTE:
 190        case NFT_CMP_GT:
 191        case NFT_CMP_GTE:
 192                break;
 193        default:
 194                return ERR_PTR(-EINVAL);
 195        }
 196
 197        err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
 198        if (err < 0)
 199                return ERR_PTR(err);
 200
 201        if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ)
 202                return &nft_cmp_fast_ops;
 203        else
 204                return &nft_cmp_ops;
 205}
 206
 207static struct nft_expr_type nft_cmp_type __read_mostly = {
 208        .name           = "cmp",
 209        .select_ops     = nft_cmp_select_ops,
 210        .policy         = nft_cmp_policy,
 211        .maxattr        = NFTA_CMP_MAX,
 212        .owner          = THIS_MODULE,
 213};
 214
 215int __init nft_cmp_module_init(void)
 216{
 217        return nft_register_expr(&nft_cmp_type);
 218}
 219
 220void nft_cmp_module_exit(void)
 221{
 222        nft_unregister_expr(&nft_cmp_type);
 223}
 224