1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2015 Patrick McHardy <kaber@trash.net>
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>
17 struct nft_set_ext_tmpl tmpl
;
18 enum nft_dynset_ops op
:8;
25 struct nft_expr
*expr_array
[NFT_SET_EXPR_MAX
];
26 struct nft_set_binding binding
;
29 static int nft_dynset_expr_setup(const struct nft_dynset
*priv
,
30 const struct nft_set_ext
*ext
)
32 struct nft_set_elem_expr
*elem_expr
= nft_set_ext_expr(ext
);
33 struct nft_expr
*expr
;
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
], GFP_ATOMIC
) < 0)
41 elem_expr
->size
+= priv
->expr_array
[i
]->ops
->size
;
47 static struct nft_elem_priv
*nft_dynset_new(struct nft_set
*set
,
48 const struct nft_expr
*expr
,
49 struct nft_regs
*regs
)
51 const struct nft_dynset
*priv
= nft_expr_priv(expr
);
52 struct nft_set_ext
*ext
;
56 if (!atomic_add_unless(&set
->nelems
, 1, set
->size
))
59 timeout
= priv
->timeout
? : READ_ONCE(set
->timeout
);
60 elem_priv
= nft_set_elem_init(set
, &priv
->tmpl
,
61 ®s
->data
[priv
->sreg_key
], NULL
,
62 ®s
->data
[priv
->sreg_data
],
63 timeout
, 0, GFP_ATOMIC
);
64 if (IS_ERR(elem_priv
))
67 ext
= nft_set_elem_ext(set
, elem_priv
);
68 if (priv
->num_exprs
&& nft_dynset_expr_setup(priv
, ext
) < 0)
74 nft_set_elem_destroy(set
, elem_priv
, false);
77 atomic_dec(&set
->nelems
);
81 void nft_dynset_eval(const struct nft_expr
*expr
,
82 struct nft_regs
*regs
, const struct nft_pktinfo
*pkt
)
84 const struct nft_dynset
*priv
= nft_expr_priv(expr
);
85 struct nft_set
*set
= priv
->set
;
86 const struct nft_set_ext
*ext
;
89 if (priv
->op
== NFT_DYNSET_OP_DELETE
) {
90 set
->ops
->delete(set
, ®s
->data
[priv
->sreg_key
]);
94 if (set
->ops
->update(set
, ®s
->data
[priv
->sreg_key
], nft_dynset_new
,
96 if (priv
->op
== NFT_DYNSET_OP_UPDATE
&&
97 nft_set_ext_exists(ext
, NFT_SET_EXT_TIMEOUT
) &&
98 READ_ONCE(nft_set_ext_timeout(ext
)->timeout
) != 0) {
99 timeout
= priv
->timeout
? : READ_ONCE(set
->timeout
);
100 WRITE_ONCE(nft_set_ext_timeout(ext
)->expiration
, get_jiffies_64() + timeout
);
103 nft_set_elem_update_expr(ext
, regs
, pkt
);
106 regs
->verdict
.code
= NFT_BREAK
;
111 regs
->verdict
.code
= NFT_BREAK
;
114 static void nft_dynset_ext_add_expr(struct nft_dynset
*priv
)
119 for (i
= 0; i
< priv
->num_exprs
; i
++)
120 size
+= priv
->expr_array
[i
]->ops
->size
;
122 nft_set_ext_add_length(&priv
->tmpl
, NFT_SET_EXT_EXPRESSIONS
,
123 sizeof(struct nft_set_elem_expr
) + size
);
126 static struct nft_expr
*
127 nft_dynset_expr_alloc(const struct nft_ctx
*ctx
, const struct nft_set
*set
,
128 const struct nlattr
*attr
, int pos
)
130 struct nft_expr
*expr
;
133 expr
= nft_set_elem_expr_alloc(ctx
, set
, attr
);
137 if (set
->exprs
[pos
] && set
->exprs
[pos
]->ops
!= expr
->ops
) {
139 goto err_dynset_expr
;
145 nft_expr_destroy(ctx
, expr
);
149 static const struct nla_policy nft_dynset_policy
[NFTA_DYNSET_MAX
+ 1] = {
150 [NFTA_DYNSET_SET_NAME
] = { .type
= NLA_STRING
,
151 .len
= NFT_SET_MAXNAMELEN
- 1 },
152 [NFTA_DYNSET_SET_ID
] = { .type
= NLA_U32
},
153 [NFTA_DYNSET_OP
] = NLA_POLICY_MAX(NLA_BE32
, 255),
154 [NFTA_DYNSET_SREG_KEY
] = { .type
= NLA_U32
},
155 [NFTA_DYNSET_SREG_DATA
] = { .type
= NLA_U32
},
156 [NFTA_DYNSET_TIMEOUT
] = { .type
= NLA_U64
},
157 [NFTA_DYNSET_EXPR
] = { .type
= NLA_NESTED
},
158 [NFTA_DYNSET_FLAGS
] = { .type
= NLA_U32
},
159 [NFTA_DYNSET_EXPRESSIONS
] = { .type
= NLA_NESTED
},
162 static int nft_dynset_init(const struct nft_ctx
*ctx
,
163 const struct nft_expr
*expr
,
164 const struct nlattr
* const tb
[])
166 struct nftables_pernet
*nft_net
= nft_pernet(ctx
->net
);
167 struct nft_dynset
*priv
= nft_expr_priv(expr
);
168 u8 genmask
= nft_genmask_next(ctx
->net
);
173 lockdep_assert_held(&nft_net
->commit_mutex
);
175 if (tb
[NFTA_DYNSET_SET_NAME
] == NULL
||
176 tb
[NFTA_DYNSET_OP
] == NULL
||
177 tb
[NFTA_DYNSET_SREG_KEY
] == NULL
)
180 if (tb
[NFTA_DYNSET_FLAGS
]) {
181 u32 flags
= ntohl(nla_get_be32(tb
[NFTA_DYNSET_FLAGS
]));
182 if (flags
& ~(NFT_DYNSET_F_INV
| NFT_DYNSET_F_EXPR
))
184 if (flags
& NFT_DYNSET_F_INV
)
186 if (flags
& NFT_DYNSET_F_EXPR
)
190 set
= nft_set_lookup_global(ctx
->net
, ctx
->table
,
191 tb
[NFTA_DYNSET_SET_NAME
],
192 tb
[NFTA_DYNSET_SET_ID
], genmask
);
196 if (set
->flags
& NFT_SET_OBJECT
)
199 if (set
->ops
->update
== NULL
)
202 if (set
->flags
& NFT_SET_CONSTANT
)
205 priv
->op
= ntohl(nla_get_be32(tb
[NFTA_DYNSET_OP
]));
206 if (priv
->op
> NFT_DYNSET_OP_DELETE
)
210 if (tb
[NFTA_DYNSET_TIMEOUT
] != NULL
) {
211 if (!(set
->flags
& NFT_SET_TIMEOUT
))
214 err
= nf_msecs_to_jiffies64(tb
[NFTA_DYNSET_TIMEOUT
], &timeout
);
219 err
= nft_parse_register_load(ctx
, tb
[NFTA_DYNSET_SREG_KEY
], &priv
->sreg_key
,
224 if (tb
[NFTA_DYNSET_SREG_DATA
] != NULL
) {
225 if (!(set
->flags
& NFT_SET_MAP
))
227 if (set
->dtype
== NFT_DATA_VERDICT
)
230 err
= nft_parse_register_load(ctx
, tb
[NFTA_DYNSET_SREG_DATA
],
231 &priv
->sreg_data
, set
->dlen
);
234 } else if (set
->flags
& NFT_SET_MAP
)
237 if ((tb
[NFTA_DYNSET_EXPR
] || tb
[NFTA_DYNSET_EXPRESSIONS
]) &&
238 !(set
->flags
& NFT_SET_EVAL
))
241 if (tb
[NFTA_DYNSET_EXPR
]) {
242 struct nft_expr
*dynset_expr
;
244 dynset_expr
= nft_dynset_expr_alloc(ctx
, set
,
245 tb
[NFTA_DYNSET_EXPR
], 0);
246 if (IS_ERR(dynset_expr
))
247 return PTR_ERR(dynset_expr
);
250 priv
->expr_array
[0] = dynset_expr
;
252 if (set
->num_exprs
> 1 ||
253 (set
->num_exprs
== 1 &&
254 dynset_expr
->ops
!= set
->exprs
[0]->ops
)) {
258 } else if (tb
[NFTA_DYNSET_EXPRESSIONS
]) {
259 struct nft_expr
*dynset_expr
;
267 nla_for_each_nested(tmp
, tb
[NFTA_DYNSET_EXPRESSIONS
], left
) {
268 if (i
== NFT_SET_EXPR_MAX
) {
272 if (nla_type(tmp
) != NFTA_LIST_ELEM
) {
276 dynset_expr
= nft_dynset_expr_alloc(ctx
, set
, tmp
, i
);
277 if (IS_ERR(dynset_expr
)) {
278 err
= PTR_ERR(dynset_expr
);
281 priv
->expr_array
[i
] = dynset_expr
;
284 if (set
->num_exprs
) {
285 if (i
>= set
->num_exprs
) {
289 if (dynset_expr
->ops
!= set
->exprs
[i
]->ops
) {
296 if (set
->num_exprs
&& set
->num_exprs
!= i
) {
300 } else if (set
->num_exprs
> 0) {
301 err
= nft_set_elem_expr_clone(ctx
, set
, priv
->expr_array
);
305 priv
->num_exprs
= set
->num_exprs
;
308 nft_set_ext_prepare(&priv
->tmpl
);
309 nft_set_ext_add_length(&priv
->tmpl
, NFT_SET_EXT_KEY
, set
->klen
);
310 if (set
->flags
& NFT_SET_MAP
)
311 nft_set_ext_add_length(&priv
->tmpl
, NFT_SET_EXT_DATA
, set
->dlen
);
314 nft_dynset_ext_add_expr(priv
);
316 if (set
->flags
& NFT_SET_TIMEOUT
&&
317 (timeout
|| READ_ONCE(set
->timeout
)))
318 nft_set_ext_add(&priv
->tmpl
, NFT_SET_EXT_TIMEOUT
);
320 priv
->timeout
= timeout
;
322 err
= nf_tables_bind_set(ctx
, set
, &priv
->binding
);
333 for (i
= 0; i
< priv
->num_exprs
; i
++)
334 nft_expr_destroy(ctx
, priv
->expr_array
[i
]);
338 static void nft_dynset_deactivate(const struct nft_ctx
*ctx
,
339 const struct nft_expr
*expr
,
340 enum nft_trans_phase phase
)
342 struct nft_dynset
*priv
= nft_expr_priv(expr
);
344 nf_tables_deactivate_set(ctx
, priv
->set
, &priv
->binding
, phase
);
347 static void nft_dynset_activate(const struct nft_ctx
*ctx
,
348 const struct nft_expr
*expr
)
350 struct nft_dynset
*priv
= nft_expr_priv(expr
);
352 nf_tables_activate_set(ctx
, priv
->set
);
355 static void nft_dynset_destroy(const struct nft_ctx
*ctx
,
356 const struct nft_expr
*expr
)
358 struct nft_dynset
*priv
= nft_expr_priv(expr
);
361 for (i
= 0; i
< priv
->num_exprs
; i
++)
362 nft_expr_destroy(ctx
, priv
->expr_array
[i
]);
364 nf_tables_destroy_set(ctx
, priv
->set
);
367 static int nft_dynset_dump(struct sk_buff
*skb
,
368 const struct nft_expr
*expr
, bool reset
)
370 const struct nft_dynset
*priv
= nft_expr_priv(expr
);
371 u32 flags
= priv
->invert
? NFT_DYNSET_F_INV
: 0;
374 if (nft_dump_register(skb
, NFTA_DYNSET_SREG_KEY
, priv
->sreg_key
))
375 goto nla_put_failure
;
376 if (priv
->set
->flags
& NFT_SET_MAP
&&
377 nft_dump_register(skb
, NFTA_DYNSET_SREG_DATA
, priv
->sreg_data
))
378 goto nla_put_failure
;
379 if (nla_put_be32(skb
, NFTA_DYNSET_OP
, htonl(priv
->op
)))
380 goto nla_put_failure
;
381 if (nla_put_string(skb
, NFTA_DYNSET_SET_NAME
, priv
->set
->name
))
382 goto nla_put_failure
;
383 if (nla_put_be64(skb
, NFTA_DYNSET_TIMEOUT
,
384 nf_jiffies64_to_msecs(priv
->timeout
),
386 goto nla_put_failure
;
387 if (priv
->set
->num_exprs
== 0) {
388 if (priv
->num_exprs
== 1) {
389 if (nft_expr_dump(skb
, NFTA_DYNSET_EXPR
,
390 priv
->expr_array
[0], reset
))
391 goto nla_put_failure
;
392 } else if (priv
->num_exprs
> 1) {
395 nest
= nla_nest_start_noflag(skb
, NFTA_DYNSET_EXPRESSIONS
);
397 goto nla_put_failure
;
399 for (i
= 0; i
< priv
->num_exprs
; i
++) {
400 if (nft_expr_dump(skb
, NFTA_LIST_ELEM
,
401 priv
->expr_array
[i
], reset
))
402 goto nla_put_failure
;
404 nla_nest_end(skb
, nest
);
407 if (nla_put_be32(skb
, NFTA_DYNSET_FLAGS
, htonl(flags
)))
408 goto nla_put_failure
;
415 static const struct nft_expr_ops nft_dynset_ops
= {
416 .type
= &nft_dynset_type
,
417 .size
= NFT_EXPR_SIZE(sizeof(struct nft_dynset
)),
418 .eval
= nft_dynset_eval
,
419 .init
= nft_dynset_init
,
420 .destroy
= nft_dynset_destroy
,
421 .activate
= nft_dynset_activate
,
422 .deactivate
= nft_dynset_deactivate
,
423 .dump
= nft_dynset_dump
,
424 .reduce
= NFT_REDUCE_READONLY
,
427 struct nft_expr_type nft_dynset_type __read_mostly
= {
429 .ops
= &nft_dynset_ops
,
430 .policy
= nft_dynset_policy
,
431 .maxattr
= NFTA_DYNSET_MAX
,
432 .owner
= THIS_MODULE
,