Commit 7b225d0b authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: nf_tables: add NFTA_SET_ELEM_KEY_END attribute

Add NFTA_SET_ELEM_KEY_END attribute to convey the closing element of the
interval between kernel and userspace.

This patch also adds the NFT_SET_EXT_KEY_END extension to store the
closing element value in this interval.

v4: No changes
v3: New patch

[sbrivio: refactor error paths and labels; add corresponding
  nft_set_ext_type for new key; rebase]
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 20a1452c
...@@ -231,6 +231,7 @@ struct nft_userdata { ...@@ -231,6 +231,7 @@ struct nft_userdata {
* struct nft_set_elem - generic representation of set elements * struct nft_set_elem - generic representation of set elements
* *
* @key: element key * @key: element key
* @key_end: closing element key
* @priv: element private data and extensions * @priv: element private data and extensions
*/ */
struct nft_set_elem { struct nft_set_elem {
...@@ -238,6 +239,10 @@ struct nft_set_elem { ...@@ -238,6 +239,10 @@ struct nft_set_elem {
u32 buf[NFT_DATA_VALUE_MAXLEN / sizeof(u32)]; u32 buf[NFT_DATA_VALUE_MAXLEN / sizeof(u32)];
struct nft_data val; struct nft_data val;
} key; } key;
union {
u32 buf[NFT_DATA_VALUE_MAXLEN / sizeof(u32)];
struct nft_data val;
} key_end;
void *priv; void *priv;
}; };
...@@ -502,6 +507,7 @@ void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set); ...@@ -502,6 +507,7 @@ void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set);
* enum nft_set_extensions - set extension type IDs * enum nft_set_extensions - set extension type IDs
* *
* @NFT_SET_EXT_KEY: element key * @NFT_SET_EXT_KEY: element key
* @NFT_SET_EXT_KEY_END: upper bound element key, for ranges
* @NFT_SET_EXT_DATA: mapping data * @NFT_SET_EXT_DATA: mapping data
* @NFT_SET_EXT_FLAGS: element flags * @NFT_SET_EXT_FLAGS: element flags
* @NFT_SET_EXT_TIMEOUT: element timeout * @NFT_SET_EXT_TIMEOUT: element timeout
...@@ -513,6 +519,7 @@ void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set); ...@@ -513,6 +519,7 @@ void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set);
*/ */
enum nft_set_extensions { enum nft_set_extensions {
NFT_SET_EXT_KEY, NFT_SET_EXT_KEY,
NFT_SET_EXT_KEY_END,
NFT_SET_EXT_DATA, NFT_SET_EXT_DATA,
NFT_SET_EXT_FLAGS, NFT_SET_EXT_FLAGS,
NFT_SET_EXT_TIMEOUT, NFT_SET_EXT_TIMEOUT,
...@@ -606,6 +613,11 @@ static inline struct nft_data *nft_set_ext_key(const struct nft_set_ext *ext) ...@@ -606,6 +613,11 @@ static inline struct nft_data *nft_set_ext_key(const struct nft_set_ext *ext)
return nft_set_ext(ext, NFT_SET_EXT_KEY); return nft_set_ext(ext, NFT_SET_EXT_KEY);
} }
static inline struct nft_data *nft_set_ext_key_end(const struct nft_set_ext *ext)
{
return nft_set_ext(ext, NFT_SET_EXT_KEY_END);
}
static inline struct nft_data *nft_set_ext_data(const struct nft_set_ext *ext) static inline struct nft_data *nft_set_ext_data(const struct nft_set_ext *ext)
{ {
return nft_set_ext(ext, NFT_SET_EXT_DATA); return nft_set_ext(ext, NFT_SET_EXT_DATA);
...@@ -655,7 +667,7 @@ static inline struct nft_object **nft_set_ext_obj(const struct nft_set_ext *ext) ...@@ -655,7 +667,7 @@ static inline struct nft_object **nft_set_ext_obj(const struct nft_set_ext *ext)
void *nft_set_elem_init(const struct nft_set *set, void *nft_set_elem_init(const struct nft_set *set,
const struct nft_set_ext_tmpl *tmpl, const struct nft_set_ext_tmpl *tmpl,
const u32 *key, const u32 *data, const u32 *key, const u32 *key_end, const u32 *data,
u64 timeout, u64 expiration, gfp_t gfp); u64 timeout, u64 expiration, gfp_t gfp);
void nft_set_elem_destroy(const struct nft_set *set, void *elem, void nft_set_elem_destroy(const struct nft_set *set, void *elem,
bool destroy_expr); bool destroy_expr);
......
...@@ -370,6 +370,7 @@ enum nft_set_elem_flags { ...@@ -370,6 +370,7 @@ enum nft_set_elem_flags {
* @NFTA_SET_ELEM_USERDATA: user data (NLA_BINARY) * @NFTA_SET_ELEM_USERDATA: user data (NLA_BINARY)
* @NFTA_SET_ELEM_EXPR: expression (NLA_NESTED: nft_expr_attributes) * @NFTA_SET_ELEM_EXPR: expression (NLA_NESTED: nft_expr_attributes)
* @NFTA_SET_ELEM_OBJREF: stateful object reference (NLA_STRING) * @NFTA_SET_ELEM_OBJREF: stateful object reference (NLA_STRING)
* @NFTA_SET_ELEM_KEY_END: closing key value (NLA_NESTED: nft_data)
*/ */
enum nft_set_elem_attributes { enum nft_set_elem_attributes {
NFTA_SET_ELEM_UNSPEC, NFTA_SET_ELEM_UNSPEC,
...@@ -382,6 +383,7 @@ enum nft_set_elem_attributes { ...@@ -382,6 +383,7 @@ enum nft_set_elem_attributes {
NFTA_SET_ELEM_EXPR, NFTA_SET_ELEM_EXPR,
NFTA_SET_ELEM_PAD, NFTA_SET_ELEM_PAD,
NFTA_SET_ELEM_OBJREF, NFTA_SET_ELEM_OBJREF,
NFTA_SET_ELEM_KEY_END,
__NFTA_SET_ELEM_MAX __NFTA_SET_ELEM_MAX
}; };
#define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1) #define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1)
......
...@@ -4215,6 +4215,9 @@ const struct nft_set_ext_type nft_set_ext_types[] = { ...@@ -4215,6 +4215,9 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
.len = sizeof(struct nft_userdata), .len = sizeof(struct nft_userdata),
.align = __alignof__(struct nft_userdata), .align = __alignof__(struct nft_userdata),
}, },
[NFT_SET_EXT_KEY_END] = {
.align = __alignof__(u32),
},
}; };
EXPORT_SYMBOL_GPL(nft_set_ext_types); EXPORT_SYMBOL_GPL(nft_set_ext_types);
...@@ -4233,6 +4236,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = { ...@@ -4233,6 +4236,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
[NFTA_SET_ELEM_EXPR] = { .type = NLA_NESTED }, [NFTA_SET_ELEM_EXPR] = { .type = NLA_NESTED },
[NFTA_SET_ELEM_OBJREF] = { .type = NLA_STRING, [NFTA_SET_ELEM_OBJREF] = { .type = NLA_STRING,
.len = NFT_OBJ_MAXNAMELEN - 1 }, .len = NFT_OBJ_MAXNAMELEN - 1 },
[NFTA_SET_ELEM_KEY_END] = { .type = NLA_NESTED },
}; };
static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = { static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
...@@ -4282,6 +4286,11 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, ...@@ -4282,6 +4286,11 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
NFT_DATA_VALUE, set->klen) < 0) NFT_DATA_VALUE, set->klen) < 0)
goto nla_put_failure; goto nla_put_failure;
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) &&
nft_data_dump(skb, NFTA_SET_ELEM_KEY_END, nft_set_ext_key_end(ext),
NFT_DATA_VALUE, set->klen) < 0)
goto nla_put_failure;
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
nft_data_dump(skb, NFTA_SET_ELEM_DATA, nft_set_ext_data(ext), nft_data_dump(skb, NFTA_SET_ELEM_DATA, nft_set_ext_data(ext),
set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE, set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE,
...@@ -4569,6 +4578,13 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -4569,6 +4578,13 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (err < 0) if (err < 0)
return err; return err;
if (nla[NFTA_SET_ELEM_KEY_END]) {
err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
nla[NFTA_SET_ELEM_KEY_END]);
if (err < 0)
return err;
}
priv = set->ops->get(ctx->net, set, &elem, flags); priv = set->ops->get(ctx->net, set, &elem, flags);
if (IS_ERR(priv)) if (IS_ERR(priv))
return PTR_ERR(priv); return PTR_ERR(priv);
...@@ -4694,8 +4710,8 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx, ...@@ -4694,8 +4710,8 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
void *nft_set_elem_init(const struct nft_set *set, void *nft_set_elem_init(const struct nft_set *set,
const struct nft_set_ext_tmpl *tmpl, const struct nft_set_ext_tmpl *tmpl,
const u32 *key, const u32 *data, const u32 *key, const u32 *key_end,
u64 timeout, u64 expiration, gfp_t gfp) const u32 *data, u64 timeout, u64 expiration, gfp_t gfp)
{ {
struct nft_set_ext *ext; struct nft_set_ext *ext;
void *elem; void *elem;
...@@ -4708,6 +4724,8 @@ void *nft_set_elem_init(const struct nft_set *set, ...@@ -4708,6 +4724,8 @@ void *nft_set_elem_init(const struct nft_set *set,
nft_set_ext_init(ext, tmpl); nft_set_ext_init(ext, tmpl);
memcpy(nft_set_ext_key(ext), key, set->klen); 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)) if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
memcpy(nft_set_ext_data(ext), data, set->dlen); memcpy(nft_set_ext_data(ext), data, set->dlen);
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) { if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
...@@ -4842,9 +4860,19 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -4842,9 +4860,19 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
err = nft_setelem_parse_key(ctx, set, &elem.key.val, err = nft_setelem_parse_key(ctx, set, &elem.key.val,
nla[NFTA_SET_ELEM_KEY]); nla[NFTA_SET_ELEM_KEY]);
if (err < 0) if (err < 0)
goto err1; return err;
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen); nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
if (nla[NFTA_SET_ELEM_KEY_END]) {
err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
nla[NFTA_SET_ELEM_KEY_END]);
if (err < 0)
goto err_parse_key;
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY_END, set->klen);
}
if (timeout > 0) { if (timeout > 0) {
nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION); nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION);
if (timeout != set->timeout) if (timeout != set->timeout)
...@@ -4854,14 +4882,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -4854,14 +4882,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (nla[NFTA_SET_ELEM_OBJREF] != NULL) { if (nla[NFTA_SET_ELEM_OBJREF] != NULL) {
if (!(set->flags & NFT_SET_OBJECT)) { if (!(set->flags & NFT_SET_OBJECT)) {
err = -EINVAL; err = -EINVAL;
goto err2; goto err_parse_key_end;
} }
obj = nft_obj_lookup(ctx->net, ctx->table, obj = nft_obj_lookup(ctx->net, ctx->table,
nla[NFTA_SET_ELEM_OBJREF], nla[NFTA_SET_ELEM_OBJREF],
set->objtype, genmask); set->objtype, genmask);
if (IS_ERR(obj)) { if (IS_ERR(obj)) {
err = PTR_ERR(obj); err = PTR_ERR(obj);
goto err2; goto err_parse_key_end;
} }
nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF); nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF);
} }
...@@ -4870,11 +4898,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -4870,11 +4898,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
err = nft_data_init(ctx, &data, sizeof(data), &desc, err = nft_data_init(ctx, &data, sizeof(data), &desc,
nla[NFTA_SET_ELEM_DATA]); nla[NFTA_SET_ELEM_DATA]);
if (err < 0) if (err < 0)
goto err2; goto err_parse_key_end;
err = -EINVAL; err = -EINVAL;
if (set->dtype != NFT_DATA_VERDICT && desc.len != set->dlen) if (set->dtype != NFT_DATA_VERDICT && desc.len != set->dlen)
goto err3; goto err_parse_data;
dreg = nft_type_to_reg(set->dtype); dreg = nft_type_to_reg(set->dtype);
list_for_each_entry(binding, &set->bindings, list) { list_for_each_entry(binding, &set->bindings, list) {
...@@ -4892,7 +4920,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -4892,7 +4920,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
&data, &data,
desc.type, desc.len); desc.type, desc.len);
if (err < 0) if (err < 0)
goto err3; goto err_parse_data;
if (desc.type == NFT_DATA_VERDICT && if (desc.type == NFT_DATA_VERDICT &&
(data.verdict.code == NFT_GOTO || (data.verdict.code == NFT_GOTO ||
...@@ -4917,10 +4945,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -4917,10 +4945,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
} }
err = -ENOMEM; err = -ENOMEM;
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, data.data, elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
elem.key_end.val.data, data.data,
timeout, expiration, GFP_KERNEL); timeout, expiration, GFP_KERNEL);
if (elem.priv == NULL) if (elem.priv == NULL)
goto err3; goto err_parse_data;
ext = nft_set_elem_ext(set, elem.priv); ext = nft_set_elem_ext(set, elem.priv);
if (flags) if (flags)
...@@ -4937,7 +4966,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -4937,7 +4966,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
if (trans == NULL) if (trans == NULL)
goto err4; goto err_trans;
ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK; ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
err = set->ops->insert(ctx->net, set, &elem, &ext2); err = set->ops->insert(ctx->net, set, &elem, &ext2);
...@@ -4948,7 +4977,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -4948,7 +4977,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) ^ nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) ^
nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF)) { nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF)) {
err = -EBUSY; err = -EBUSY;
goto err5; goto err_element_clash;
} }
if ((nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && if ((nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) && nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) &&
...@@ -4961,33 +4990,35 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -4961,33 +4990,35 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
else if (!(nlmsg_flags & NLM_F_EXCL)) else if (!(nlmsg_flags & NLM_F_EXCL))
err = 0; err = 0;
} }
goto err5; goto err_element_clash;
} }
if (set->size && if (set->size &&
!atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) { !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) {
err = -ENFILE; err = -ENFILE;
goto err6; goto err_set_full;
} }
nft_trans_elem(trans) = elem; nft_trans_elem(trans) = elem;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0; return 0;
err6: err_set_full:
set->ops->remove(ctx->net, set, &elem); set->ops->remove(ctx->net, set, &elem);
err5: err_element_clash:
kfree(trans); kfree(trans);
err4: err_trans:
if (obj) if (obj)
obj->use--; obj->use--;
kfree(elem.priv); kfree(elem.priv);
err3: err_parse_data:
if (nla[NFTA_SET_ELEM_DATA] != NULL) if (nla[NFTA_SET_ELEM_DATA] != NULL)
nft_data_release(&data, desc.type); nft_data_release(&data, desc.type);
err2: err_parse_key_end:
nft_data_release(&elem.key_end.val, NFT_DATA_VALUE);
err_parse_key:
nft_data_release(&elem.key.val, NFT_DATA_VALUE); nft_data_release(&elem.key.val, NFT_DATA_VALUE);
err1:
return err; return err;
} }
...@@ -5112,9 +5143,19 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -5112,9 +5143,19 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen); nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
if (nla[NFTA_SET_ELEM_KEY_END]) {
err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
nla[NFTA_SET_ELEM_KEY_END]);
if (err < 0)
return err;
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY_END, set->klen);
}
err = -ENOMEM; err = -ENOMEM;
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, NULL, 0, elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
0, GFP_KERNEL); elem.key_end.val.data, NULL, 0, 0,
GFP_KERNEL);
if (elem.priv == NULL) if (elem.priv == NULL)
goto fail_elem; goto fail_elem;
......
...@@ -54,7 +54,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, ...@@ -54,7 +54,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
timeout = priv->timeout ? : set->timeout; timeout = priv->timeout ? : set->timeout;
elem = nft_set_elem_init(set, &priv->tmpl, elem = nft_set_elem_init(set, &priv->tmpl,
&regs->data[priv->sreg_key], &regs->data[priv->sreg_key], NULL,
&regs->data[priv->sreg_data], &regs->data[priv->sreg_data],
timeout, 0, GFP_ATOMIC); timeout, 0, GFP_ATOMIC);
if (elem == NULL) if (elem == NULL)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment