Commit 7d740264 authored by Patrick McHardy's avatar Patrick McHardy Committed by Pablo Neira Ayuso

netfilter: nf_tables: variable sized set element keys / data

This patch changes sets to support variable sized set element keys / data
up to 64 bytes each by using variable sized set extensions. This allows
to use concatenations with bigger data items suchs as IPv6 addresses.

As a side effect, small keys/data now don't require the full 16 bytes
of struct nft_data anymore but just the space they need.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent d0a11fc3
...@@ -158,7 +158,10 @@ struct nft_userdata { ...@@ -158,7 +158,10 @@ struct nft_userdata {
* @priv: element private data and extensions * @priv: element private data and extensions
*/ */
struct nft_set_elem { struct nft_set_elem {
struct nft_data key; union {
u32 buf[NFT_DATA_VALUE_MAXLEN / sizeof(u32)];
struct nft_data val;
} key;
void *priv; void *priv;
}; };
......
...@@ -388,6 +388,9 @@ enum nft_data_attributes { ...@@ -388,6 +388,9 @@ enum nft_data_attributes {
}; };
#define NFTA_DATA_MAX (__NFTA_DATA_MAX - 1) #define NFTA_DATA_MAX (__NFTA_DATA_MAX - 1)
/* Maximum length of a value */
#define NFT_DATA_VALUE_MAXLEN 64
/** /**
* enum nft_verdict_attributes - nf_tables verdict netlink attributes * enum nft_verdict_attributes - nf_tables verdict netlink attributes
* *
......
...@@ -2608,7 +2608,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2608,7 +2608,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
} }
desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN])); desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
if (desc.klen == 0 || desc.klen > FIELD_SIZEOF(struct nft_data, data)) if (desc.klen == 0 || desc.klen > NFT_DATA_VALUE_MAXLEN)
return -EINVAL; return -EINVAL;
flags = 0; flags = 0;
...@@ -2634,11 +2634,10 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2634,11 +2634,10 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
if (nla[NFTA_SET_DATA_LEN] == NULL) if (nla[NFTA_SET_DATA_LEN] == NULL)
return -EINVAL; return -EINVAL;
desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN])); desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN]));
if (desc.dlen == 0 || if (desc.dlen == 0 || desc.dlen > NFT_DATA_VALUE_MAXLEN)
desc.dlen > FIELD_SIZEOF(struct nft_data, data))
return -EINVAL; return -EINVAL;
} else } else
desc.dlen = sizeof(struct nft_data); desc.dlen = sizeof(struct nft_verdict);
} else if (flags & NFT_SET_MAP) } else if (flags & NFT_SET_MAP)
return -EINVAL; return -EINVAL;
...@@ -2854,12 +2853,10 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -2854,12 +2853,10 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
const struct nft_set_ext_type nft_set_ext_types[] = { const struct nft_set_ext_type nft_set_ext_types[] = {
[NFT_SET_EXT_KEY] = { [NFT_SET_EXT_KEY] = {
.len = sizeof(struct nft_data), .align = __alignof__(u32),
.align = __alignof__(struct nft_data),
}, },
[NFT_SET_EXT_DATA] = { [NFT_SET_EXT_DATA] = {
.len = sizeof(struct nft_data), .align = __alignof__(u32),
.align = __alignof__(struct nft_data),
}, },
[NFT_SET_EXT_FLAGS] = { [NFT_SET_EXT_FLAGS] = {
.len = sizeof(u8), .len = sizeof(u8),
...@@ -3299,7 +3296,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3299,7 +3296,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
timeout = set->timeout; timeout = set->timeout;
} }
err = nft_data_init(ctx, &elem.key, sizeof(elem.key), &d1, err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &d1,
nla[NFTA_SET_ELEM_KEY]); nla[NFTA_SET_ELEM_KEY]);
if (err < 0) if (err < 0)
goto err1; goto err1;
...@@ -3307,7 +3304,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3307,7 +3304,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (d1.type != NFT_DATA_VALUE || d1.len != set->klen) if (d1.type != NFT_DATA_VALUE || d1.len != set->klen)
goto err2; goto err2;
nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY); nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, d1.len);
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)
...@@ -3342,7 +3339,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3342,7 +3339,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
goto err3; goto err3;
} }
nft_set_ext_add(&tmpl, NFT_SET_EXT_DATA); nft_set_ext_add_length(&tmpl, NFT_SET_EXT_DATA, d2.len);
} }
/* The full maximum length of userdata can exceed the maximum /* The full maximum length of userdata can exceed the maximum
...@@ -3358,7 +3355,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3358,7 +3355,7 @@ 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.data, data.data, elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, data.data,
timeout, GFP_KERNEL); timeout, GFP_KERNEL);
if (elem.priv == NULL) if (elem.priv == NULL)
goto err3; goto err3;
...@@ -3393,7 +3390,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3393,7 +3390,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (nla[NFTA_SET_ELEM_DATA] != NULL) if (nla[NFTA_SET_ELEM_DATA] != NULL)
nft_data_uninit(&data, d2.type); nft_data_uninit(&data, d2.type);
err2: err2:
nft_data_uninit(&elem.key, d1.type); nft_data_uninit(&elem.key.val, d1.type);
err1: err1:
return err; return err;
} }
...@@ -3460,7 +3457,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3460,7 +3457,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
if (nla[NFTA_SET_ELEM_KEY] == NULL) if (nla[NFTA_SET_ELEM_KEY] == NULL)
goto err1; goto err1;
err = nft_data_init(ctx, &elem.key, sizeof(elem.key), &desc, err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &desc,
nla[NFTA_SET_ELEM_KEY]); nla[NFTA_SET_ELEM_KEY]);
if (err < 0) if (err < 0)
goto err1; goto err1;
...@@ -3488,7 +3485,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3488,7 +3485,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
err3: err3:
kfree(trans); kfree(trans);
err2: err2:
nft_data_uninit(&elem.key, desc.type); nft_data_uninit(&elem.key.val, desc.type);
err1: err1:
return err; return err;
} }
......
...@@ -133,7 +133,7 @@ static int nft_hash_insert(const struct nft_set *set, ...@@ -133,7 +133,7 @@ static int nft_hash_insert(const struct nft_set *set,
struct nft_hash_cmp_arg arg = { struct nft_hash_cmp_arg arg = {
.genmask = nft_genmask_next(read_pnet(&set->pnet)), .genmask = nft_genmask_next(read_pnet(&set->pnet)),
.set = set, .set = set,
.key = elem->key.data, .key = elem->key.val.data,
}; };
return rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node, return rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node,
...@@ -157,7 +157,7 @@ static void *nft_hash_deactivate(const struct nft_set *set, ...@@ -157,7 +157,7 @@ static void *nft_hash_deactivate(const struct nft_set *set,
struct nft_hash_cmp_arg arg = { struct nft_hash_cmp_arg arg = {
.genmask = nft_genmask_next(read_pnet(&set->pnet)), .genmask = nft_genmask_next(read_pnet(&set->pnet)),
.set = set, .set = set,
.key = elem->key.data, .key = elem->key.val.data,
}; };
rcu_read_lock(); rcu_read_lock();
......
...@@ -152,7 +152,8 @@ static void *nft_rbtree_deactivate(const struct nft_set *set, ...@@ -152,7 +152,8 @@ static void *nft_rbtree_deactivate(const struct nft_set *set,
while (parent != NULL) { while (parent != NULL) {
rbe = rb_entry(parent, struct nft_rbtree_elem, node); rbe = rb_entry(parent, struct nft_rbtree_elem, node);
d = memcmp(nft_set_ext_key(&rbe->ext), &elem->key, set->klen); d = memcmp(nft_set_ext_key(&rbe->ext), &elem->key.val,
set->klen);
if (d < 0) if (d < 0)
parent = parent->rb_left; parent = parent->rb_left;
else if (d > 0) else if (d > 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