File Index
Files and directories
Sign in
FILE

nft_dynset.c

File preview with syntax highlighting.

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