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        u8                      sreg;
  18        u8                      dreg;
  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        u8                      dreg;
  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        err = nft_parse_u32_check(tb[NFTA_HASH_LEN], U8_MAX, &len);
  87        if (err < 0)
  88                return err;
  89        if (len == 0)
  90                return -ERANGE;
  91
  92        priv->len = len;
  93
  94        err = nft_parse_register_load(tb[NFTA_HASH_SREG], &priv->sreg, len);
  95        if (err < 0)
  96                return err;
  97
  98        priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
  99        if (priv->modulus < 1)
 100                return -ERANGE;
 101
 102        if (priv->offset + priv->modulus - 1 < priv->offset)
 103                return -EOVERFLOW;
 104
 105        if (tb[NFTA_HASH_SEED]) {
 106                priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED]));
 107        } else {
 108                priv->autogen_seed = true;
 109                get_random_bytes(&priv->seed, sizeof(priv->seed));
 110        }
 111
 112        return nft_parse_register_store(ctx, tb[NFTA_HASH_DREG], &priv->dreg,
 113                                        NULL, 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->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
 130        if (priv->modulus < 1)
 131                return -ERANGE;
 132
 133        if (priv->offset + priv->modulus - 1 < priv->offset)
 134                return -EOVERFLOW;
 135
 136        return nft_parse_register_store(ctx, tb[NFTA_HASH_DREG],
 137                                        &priv->dreg, NULL, NFT_DATA_VALUE,
 138                                        sizeof(u32));
 139}
 140
 141static int nft_jhash_dump(struct sk_buff *skb,
 142                          const struct nft_expr *expr)
 143{
 144        const struct nft_jhash *priv = nft_expr_priv(expr);
 145
 146        if (nft_dump_register(skb, NFTA_HASH_SREG, priv->sreg))
 147                goto nla_put_failure;
 148        if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
 149                goto nla_put_failure;
 150        if (nla_put_be32(skb, NFTA_HASH_LEN, htonl(priv->len)))
 151                goto nla_put_failure;
 152        if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
 153                goto nla_put_failure;
 154        if (!priv->autogen_seed &&
 155            nla_put_be32(skb, NFTA_HASH_SEED, htonl(priv->seed)))
 156                goto nla_put_failure;
 157        if (priv->offset != 0)
 158                if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
 159                        goto nla_put_failure;
 160        if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS)))
 161                goto nla_put_failure;
 162        return 0;
 163
 164nla_put_failure:
 165        return -1;
 166}
 167
 168static int nft_symhash_dump(struct sk_buff *skb,
 169                            const struct nft_expr *expr)
 170{
 171        const struct nft_symhash *priv = nft_expr_priv(expr);
 172
 173        if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
 174                goto nla_put_failure;
 175        if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
 176                goto nla_put_failure;
 177        if (priv->offset != 0)
 178                if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
 179                        goto nla_put_failure;
 180        if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM)))
 181                goto nla_put_failure;
 182        return 0;
 183
 184nla_put_failure:
 185        return -1;
 186}
 187
 188static struct nft_expr_type nft_hash_type;
 189static const struct nft_expr_ops nft_jhash_ops = {
 190        .type           = &nft_hash_type,
 191        .size           = NFT_EXPR_SIZE(sizeof(struct nft_jhash)),
 192        .eval           = nft_jhash_eval,
 193        .init           = nft_jhash_init,
 194        .dump           = nft_jhash_dump,
 195};
 196
 197static const struct nft_expr_ops nft_symhash_ops = {
 198        .type           = &nft_hash_type,
 199        .size           = NFT_EXPR_SIZE(sizeof(struct nft_symhash)),
 200        .eval           = nft_symhash_eval,
 201        .init           = nft_symhash_init,
 202        .dump           = nft_symhash_dump,
 203};
 204
 205static const struct nft_expr_ops *
 206nft_hash_select_ops(const struct nft_ctx *ctx,
 207                    const struct nlattr * const tb[])
 208{
 209        u32 type;
 210
 211        if (!tb[NFTA_HASH_TYPE])
 212                return &nft_jhash_ops;
 213
 214        type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE]));
 215        switch (type) {
 216        case NFT_HASH_SYM:
 217                return &nft_symhash_ops;
 218        case NFT_HASH_JENKINS:
 219                return &nft_jhash_ops;
 220        default:
 221                break;
 222        }
 223        return ERR_PTR(-EOPNOTSUPP);
 224}
 225
 226static struct nft_expr_type nft_hash_type __read_mostly = {
 227        .name           = "hash",
 228        .select_ops     = nft_hash_select_ops,
 229        .policy         = nft_hash_policy,
 230        .maxattr        = NFTA_HASH_MAX,
 231        .owner          = THIS_MODULE,
 232};
 233
 234static int __init nft_hash_module_init(void)
 235{
 236        return nft_register_expr(&nft_hash_type);
 237}
 238
 239static void __exit nft_hash_module_exit(void)
 240{
 241        nft_unregister_expr(&nft_hash_type);
 242}
 243
 244module_init(nft_hash_module_init);
 245module_exit(nft_hash_module_exit);
 246
 247MODULE_LICENSE("GPL");
 248MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
 249MODULE_ALIAS_NFT_EXPR("hash");
 250MODULE_DESCRIPTION("Netfilter nftables hash module");
 251