netfilter: nf_tables: add number generator expression

This patch adds the numgen expression that allows us to generated
incremental and random numbers, this generator is bound to a upper limit
that is specified by userspace.

This expression is useful to distribute packets in a round-robin fashion
as well as randomly.

Signed-off-by: Laura Garcia Liebana <nevola@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Laura Garcia Liebana 2016-08-18 12:13:13 +02:00 committed by Pablo Neira Ayuso
parent 3d2f30a1df
commit 91dbc6be0a
4 changed files with 223 additions and 0 deletions

View file

@ -1121,4 +1121,28 @@ enum nft_trace_types {
__NFT_TRACETYPE_MAX __NFT_TRACETYPE_MAX
}; };
#define NFT_TRACETYPE_MAX (__NFT_TRACETYPE_MAX - 1) #define NFT_TRACETYPE_MAX (__NFT_TRACETYPE_MAX - 1)
/**
* enum nft_ng_attributes - nf_tables number generator expression netlink attributes
*
* @NFTA_NG_DREG: destination register (NLA_U32)
* @NFTA_NG_UNTIL: source value to increment the counter until reset (NLA_U32)
* @NFTA_NG_TYPE: operation type (NLA_U32)
*/
enum nft_ng_attributes {
NFTA_NG_UNSPEC,
NFTA_NG_DREG,
NFTA_NG_UNTIL,
NFTA_NG_TYPE,
__NFTA_NG_MAX
};
#define NFTA_NG_MAX (__NFTA_NG_MAX - 1)
enum nft_ng_types {
NFT_NG_INCREMENTAL,
NFT_NG_RANDOM,
__NFT_NG_MAX
};
#define NFT_NG_MAX (__NFT_NG_MAX - 1)
#endif /* _LINUX_NF_TABLES_H */ #endif /* _LINUX_NF_TABLES_H */

View file

@ -474,6 +474,12 @@ config NFT_META
This option adds the "meta" expression that you can use to match and This option adds the "meta" expression that you can use to match and
to set packet metainformation such as the packet mark. to set packet metainformation such as the packet mark.
config NFT_NUMGEN
tristate "Netfilter nf_tables number generator module"
help
This option adds the number generator expression used to perform
incremental counting and random numbers bound to a upper limit.
config NFT_CT config NFT_CT
depends on NF_CONNTRACK depends on NF_CONNTRACK
tristate "Netfilter nf_tables conntrack module" tristate "Netfilter nf_tables conntrack module"

View file

@ -80,6 +80,7 @@ obj-$(CONFIG_NF_TABLES_NETDEV) += nf_tables_netdev.o
obj-$(CONFIG_NFT_COMPAT) += nft_compat.o obj-$(CONFIG_NFT_COMPAT) += nft_compat.o
obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o
obj-$(CONFIG_NFT_META) += nft_meta.o obj-$(CONFIG_NFT_META) += nft_meta.o
obj-$(CONFIG_NFT_NUMGEN) += nft_numgen.o
obj-$(CONFIG_NFT_CT) += nft_ct.o obj-$(CONFIG_NFT_CT) += nft_ct.o
obj-$(CONFIG_NFT_LIMIT) += nft_limit.o obj-$(CONFIG_NFT_LIMIT) += nft_limit.o
obj-$(CONFIG_NFT_NAT) += nft_nat.o obj-$(CONFIG_NFT_NAT) += nft_nat.o

192
net/netfilter/nft_numgen.c Normal file
View file

@ -0,0 +1,192 @@
/*
* Copyright (c) 2016 Laura Garcia <nevola@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/static_key.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
static DEFINE_PER_CPU(struct rnd_state, nft_numgen_prandom_state);
struct nft_ng_inc {
enum nft_registers dreg:8;
u32 until;
atomic_t counter;
};
static void nft_ng_inc_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_ng_inc *priv = nft_expr_priv(expr);
u32 nval, oval;
do {
oval = atomic_read(&priv->counter);
nval = (oval + 1 < priv->until) ? oval + 1 : 0;
} while (atomic_cmpxchg(&priv->counter, oval, nval) != oval);
memcpy(&regs->data[priv->dreg], &priv->counter, sizeof(u32));
}
static const struct nla_policy nft_ng_policy[NFTA_NG_MAX + 1] = {
[NFTA_NG_DREG] = { .type = NLA_U32 },
[NFTA_NG_UNTIL] = { .type = NLA_U32 },
[NFTA_NG_TYPE] = { .type = NLA_U32 },
};
static int nft_ng_inc_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_ng_inc *priv = nft_expr_priv(expr);
priv->until = ntohl(nla_get_be32(tb[NFTA_NG_UNTIL]));
if (priv->until == 0)
return -ERANGE;
priv->dreg = nft_parse_register(tb[NFTA_NG_DREG]);
atomic_set(&priv->counter, 0);
return nft_validate_register_store(ctx, priv->dreg, NULL,
NFT_DATA_VALUE, sizeof(u32));
}
static int nft_ng_dump(struct sk_buff *skb, enum nft_registers dreg,
u32 until, enum nft_ng_types type)
{
if (nft_dump_register(skb, NFTA_NG_DREG, dreg))
goto nla_put_failure;
if (nft_dump_register(skb, NFTA_NG_UNTIL, until))
goto nla_put_failure;
if (nft_dump_register(skb, NFTA_NG_TYPE, type))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static int nft_ng_inc_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_ng_inc *priv = nft_expr_priv(expr);
return nft_ng_dump(skb, priv->dreg, priv->until, NFT_NG_INCREMENTAL);
}
struct nft_ng_random {
enum nft_registers dreg:8;
u32 until;
};
static void nft_ng_random_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_ng_random *priv = nft_expr_priv(expr);
struct rnd_state *state = this_cpu_ptr(&nft_numgen_prandom_state);
regs->data[priv->dreg] = reciprocal_scale(prandom_u32_state(state),
priv->until);
}
static int nft_ng_random_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_ng_random *priv = nft_expr_priv(expr);
priv->until = ntohl(nla_get_be32(tb[NFTA_NG_UNTIL]));
if (priv->until == 0)
return -ERANGE;
prandom_init_once(&nft_numgen_prandom_state);
priv->dreg = nft_parse_register(tb[NFTA_NG_DREG]);
return nft_validate_register_store(ctx, priv->dreg, NULL,
NFT_DATA_VALUE, sizeof(u32));
}
static int nft_ng_random_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_ng_random *priv = nft_expr_priv(expr);
return nft_ng_dump(skb, priv->dreg, priv->until, NFT_NG_RANDOM);
}
static struct nft_expr_type nft_ng_type;
static const struct nft_expr_ops nft_ng_inc_ops = {
.type = &nft_ng_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_ng_inc)),
.eval = nft_ng_inc_eval,
.init = nft_ng_inc_init,
.dump = nft_ng_inc_dump,
};
static const struct nft_expr_ops nft_ng_random_ops = {
.type = &nft_ng_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_ng_random)),
.eval = nft_ng_random_eval,
.init = nft_ng_random_init,
.dump = nft_ng_random_dump,
};
static const struct nft_expr_ops *
nft_ng_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
{
u32 type;
if (!tb[NFTA_NG_DREG] ||
!tb[NFTA_NG_UNTIL] ||
!tb[NFTA_NG_TYPE])
return ERR_PTR(-EINVAL);
type = ntohl(nla_get_be32(tb[NFTA_NG_TYPE]));
switch (type) {
case NFT_NG_INCREMENTAL:
return &nft_ng_inc_ops;
case NFT_NG_RANDOM:
return &nft_ng_random_ops;
}
return ERR_PTR(-EINVAL);
}
static struct nft_expr_type nft_ng_type __read_mostly = {
.name = "numgen",
.select_ops = &nft_ng_select_ops,
.policy = nft_ng_policy,
.maxattr = NFTA_NG_MAX,
.owner = THIS_MODULE,
};
static int __init nft_ng_module_init(void)
{
return nft_register_expr(&nft_ng_type);
}
static void __exit nft_ng_module_exit(void)
{
nft_unregister_expr(&nft_ng_type);
}
module_init(nft_ng_module_init);
module_exit(nft_ng_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
MODULE_ALIAS_NFT_EXPR("numgen");