FILE
nft_dynset.c
File preview with syntax highlighting.
linux-6.19.14/net/netfilter/nft_dynset.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 ®s->data[priv->sreg_key], NULL, 70 ®s->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, ®s->data[priv->sreg_key]); 99 return; 100 } 101 102 ext = set->ops->update(set, ®s->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.