mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 17:30:02 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf
Pablo Neira Ayuso says: ==================== Netfilter fixes for net The following patchset contains Netfilter fixes for net: 1) Harden set element field checks to avoid out-of-bound memory access, this patch also fixes the type of issue described in7e6bc1f6ca
("netfilter: nf_tables: stricter validation of element data") in a broader way. 2) Patches to restrict the chain, set, and rule id lookup in the transaction to the corresponding top-level table, patches from Thadeu Lima de Souza Cascardo. 3) Fix incorrect comment in ip6t_LOG.h 4) nft_data_init() performs upfront validation of the expected data. struct nft_data_desc is used to describe the expected data to be received from userspace. The .size field represents the maximum size that can be stored, for bound checks. Then, .len is an input/output field which stores the expected length as input (this is optional, to restrict the checks), as output it stores the real length received from userspace (if it was not specified as input). This patch comes in response to7e6bc1f6ca
("netfilter: nf_tables: stricter validation of element data") to address this type of issue in a more generic way by avoid opencoded data validation. Next patch requires this as a dependency. 5) Disallow jump to implicit chain from set element, this configuration is invalid. Only allow jump to chain via immediate expression is supported at this stage. 6) Fix possible null-pointer derefence in the error path of table updates, if memory allocation of the transaction fails. From Florian Westphal. * git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf: netfilter: nf_tables: fix null deref due to zeroed list head netfilter: nf_tables: disallow jump to implicit chain from set element netfilter: nf_tables: upfront validation of data via nft_data_init() netfilter: ip6t_LOG: Fix a typo in a comment netfilter: nf_tables: do not allow RULE_ID to refer to another chain netfilter: nf_tables: do not allow CHAIN_ID to refer to another table netfilter: nf_tables: do not allow SET_ID to refer to another table netfilter: nf_tables: validate variable length element extension ==================== Link: https://lore.kernel.org/r/20220809220532.130240-1-pablo@netfilter.org/ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
690bf64395
8 changed files with 225 additions and 141 deletions
|
@ -221,13 +221,18 @@ struct nft_ctx {
|
|||
bool report;
|
||||
};
|
||||
|
||||
struct nft_data_desc {
|
||||
enum nft_data_types type;
|
||||
unsigned int len;
|
||||
enum nft_data_desc_flags {
|
||||
NFT_DATA_DESC_SETELEM = (1 << 0),
|
||||
};
|
||||
|
||||
int nft_data_init(const struct nft_ctx *ctx,
|
||||
struct nft_data *data, unsigned int size,
|
||||
struct nft_data_desc {
|
||||
enum nft_data_types type;
|
||||
unsigned int size;
|
||||
unsigned int len;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
|
||||
struct nft_data_desc *desc, const struct nlattr *nla);
|
||||
void nft_data_hold(const struct nft_data *data, enum nft_data_types type);
|
||||
void nft_data_release(const struct nft_data *data, enum nft_data_types type);
|
||||
|
@ -651,6 +656,7 @@ extern const struct nft_set_ext_type nft_set_ext_types[];
|
|||
struct nft_set_ext_tmpl {
|
||||
u16 len;
|
||||
u8 offset[NFT_SET_EXT_NUM];
|
||||
u8 ext_len[NFT_SET_EXT_NUM];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -680,7 +686,8 @@ static inline int nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id,
|
|||
return -EINVAL;
|
||||
|
||||
tmpl->offset[id] = tmpl->len;
|
||||
tmpl->len += nft_set_ext_types[id].len + len;
|
||||
tmpl->ext_len[id] = nft_set_ext_types[id].len + len;
|
||||
tmpl->len += tmpl->ext_len[id];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -17,4 +17,4 @@ struct ip6t_log_info {
|
|||
char prefix[30];
|
||||
};
|
||||
|
||||
#endif /*_IPT_LOG_H*/
|
||||
#endif /* _IP6T_LOG_H */
|
||||
|
|
|
@ -153,6 +153,7 @@ static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx,
|
|||
if (trans == NULL)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&trans->list);
|
||||
trans->msg_type = msg_type;
|
||||
trans->ctx = *ctx;
|
||||
|
||||
|
@ -2472,6 +2473,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
|
|||
}
|
||||
|
||||
static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
|
||||
const struct nft_table *table,
|
||||
const struct nlattr *nla)
|
||||
{
|
||||
struct nftables_pernet *nft_net = nft_pernet(net);
|
||||
|
@ -2482,6 +2484,7 @@ static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
|
|||
struct nft_chain *chain = trans->ctx.chain;
|
||||
|
||||
if (trans->msg_type == NFT_MSG_NEWCHAIN &&
|
||||
chain->table == table &&
|
||||
id == nft_trans_chain_id(trans))
|
||||
return chain;
|
||||
}
|
||||
|
@ -3371,6 +3374,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table)
|
|||
}
|
||||
|
||||
static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
|
||||
const struct nft_chain *chain,
|
||||
const struct nlattr *nla);
|
||||
|
||||
#define NFT_RULE_MAXEXPRS 128
|
||||
|
@ -3417,7 +3421,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
|
|||
return -EOPNOTSUPP;
|
||||
|
||||
} else if (nla[NFTA_RULE_CHAIN_ID]) {
|
||||
chain = nft_chain_lookup_byid(net, nla[NFTA_RULE_CHAIN_ID]);
|
||||
chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]);
|
||||
if (IS_ERR(chain)) {
|
||||
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]);
|
||||
return PTR_ERR(chain);
|
||||
|
@ -3459,7 +3463,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
|
|||
return PTR_ERR(old_rule);
|
||||
}
|
||||
} else if (nla[NFTA_RULE_POSITION_ID]) {
|
||||
old_rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_POSITION_ID]);
|
||||
old_rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_POSITION_ID]);
|
||||
if (IS_ERR(old_rule)) {
|
||||
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION_ID]);
|
||||
return PTR_ERR(old_rule);
|
||||
|
@ -3604,6 +3608,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
|
|||
}
|
||||
|
||||
static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
|
||||
const struct nft_chain *chain,
|
||||
const struct nlattr *nla)
|
||||
{
|
||||
struct nftables_pernet *nft_net = nft_pernet(net);
|
||||
|
@ -3614,6 +3619,7 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
|
|||
struct nft_rule *rule = nft_trans_rule(trans);
|
||||
|
||||
if (trans->msg_type == NFT_MSG_NEWRULE &&
|
||||
trans->ctx.chain == chain &&
|
||||
id == nft_trans_rule_id(trans))
|
||||
return rule;
|
||||
}
|
||||
|
@ -3663,7 +3669,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
|
|||
|
||||
err = nft_delrule(&ctx, rule);
|
||||
} else if (nla[NFTA_RULE_ID]) {
|
||||
rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_ID]);
|
||||
rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_ID]);
|
||||
if (IS_ERR(rule)) {
|
||||
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_ID]);
|
||||
return PTR_ERR(rule);
|
||||
|
@ -3842,6 +3848,7 @@ static struct nft_set *nft_set_lookup_byhandle(const struct nft_table *table,
|
|||
}
|
||||
|
||||
static struct nft_set *nft_set_lookup_byid(const struct net *net,
|
||||
const struct nft_table *table,
|
||||
const struct nlattr *nla, u8 genmask)
|
||||
{
|
||||
struct nftables_pernet *nft_net = nft_pernet(net);
|
||||
|
@ -3853,6 +3860,7 @@ static struct nft_set *nft_set_lookup_byid(const struct net *net,
|
|||
struct nft_set *set = nft_trans_set(trans);
|
||||
|
||||
if (id == nft_trans_set_id(trans) &&
|
||||
set->table == table &&
|
||||
nft_active_genmask(set, genmask))
|
||||
return set;
|
||||
}
|
||||
|
@ -3873,7 +3881,7 @@ struct nft_set *nft_set_lookup_global(const struct net *net,
|
|||
if (!nla_set_id)
|
||||
return set;
|
||||
|
||||
set = nft_set_lookup_byid(net, nla_set_id, genmask);
|
||||
set = nft_set_lookup_byid(net, table, nla_set_id, genmask);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
@ -5195,19 +5203,13 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
|
|||
static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set,
|
||||
struct nft_data *key, struct nlattr *attr)
|
||||
{
|
||||
struct nft_data_desc desc;
|
||||
int err;
|
||||
struct nft_data_desc desc = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = NFT_DATA_VALUE_MAXLEN,
|
||||
.len = set->klen,
|
||||
};
|
||||
|
||||
err = nft_data_init(ctx, key, NFT_DATA_VALUE_MAXLEN, &desc, attr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) {
|
||||
nft_data_release(key, desc.type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nft_data_init(ctx, key, &desc, attr);
|
||||
}
|
||||
|
||||
static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set,
|
||||
|
@ -5216,24 +5218,18 @@ static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set,
|
|||
struct nlattr *attr)
|
||||
{
|
||||
u32 dtype;
|
||||
int err;
|
||||
|
||||
err = nft_data_init(ctx, data, NFT_DATA_VALUE_MAXLEN, desc, attr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (set->dtype == NFT_DATA_VERDICT)
|
||||
dtype = NFT_DATA_VERDICT;
|
||||
else
|
||||
dtype = NFT_DATA_VALUE;
|
||||
|
||||
if (dtype != desc->type ||
|
||||
set->dlen != desc->len) {
|
||||
nft_data_release(data, desc->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
desc->type = dtype;
|
||||
desc->size = NFT_DATA_VALUE_MAXLEN;
|
||||
desc->len = set->dlen;
|
||||
desc->flags = NFT_DATA_DESC_SETELEM;
|
||||
|
||||
return 0;
|
||||
return nft_data_init(ctx, data, desc, attr);
|
||||
}
|
||||
|
||||
static void *nft_setelem_catchall_get(const struct net *net,
|
||||
|
@ -5467,6 +5463,27 @@ struct nft_expr *nft_set_elem_expr_alloc(const struct nft_ctx *ctx,
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int nft_set_ext_check(const struct nft_set_ext_tmpl *tmpl, u8 id, u32 len)
|
||||
{
|
||||
len += nft_set_ext_types[id].len;
|
||||
if (len > tmpl->ext_len[id] ||
|
||||
len > U8_MAX)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nft_set_ext_memcpy(const struct nft_set_ext_tmpl *tmpl, u8 id,
|
||||
void *to, const void *from, u32 len)
|
||||
{
|
||||
if (nft_set_ext_check(tmpl, id, len) < 0)
|
||||
return -1;
|
||||
|
||||
memcpy(to, from, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *nft_set_elem_init(const struct nft_set *set,
|
||||
const struct nft_set_ext_tmpl *tmpl,
|
||||
const u32 *key, const u32 *key_end,
|
||||
|
@ -5477,17 +5494,26 @@ void *nft_set_elem_init(const struct nft_set *set,
|
|||
|
||||
elem = kzalloc(set->ops->elemsize + tmpl->len, gfp);
|
||||
if (elem == NULL)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ext = nft_set_elem_ext(set, elem);
|
||||
nft_set_ext_init(ext, tmpl);
|
||||
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY))
|
||||
memcpy(nft_set_ext_key(ext), key, set->klen);
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
|
||||
memcpy(nft_set_ext_key_end(ext), key_end, set->klen);
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
|
||||
memcpy(nft_set_ext_data(ext), data, set->dlen);
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) &&
|
||||
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY,
|
||||
nft_set_ext_key(ext), key, set->klen) < 0)
|
||||
goto err_ext_check;
|
||||
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) &&
|
||||
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY_END,
|
||||
nft_set_ext_key_end(ext), key_end, set->klen) < 0)
|
||||
goto err_ext_check;
|
||||
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
|
||||
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_DATA,
|
||||
nft_set_ext_data(ext), data, set->dlen) < 0)
|
||||
goto err_ext_check;
|
||||
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
|
||||
*nft_set_ext_expiration(ext) = get_jiffies_64() + expiration;
|
||||
if (expiration == 0)
|
||||
|
@ -5497,6 +5523,11 @@ void *nft_set_elem_init(const struct nft_set *set,
|
|||
*nft_set_ext_timeout(ext) = timeout;
|
||||
|
||||
return elem;
|
||||
|
||||
err_ext_check:
|
||||
kfree(elem);
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
|
||||
|
@ -5584,14 +5615,25 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
|
|||
}
|
||||
|
||||
static int nft_set_elem_expr_setup(struct nft_ctx *ctx,
|
||||
const struct nft_set_ext_tmpl *tmpl,
|
||||
const struct nft_set_ext *ext,
|
||||
struct nft_expr *expr_array[],
|
||||
u32 num_exprs)
|
||||
{
|
||||
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
|
||||
u32 len = sizeof(struct nft_set_elem_expr);
|
||||
struct nft_expr *expr;
|
||||
int i, err;
|
||||
|
||||
if (num_exprs == 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < num_exprs; i++)
|
||||
len += expr_array[i]->ops->size;
|
||||
|
||||
if (nft_set_ext_check(tmpl, NFT_SET_EXT_EXPRESSIONS, len) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < num_exprs; i++) {
|
||||
expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
|
||||
err = nft_expr_clone(expr, expr_array[i]);
|
||||
|
@ -6054,17 +6096,23 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
|||
}
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
|
||||
elem.key_end.val.data, elem.data.val.data,
|
||||
timeout, expiration, GFP_KERNEL_ACCOUNT);
|
||||
if (elem.priv == NULL)
|
||||
if (IS_ERR(elem.priv)) {
|
||||
err = PTR_ERR(elem.priv);
|
||||
goto err_parse_data;
|
||||
}
|
||||
|
||||
ext = nft_set_elem_ext(set, elem.priv);
|
||||
if (flags)
|
||||
*nft_set_ext_flags(ext) = flags;
|
||||
|
||||
if (ulen > 0) {
|
||||
if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) {
|
||||
err = -EINVAL;
|
||||
goto err_elem_userdata;
|
||||
}
|
||||
udata = nft_set_ext_userdata(ext);
|
||||
udata->len = ulen - 1;
|
||||
nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
|
||||
|
@ -6073,14 +6121,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
|||
*nft_set_ext_obj(ext) = obj;
|
||||
obj->use++;
|
||||
}
|
||||
err = nft_set_elem_expr_setup(ctx, ext, expr_array, num_exprs);
|
||||
err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs);
|
||||
if (err < 0)
|
||||
goto err_elem_expr;
|
||||
goto err_elem_free;
|
||||
|
||||
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
|
||||
if (trans == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto err_elem_expr;
|
||||
goto err_elem_free;
|
||||
}
|
||||
|
||||
ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
|
||||
|
@ -6126,10 +6174,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
|||
nft_setelem_remove(ctx->net, set, &elem);
|
||||
err_element_clash:
|
||||
kfree(trans);
|
||||
err_elem_expr:
|
||||
err_elem_free:
|
||||
if (obj)
|
||||
obj->use--;
|
||||
|
||||
err_elem_userdata:
|
||||
nf_tables_set_elem_destroy(ctx, set, elem.priv);
|
||||
err_parse_data:
|
||||
if (nla[NFTA_SET_ELEM_DATA] != NULL)
|
||||
|
@ -6311,8 +6359,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
|
|||
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
|
||||
elem.key_end.val.data, NULL, 0, 0,
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
if (elem.priv == NULL)
|
||||
if (IS_ERR(elem.priv)) {
|
||||
err = PTR_ERR(elem.priv);
|
||||
goto fail_elem_key_end;
|
||||
}
|
||||
|
||||
ext = nft_set_elem_ext(set, elem.priv);
|
||||
if (flags)
|
||||
|
@ -9605,7 +9655,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
|
|||
tb[NFTA_VERDICT_CHAIN],
|
||||
genmask);
|
||||
} else if (tb[NFTA_VERDICT_CHAIN_ID]) {
|
||||
chain = nft_chain_lookup_byid(ctx->net,
|
||||
chain = nft_chain_lookup_byid(ctx->net, ctx->table,
|
||||
tb[NFTA_VERDICT_CHAIN_ID]);
|
||||
if (IS_ERR(chain))
|
||||
return PTR_ERR(chain);
|
||||
|
@ -9617,6 +9667,9 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
|
|||
return PTR_ERR(chain);
|
||||
if (nft_is_base_chain(chain))
|
||||
return -EOPNOTSUPP;
|
||||
if (desc->flags & NFT_DATA_DESC_SETELEM &&
|
||||
chain->flags & NFT_CHAIN_BINDING)
|
||||
return -EINVAL;
|
||||
|
||||
chain->use++;
|
||||
data->verdict.chain = chain;
|
||||
|
@ -9624,7 +9677,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
|
|||
}
|
||||
|
||||
desc->len = sizeof(data->verdict);
|
||||
desc->type = NFT_DATA_VERDICT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -9677,20 +9730,25 @@ int nft_verdict_dump(struct sk_buff *skb, int type, const struct nft_verdict *v)
|
|||
}
|
||||
|
||||
static int nft_value_init(const struct nft_ctx *ctx,
|
||||
struct nft_data *data, unsigned int size,
|
||||
struct nft_data_desc *desc, const struct nlattr *nla)
|
||||
struct nft_data *data, struct nft_data_desc *desc,
|
||||
const struct nlattr *nla)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
len = nla_len(nla);
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
if (len > size)
|
||||
if (len > desc->size)
|
||||
return -EOVERFLOW;
|
||||
if (desc->len) {
|
||||
if (len != desc->len)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
desc->len = len;
|
||||
}
|
||||
|
||||
nla_memcpy(data->data, nla, len);
|
||||
desc->type = NFT_DATA_VALUE;
|
||||
desc->len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -9710,7 +9768,6 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = {
|
|||
*
|
||||
* @ctx: context of the expression using the data
|
||||
* @data: destination struct nft_data
|
||||
* @size: maximum data length
|
||||
* @desc: data description
|
||||
* @nla: netlink attribute containing data
|
||||
*
|
||||
|
@ -9720,24 +9777,35 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = {
|
|||
* The caller can indicate that it only wants to accept data of type
|
||||
* NFT_DATA_VALUE by passing NULL for the ctx argument.
|
||||
*/
|
||||
int nft_data_init(const struct nft_ctx *ctx,
|
||||
struct nft_data *data, unsigned int size,
|
||||
int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
|
||||
struct nft_data_desc *desc, const struct nlattr *nla)
|
||||
{
|
||||
struct nlattr *tb[NFTA_DATA_MAX + 1];
|
||||
int err;
|
||||
|
||||
if (WARN_ON_ONCE(!desc->size))
|
||||
return -EINVAL;
|
||||
|
||||
err = nla_parse_nested_deprecated(tb, NFTA_DATA_MAX, nla,
|
||||
nft_data_policy, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (tb[NFTA_DATA_VALUE])
|
||||
return nft_value_init(ctx, data, size, desc,
|
||||
tb[NFTA_DATA_VALUE]);
|
||||
if (tb[NFTA_DATA_VERDICT] && ctx != NULL)
|
||||
return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
|
||||
return -EINVAL;
|
||||
if (tb[NFTA_DATA_VALUE]) {
|
||||
if (desc->type != NFT_DATA_VALUE)
|
||||
return -EINVAL;
|
||||
|
||||
err = nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]);
|
||||
} else if (tb[NFTA_DATA_VERDICT] && ctx != NULL) {
|
||||
if (desc->type != NFT_DATA_VERDICT)
|
||||
return -EINVAL;
|
||||
|
||||
err = nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nft_data_init);
|
||||
|
||||
|
|
|
@ -93,7 +93,16 @@ static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = {
|
|||
static int nft_bitwise_init_bool(struct nft_bitwise *priv,
|
||||
const struct nlattr *const tb[])
|
||||
{
|
||||
struct nft_data_desc mask, xor;
|
||||
struct nft_data_desc mask = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = sizeof(priv->mask),
|
||||
.len = priv->len,
|
||||
};
|
||||
struct nft_data_desc xor = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = sizeof(priv->xor),
|
||||
.len = priv->len,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (tb[NFTA_BITWISE_DATA])
|
||||
|
@ -103,37 +112,30 @@ static int nft_bitwise_init_bool(struct nft_bitwise *priv,
|
|||
!tb[NFTA_BITWISE_XOR])
|
||||
return -EINVAL;
|
||||
|
||||
err = nft_data_init(NULL, &priv->mask, sizeof(priv->mask), &mask,
|
||||
tb[NFTA_BITWISE_MASK]);
|
||||
err = nft_data_init(NULL, &priv->mask, &mask, tb[NFTA_BITWISE_MASK]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (mask.type != NFT_DATA_VALUE || mask.len != priv->len) {
|
||||
err = -EINVAL;
|
||||
goto err_mask_release;
|
||||
}
|
||||
|
||||
err = nft_data_init(NULL, &priv->xor, sizeof(priv->xor), &xor,
|
||||
tb[NFTA_BITWISE_XOR]);
|
||||
err = nft_data_init(NULL, &priv->xor, &xor, tb[NFTA_BITWISE_XOR]);
|
||||
if (err < 0)
|
||||
goto err_mask_release;
|
||||
if (xor.type != NFT_DATA_VALUE || xor.len != priv->len) {
|
||||
err = -EINVAL;
|
||||
goto err_xor_release;
|
||||
}
|
||||
goto err_xor_err;
|
||||
|
||||
return 0;
|
||||
|
||||
err_xor_release:
|
||||
nft_data_release(&priv->xor, xor.type);
|
||||
err_mask_release:
|
||||
err_xor_err:
|
||||
nft_data_release(&priv->mask, mask.type);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nft_bitwise_init_shift(struct nft_bitwise *priv,
|
||||
const struct nlattr *const tb[])
|
||||
{
|
||||
struct nft_data_desc d;
|
||||
struct nft_data_desc desc = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = sizeof(priv->data),
|
||||
.len = sizeof(u32),
|
||||
};
|
||||
int err;
|
||||
|
||||
if (tb[NFTA_BITWISE_MASK] ||
|
||||
|
@ -143,13 +145,12 @@ static int nft_bitwise_init_shift(struct nft_bitwise *priv,
|
|||
if (!tb[NFTA_BITWISE_DATA])
|
||||
return -EINVAL;
|
||||
|
||||
err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &d,
|
||||
tb[NFTA_BITWISE_DATA]);
|
||||
err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_BITWISE_DATA]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (d.type != NFT_DATA_VALUE || d.len != sizeof(u32) ||
|
||||
priv->data.data[0] >= BITS_PER_TYPE(u32)) {
|
||||
nft_data_release(&priv->data, d.type);
|
||||
|
||||
if (priv->data.data[0] >= BITS_PER_TYPE(u32)) {
|
||||
nft_data_release(&priv->data, desc.type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -339,22 +340,21 @@ static const struct nft_expr_ops nft_bitwise_ops = {
|
|||
static int
|
||||
nft_bitwise_extract_u32_data(const struct nlattr * const tb, u32 *out)
|
||||
{
|
||||
struct nft_data_desc desc;
|
||||
struct nft_data data;
|
||||
int err = 0;
|
||||
struct nft_data_desc desc = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = sizeof(data),
|
||||
.len = sizeof(u32),
|
||||
};
|
||||
int err;
|
||||
|
||||
err = nft_data_init(NULL, &data, sizeof(data), &desc, tb);
|
||||
err = nft_data_init(NULL, &data, &desc, tb);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (desc.type != NFT_DATA_VALUE || desc.len != sizeof(u32)) {
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
*out = data.data[0];
|
||||
err:
|
||||
nft_data_release(&data, desc.type);
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nft_bitwise_fast_init(const struct nft_ctx *ctx,
|
||||
|
|
|
@ -73,20 +73,16 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
|||
const struct nlattr * const tb[])
|
||||
{
|
||||
struct nft_cmp_expr *priv = nft_expr_priv(expr);
|
||||
struct nft_data_desc desc;
|
||||
struct nft_data_desc desc = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = sizeof(priv->data),
|
||||
};
|
||||
int err;
|
||||
|
||||
err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc,
|
||||
tb[NFTA_CMP_DATA]);
|
||||
err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (desc.type != NFT_DATA_VALUE) {
|
||||
err = -EINVAL;
|
||||
nft_data_release(&priv->data, desc.type);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = nft_parse_register_load(tb[NFTA_CMP_SREG], &priv->sreg, desc.len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -214,12 +210,14 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx,
|
|||
const struct nlattr * const tb[])
|
||||
{
|
||||
struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
|
||||
struct nft_data_desc desc;
|
||||
struct nft_data data;
|
||||
struct nft_data_desc desc = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = sizeof(data),
|
||||
};
|
||||
int err;
|
||||
|
||||
err = nft_data_init(NULL, &data, sizeof(data), &desc,
|
||||
tb[NFTA_CMP_DATA]);
|
||||
err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -313,11 +311,13 @@ static int nft_cmp16_fast_init(const struct nft_ctx *ctx,
|
|||
const struct nlattr * const tb[])
|
||||
{
|
||||
struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr);
|
||||
struct nft_data_desc desc;
|
||||
struct nft_data_desc desc = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = sizeof(priv->data),
|
||||
};
|
||||
int err;
|
||||
|
||||
err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc,
|
||||
tb[NFTA_CMP_DATA]);
|
||||
err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -380,8 +380,11 @@ const struct nft_expr_ops nft_cmp16_fast_ops = {
|
|||
static const struct nft_expr_ops *
|
||||
nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
|
||||
{
|
||||
struct nft_data_desc desc;
|
||||
struct nft_data data;
|
||||
struct nft_data_desc desc = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = sizeof(data),
|
||||
};
|
||||
enum nft_cmp_ops op;
|
||||
u8 sreg;
|
||||
int err;
|
||||
|
@ -404,14 +407,10 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
|
|||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
err = nft_data_init(NULL, &data, sizeof(data), &desc,
|
||||
tb[NFTA_CMP_DATA]);
|
||||
err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
|
||||
if (desc.type != NFT_DATA_VALUE)
|
||||
goto err1;
|
||||
|
||||
sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
|
||||
|
||||
if (op == NFT_CMP_EQ || op == NFT_CMP_NEQ) {
|
||||
|
@ -423,9 +422,6 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
|
|||
return &nft_cmp16_fast_ops;
|
||||
}
|
||||
return &nft_cmp_ops;
|
||||
err1:
|
||||
nft_data_release(&data, desc.type);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
struct nft_expr_type nft_cmp_type __read_mostly = {
|
||||
|
|
|
@ -60,7 +60,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
|
|||
®s->data[priv->sreg_key], NULL,
|
||||
®s->data[priv->sreg_data],
|
||||
timeout, 0, GFP_ATOMIC);
|
||||
if (elem == NULL)
|
||||
if (IS_ERR(elem))
|
||||
goto err1;
|
||||
|
||||
ext = nft_set_elem_ext(set, elem);
|
||||
|
|
|
@ -29,20 +29,36 @@ static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = {
|
|||
[NFTA_IMMEDIATE_DATA] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
static enum nft_data_types nft_reg_to_type(const struct nlattr *nla)
|
||||
{
|
||||
enum nft_data_types type;
|
||||
u8 reg;
|
||||
|
||||
reg = ntohl(nla_get_be32(nla));
|
||||
if (reg == NFT_REG_VERDICT)
|
||||
type = NFT_DATA_VERDICT;
|
||||
else
|
||||
type = NFT_DATA_VALUE;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static int nft_immediate_init(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
const struct nlattr * const tb[])
|
||||
{
|
||||
struct nft_immediate_expr *priv = nft_expr_priv(expr);
|
||||
struct nft_data_desc desc;
|
||||
struct nft_data_desc desc = {
|
||||
.size = sizeof(priv->data),
|
||||
};
|
||||
int err;
|
||||
|
||||
if (tb[NFTA_IMMEDIATE_DREG] == NULL ||
|
||||
tb[NFTA_IMMEDIATE_DATA] == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
err = nft_data_init(ctx, &priv->data, sizeof(priv->data), &desc,
|
||||
tb[NFTA_IMMEDIATE_DATA]);
|
||||
desc.type = nft_reg_to_type(tb[NFTA_IMMEDIATE_DREG]);
|
||||
err = nft_data_init(ctx, &priv->data, &desc, tb[NFTA_IMMEDIATE_DATA]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
|
|
@ -51,7 +51,14 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr
|
|||
const struct nlattr * const tb[])
|
||||
{
|
||||
struct nft_range_expr *priv = nft_expr_priv(expr);
|
||||
struct nft_data_desc desc_from, desc_to;
|
||||
struct nft_data_desc desc_from = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = sizeof(priv->data_from),
|
||||
};
|
||||
struct nft_data_desc desc_to = {
|
||||
.type = NFT_DATA_VALUE,
|
||||
.size = sizeof(priv->data_to),
|
||||
};
|
||||
int err;
|
||||
u32 op;
|
||||
|
||||
|
@ -61,26 +68,16 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr
|
|||
!tb[NFTA_RANGE_TO_DATA])
|
||||
return -EINVAL;
|
||||
|
||||
err = nft_data_init(NULL, &priv->data_from, sizeof(priv->data_from),
|
||||
&desc_from, tb[NFTA_RANGE_FROM_DATA]);
|
||||
err = nft_data_init(NULL, &priv->data_from, &desc_from,
|
||||
tb[NFTA_RANGE_FROM_DATA]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (desc_from.type != NFT_DATA_VALUE) {
|
||||
err = -EINVAL;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
err = nft_data_init(NULL, &priv->data_to, sizeof(priv->data_to),
|
||||
&desc_to, tb[NFTA_RANGE_TO_DATA]);
|
||||
err = nft_data_init(NULL, &priv->data_to, &desc_to,
|
||||
tb[NFTA_RANGE_TO_DATA]);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
if (desc_to.type != NFT_DATA_VALUE) {
|
||||
err = -EINVAL;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
if (desc_from.len != desc_to.len) {
|
||||
err = -EINVAL;
|
||||
goto err2;
|
||||
|
|
Loading…
Reference in a new issue