This new helper function allows you to parse a list expression netlink attributes. This also to reuse the same funcion to support for several expressions in a set element. Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx> --- include/net/netfilter/nf_tables.h | 15 ++++ net/netfilter/nf_tables_api.c | 140 ++++++++++++++++++------------ 2 files changed, 100 insertions(+), 55 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 572bc780609c..1a2e6ebd3610 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -331,6 +331,21 @@ void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr); int nft_expr_dump(struct sk_buff *skb, unsigned int attr, const struct nft_expr *expr); +struct nft_expr_info { + const struct nft_expr_ops *ops; + const struct nlattr *attr; + struct nlattr *tb[NFT_EXPR_MAXATTR + 1]; +}; + +struct nft_expr_info_array { + u32 size; + u32 num_exprs; + struct nft_expr_info *info; +}; + +int nft_expr_parse(const struct nft_ctx *ctx, const struct nlattr *attr, + struct nft_expr_info_array *info_array, u32 max_exprs); + struct nft_set_ext; /** diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 41b83278efef..a98e4ce4e796 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2575,12 +2575,6 @@ int nft_expr_dump(struct sk_buff *skb, unsigned int attr, return -1; } -struct nft_expr_info { - const struct nft_expr_ops *ops; - const struct nlattr *attr; - struct nlattr *tb[NFT_EXPR_MAXATTR + 1]; -}; - static int nf_tables_expr_parse(const struct nft_ctx *ctx, const struct nlattr *nla, struct nft_expr_info *info) @@ -3145,6 +3139,61 @@ static int nft_table_validate(struct net *net, const struct nft_table *table) return 0; } +static void nft_expr_info_array_free(struct nft_expr_info_array *info_array) +{ + struct nft_expr_info *info; + int i; + + for (i = 0; i < info_array->num_exprs; i++) { + info = &info_array->info[i]; + if (info->ops) { + module_put(info->ops->type->owner); + if (info->ops->type->release_ops) + info->ops->type->release_ops(info->ops); + } + } + kvfree(info_array->info); +} + +int nft_expr_parse(const struct nft_ctx *ctx, const struct nlattr *attr, + struct nft_expr_info_array *info_array, u32 max_exprs) +{ + unsigned int num_exprs = 0, size = 0; + struct nft_expr_info *info; + struct nlattr *tmp; + int rem, err; + + info = kvmalloc_array(max_exprs, sizeof(struct nft_expr_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info_array->info = info; + nla_for_each_nested(tmp, attr, rem) { + if (nla_type(tmp) != NFTA_LIST_ELEM) { + err = -EINVAL; + goto err_expr_parse; + } + if (num_exprs == max_exprs) + goto err_expr_parse; + err = nf_tables_expr_parse(ctx, tmp, &info[num_exprs]); + if (err < 0) + goto err_expr_parse; + size += info[num_exprs].ops->size; + num_exprs++; + } + info_array->num_exprs = num_exprs; + info_array->size = size; + + return 0; + +err_expr_parse: + info_array->num_exprs = num_exprs; + nft_expr_info_array_free(info_array); + + return err; +} + static struct nft_rule *nft_rule_lookup_byid(const struct net *net, const struct nlattr *nla); @@ -3156,8 +3205,11 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, struct netlink_ext_ack *extack) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + struct nft_expr_info_array info_array = { + .size = 0, + .num_exprs = 0, + }; u8 genmask = nft_genmask_next(net); - struct nft_expr_info *info = NULL; int family = nfmsg->nfgen_family; struct nft_flow_rule *flow; struct nft_table *table; @@ -3167,9 +3219,8 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, struct nft_trans *trans = NULL; struct nft_expr *expr; struct nft_ctx ctx; - struct nlattr *tmp; - unsigned int size, i, n, ulen = 0, usize = 0; - int err, rem; + unsigned int i, ulen = 0, usize = 0; + int err; u64 handle, pos_handle; lockdep_assert_held(&net->nft.commit_mutex); @@ -3243,32 +3294,17 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla); - n = 0; - size = 0; if (nla[NFTA_RULE_EXPRESSIONS]) { - info = kvmalloc_array(NFT_RULE_MAXEXPRS, - sizeof(struct nft_expr_info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - - nla_for_each_nested(tmp, nla[NFTA_RULE_EXPRESSIONS], rem) { - err = -EINVAL; - if (nla_type(tmp) != NFTA_LIST_ELEM) - goto err1; - if (n == NFT_RULE_MAXEXPRS) - goto err1; - err = nf_tables_expr_parse(&ctx, tmp, &info[n]); - if (err < 0) - goto err1; - size += info[n].ops->size; - n++; - } + err = nft_expr_parse(&ctx, nla[NFTA_RULE_EXPRESSIONS], + &info_array, NFT_RULE_MAXEXPRS); + if (err < 0) + return err; } + /* Check for overflow of dlen field */ err = -EFBIG; - if (size >= 1 << 12) - goto err1; + if (info_array.size >= 1 << 12) + goto err_rule_expressions; if (nla[NFTA_RULE_USERDATA]) { ulen = nla_len(nla[NFTA_RULE_USERDATA]); @@ -3277,14 +3313,14 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, } err = -ENOMEM; - rule = kzalloc(sizeof(*rule) + size + usize, GFP_KERNEL); + rule = kzalloc(sizeof(*rule) + info_array.size + usize, GFP_KERNEL); if (rule == NULL) - goto err1; + goto err_rule_expressions; nft_activate_next(net, rule); rule->handle = handle; - rule->dlen = size; + rule->dlen = info_array.size; rule->udata = ulen ? 1 : 0; if (ulen) { @@ -3294,17 +3330,17 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, } expr = nft_expr_first(rule); - for (i = 0; i < n; i++) { - err = nf_tables_newexpr(&ctx, &info[i], expr); + for (i = 0; i < info_array.num_exprs; i++) { + err = nf_tables_newexpr(&ctx, &info_array.info[i], expr); if (err < 0) { - NL_SET_BAD_ATTR(extack, info[i].attr); - goto err2; + NL_SET_BAD_ATTR(extack, info_array.info[i].attr); + goto err_rule_release; } - if (info[i].ops->validate) + if (info_array.info[i].ops->validate) nft_validate_state_update(net, NFT_VALIDATE_NEED); - info[i].ops = NULL; + info_array.info[i].ops = NULL; expr = nft_expr_next(expr); } @@ -3312,12 +3348,12 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule); if (trans == NULL) { err = -ENOMEM; - goto err2; + goto err_rule_release; } err = nft_delrule(&ctx, old_rule); if (err < 0) { nft_trans_destroy(trans); - goto err2; + goto err_rule_release; } list_add_tail_rcu(&rule->list, &old_rule->list); @@ -3325,7 +3361,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule); if (!trans) { err = -ENOMEM; - goto err2; + goto err_rule_release; } if (nlh->nlmsg_flags & NLM_F_APPEND) { @@ -3340,7 +3376,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, list_add_rcu(&rule->list, &chain->rules); } } - kvfree(info); + kvfree(info_array.info); chain->use++; if (net->nft.validate_state == NFT_VALIDATE_DO) @@ -3355,17 +3391,11 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, } return 0; -err2: +err_rule_release: nf_tables_rule_release(&ctx, rule); -err1: - for (i = 0; i < n; i++) { - if (info[i].ops) { - module_put(info[i].ops->type->owner); - if (info[i].ops->type->release_ops) - info[i].ops->type->release_ops(info[i].ops); - } - } - kvfree(info); +err_rule_expressions: + nft_expr_info_array_free(&info_array); + return err; } -- 2.20.1