cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nft_dynset.c (10718B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2015 Patrick McHardy <kaber@trash.net>
      4 */
      5
      6#include <linux/kernel.h>
      7#include <linux/module.h>
      8#include <linux/init.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
     15struct nft_dynset {
     16	struct nft_set			*set;
     17	struct nft_set_ext_tmpl		tmpl;
     18	enum nft_dynset_ops		op:8;
     19	u8				sreg_key;
     20	u8				sreg_data;
     21	bool				invert;
     22	bool				expr;
     23	u8				num_exprs;
     24	u64				timeout;
     25	struct nft_expr			*expr_array[NFT_SET_EXPR_MAX];
     26	struct nft_set_binding		binding;
     27};
     28
     29static int nft_dynset_expr_setup(const struct nft_dynset *priv,
     30				 const struct nft_set_ext *ext)
     31{
     32	struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
     33	struct nft_expr *expr;
     34	int i;
     35
     36	for (i = 0; i < priv->num_exprs; i++) {
     37		expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
     38		if (nft_expr_clone(expr, priv->expr_array[i]) < 0)
     39			return -1;
     40
     41		elem_expr->size += priv->expr_array[i]->ops->size;
     42	}
     43
     44	return 0;
     45}
     46
     47static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
     48			    struct nft_regs *regs)
     49{
     50	const struct nft_dynset *priv = nft_expr_priv(expr);
     51	struct nft_set_ext *ext;
     52	u64 timeout;
     53	void *elem;
     54
     55	if (!atomic_add_unless(&set->nelems, 1, set->size))
     56		return NULL;
     57
     58	timeout = priv->timeout ? : set->timeout;
     59	elem = nft_set_elem_init(set, &priv->tmpl,
     60				 &regs->data[priv->sreg_key], NULL,
     61				 &regs->data[priv->sreg_data],
     62				 timeout, 0, GFP_ATOMIC);
     63	if (elem == NULL)
     64		goto err1;
     65
     66	ext = nft_set_elem_ext(set, elem);
     67	if (priv->num_exprs && nft_dynset_expr_setup(priv, ext) < 0)
     68		goto err2;
     69
     70	return elem;
     71
     72err2:
     73	nft_set_elem_destroy(set, elem, false);
     74err1:
     75	if (set->size)
     76		atomic_dec(&set->nelems);
     77	return NULL;
     78}
     79
     80void nft_dynset_eval(const struct nft_expr *expr,
     81		     struct nft_regs *regs, const struct nft_pktinfo *pkt)
     82{
     83	const struct nft_dynset *priv = nft_expr_priv(expr);
     84	struct nft_set *set = priv->set;
     85	const struct nft_set_ext *ext;
     86	u64 timeout;
     87
     88	if (priv->op == NFT_DYNSET_OP_DELETE) {
     89		set->ops->delete(set, &regs->data[priv->sreg_key]);
     90		return;
     91	}
     92
     93	if (set->ops->update(set, &regs->data[priv->sreg_key], nft_dynset_new,
     94			     expr, regs, &ext)) {
     95		if (priv->op == NFT_DYNSET_OP_UPDATE &&
     96		    nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
     97			timeout = priv->timeout ? : set->timeout;
     98			*nft_set_ext_expiration(ext) = get_jiffies_64() + timeout;
     99		}
    100
    101		nft_set_elem_update_expr(ext, regs, pkt);
    102
    103		if (priv->invert)
    104			regs->verdict.code = NFT_BREAK;
    105		return;
    106	}
    107
    108	if (!priv->invert)
    109		regs->verdict.code = NFT_BREAK;
    110}
    111
    112static void nft_dynset_ext_add_expr(struct nft_dynset *priv)
    113{
    114	u8 size = 0;
    115	int i;
    116
    117	for (i = 0; i < priv->num_exprs; i++)
    118		size += priv->expr_array[i]->ops->size;
    119
    120	nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPRESSIONS,
    121			       sizeof(struct nft_set_elem_expr) + size);
    122}
    123
    124static struct nft_expr *
    125nft_dynset_expr_alloc(const struct nft_ctx *ctx, const struct nft_set *set,
    126		      const struct nlattr *attr, int pos)
    127{
    128	struct nft_expr *expr;
    129	int err;
    130
    131	expr = nft_set_elem_expr_alloc(ctx, set, attr);
    132	if (IS_ERR(expr))
    133		return expr;
    134
    135	if (set->exprs[pos] && set->exprs[pos]->ops != expr->ops) {
    136		err = -EOPNOTSUPP;
    137		goto err_dynset_expr;
    138	}
    139
    140	return expr;
    141
    142err_dynset_expr:
    143	nft_expr_destroy(ctx, expr);
    144	return ERR_PTR(err);
    145}
    146
    147static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
    148	[NFTA_DYNSET_SET_NAME]	= { .type = NLA_STRING,
    149				    .len = NFT_SET_MAXNAMELEN - 1 },
    150	[NFTA_DYNSET_SET_ID]	= { .type = NLA_U32 },
    151	[NFTA_DYNSET_OP]	= { .type = NLA_U32 },
    152	[NFTA_DYNSET_SREG_KEY]	= { .type = NLA_U32 },
    153	[NFTA_DYNSET_SREG_DATA]	= { .type = NLA_U32 },
    154	[NFTA_DYNSET_TIMEOUT]	= { .type = NLA_U64 },
    155	[NFTA_DYNSET_EXPR]	= { .type = NLA_NESTED },
    156	[NFTA_DYNSET_FLAGS]	= { .type = NLA_U32 },
    157	[NFTA_DYNSET_EXPRESSIONS] = { .type = NLA_NESTED },
    158};
    159
    160static int nft_dynset_init(const struct nft_ctx *ctx,
    161			   const struct nft_expr *expr,
    162			   const struct nlattr * const tb[])
    163{
    164	struct nftables_pernet *nft_net = nft_pernet(ctx->net);
    165	struct nft_dynset *priv = nft_expr_priv(expr);
    166	u8 genmask = nft_genmask_next(ctx->net);
    167	struct nft_set *set;
    168	u64 timeout;
    169	int err, i;
    170
    171	lockdep_assert_held(&nft_net->commit_mutex);
    172
    173	if (tb[NFTA_DYNSET_SET_NAME] == NULL ||
    174	    tb[NFTA_DYNSET_OP] == NULL ||
    175	    tb[NFTA_DYNSET_SREG_KEY] == NULL)
    176		return -EINVAL;
    177
    178	if (tb[NFTA_DYNSET_FLAGS]) {
    179		u32 flags = ntohl(nla_get_be32(tb[NFTA_DYNSET_FLAGS]));
    180		if (flags & ~(NFT_DYNSET_F_INV | NFT_DYNSET_F_EXPR))
    181			return -EOPNOTSUPP;
    182		if (flags & NFT_DYNSET_F_INV)
    183			priv->invert = true;
    184		if (flags & NFT_DYNSET_F_EXPR)
    185			priv->expr = true;
    186	}
    187
    188	set = nft_set_lookup_global(ctx->net, ctx->table,
    189				    tb[NFTA_DYNSET_SET_NAME],
    190				    tb[NFTA_DYNSET_SET_ID], genmask);
    191	if (IS_ERR(set))
    192		return PTR_ERR(set);
    193
    194	if (set->ops->update == NULL)
    195		return -EOPNOTSUPP;
    196
    197	if (set->flags & NFT_SET_CONSTANT)
    198		return -EBUSY;
    199
    200	priv->op = ntohl(nla_get_be32(tb[NFTA_DYNSET_OP]));
    201	if (priv->op > NFT_DYNSET_OP_DELETE)
    202		return -EOPNOTSUPP;
    203
    204	timeout = 0;
    205	if (tb[NFTA_DYNSET_TIMEOUT] != NULL) {
    206		if (!(set->flags & NFT_SET_TIMEOUT))
    207			return -EOPNOTSUPP;
    208
    209		err = nf_msecs_to_jiffies64(tb[NFTA_DYNSET_TIMEOUT], &timeout);
    210		if (err)
    211			return err;
    212	}
    213
    214	err = nft_parse_register_load(tb[NFTA_DYNSET_SREG_KEY], &priv->sreg_key,
    215				      set->klen);
    216	if (err < 0)
    217		return err;
    218
    219	if (tb[NFTA_DYNSET_SREG_DATA] != NULL) {
    220		if (!(set->flags & NFT_SET_MAP))
    221			return -EOPNOTSUPP;
    222		if (set->dtype == NFT_DATA_VERDICT)
    223			return -EOPNOTSUPP;
    224
    225		err = nft_parse_register_load(tb[NFTA_DYNSET_SREG_DATA],
    226					      &priv->sreg_data, set->dlen);
    227		if (err < 0)
    228			return err;
    229	} else if (set->flags & NFT_SET_MAP)
    230		return -EINVAL;
    231
    232	if ((tb[NFTA_DYNSET_EXPR] || tb[NFTA_DYNSET_EXPRESSIONS]) &&
    233	    !(set->flags & NFT_SET_EVAL))
    234		return -EINVAL;
    235
    236	if (tb[NFTA_DYNSET_EXPR]) {
    237		struct nft_expr *dynset_expr;
    238
    239		dynset_expr = nft_dynset_expr_alloc(ctx, set,
    240						    tb[NFTA_DYNSET_EXPR], 0);
    241		if (IS_ERR(dynset_expr))
    242			return PTR_ERR(dynset_expr);
    243
    244		priv->num_exprs++;
    245		priv->expr_array[0] = dynset_expr;
    246
    247		if (set->num_exprs > 1 ||
    248		    (set->num_exprs == 1 &&
    249		     dynset_expr->ops != set->exprs[0]->ops)) {
    250			err = -EOPNOTSUPP;
    251			goto err_expr_free;
    252		}
    253	} else if (tb[NFTA_DYNSET_EXPRESSIONS]) {
    254		struct nft_expr *dynset_expr;
    255		struct nlattr *tmp;
    256		int left;
    257
    258		if (!priv->expr)
    259			return -EINVAL;
    260
    261		i = 0;
    262		nla_for_each_nested(tmp, tb[NFTA_DYNSET_EXPRESSIONS], left) {
    263			if (i == NFT_SET_EXPR_MAX) {
    264				err = -E2BIG;
    265				goto err_expr_free;
    266			}
    267			if (nla_type(tmp) != NFTA_LIST_ELEM) {
    268				err = -EINVAL;
    269				goto err_expr_free;
    270			}
    271			dynset_expr = nft_dynset_expr_alloc(ctx, set, tmp, i);
    272			if (IS_ERR(dynset_expr)) {
    273				err = PTR_ERR(dynset_expr);
    274				goto err_expr_free;
    275			}
    276			priv->expr_array[i] = dynset_expr;
    277			priv->num_exprs++;
    278
    279			if (set->num_exprs &&
    280			    dynset_expr->ops != set->exprs[i]->ops) {
    281				err = -EOPNOTSUPP;
    282				goto err_expr_free;
    283			}
    284			i++;
    285		}
    286		if (set->num_exprs && set->num_exprs != i) {
    287			err = -EOPNOTSUPP;
    288			goto err_expr_free;
    289		}
    290	} else if (set->num_exprs > 0) {
    291		err = nft_set_elem_expr_clone(ctx, set, priv->expr_array);
    292		if (err < 0)
    293			return err;
    294
    295		priv->num_exprs = set->num_exprs;
    296	}
    297
    298	nft_set_ext_prepare(&priv->tmpl);
    299	nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
    300	if (set->flags & NFT_SET_MAP)
    301		nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen);
    302
    303	if (priv->num_exprs)
    304		nft_dynset_ext_add_expr(priv);
    305
    306	if (set->flags & NFT_SET_TIMEOUT) {
    307		if (timeout || set->timeout) {
    308			nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_TIMEOUT);
    309			nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
    310		}
    311	}
    312
    313	priv->timeout = timeout;
    314
    315	err = nf_tables_bind_set(ctx, set, &priv->binding);
    316	if (err < 0)
    317		goto err_expr_free;
    318
    319	if (set->size == 0)
    320		set->size = 0xffff;
    321
    322	priv->set = set;
    323	return 0;
    324
    325err_expr_free:
    326	for (i = 0; i < priv->num_exprs; i++)
    327		nft_expr_destroy(ctx, priv->expr_array[i]);
    328	return err;
    329}
    330
    331static void nft_dynset_deactivate(const struct nft_ctx *ctx,
    332				  const struct nft_expr *expr,
    333				  enum nft_trans_phase phase)
    334{
    335	struct nft_dynset *priv = nft_expr_priv(expr);
    336
    337	nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
    338}
    339
    340static void nft_dynset_activate(const struct nft_ctx *ctx,
    341				const struct nft_expr *expr)
    342{
    343	struct nft_dynset *priv = nft_expr_priv(expr);
    344
    345	priv->set->use++;
    346}
    347
    348static void nft_dynset_destroy(const struct nft_ctx *ctx,
    349			       const struct nft_expr *expr)
    350{
    351	struct nft_dynset *priv = nft_expr_priv(expr);
    352	int i;
    353
    354	for (i = 0; i < priv->num_exprs; i++)
    355		nft_expr_destroy(ctx, priv->expr_array[i]);
    356
    357	nf_tables_destroy_set(ctx, priv->set);
    358}
    359
    360static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
    361{
    362	const struct nft_dynset *priv = nft_expr_priv(expr);
    363	u32 flags = priv->invert ? NFT_DYNSET_F_INV : 0;
    364	int i;
    365
    366	if (nft_dump_register(skb, NFTA_DYNSET_SREG_KEY, priv->sreg_key))
    367		goto nla_put_failure;
    368	if (priv->set->flags & NFT_SET_MAP &&
    369	    nft_dump_register(skb, NFTA_DYNSET_SREG_DATA, priv->sreg_data))
    370		goto nla_put_failure;
    371	if (nla_put_be32(skb, NFTA_DYNSET_OP, htonl(priv->op)))
    372		goto nla_put_failure;
    373	if (nla_put_string(skb, NFTA_DYNSET_SET_NAME, priv->set->name))
    374		goto nla_put_failure;
    375	if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT,
    376			 nf_jiffies64_to_msecs(priv->timeout),
    377			 NFTA_DYNSET_PAD))
    378		goto nla_put_failure;
    379	if (priv->set->num_exprs == 0) {
    380		if (priv->num_exprs == 1) {
    381			if (nft_expr_dump(skb, NFTA_DYNSET_EXPR,
    382					  priv->expr_array[0]))
    383				goto nla_put_failure;
    384		} else if (priv->num_exprs > 1) {
    385			struct nlattr *nest;
    386
    387			nest = nla_nest_start_noflag(skb, NFTA_DYNSET_EXPRESSIONS);
    388			if (!nest)
    389				goto nla_put_failure;
    390
    391			for (i = 0; i < priv->num_exprs; i++) {
    392				if (nft_expr_dump(skb, NFTA_LIST_ELEM,
    393						  priv->expr_array[i]))
    394					goto nla_put_failure;
    395			}
    396			nla_nest_end(skb, nest);
    397		}
    398	}
    399	if (nla_put_be32(skb, NFTA_DYNSET_FLAGS, htonl(flags)))
    400		goto nla_put_failure;
    401	return 0;
    402
    403nla_put_failure:
    404	return -1;
    405}
    406
    407static const struct nft_expr_ops nft_dynset_ops = {
    408	.type		= &nft_dynset_type,
    409	.size		= NFT_EXPR_SIZE(sizeof(struct nft_dynset)),
    410	.eval		= nft_dynset_eval,
    411	.init		= nft_dynset_init,
    412	.destroy	= nft_dynset_destroy,
    413	.activate	= nft_dynset_activate,
    414	.deactivate	= nft_dynset_deactivate,
    415	.dump		= nft_dynset_dump,
    416	.reduce		= NFT_REDUCE_READONLY,
    417};
    418
    419struct nft_expr_type nft_dynset_type __read_mostly = {
    420	.name		= "dynset",
    421	.ops		= &nft_dynset_ops,
    422	.policy		= nft_dynset_policy,
    423	.maxattr	= NFTA_DYNSET_MAX,
    424	.owner		= THIS_MODULE,
    425};