Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at master 441 lines 11 kB view raw
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] = NLA_POLICY_MASK(NLA_BE32, NFT_DYNSET_F_INV | 167 NFT_DYNSET_F_EXPR), 168 [NFTA_DYNSET_EXPRESSIONS] = { .type = NLA_NESTED }, 169}; 170 171static int nft_dynset_init(const struct nft_ctx *ctx, 172 const struct nft_expr *expr, 173 const struct nlattr * const tb[]) 174{ 175 struct nftables_pernet *nft_net = nft_pernet(ctx->net); 176 struct nft_dynset *priv = nft_expr_priv(expr); 177 u8 genmask = nft_genmask_next(ctx->net); 178 struct nft_set *set; 179 u64 timeout; 180 int err, i; 181 182 lockdep_assert_held(&nft_net->commit_mutex); 183 184 if (tb[NFTA_DYNSET_SET_NAME] == NULL || 185 tb[NFTA_DYNSET_OP] == NULL || 186 tb[NFTA_DYNSET_SREG_KEY] == NULL) 187 return -EINVAL; 188 189 if (tb[NFTA_DYNSET_FLAGS]) { 190 u32 flags = ntohl(nla_get_be32(tb[NFTA_DYNSET_FLAGS])); 191 if (flags & ~(NFT_DYNSET_F_INV | NFT_DYNSET_F_EXPR)) 192 return -EOPNOTSUPP; 193 if (flags & NFT_DYNSET_F_INV) 194 priv->invert = true; 195 if (flags & NFT_DYNSET_F_EXPR) 196 priv->expr = true; 197 } 198 199 set = nft_set_lookup_global(ctx->net, ctx->table, 200 tb[NFTA_DYNSET_SET_NAME], 201 tb[NFTA_DYNSET_SET_ID], genmask); 202 if (IS_ERR(set)) 203 return PTR_ERR(set); 204 205 if (set->flags & NFT_SET_OBJECT) 206 return -EOPNOTSUPP; 207 208 if (set->ops->update == NULL) 209 return -EOPNOTSUPP; 210 211 if (set->flags & NFT_SET_CONSTANT) 212 return -EBUSY; 213 214 priv->op = ntohl(nla_get_be32(tb[NFTA_DYNSET_OP])); 215 if (priv->op > NFT_DYNSET_OP_DELETE) 216 return -EOPNOTSUPP; 217 218 timeout = 0; 219 if (tb[NFTA_DYNSET_TIMEOUT] != NULL) { 220 if (!(set->flags & NFT_SET_TIMEOUT)) 221 return -EOPNOTSUPP; 222 223 err = nf_msecs_to_jiffies64(tb[NFTA_DYNSET_TIMEOUT], &timeout); 224 if (err) 225 return err; 226 } 227 228 err = nft_parse_register_load(ctx, tb[NFTA_DYNSET_SREG_KEY], &priv->sreg_key, 229 set->klen); 230 if (err < 0) 231 return err; 232 233 if (tb[NFTA_DYNSET_SREG_DATA] != NULL) { 234 if (!(set->flags & NFT_SET_MAP)) 235 return -EOPNOTSUPP; 236 if (set->dtype == NFT_DATA_VERDICT) 237 return -EOPNOTSUPP; 238 239 err = nft_parse_register_load(ctx, tb[NFTA_DYNSET_SREG_DATA], 240 &priv->sreg_data, set->dlen); 241 if (err < 0) 242 return err; 243 } else if (set->flags & NFT_SET_MAP) 244 return -EINVAL; 245 246 if ((tb[NFTA_DYNSET_EXPR] || tb[NFTA_DYNSET_EXPRESSIONS]) && 247 !(set->flags & NFT_SET_EVAL)) 248 return -EINVAL; 249 250 if (tb[NFTA_DYNSET_EXPR]) { 251 struct nft_expr *dynset_expr; 252 253 dynset_expr = nft_dynset_expr_alloc(ctx, set, 254 tb[NFTA_DYNSET_EXPR], 0); 255 if (IS_ERR(dynset_expr)) 256 return PTR_ERR(dynset_expr); 257 258 priv->num_exprs++; 259 priv->expr_array[0] = dynset_expr; 260 261 if (set->num_exprs > 1 || 262 (set->num_exprs == 1 && 263 dynset_expr->ops != set->exprs[0]->ops)) { 264 err = -EOPNOTSUPP; 265 goto err_expr_free; 266 } 267 } else if (tb[NFTA_DYNSET_EXPRESSIONS]) { 268 struct nft_expr *dynset_expr; 269 struct nlattr *tmp; 270 int left; 271 272 if (!priv->expr) 273 return -EINVAL; 274 275 i = 0; 276 nla_for_each_nested(tmp, tb[NFTA_DYNSET_EXPRESSIONS], left) { 277 if (i == NFT_SET_EXPR_MAX) { 278 err = -E2BIG; 279 goto err_expr_free; 280 } 281 if (nla_type(tmp) != NFTA_LIST_ELEM) { 282 err = -EINVAL; 283 goto err_expr_free; 284 } 285 dynset_expr = nft_dynset_expr_alloc(ctx, set, tmp, i); 286 if (IS_ERR(dynset_expr)) { 287 err = PTR_ERR(dynset_expr); 288 goto err_expr_free; 289 } 290 priv->expr_array[i] = dynset_expr; 291 priv->num_exprs++; 292 293 if (set->num_exprs) { 294 if (i >= set->num_exprs) { 295 err = -EINVAL; 296 goto err_expr_free; 297 } 298 if (dynset_expr->ops != set->exprs[i]->ops) { 299 err = -EOPNOTSUPP; 300 goto err_expr_free; 301 } 302 } 303 i++; 304 } 305 if (set->num_exprs && set->num_exprs != i) { 306 err = -EOPNOTSUPP; 307 goto err_expr_free; 308 } 309 } else if (set->num_exprs > 0) { 310 err = nft_set_elem_expr_clone(ctx, set, priv->expr_array); 311 if (err < 0) 312 return err; 313 314 priv->num_exprs = set->num_exprs; 315 } 316 317 nft_set_ext_prepare(&priv->tmpl); 318 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen); 319 if (set->flags & NFT_SET_MAP) 320 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen); 321 322 if (priv->num_exprs) 323 nft_dynset_ext_add_expr(priv); 324 325 if (set->flags & NFT_SET_TIMEOUT && 326 (timeout || READ_ONCE(set->timeout))) 327 nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_TIMEOUT); 328 329 priv->timeout = timeout; 330 331 err = nf_tables_bind_set(ctx, set, &priv->binding); 332 if (err < 0) 333 goto err_expr_free; 334 335 if (set->size == 0) 336 set->size = 0xffff; 337 338 priv->set = set; 339 return 0; 340 341err_expr_free: 342 for (i = 0; i < priv->num_exprs; i++) 343 nft_expr_destroy(ctx, priv->expr_array[i]); 344 return err; 345} 346 347static void nft_dynset_deactivate(const struct nft_ctx *ctx, 348 const struct nft_expr *expr, 349 enum nft_trans_phase phase) 350{ 351 struct nft_dynset *priv = nft_expr_priv(expr); 352 353 nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase); 354} 355 356static void nft_dynset_activate(const struct nft_ctx *ctx, 357 const struct nft_expr *expr) 358{ 359 struct nft_dynset *priv = nft_expr_priv(expr); 360 361 nf_tables_activate_set(ctx, priv->set); 362} 363 364static void nft_dynset_destroy(const struct nft_ctx *ctx, 365 const struct nft_expr *expr) 366{ 367 struct nft_dynset *priv = nft_expr_priv(expr); 368 int i; 369 370 for (i = 0; i < priv->num_exprs; i++) 371 nft_expr_destroy(ctx, priv->expr_array[i]); 372 373 nf_tables_destroy_set(ctx, priv->set); 374} 375 376static int nft_dynset_dump(struct sk_buff *skb, 377 const struct nft_expr *expr, bool reset) 378{ 379 const struct nft_dynset *priv = nft_expr_priv(expr); 380 u32 flags = priv->invert ? NFT_DYNSET_F_INV : 0; 381 int i; 382 383 if (nft_dump_register(skb, NFTA_DYNSET_SREG_KEY, priv->sreg_key)) 384 goto nla_put_failure; 385 if (priv->set->flags & NFT_SET_MAP && 386 nft_dump_register(skb, NFTA_DYNSET_SREG_DATA, priv->sreg_data)) 387 goto nla_put_failure; 388 if (nla_put_be32(skb, NFTA_DYNSET_OP, htonl(priv->op))) 389 goto nla_put_failure; 390 if (nla_put_string(skb, NFTA_DYNSET_SET_NAME, priv->set->name)) 391 goto nla_put_failure; 392 if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, 393 nf_jiffies64_to_msecs(priv->timeout), 394 NFTA_DYNSET_PAD)) 395 goto nla_put_failure; 396 if (priv->set->num_exprs == 0) { 397 if (priv->num_exprs == 1) { 398 if (nft_expr_dump(skb, NFTA_DYNSET_EXPR, 399 priv->expr_array[0], reset)) 400 goto nla_put_failure; 401 } else if (priv->num_exprs > 1) { 402 struct nlattr *nest; 403 404 nest = nla_nest_start_noflag(skb, NFTA_DYNSET_EXPRESSIONS); 405 if (!nest) 406 goto nla_put_failure; 407 408 for (i = 0; i < priv->num_exprs; i++) { 409 if (nft_expr_dump(skb, NFTA_LIST_ELEM, 410 priv->expr_array[i], reset)) 411 goto nla_put_failure; 412 } 413 nla_nest_end(skb, nest); 414 } 415 } 416 if (nla_put_be32(skb, NFTA_DYNSET_FLAGS, htonl(flags))) 417 goto nla_put_failure; 418 return 0; 419 420nla_put_failure: 421 return -1; 422} 423 424static const struct nft_expr_ops nft_dynset_ops = { 425 .type = &nft_dynset_type, 426 .size = NFT_EXPR_SIZE(sizeof(struct nft_dynset)), 427 .eval = nft_dynset_eval, 428 .init = nft_dynset_init, 429 .destroy = nft_dynset_destroy, 430 .activate = nft_dynset_activate, 431 .deactivate = nft_dynset_deactivate, 432 .dump = nft_dynset_dump, 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};