Commit 8aeff920 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: nf_tables: add stateful object reference to set elements

This patch allows you to refer to stateful objects from set elements.
This provides the infrastructure to create maps where the right hand
side of the mapping is a stateful object.

This allows us to build dictionaries of stateful objects, that you can
use to perform fast lookups using any arbitrary key combination.
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 18965317
...@@ -326,6 +326,7 @@ void nft_unregister_set(struct nft_set_ops *ops); ...@@ -326,6 +326,7 @@ void nft_unregister_set(struct nft_set_ops *ops);
* @name: name of the set * @name: name of the set
* @ktype: key type (numeric type defined by userspace, not used in the kernel) * @ktype: key type (numeric type defined by userspace, not used in the kernel)
* @dtype: data type (verdict or numeric type defined by userspace) * @dtype: data type (verdict or numeric type defined by userspace)
* @objtype: object type (see NFT_OBJECT_* definitions)
* @size: maximum set size * @size: maximum set size
* @nelems: number of elements * @nelems: number of elements
* @ndeact: number of deactivated elements queued for removal * @ndeact: number of deactivated elements queued for removal
...@@ -347,6 +348,7 @@ struct nft_set { ...@@ -347,6 +348,7 @@ struct nft_set {
char name[NFT_SET_MAXNAMELEN]; char name[NFT_SET_MAXNAMELEN];
u32 ktype; u32 ktype;
u32 dtype; u32 dtype;
u32 objtype;
u32 size; u32 size;
atomic_t nelems; atomic_t nelems;
u32 ndeact; u32 ndeact;
...@@ -416,6 +418,7 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -416,6 +418,7 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
* @NFT_SET_EXT_EXPIRATION: element expiration time * @NFT_SET_EXT_EXPIRATION: element expiration time
* @NFT_SET_EXT_USERDATA: user data associated with the element * @NFT_SET_EXT_USERDATA: user data associated with the element
* @NFT_SET_EXT_EXPR: expression assiociated with the element * @NFT_SET_EXT_EXPR: expression assiociated with the element
* @NFT_SET_EXT_OBJREF: stateful object reference associated with element
* @NFT_SET_EXT_NUM: number of extension types * @NFT_SET_EXT_NUM: number of extension types
*/ */
enum nft_set_extensions { enum nft_set_extensions {
...@@ -426,6 +429,7 @@ enum nft_set_extensions { ...@@ -426,6 +429,7 @@ enum nft_set_extensions {
NFT_SET_EXT_EXPIRATION, NFT_SET_EXT_EXPIRATION,
NFT_SET_EXT_USERDATA, NFT_SET_EXT_USERDATA,
NFT_SET_EXT_EXPR, NFT_SET_EXT_EXPR,
NFT_SET_EXT_OBJREF,
NFT_SET_EXT_NUM NFT_SET_EXT_NUM
}; };
...@@ -554,6 +558,11 @@ static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, ...@@ -554,6 +558,11 @@ static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
return elem + set->ops->elemsize; return elem + set->ops->elemsize;
} }
static inline struct nft_object **nft_set_ext_obj(const struct nft_set_ext *ext)
{
return nft_set_ext(ext, NFT_SET_EXT_OBJREF);
}
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 *data,
......
...@@ -255,6 +255,7 @@ enum nft_rule_compat_attributes { ...@@ -255,6 +255,7 @@ enum nft_rule_compat_attributes {
* @NFT_SET_MAP: set is used as a dictionary * @NFT_SET_MAP: set is used as a dictionary
* @NFT_SET_TIMEOUT: set uses timeouts * @NFT_SET_TIMEOUT: set uses timeouts
* @NFT_SET_EVAL: set contains expressions for evaluation * @NFT_SET_EVAL: set contains expressions for evaluation
* @NFT_SET_OBJECT: set contains stateful objects
*/ */
enum nft_set_flags { enum nft_set_flags {
NFT_SET_ANONYMOUS = 0x1, NFT_SET_ANONYMOUS = 0x1,
...@@ -263,6 +264,7 @@ enum nft_set_flags { ...@@ -263,6 +264,7 @@ enum nft_set_flags {
NFT_SET_MAP = 0x8, NFT_SET_MAP = 0x8,
NFT_SET_TIMEOUT = 0x10, NFT_SET_TIMEOUT = 0x10,
NFT_SET_EVAL = 0x20, NFT_SET_EVAL = 0x20,
NFT_SET_OBJECT = 0x40,
}; };
/** /**
...@@ -304,6 +306,7 @@ enum nft_set_desc_attributes { ...@@ -304,6 +306,7 @@ enum nft_set_desc_attributes {
* @NFTA_SET_TIMEOUT: default timeout value (NLA_U64) * @NFTA_SET_TIMEOUT: default timeout value (NLA_U64)
* @NFTA_SET_GC_INTERVAL: garbage collection interval (NLA_U32) * @NFTA_SET_GC_INTERVAL: garbage collection interval (NLA_U32)
* @NFTA_SET_USERDATA: user data (NLA_BINARY) * @NFTA_SET_USERDATA: user data (NLA_BINARY)
* @NFTA_SET_OBJ_TYPE: stateful object type (NLA_U32: NFT_OBJECT_*)
*/ */
enum nft_set_attributes { enum nft_set_attributes {
NFTA_SET_UNSPEC, NFTA_SET_UNSPEC,
...@@ -321,6 +324,7 @@ enum nft_set_attributes { ...@@ -321,6 +324,7 @@ enum nft_set_attributes {
NFTA_SET_GC_INTERVAL, NFTA_SET_GC_INTERVAL,
NFTA_SET_USERDATA, NFTA_SET_USERDATA,
NFTA_SET_PAD, NFTA_SET_PAD,
NFTA_SET_OBJ_TYPE,
__NFTA_SET_MAX __NFTA_SET_MAX
}; };
#define NFTA_SET_MAX (__NFTA_SET_MAX - 1) #define NFTA_SET_MAX (__NFTA_SET_MAX - 1)
...@@ -344,6 +348,7 @@ enum nft_set_elem_flags { ...@@ -344,6 +348,7 @@ enum nft_set_elem_flags {
* @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64) * @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64)
* @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)
*/ */
enum nft_set_elem_attributes { enum nft_set_elem_attributes {
NFTA_SET_ELEM_UNSPEC, NFTA_SET_ELEM_UNSPEC,
...@@ -355,6 +360,7 @@ enum nft_set_elem_attributes { ...@@ -355,6 +360,7 @@ enum nft_set_elem_attributes {
NFTA_SET_ELEM_USERDATA, NFTA_SET_ELEM_USERDATA,
NFTA_SET_ELEM_EXPR, NFTA_SET_ELEM_EXPR,
NFTA_SET_ELEM_PAD, NFTA_SET_ELEM_PAD,
NFTA_SET_ELEM_OBJREF,
__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)
...@@ -1207,6 +1213,8 @@ enum nft_fib_flags { ...@@ -1207,6 +1213,8 @@ enum nft_fib_flags {
#define NFT_OBJECT_UNSPEC 0 #define NFT_OBJECT_UNSPEC 0
#define NFT_OBJECT_COUNTER 1 #define NFT_OBJECT_COUNTER 1
#define NFT_OBJECT_QUOTA 2 #define NFT_OBJECT_QUOTA 2
#define __NFT_OBJECT_MAX 3
#define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1)
/** /**
* enum nft_object_attributes - nf_tables stateful object netlink attributes * enum nft_object_attributes - nf_tables stateful object netlink attributes
......
...@@ -2452,6 +2452,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { ...@@ -2452,6 +2452,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
[NFTA_SET_GC_INTERVAL] = { .type = NLA_U32 }, [NFTA_SET_GC_INTERVAL] = { .type = NLA_U32 },
[NFTA_SET_USERDATA] = { .type = NLA_BINARY, [NFTA_SET_USERDATA] = { .type = NLA_BINARY,
.len = NFT_USERDATA_MAXLEN }, .len = NFT_USERDATA_MAXLEN },
[NFTA_SET_OBJ_TYPE] = { .type = NLA_U32 },
}; };
static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = { static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
...@@ -2609,6 +2610,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, ...@@ -2609,6 +2610,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
if (nla_put_be32(skb, NFTA_SET_DATA_LEN, htonl(set->dlen))) if (nla_put_be32(skb, NFTA_SET_DATA_LEN, htonl(set->dlen)))
goto nla_put_failure; goto nla_put_failure;
} }
if (set->flags & NFT_SET_OBJECT &&
nla_put_be32(skb, NFTA_SET_OBJ_TYPE, htonl(set->objtype)))
goto nla_put_failure;
if (set->timeout && if (set->timeout &&
nla_put_be64(skb, NFTA_SET_TIMEOUT, nla_put_be64(skb, NFTA_SET_TIMEOUT,
...@@ -2838,7 +2842,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, ...@@ -2838,7 +2842,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
unsigned int size; unsigned int size;
bool create; bool create;
u64 timeout; u64 timeout;
u32 ktype, dtype, flags, policy, gc_int; u32 ktype, dtype, flags, policy, gc_int, objtype;
struct nft_set_desc desc; struct nft_set_desc desc;
unsigned char *udata; unsigned char *udata;
u16 udlen; u16 udlen;
...@@ -2868,11 +2872,12 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, ...@@ -2868,11 +2872,12 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS])); flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT | if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
NFT_SET_INTERVAL | NFT_SET_TIMEOUT | NFT_SET_INTERVAL | NFT_SET_TIMEOUT |
NFT_SET_MAP | NFT_SET_EVAL)) NFT_SET_MAP | NFT_SET_EVAL |
NFT_SET_OBJECT))
return -EINVAL; return -EINVAL;
/* Only one of both operations is supported */ /* Only one of these operations is supported */
if ((flags & (NFT_SET_MAP | NFT_SET_EVAL)) == if ((flags & (NFT_SET_MAP | NFT_SET_EVAL | NFT_SET_OBJECT)) ==
(NFT_SET_MAP | NFT_SET_EVAL)) (NFT_SET_MAP | NFT_SET_EVAL | NFT_SET_OBJECT))
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -2897,6 +2902,19 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, ...@@ -2897,6 +2902,19 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
} else if (flags & NFT_SET_MAP) } else if (flags & NFT_SET_MAP)
return -EINVAL; return -EINVAL;
if (nla[NFTA_SET_OBJ_TYPE] != NULL) {
if (!(flags & NFT_SET_OBJECT))
return -EINVAL;
objtype = ntohl(nla_get_be32(nla[NFTA_SET_OBJ_TYPE]));
if (objtype == NFT_OBJECT_UNSPEC ||
objtype > NFT_OBJECT_MAX)
return -EINVAL;
} else if (flags & NFT_SET_OBJECT)
return -EINVAL;
else
objtype = NFT_OBJECT_UNSPEC;
timeout = 0; timeout = 0;
if (nla[NFTA_SET_TIMEOUT] != NULL) { if (nla[NFTA_SET_TIMEOUT] != NULL) {
if (!(flags & NFT_SET_TIMEOUT)) if (!(flags & NFT_SET_TIMEOUT))
...@@ -2984,6 +3002,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, ...@@ -2984,6 +3002,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
set->ktype = ktype; set->ktype = ktype;
set->klen = desc.klen; set->klen = desc.klen;
set->dtype = dtype; set->dtype = dtype;
set->objtype = objtype;
set->dlen = desc.dlen; set->dlen = desc.dlen;
set->flags = flags; set->flags = flags;
set->size = desc.size; set->size = desc.size;
...@@ -3126,6 +3145,10 @@ const struct nft_set_ext_type nft_set_ext_types[] = { ...@@ -3126,6 +3145,10 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
[NFT_SET_EXT_EXPR] = { [NFT_SET_EXT_EXPR] = {
.align = __alignof__(struct nft_expr), .align = __alignof__(struct nft_expr),
}, },
[NFT_SET_EXT_OBJREF] = {
.len = sizeof(struct nft_object *),
.align = __alignof__(struct nft_object *),
},
[NFT_SET_EXT_FLAGS] = { [NFT_SET_EXT_FLAGS] = {
.len = sizeof(u8), .len = sizeof(u8),
.align = __alignof__(u8), .align = __alignof__(u8),
...@@ -3214,6 +3237,11 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, ...@@ -3214,6 +3237,11 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, nft_set_ext_expr(ext)) < 0) nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, nft_set_ext_expr(ext)) < 0)
goto nla_put_failure; goto nla_put_failure;
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) &&
nla_put_string(skb, NFTA_SET_ELEM_OBJREF,
(*nft_set_ext_obj(ext))->name) < 0)
goto nla_put_failure;
if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) && if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, nla_put_be32(skb, NFTA_SET_ELEM_FLAGS,
htonl(*nft_set_ext_flags(ext)))) htonl(*nft_set_ext_flags(ext))))
...@@ -3508,7 +3536,8 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem, ...@@ -3508,7 +3536,8 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
nft_data_uninit(nft_set_ext_data(ext), set->dtype); nft_data_uninit(nft_set_ext_data(ext), set->dtype);
if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext)); nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext));
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
(*nft_set_ext_obj(ext))->use--;
kfree(elem); kfree(elem);
} }
EXPORT_SYMBOL_GPL(nft_set_elem_destroy); EXPORT_SYMBOL_GPL(nft_set_elem_destroy);
...@@ -3533,11 +3562,13 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3533,11 +3562,13 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
const struct nlattr *attr, u32 nlmsg_flags) const struct nlattr *attr, u32 nlmsg_flags)
{ {
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
u8 genmask = nft_genmask_next(ctx->net);
struct nft_data_desc d1, d2; struct nft_data_desc d1, d2;
struct nft_set_ext_tmpl tmpl; struct nft_set_ext_tmpl tmpl;
struct nft_set_ext *ext, *ext2; struct nft_set_ext *ext, *ext2;
struct nft_set_elem elem; struct nft_set_elem elem;
struct nft_set_binding *binding; struct nft_set_binding *binding;
struct nft_object *obj = NULL;
struct nft_userdata *udata; struct nft_userdata *udata;
struct nft_data data; struct nft_data data;
enum nft_registers dreg; enum nft_registers dreg;
...@@ -3600,6 +3631,20 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3600,6 +3631,20 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT); nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
} }
if (nla[NFTA_SET_ELEM_OBJREF] != NULL) {
if (!(set->flags & NFT_SET_OBJECT)) {
err = -EINVAL;
goto err2;
}
obj = nf_tables_obj_lookup(ctx->table, nla[NFTA_SET_ELEM_OBJREF],
set->objtype, genmask);
if (IS_ERR(obj)) {
err = PTR_ERR(obj);
goto err2;
}
nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF);
}
if (nla[NFTA_SET_ELEM_DATA] != NULL) { if (nla[NFTA_SET_ELEM_DATA] != NULL) {
err = nft_data_init(ctx, &data, sizeof(data), &d2, err = nft_data_init(ctx, &data, sizeof(data), &d2,
nla[NFTA_SET_ELEM_DATA]); nla[NFTA_SET_ELEM_DATA]);
...@@ -3658,6 +3703,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3658,6 +3703,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
udata->len = ulen - 1; udata->len = ulen - 1;
nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen); nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
} }
if (obj) {
*nft_set_ext_obj(ext) = obj;
obj->use++;
}
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)
...@@ -3667,10 +3716,13 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3667,10 +3716,13 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
err = set->ops->insert(ctx->net, set, &elem, &ext2); err = set->ops->insert(ctx->net, set, &elem, &ext2);
if (err) { if (err) {
if (err == -EEXIST) { if (err == -EEXIST) {
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) &&
memcmp(nft_set_ext_data(ext), memcmp(nft_set_ext_data(ext),
nft_set_ext_data(ext2), set->dlen) != 0) nft_set_ext_data(ext2), set->dlen) != 0) ||
(nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) &&
nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF) &&
*nft_set_ext_obj(ext) != *nft_set_ext_obj(ext2)))
err = -EBUSY; err = -EBUSY;
else if (!(nlmsg_flags & NLM_F_EXCL)) else if (!(nlmsg_flags & NLM_F_EXCL))
err = 0; err = 0;
......
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