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 <linux/random.h>
0013 #include <linux/static_key.h>
0014 #include <net/netfilter/nf_tables.h>
0015 #include <net/netfilter/nf_tables_core.h>
0016 
0017 struct nft_ng_inc {
0018     u8          dreg;
0019     u32         modulus;
0020     atomic_t        *counter;
0021     u32         offset;
0022 };
0023 
0024 static u32 nft_ng_inc_gen(struct nft_ng_inc *priv)
0025 {
0026     u32 nval, oval;
0027 
0028     do {
0029         oval = atomic_read(priv->counter);
0030         nval = (oval + 1 < priv->modulus) ? oval + 1 : 0;
0031     } while (atomic_cmpxchg(priv->counter, oval, nval) != oval);
0032 
0033     return nval + priv->offset;
0034 }
0035 
0036 static void nft_ng_inc_eval(const struct nft_expr *expr,
0037                 struct nft_regs *regs,
0038                 const struct nft_pktinfo *pkt)
0039 {
0040     struct nft_ng_inc *priv = nft_expr_priv(expr);
0041 
0042     regs->data[priv->dreg] = nft_ng_inc_gen(priv);
0043 }
0044 
0045 static const struct nla_policy nft_ng_policy[NFTA_NG_MAX + 1] = {
0046     [NFTA_NG_DREG]      = { .type = NLA_U32 },
0047     [NFTA_NG_MODULUS]   = { .type = NLA_U32 },
0048     [NFTA_NG_TYPE]      = { .type = NLA_U32 },
0049     [NFTA_NG_OFFSET]    = { .type = NLA_U32 },
0050 };
0051 
0052 static int nft_ng_inc_init(const struct nft_ctx *ctx,
0053                const struct nft_expr *expr,
0054                const struct nlattr * const tb[])
0055 {
0056     struct nft_ng_inc *priv = nft_expr_priv(expr);
0057     int err;
0058 
0059     if (tb[NFTA_NG_OFFSET])
0060         priv->offset = ntohl(nla_get_be32(tb[NFTA_NG_OFFSET]));
0061 
0062     priv->modulus = ntohl(nla_get_be32(tb[NFTA_NG_MODULUS]));
0063     if (priv->modulus == 0)
0064         return -ERANGE;
0065 
0066     if (priv->offset + priv->modulus - 1 < priv->offset)
0067         return -EOVERFLOW;
0068 
0069     priv->counter = kmalloc(sizeof(*priv->counter), GFP_KERNEL);
0070     if (!priv->counter)
0071         return -ENOMEM;
0072 
0073     atomic_set(priv->counter, priv->modulus - 1);
0074 
0075     err = nft_parse_register_store(ctx, tb[NFTA_NG_DREG], &priv->dreg,
0076                        NULL, NFT_DATA_VALUE, sizeof(u32));
0077     if (err < 0)
0078         goto err;
0079 
0080     return 0;
0081 err:
0082     kfree(priv->counter);
0083 
0084     return err;
0085 }
0086 
0087 static bool nft_ng_inc_reduce(struct nft_regs_track *track,
0088                  const struct nft_expr *expr)
0089 {
0090     const struct nft_ng_inc *priv = nft_expr_priv(expr);
0091 
0092     nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
0093 
0094     return false;
0095 }
0096 
0097 static int nft_ng_dump(struct sk_buff *skb, enum nft_registers dreg,
0098                u32 modulus, enum nft_ng_types type, u32 offset)
0099 {
0100     if (nft_dump_register(skb, NFTA_NG_DREG, dreg))
0101         goto nla_put_failure;
0102     if (nla_put_be32(skb, NFTA_NG_MODULUS, htonl(modulus)))
0103         goto nla_put_failure;
0104     if (nla_put_be32(skb, NFTA_NG_TYPE, htonl(type)))
0105         goto nla_put_failure;
0106     if (nla_put_be32(skb, NFTA_NG_OFFSET, htonl(offset)))
0107         goto nla_put_failure;
0108 
0109     return 0;
0110 
0111 nla_put_failure:
0112     return -1;
0113 }
0114 
0115 static int nft_ng_inc_dump(struct sk_buff *skb, const struct nft_expr *expr)
0116 {
0117     const struct nft_ng_inc *priv = nft_expr_priv(expr);
0118 
0119     return nft_ng_dump(skb, priv->dreg, priv->modulus, NFT_NG_INCREMENTAL,
0120                priv->offset);
0121 }
0122 
0123 static void nft_ng_inc_destroy(const struct nft_ctx *ctx,
0124                    const struct nft_expr *expr)
0125 {
0126     const struct nft_ng_inc *priv = nft_expr_priv(expr);
0127 
0128     kfree(priv->counter);
0129 }
0130 
0131 struct nft_ng_random {
0132     u8          dreg;
0133     u32         modulus;
0134     u32         offset;
0135 };
0136 
0137 static u32 nft_ng_random_gen(const struct nft_ng_random *priv)
0138 {
0139     return reciprocal_scale(get_random_u32(), priv->modulus) + priv->offset;
0140 }
0141 
0142 static void nft_ng_random_eval(const struct nft_expr *expr,
0143                    struct nft_regs *regs,
0144                    const struct nft_pktinfo *pkt)
0145 {
0146     struct nft_ng_random *priv = nft_expr_priv(expr);
0147 
0148     regs->data[priv->dreg] = nft_ng_random_gen(priv);
0149 }
0150 
0151 static int nft_ng_random_init(const struct nft_ctx *ctx,
0152                   const struct nft_expr *expr,
0153                   const struct nlattr * const tb[])
0154 {
0155     struct nft_ng_random *priv = nft_expr_priv(expr);
0156 
0157     if (tb[NFTA_NG_OFFSET])
0158         priv->offset = ntohl(nla_get_be32(tb[NFTA_NG_OFFSET]));
0159 
0160     priv->modulus = ntohl(nla_get_be32(tb[NFTA_NG_MODULUS]));
0161     if (priv->modulus == 0)
0162         return -ERANGE;
0163 
0164     if (priv->offset + priv->modulus - 1 < priv->offset)
0165         return -EOVERFLOW;
0166 
0167     return nft_parse_register_store(ctx, tb[NFTA_NG_DREG], &priv->dreg,
0168                     NULL, NFT_DATA_VALUE, sizeof(u32));
0169 }
0170 
0171 static int nft_ng_random_dump(struct sk_buff *skb, const struct nft_expr *expr)
0172 {
0173     const struct nft_ng_random *priv = nft_expr_priv(expr);
0174 
0175     return nft_ng_dump(skb, priv->dreg, priv->modulus, NFT_NG_RANDOM,
0176                priv->offset);
0177 }
0178 
0179 static bool nft_ng_random_reduce(struct nft_regs_track *track,
0180                  const struct nft_expr *expr)
0181 {
0182     const struct nft_ng_random *priv = nft_expr_priv(expr);
0183 
0184     nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
0185 
0186     return false;
0187 }
0188 
0189 static struct nft_expr_type nft_ng_type;
0190 static const struct nft_expr_ops nft_ng_inc_ops = {
0191     .type       = &nft_ng_type,
0192     .size       = NFT_EXPR_SIZE(sizeof(struct nft_ng_inc)),
0193     .eval       = nft_ng_inc_eval,
0194     .init       = nft_ng_inc_init,
0195     .destroy    = nft_ng_inc_destroy,
0196     .dump       = nft_ng_inc_dump,
0197     .reduce     = nft_ng_inc_reduce,
0198 };
0199 
0200 static const struct nft_expr_ops nft_ng_random_ops = {
0201     .type       = &nft_ng_type,
0202     .size       = NFT_EXPR_SIZE(sizeof(struct nft_ng_random)),
0203     .eval       = nft_ng_random_eval,
0204     .init       = nft_ng_random_init,
0205     .dump       = nft_ng_random_dump,
0206     .reduce     = nft_ng_random_reduce,
0207 };
0208 
0209 static const struct nft_expr_ops *
0210 nft_ng_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
0211 {
0212     u32 type;
0213 
0214     if (!tb[NFTA_NG_DREG]    ||
0215         !tb[NFTA_NG_MODULUS] ||
0216         !tb[NFTA_NG_TYPE])
0217         return ERR_PTR(-EINVAL);
0218 
0219     type = ntohl(nla_get_be32(tb[NFTA_NG_TYPE]));
0220 
0221     switch (type) {
0222     case NFT_NG_INCREMENTAL:
0223         return &nft_ng_inc_ops;
0224     case NFT_NG_RANDOM:
0225         return &nft_ng_random_ops;
0226     }
0227 
0228     return ERR_PTR(-EINVAL);
0229 }
0230 
0231 static struct nft_expr_type nft_ng_type __read_mostly = {
0232     .name       = "numgen",
0233     .select_ops = nft_ng_select_ops,
0234     .policy     = nft_ng_policy,
0235     .maxattr    = NFTA_NG_MAX,
0236     .owner      = THIS_MODULE,
0237 };
0238 
0239 static int __init nft_ng_module_init(void)
0240 {
0241     return nft_register_expr(&nft_ng_type);
0242 }
0243 
0244 static void __exit nft_ng_module_exit(void)
0245 {
0246     nft_unregister_expr(&nft_ng_type);
0247 }
0248 
0249 module_init(nft_ng_module_init);
0250 module_exit(nft_ng_module_exit);
0251 
0252 MODULE_LICENSE("GPL");
0253 MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
0254 MODULE_ALIAS_NFT_EXPR("numgen");
0255 MODULE_DESCRIPTION("nftables number generator module");