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

netfilter: nf_tables: add set element timeout support

Add API support for set element timeouts. Elements can have a individual
timeout value specified, overriding the sets' default.

Two new extension types are used for timeouts - the timeout value and
the expiration time. The timeout value only exists if it differs from
the default value.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 761da293
...@@ -329,12 +329,16 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -329,12 +329,16 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
* @NFT_SET_EXT_KEY: element key * @NFT_SET_EXT_KEY: element key
* @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_EXPIRATION: element expiration time
* @NFT_SET_EXT_NUM: number of extension types * @NFT_SET_EXT_NUM: number of extension types
*/ */
enum nft_set_extensions { enum nft_set_extensions {
NFT_SET_EXT_KEY, NFT_SET_EXT_KEY,
NFT_SET_EXT_DATA, NFT_SET_EXT_DATA,
NFT_SET_EXT_FLAGS, NFT_SET_EXT_FLAGS,
NFT_SET_EXT_TIMEOUT,
NFT_SET_EXT_EXPIRATION,
NFT_SET_EXT_NUM NFT_SET_EXT_NUM
}; };
...@@ -431,6 +435,22 @@ static inline u8 *nft_set_ext_flags(const struct nft_set_ext *ext) ...@@ -431,6 +435,22 @@ static inline u8 *nft_set_ext_flags(const struct nft_set_ext *ext)
return nft_set_ext(ext, NFT_SET_EXT_FLAGS); return nft_set_ext(ext, NFT_SET_EXT_FLAGS);
} }
static inline u64 *nft_set_ext_timeout(const struct nft_set_ext *ext)
{
return nft_set_ext(ext, NFT_SET_EXT_TIMEOUT);
}
static inline unsigned long *nft_set_ext_expiration(const struct nft_set_ext *ext)
{
return nft_set_ext(ext, NFT_SET_EXT_EXPIRATION);
}
static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
{
return nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION) &&
time_is_before_eq_jiffies(*nft_set_ext_expiration(ext));
}
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem) void *elem)
{ {
......
...@@ -290,12 +290,16 @@ enum nft_set_elem_flags { ...@@ -290,12 +290,16 @@ enum nft_set_elem_flags {
* @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data) * @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data)
* @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes) * @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes)
* @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32) * @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32)
* @NFTA_SET_ELEM_TIMEOUT: timeout value (NLA_U64)
* @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64)
*/ */
enum nft_set_elem_attributes { enum nft_set_elem_attributes {
NFTA_SET_ELEM_UNSPEC, NFTA_SET_ELEM_UNSPEC,
NFTA_SET_ELEM_KEY, NFTA_SET_ELEM_KEY,
NFTA_SET_ELEM_DATA, NFTA_SET_ELEM_DATA,
NFTA_SET_ELEM_FLAGS, NFTA_SET_ELEM_FLAGS,
NFTA_SET_ELEM_TIMEOUT,
NFTA_SET_ELEM_EXPIRATION,
__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)
......
...@@ -2863,6 +2863,14 @@ const struct nft_set_ext_type nft_set_ext_types[] = { ...@@ -2863,6 +2863,14 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
.len = sizeof(u8), .len = sizeof(u8),
.align = __alignof__(u8), .align = __alignof__(u8),
}, },
[NFT_SET_EXT_TIMEOUT] = {
.len = sizeof(u64),
.align = __alignof__(u64),
},
[NFT_SET_EXT_EXPIRATION] = {
.len = sizeof(unsigned long),
.align = __alignof__(unsigned long),
},
}; };
EXPORT_SYMBOL_GPL(nft_set_ext_types); EXPORT_SYMBOL_GPL(nft_set_ext_types);
...@@ -2874,6 +2882,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = { ...@@ -2874,6 +2882,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
[NFTA_SET_ELEM_KEY] = { .type = NLA_NESTED }, [NFTA_SET_ELEM_KEY] = { .type = NLA_NESTED },
[NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED }, [NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED },
[NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 }, [NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 },
[NFTA_SET_ELEM_TIMEOUT] = { .type = NLA_U64 },
}; };
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] = {
...@@ -2935,6 +2944,25 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, ...@@ -2935,6 +2944,25 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
htonl(*nft_set_ext_flags(ext)))) htonl(*nft_set_ext_flags(ext))))
goto nla_put_failure; goto nla_put_failure;
if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) &&
nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
cpu_to_be64(*nft_set_ext_timeout(ext))))
goto nla_put_failure;
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
unsigned long expires, now = jiffies;
expires = *nft_set_ext_expiration(ext);
if (time_before(now, expires))
expires -= now;
else
expires = 0;
if (nla_put_be64(skb, NFTA_SET_ELEM_EXPIRATION,
cpu_to_be64(jiffies_to_msecs(expires))))
goto nla_put_failure;
}
nla_nest_end(skb, nest); nla_nest_end(skb, nest);
return 0; return 0;
...@@ -3158,7 +3186,7 @@ static void *nft_set_elem_init(const struct nft_set *set, ...@@ -3158,7 +3186,7 @@ static 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 struct nft_data *key, const struct nft_data *key,
const struct nft_data *data, const struct nft_data *data,
gfp_t gfp) u64 timeout, gfp_t gfp)
{ {
struct nft_set_ext *ext; struct nft_set_ext *ext;
void *elem; void *elem;
...@@ -3173,6 +3201,11 @@ static void *nft_set_elem_init(const struct nft_set *set, ...@@ -3173,6 +3201,11 @@ static void *nft_set_elem_init(const struct nft_set *set,
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_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))
*nft_set_ext_expiration(ext) =
jiffies + msecs_to_jiffies(timeout);
if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT))
*nft_set_ext_timeout(ext) = timeout;
return elem; return elem;
} }
...@@ -3201,6 +3234,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3201,6 +3234,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
struct nft_data data; struct nft_data data;
enum nft_registers dreg; enum nft_registers dreg;
struct nft_trans *trans; struct nft_trans *trans;
u64 timeout;
u32 flags; u32 flags;
int err; int err;
...@@ -3241,6 +3275,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3241,6 +3275,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
return -EINVAL; return -EINVAL;
} }
timeout = 0;
if (nla[NFTA_SET_ELEM_TIMEOUT] != NULL) {
if (!(set->flags & NFT_SET_TIMEOUT))
return -EINVAL;
timeout = be64_to_cpu(nla_get_be64(nla[NFTA_SET_ELEM_TIMEOUT]));
} else if (set->flags & NFT_SET_TIMEOUT) {
timeout = set->timeout;
}
err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]); err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]);
if (err < 0) if (err < 0)
goto err1; goto err1;
...@@ -3249,6 +3292,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3249,6 +3292,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
goto err2; goto err2;
nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY); nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY);
if (timeout > 0) {
nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION);
if (timeout != set->timeout)
nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
}
if (nla[NFTA_SET_ELEM_DATA] != NULL) { if (nla[NFTA_SET_ELEM_DATA] != NULL) {
err = nft_data_init(ctx, &data, &d2, nla[NFTA_SET_ELEM_DATA]); err = nft_data_init(ctx, &data, &d2, nla[NFTA_SET_ELEM_DATA]);
...@@ -3277,7 +3325,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3277,7 +3325,8 @@ 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, GFP_KERNEL); elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data,
timeout, GFP_KERNEL);
if (elem.priv == NULL) if (elem.priv == NULL)
goto err3; goto err3;
......
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