linux/net/netfilter/nft_hash.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2016 Laura Garcia <nevola@gmail.com>
   4 */
   5
   6#include <linux/kernel.h>
   7#include <linux/init.h>
   8#include <linux/module.h>
   9#include <linux/netlink.h>
  10#include <linux/netfilter.h>
  11#include <linux/netfilter/nf_tables.h>
  12#include <net/netfilter/nf_tables.h>
  13#include <net/netfilter/nf_tables_core.h>
  14#include <linux/jhash.h>
  15
  16struct nft_jhash {
  17        enum nft_registers      sreg:8;
  18        enum nft_registers      dreg:8;
  19        u8                      len;
  20        bool                    autogen_seed:1;
  21        u32                     modulus;
  22        u32                     seed;
  23        u32                     offset;
  24};
  25
  26static void nft_jhash_eval(const struct nft_expr *expr,
  27                           struct nft_regs *regs,
  28                           const struct nft_pktinfo *pkt)
  29{
  30        struct nft_jhash *priv = nft_expr_priv(expr);
  31        const void *data = &regs->data[priv->sreg];
  32        u32 h;
  33
  34        h = reciprocal_scale(jhash(data, priv->len, priv->seed),
  35                             priv->modulus);
  36
  37        regs->data[priv->dreg] = h + priv->offset;
  38}
  39
  40struct nft_symhash {
  41        enum nft_registers      dreg:8;
  42        u32                     modulus;
  43        u32                     offset;
  44};
  45
  46static void nft_symhash_eval(const struct nft_expr *expr,
  47                             struct nft_regs *regs,
  48                             const struct nft_pktinfo *pkt)
  49{
  50        struct nft_symhash *priv = nft_expr_priv(expr);
  51        struct sk_buff *skb = pkt->skb;
  52        u32 h;
  53
  54        h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus);
  55
  56        regs->data[priv->dreg] = h + priv->offset;
  57}
  58
  59static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = {
  60        [NFTA_HASH_SREG]        = { .type = NLA_U32 },
  61        [NFTA_HASH_DREG]        = { .type = NLA_U32 },
  62        [NFTA_HASH_LEN]         = { .type = NLA_U32 },
  63        [NFTA_HASH_MODULUS]     = { .type = NLA_U32 },
  64        [NFTA_HASH_SEED]        = { .type = NLA_U32 },
  65        [NFTA_HASH_OFFSET]      = { .type = NLA_U32 },
  66        [NFTA_HASH_TYPE]        = { .type = NLA_U32 },
  67};
  68
  69static int nft_jhash_init(const struct nft_ctx *ctx,
  70                          const struct nft_expr *expr,
  71                          const struct nlattr * const tb[])
  72{
  73        struct nft_jhash *priv = nft_expr_priv(expr);
  74        u32 len;
  75        int err;
  76
  77        if (!tb[NFTA_HASH_SREG] ||
  78            !tb[NFTA_HASH_DREG] ||
  79            !tb[NFTA_HASH_LEN]  ||
  80            !tb[NFTA_HASH_MODULUS])
  81                return -EINVAL;
  82
  83        if (tb[NFTA_HASH_OFFSET])
  84                priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
  85
  86        priv->sreg = nft_parse_register(tb[NFTA_HASH_SREG]);
  87        priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]);
  88
  89        err = nft_parse_u32_check(tb[NFTA_HASH_LEN], U8_MAX, &len);
  90        if (err < 0)
  91                return err;
  92        if (len == 0)
  93                return -ERANGE;
  94
  95        priv->len = len;
  96
  97        priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
  98        if (priv->modulus < 1)
  99                return -ERANGE;
 100
 101        if (priv->offset + priv->modulus - 1 < priv->offset)
 102                return -EOVERFLOW;
 103
 104        if (tb[NFTA_HASH_SEED]) {
 105                priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED]));
 106        } else {
 107                priv->autogen_seed = true;
 108                get_random_bytes(&priv->seed, sizeof(priv->seed));
 109        }
 110
 111        return nft_validate_register_load(priv->sreg, len) &&
 112               nft_validate_register_store(ctx, priv->dreg, NULL,
 113                                           NFT_DATA_VALUE, sizeof(u32));
 114}
 115
 116static int nft_symhash_init(const struct nft_ctx *ctx,
 117                            const struct nft_expr *expr,
 118                            const struct nlattr * const tb[])
 119{
 120        struct nft_symhash *priv = nft_expr_priv(expr);
 121
 122        if (!tb[NFTA_HASH_DREG]    ||
 123            !tb[NFTA_HASH_MODULUS])
 124                return -EINVAL;
 125
 126        if (tb[NFTA_HASH_OFFSET])
 127                priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
 128
 129        priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]);
 130
 131        priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
 132        if (priv->modulus < 1)
 133                return -ERANGE;
 134
 135        if (priv->offset + priv->modulus - 1 < priv->offset)
 136                return -EOVERFLOW;
 137
 138        return nft_validate_register_store(ctx, priv->dreg, NULL,
 139                                           NFT_DATA_VALUE, sizeof(u32));
 140}
 141
 142static int nft_jhash_dump(struct sk_buff *skb,
 143                          const struct nft_expr *expr)
 144{
 145        const struct nft_jhash *priv = nft_expr_priv(expr);
 146
 147        if (nft_dump_register(skb, NFTA_HASH_SREG, priv->sreg))
 148                goto nla_put_failure;
 149        if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
 150                goto nla_put_failure;
 151        if (nla_put_be32(skb, NFTA_HASH_LEN, htonl(priv->len)))
 152                goto nla_put_failure;
 153        if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
 154                goto nla_put_failure;
 155        if (!priv->autogen_seed &&
 156            nla_put_be32(skb, NFTA_HASH_SEED, htonl(priv->seed)))
 157                goto nla_put_failure;
 158        if (priv->offset != 0)
 159                if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
 160                        goto nla_put_failure;
 161        if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS)))
 162                goto nla_put_failure;
 163        return 0;
 164
 165nla_put_failure:
 166        return -1;
 167}
 168
 169static int nft_symhash_dump(struct sk_buff *skb,
 170                            const struct nft_expr *expr)
 171{
 172        const struct nft_symhash *priv = nft_expr_priv(expr);
 173
 174        if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
 175                goto nla_put_failure;
 176        if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
 177                goto nla_put_failure;
 178        if (priv->offset != 0)
 179                if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
 180                        goto nla_put_failure;
 181        if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM)))
 182                goto nla_put_failure;
 183        return 0;
 184
 185nla_put_failure:
 186        return -1;
 187}
 188
 189static struct nft_expr_type nft_hash_type;
 190static const struct nft_expr_ops nft_jhash_ops = {
 191        .type           = &nft_hash_type,
 192        .size           = NFT_EXPR_SIZE(sizeof(struct nft_jhash)),
 193        .eval           = nft_jhash_eval,
 194        .init           = nft_jhash_init,
 195        .dump           = nft_jhash_dump,
 196};
 197
 198static const struct nft_expr_ops nft_symhash_ops = {
 199        .type           = &nft_hash_type,
 200        .size           = NFT_EXPR_SIZE(sizeof(struct nft_symhash)),
 201        .eval           = nft_symhash_eval,
 202        .init           = nft_symhash_init,
 203        .dump           = nft_symhash_dump,
 204};
 205
 206static const struct nft_expr_ops *
 207nft_hash_select_ops(const struct nft_ctx *ctx,
 208                    const struct nlattr * const tb[])
 209{
 210        u32 type;
 211
 212        if (!tb[NFTA_HASH_TYPE])
 213                return &nft_jhash_ops;
 214
 215        type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE]));
 216        switch (type) {
 217        case NFT_HASH_SYM:
 218                return &nft_symhash_ops;
 219        case NFT_HASH_JENKINS:
 220                return &nft_jhash_ops;
 221        default:
 222                break;
 223        }
 224        return ERR_PTR(-EOPNOTSUPP);
 225}
 226
 227static struct nft_expr_type nft_hash_type __read_mostly = {
 228        .name           = "hash",
 229        .select_ops     = nft_hash_select_ops,
 230        .policy         = nft_hash_policy,
 231        .maxattr        = NFTA_HASH_MAX,
 232        .owner          = THIS_MODULE,
 233};
 234
 235static int __init nft_hash_module_init(void)
 236{
 237        return nft_register_expr(&nft_hash_type);
 238}
 239
 240static void __exit nft_hash_module_exit(void)
 241{
 242        nft_unregister_expr(&nft_hash_type);
 243}
 244
 245module_init(nft_hash_module_init);
 246module_exit(nft_hash_module_exit);
 247
 248MODULE_LICENSE("GPL");
 249MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
 250MODULE_ALIAS_NFT_EXPR("hash");
 251