Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2016 Laura Garcia <nevola@gmail.com>
0004  */
0005 
0006 #include <linux/kernel.h>
0007 #include <linux/init.h>
0008 #include <linux/module.h>
0009 #include <linux/netlink.h>
0010 #include <linux/netfilter.h>
0011 #include <linux/netfilter/nf_tables.h>
0012 #include <net/netfilter/nf_tables.h>
0013 #include <net/netfilter/nf_tables_core.h>
0014 #include <linux/jhash.h>
0015 
0016 struct nft_jhash {
0017     u8          sreg;
0018     u8          dreg;
0019     u8          len;
0020     bool            autogen_seed:1;
0021     u32         modulus;
0022     u32         seed;
0023     u32         offset;
0024 };
0025 
0026 static void nft_jhash_eval(const struct nft_expr *expr,
0027                struct nft_regs *regs,
0028                const struct nft_pktinfo *pkt)
0029 {
0030     struct nft_jhash *priv = nft_expr_priv(expr);
0031     const void *data = &regs->data[priv->sreg];
0032     u32 h;
0033 
0034     h = reciprocal_scale(jhash(data, priv->len, priv->seed),
0035                  priv->modulus);
0036 
0037     regs->data[priv->dreg] = h + priv->offset;
0038 }
0039 
0040 struct nft_symhash {
0041     u8          dreg;
0042     u32         modulus;
0043     u32         offset;
0044 };
0045 
0046 static void nft_symhash_eval(const struct nft_expr *expr,
0047                  struct nft_regs *regs,
0048                  const struct nft_pktinfo *pkt)
0049 {
0050     struct nft_symhash *priv = nft_expr_priv(expr);
0051     struct sk_buff *skb = pkt->skb;
0052     u32 h;
0053 
0054     h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus);
0055 
0056     regs->data[priv->dreg] = h + priv->offset;
0057 }
0058 
0059 static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = {
0060     [NFTA_HASH_SREG]    = { .type = NLA_U32 },
0061     [NFTA_HASH_DREG]    = { .type = NLA_U32 },
0062     [NFTA_HASH_LEN]     = { .type = NLA_U32 },
0063     [NFTA_HASH_MODULUS] = { .type = NLA_U32 },
0064     [NFTA_HASH_SEED]    = { .type = NLA_U32 },
0065     [NFTA_HASH_OFFSET]  = { .type = NLA_U32 },
0066     [NFTA_HASH_TYPE]    = { .type = NLA_U32 },
0067 };
0068 
0069 static int nft_jhash_init(const struct nft_ctx *ctx,
0070               const struct nft_expr *expr,
0071               const struct nlattr * const tb[])
0072 {
0073     struct nft_jhash *priv = nft_expr_priv(expr);
0074     u32 len;
0075     int err;
0076 
0077     if (!tb[NFTA_HASH_SREG] ||
0078         !tb[NFTA_HASH_DREG] ||
0079         !tb[NFTA_HASH_LEN]  ||
0080         !tb[NFTA_HASH_MODULUS])
0081         return -EINVAL;
0082 
0083     if (tb[NFTA_HASH_OFFSET])
0084         priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
0085 
0086     err = nft_parse_u32_check(tb[NFTA_HASH_LEN], U8_MAX, &len);
0087     if (err < 0)
0088         return err;
0089     if (len == 0)
0090         return -ERANGE;
0091 
0092     priv->len = len;
0093 
0094     err = nft_parse_register_load(tb[NFTA_HASH_SREG], &priv->sreg, len);
0095     if (err < 0)
0096         return err;
0097 
0098     priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
0099     if (priv->modulus < 1)
0100         return -ERANGE;
0101 
0102     if (priv->offset + priv->modulus - 1 < priv->offset)
0103         return -EOVERFLOW;
0104 
0105     if (tb[NFTA_HASH_SEED]) {
0106         priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED]));
0107     } else {
0108         priv->autogen_seed = true;
0109         get_random_bytes(&priv->seed, sizeof(priv->seed));
0110     }
0111 
0112     return nft_parse_register_store(ctx, tb[NFTA_HASH_DREG], &priv->dreg,
0113                     NULL, NFT_DATA_VALUE, sizeof(u32));
0114 }
0115 
0116 static int nft_symhash_init(const struct nft_ctx *ctx,
0117                 const struct nft_expr *expr,
0118                 const struct nlattr * const tb[])
0119 {
0120     struct nft_symhash *priv = nft_expr_priv(expr);
0121 
0122     if (!tb[NFTA_HASH_DREG]    ||
0123         !tb[NFTA_HASH_MODULUS])
0124         return -EINVAL;
0125 
0126     if (tb[NFTA_HASH_OFFSET])
0127         priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
0128 
0129     priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
0130     if (priv->modulus < 1)
0131         return -ERANGE;
0132 
0133     if (priv->offset + priv->modulus - 1 < priv->offset)
0134         return -EOVERFLOW;
0135 
0136     return nft_parse_register_store(ctx, tb[NFTA_HASH_DREG],
0137                     &priv->dreg, NULL, NFT_DATA_VALUE,
0138                     sizeof(u32));
0139 }
0140 
0141 static int nft_jhash_dump(struct sk_buff *skb,
0142               const struct nft_expr *expr)
0143 {
0144     const struct nft_jhash *priv = nft_expr_priv(expr);
0145 
0146     if (nft_dump_register(skb, NFTA_HASH_SREG, priv->sreg))
0147         goto nla_put_failure;
0148     if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
0149         goto nla_put_failure;
0150     if (nla_put_be32(skb, NFTA_HASH_LEN, htonl(priv->len)))
0151         goto nla_put_failure;
0152     if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
0153         goto nla_put_failure;
0154     if (!priv->autogen_seed &&
0155         nla_put_be32(skb, NFTA_HASH_SEED, htonl(priv->seed)))
0156         goto nla_put_failure;
0157     if (priv->offset != 0)
0158         if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
0159             goto nla_put_failure;
0160     if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS)))
0161         goto nla_put_failure;
0162     return 0;
0163 
0164 nla_put_failure:
0165     return -1;
0166 }
0167 
0168 static bool nft_jhash_reduce(struct nft_regs_track *track,
0169                  const struct nft_expr *expr)
0170 {
0171     const struct nft_jhash *priv = nft_expr_priv(expr);
0172 
0173     nft_reg_track_cancel(track, priv->dreg, sizeof(u32));
0174 
0175     return false;
0176 }
0177 
0178 static int nft_symhash_dump(struct sk_buff *skb,
0179                 const struct nft_expr *expr)
0180 {
0181     const struct nft_symhash *priv = nft_expr_priv(expr);
0182 
0183     if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
0184         goto nla_put_failure;
0185     if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
0186         goto nla_put_failure;
0187     if (priv->offset != 0)
0188         if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
0189             goto nla_put_failure;
0190     if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM)))
0191         goto nla_put_failure;
0192     return 0;
0193 
0194 nla_put_failure:
0195     return -1;
0196 }
0197 
0198 static bool nft_symhash_reduce(struct nft_regs_track *track,
0199                    const struct nft_expr *expr)
0200 {
0201     struct nft_symhash *priv = nft_expr_priv(expr);
0202     struct nft_symhash *symhash;
0203 
0204     if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
0205         nft_reg_track_update(track, expr, priv->dreg, sizeof(u32));
0206         return false;
0207     }
0208 
0209     symhash = nft_expr_priv(track->regs[priv->dreg].selector);
0210     if (priv->offset != symhash->offset ||
0211         priv->modulus != symhash->modulus) {
0212         nft_reg_track_update(track, expr, priv->dreg, sizeof(u32));
0213         return false;
0214     }
0215 
0216     if (!track->regs[priv->dreg].bitwise)
0217         return true;
0218 
0219     return false;
0220 }
0221 
0222 static struct nft_expr_type nft_hash_type;
0223 static const struct nft_expr_ops nft_jhash_ops = {
0224     .type       = &nft_hash_type,
0225     .size       = NFT_EXPR_SIZE(sizeof(struct nft_jhash)),
0226     .eval       = nft_jhash_eval,
0227     .init       = nft_jhash_init,
0228     .dump       = nft_jhash_dump,
0229     .reduce     = nft_jhash_reduce,
0230 };
0231 
0232 static const struct nft_expr_ops nft_symhash_ops = {
0233     .type       = &nft_hash_type,
0234     .size       = NFT_EXPR_SIZE(sizeof(struct nft_symhash)),
0235     .eval       = nft_symhash_eval,
0236     .init       = nft_symhash_init,
0237     .dump       = nft_symhash_dump,
0238     .reduce     = nft_symhash_reduce,
0239 };
0240 
0241 static const struct nft_expr_ops *
0242 nft_hash_select_ops(const struct nft_ctx *ctx,
0243             const struct nlattr * const tb[])
0244 {
0245     u32 type;
0246 
0247     if (!tb[NFTA_HASH_TYPE])
0248         return &nft_jhash_ops;
0249 
0250     type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE]));
0251     switch (type) {
0252     case NFT_HASH_SYM:
0253         return &nft_symhash_ops;
0254     case NFT_HASH_JENKINS:
0255         return &nft_jhash_ops;
0256     default:
0257         break;
0258     }
0259     return ERR_PTR(-EOPNOTSUPP);
0260 }
0261 
0262 static struct nft_expr_type nft_hash_type __read_mostly = {
0263     .name       = "hash",
0264     .select_ops = nft_hash_select_ops,
0265     .policy     = nft_hash_policy,
0266     .maxattr    = NFTA_HASH_MAX,
0267     .owner      = THIS_MODULE,
0268 };
0269 
0270 static int __init nft_hash_module_init(void)
0271 {
0272     return nft_register_expr(&nft_hash_type);
0273 }
0274 
0275 static void __exit nft_hash_module_exit(void)
0276 {
0277     nft_unregister_expr(&nft_hash_type);
0278 }
0279 
0280 module_init(nft_hash_module_init);
0281 module_exit(nft_hash_module_exit);
0282 
0283 MODULE_LICENSE("GPL");
0284 MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
0285 MODULE_ALIAS_NFT_EXPR("hash");
0286 MODULE_DESCRIPTION("Netfilter nftables hash module");