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