* [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation
@ 2015-04-11 9:46 Patrick McHardy
2015-04-11 9:46 ` [PATCH 1/5] netfilter: nf_tables: add helper functions for expression handling Patrick McHardy
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Patrick McHardy @ 2015-04-11 9:46 UTC (permalink / raw)
To: pablo; +Cc: netfilter-devel
The following patches are the grand finale of my nf_tables set work,
using all the building blocks put in place by the previous patches
to support something like iptables hashlimit, but a lot more powerful.
Sets are extended to allow attaching expressions to set elements.
The dynset expression dynamically instantiates these expressions
based on a template when creating new set elements and evaluates
them for all new or updated set members.
In combination with concatenations this effectively creates state
tables for arbitrary combinations of keys, using the existing
expression types to maintain that state. Regular set GC takes care
of purging expired states.
We currently support two different stateful expressions, counter
and limit. Using limit as a template we can express the functionality
of hashlimit, but completely unrestricted in the combination of keys.
Using counter we can perform accounting for arbitrary flows.
The following examples from patch 5/5 show some possibilities.
Userspace syntax is still WIP, especially the listing of state
tables will most likely be seperated from normal set listings
and use a more structured format:
1. Limit the rate of new SSH connections per host, similar to iptables
hashlimit:
# nft filter input tcp dport ssh ct state new \
flow ip saddr timeout 60s \
limit 10/second \
accept
2. Account network traffic between each set of /24 networks:
# nft filter forward \
flow ip saddr & 255.255.255.0 . ip daddr & 255.255.255.0 \
counter
3. Account traffic to each host per user:
# nft filter output \
flow skuid . ip daddr \
counter
4. Account traffic for each combination of source address and TCP flags:
# nft filter input \
flow ip saddr . tcp flags \
counter
The resulting set content after a Xmas-scan look like this:
{
192.168.122.1 . fin | psh | urg : counter packets 1001 bytes 40040,
192.168.122.1 . ack : counter packets 74 bytes 3848,
192.168.122.1 . psh | ack : counter packets 35 bytes 3144
}
In the future the "expressions attached to elements" will be extended
to also support user created non-stateful expressions to allow to
efficiently select beween a set of parameter sets, f.i. a set of log
statements with different prefixes based on the interface, which currently
require one rule each. This will most likely have to wait until the next
kernel version though.
Please apply, thanks!
Patrick McHardy (5):
netfilter: nf_tables: add helper functions for expression handling
netfilter: nf_tables: prepare for expressions associated to set elements
netfilter: nf_tables: mark stateful expressions
netfilter: nf_tables: add flag to indicate set contains expressions
netfilter: nft_dynset: dynamic stateful expression instantiation
include/net/netfilter/nf_tables.h | 24 +++++++++++
include/uapi/linux/netfilter/nf_tables.h | 6 +++
net/netfilter/nf_tables_api.c | 73 +++++++++++++++++++++++++++++---
net/netfilter/nft_counter.c | 1 +
net/netfilter/nft_dynset.c | 52 +++++++++++++++++++++--
net/netfilter/nft_limit.c | 1 +
net/netfilter/nft_lookup.c | 3 ++
7 files changed, 149 insertions(+), 11 deletions(-)
--
2.1.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/5] netfilter: nf_tables: add helper functions for expression handling
2015-04-11 9:46 [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Patrick McHardy
@ 2015-04-11 9:46 ` Patrick McHardy
2015-04-11 9:46 ` [PATCH 2/5] netfilter: nf_tables: prepare for expressions associated to set elements Patrick McHardy
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Patrick McHardy @ 2015-04-11 9:46 UTC (permalink / raw)
To: pablo; +Cc: netfilter-devel
Add helper functions for initializing, cloning, dumping and destroying
a single expression that is not part of a rule.
Signed-off-by: Patrick McHardy <kaber@trash.net>
---
include/net/netfilter/nf_tables.h | 13 +++++++++
net/netfilter/nf_tables_api.c | 56 +++++++++++++++++++++++++++++++++++----
2 files changed, 64 insertions(+), 5 deletions(-)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index cb42da1..e21623c 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1,6 +1,7 @@
#ifndef _NET_NF_TABLES_H
#define _NET_NF_TABLES_H
+#include <linux/module.h>
#include <linux/list.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
@@ -641,6 +642,18 @@ static inline void *nft_expr_priv(const struct nft_expr *expr)
return (void *)expr->data;
}
+struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
+ const struct nlattr *nla);
+void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr);
+int nft_expr_dump(struct sk_buff *skb, unsigned int attr,
+ const struct nft_expr *expr);
+
+static inline void nft_expr_clone(struct nft_expr *dst, struct nft_expr *src)
+{
+ __module_get(src->ops->type->owner);
+ memcpy(dst, src, src->ops->size);
+}
+
/**
* struct nft_rule - nf_tables rule
*
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index ed0e70e..e97bee5 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -1545,6 +1545,23 @@ nla_put_failure:
return -1;
};
+int nft_expr_dump(struct sk_buff *skb, unsigned int attr,
+ const struct nft_expr *expr)
+{
+ struct nlattr *nest;
+
+ nest = nla_nest_start(skb, attr);
+ if (!nest)
+ goto nla_put_failure;
+ if (nf_tables_fill_expr_info(skb, expr) < 0)
+ goto nla_put_failure;
+ nla_nest_end(skb, nest);
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
struct nft_expr_info {
const struct nft_expr_ops *ops;
struct nlattr *tb[NFT_EXPR_MAXATTR + 1];
@@ -1622,6 +1639,39 @@ static void nf_tables_expr_destroy(const struct nft_ctx *ctx,
module_put(expr->ops->type->owner);
}
+struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
+ const struct nlattr *nla)
+{
+ struct nft_expr_info info;
+ struct nft_expr *expr;
+ int err;
+
+ err = nf_tables_expr_parse(ctx, nla, &info);
+ if (err < 0)
+ goto err1;
+
+ err = -ENOMEM;
+ expr = kzalloc(info.ops->size, GFP_KERNEL);
+ if (expr == NULL)
+ goto err2;
+
+ err = nf_tables_newexpr(ctx, &info, expr);
+ if (err < 0)
+ goto err2;
+
+ return expr;
+err2:
+ module_put(info.ops->type->owner);
+err1:
+ return ERR_PTR(err);
+}
+
+void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr)
+{
+ nf_tables_expr_destroy(ctx, expr);
+ kfree(expr);
+}
+
/*
* Rules
*/
@@ -1703,12 +1753,8 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net,
if (list == NULL)
goto nla_put_failure;
nft_rule_for_each_expr(expr, next, rule) {
- struct nlattr *elem = nla_nest_start(skb, NFTA_LIST_ELEM);
- if (elem == NULL)
- goto nla_put_failure;
- if (nf_tables_fill_expr_info(skb, expr) < 0)
+ if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr) < 0)
goto nla_put_failure;
- nla_nest_end(skb, elem);
}
nla_nest_end(skb, list);
--
2.1.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/5] netfilter: nf_tables: prepare for expressions associated to set elements
2015-04-11 9:46 [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Patrick McHardy
2015-04-11 9:46 ` [PATCH 1/5] netfilter: nf_tables: add helper functions for expression handling Patrick McHardy
@ 2015-04-11 9:46 ` Patrick McHardy
2015-04-11 9:46 ` [PATCH 3/5] netfilter: nf_tables: mark stateful expressions Patrick McHardy
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Patrick McHardy @ 2015-04-11 9:46 UTC (permalink / raw)
To: pablo; +Cc: netfilter-devel
Preparation to attach expressions to set elements: add a set extension
type to hold an expression and dump the expression information with the
set element.
Signed-off-by: Patrick McHardy <kaber@trash.net>
---
include/net/netfilter/nf_tables.h | 7 +++++++
include/uapi/linux/netfilter/nf_tables.h | 2 ++
net/netfilter/nf_tables_api.c | 9 +++++++++
3 files changed, 18 insertions(+)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index e21623c..d45a871 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -371,6 +371,7 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
* @NFT_SET_EXT_TIMEOUT: element timeout
* @NFT_SET_EXT_EXPIRATION: element expiration time
* @NFT_SET_EXT_USERDATA: user data associated with the element
+ * @NFT_SET_EXT_EXPR: expression assiociated with the element
* @NFT_SET_EXT_NUM: number of extension types
*/
enum nft_set_extensions {
@@ -380,6 +381,7 @@ enum nft_set_extensions {
NFT_SET_EXT_TIMEOUT,
NFT_SET_EXT_EXPIRATION,
NFT_SET_EXT_USERDATA,
+ NFT_SET_EXT_EXPR,
NFT_SET_EXT_NUM
};
@@ -491,6 +493,11 @@ static inline struct nft_userdata *nft_set_ext_userdata(const struct nft_set_ext
return nft_set_ext(ext, NFT_SET_EXT_USERDATA);
}
+static inline struct nft_expr *nft_set_ext_expr(const struct nft_set_ext *ext)
+{
+ return nft_set_ext(ext, NFT_SET_EXT_EXPR);
+}
+
static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
{
return nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION) &&
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index be8584c..f9c5af2 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -322,6 +322,7 @@ enum nft_set_elem_flags {
* @NFTA_SET_ELEM_TIMEOUT: timeout value (NLA_U64)
* @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64)
* @NFTA_SET_ELEM_USERDATA: user data (NLA_BINARY)
+ * @NFTA_SET_ELEM_EXPR: expression (NLA_NESTED: nft_expr_attributes)
*/
enum nft_set_elem_attributes {
NFTA_SET_ELEM_UNSPEC,
@@ -331,6 +332,7 @@ enum nft_set_elem_attributes {
NFTA_SET_ELEM_TIMEOUT,
NFTA_SET_ELEM_EXPIRATION,
NFTA_SET_ELEM_USERDATA,
+ NFTA_SET_ELEM_EXPR,
__NFTA_SET_ELEM_MAX
};
#define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index e97bee5..8830811 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2904,6 +2904,9 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
[NFT_SET_EXT_DATA] = {
.align = __alignof__(u32),
},
+ [NFT_SET_EXT_EXPR] = {
+ .align = __alignof__(struct nft_expr),
+ },
[NFT_SET_EXT_FLAGS] = {
.len = sizeof(u8),
.align = __alignof__(u8),
@@ -2990,6 +2993,10 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
set->dlen) < 0)
goto nla_put_failure;
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR) &&
+ nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, nft_set_ext_expr(ext)) < 0)
+ goto nla_put_failure;
+
if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
nla_put_be32(skb, NFTA_SET_ELEM_FLAGS,
htonl(*nft_set_ext_flags(ext))))
@@ -3276,6 +3283,8 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem)
nft_data_uninit(nft_set_ext_key(ext), NFT_DATA_VALUE);
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
nft_data_uninit(nft_set_ext_data(ext), set->dtype);
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
+ nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext));
kfree(elem);
}
--
2.1.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/5] netfilter: nf_tables: mark stateful expressions
2015-04-11 9:46 [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Patrick McHardy
2015-04-11 9:46 ` [PATCH 1/5] netfilter: nf_tables: add helper functions for expression handling Patrick McHardy
2015-04-11 9:46 ` [PATCH 2/5] netfilter: nf_tables: prepare for expressions associated to set elements Patrick McHardy
@ 2015-04-11 9:46 ` Patrick McHardy
2015-04-11 9:46 ` [PATCH 4/5] netfilter: nf_tables: add flag to indicate set contains expressions Patrick McHardy
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Patrick McHardy @ 2015-04-11 9:46 UTC (permalink / raw)
To: pablo; +Cc: netfilter-devel
Add a flag to mark stateful expressions.
This is used for dynamic expression instanstiation to limit the usable
expressions. Strictly speaking only the dynset expression can not be
used in order to avoid recursion, but since dynamically instantiating
non-stateful expressions will simply create an identical copy, which
behaves no differently than the original, this limits to expressions
where it actually makes sense to dynamically instantiate them.
Signed-off-by: Patrick McHardy <kaber@trash.net>
---
include/net/netfilter/nf_tables.h | 4 ++++
net/netfilter/nft_counter.c | 1 +
net/netfilter/nft_limit.c | 1 +
3 files changed, 6 insertions(+)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index d45a871..e6bcf55 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -583,6 +583,7 @@ static inline void nft_set_gc_batch_add(struct nft_set_gc_batch *gcb,
* @policy: netlink attribute policy
* @maxattr: highest netlink attribute number
* @family: address family for AF-specific types
+ * @flags: expression type flags
*/
struct nft_expr_type {
const struct nft_expr_ops *(*select_ops)(const struct nft_ctx *,
@@ -594,8 +595,11 @@ struct nft_expr_type {
const struct nla_policy *policy;
unsigned int maxattr;
u8 family;
+ u8 flags;
};
+#define NFT_EXPR_STATEFUL 0x1
+
/**
* struct nft_expr_ops - nf_tables expression operations
*
diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c
index 0f6367e..1759123 100644
--- a/net/netfilter/nft_counter.c
+++ b/net/netfilter/nft_counter.c
@@ -92,6 +92,7 @@ static struct nft_expr_type nft_counter_type __read_mostly = {
.ops = &nft_counter_ops,
.policy = nft_counter_policy,
.maxattr = NFTA_COUNTER_MAX,
+ .flags = NFT_EXPR_STATEFUL,
.owner = THIS_MODULE,
};
diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c
index c862045..435c1cc 100644
--- a/net/netfilter/nft_limit.c
+++ b/net/netfilter/nft_limit.c
@@ -98,6 +98,7 @@ static struct nft_expr_type nft_limit_type __read_mostly = {
.ops = &nft_limit_ops,
.policy = nft_limit_policy,
.maxattr = NFTA_LIMIT_MAX,
+ .flags = NFT_EXPR_STATEFUL,
.owner = THIS_MODULE,
};
--
2.1.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 4/5] netfilter: nf_tables: add flag to indicate set contains expressions
2015-04-11 9:46 [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Patrick McHardy
` (2 preceding siblings ...)
2015-04-11 9:46 ` [PATCH 3/5] netfilter: nf_tables: mark stateful expressions Patrick McHardy
@ 2015-04-11 9:46 ` Patrick McHardy
2015-04-11 9:46 ` [PATCH 5/5] netfilter: nft_dynset: dynamic stateful expression instantiation Patrick McHardy
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Patrick McHardy @ 2015-04-11 9:46 UTC (permalink / raw)
To: pablo; +Cc: netfilter-devel
Add a set flag to indicate that the set is used as a state table and
contains expressions for evaluation. This operation is mutually
exclusive with the mapping operation, so sets specifying both are
rejected. The lookup expression also rejects binding to state tables
since it only deals with loopup and map operations.
Signed-off-by: Patrick McHardy <kaber@trash.net>
---
include/uapi/linux/netfilter/nf_tables.h | 2 ++
net/netfilter/nf_tables_api.c | 8 ++++++--
net/netfilter/nft_lookup.c | 3 +++
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index f9c5af2..4894238 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -238,6 +238,7 @@ enum nft_rule_compat_attributes {
* @NFT_SET_INTERVAL: set contains intervals
* @NFT_SET_MAP: set is used as a dictionary
* @NFT_SET_TIMEOUT: set uses timeouts
+ * @NFT_SET_EVAL: set contains expressions for evaluation
*/
enum nft_set_flags {
NFT_SET_ANONYMOUS = 0x1,
@@ -245,6 +246,7 @@ enum nft_set_flags {
NFT_SET_INTERVAL = 0x4,
NFT_SET_MAP = 0x8,
NFT_SET_TIMEOUT = 0x10,
+ NFT_SET_EVAL = 0x20,
};
/**
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 8830811..78af83b 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2661,9 +2661,13 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
if (nla[NFTA_SET_FLAGS] != NULL) {
flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
- NFT_SET_INTERVAL | NFT_SET_MAP |
- NFT_SET_TIMEOUT))
+ NFT_SET_INTERVAL | NFT_SET_TIMEOUT |
+ NFT_SET_MAP | NFT_SET_EVAL))
return -EINVAL;
+ /* Only one of both operations is supported */
+ if ((flags & (NFT_SET_MAP | NFT_SET_EVAL)) ==
+ (NFT_SET_MAP | NFT_SET_EVAL))
+ return -EOPNOTSUPP;
}
dtype = 0;
diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c
index ba14662..b3c31ef 100644
--- a/net/netfilter/nft_lookup.c
+++ b/net/netfilter/nft_lookup.c
@@ -71,6 +71,9 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
return PTR_ERR(set);
}
+ if (set->flags & NFT_SET_EVAL)
+ return -EOPNOTSUPP;
+
priv->sreg = nft_parse_register(tb[NFTA_LOOKUP_SREG]);
err = nft_validate_register_load(priv->sreg, set->klen);
if (err < 0)
--
2.1.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 5/5] netfilter: nft_dynset: dynamic stateful expression instantiation
2015-04-11 9:46 [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Patrick McHardy
` (3 preceding siblings ...)
2015-04-11 9:46 ` [PATCH 4/5] netfilter: nf_tables: add flag to indicate set contains expressions Patrick McHardy
@ 2015-04-11 9:46 ` Patrick McHardy
2015-04-12 20:26 ` [PATCH 6/5] netfilter: nft_dynset: make sure expr extension exists before using it Patrick McHardy
2015-04-13 18:54 ` [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Pablo Neira Ayuso
6 siblings, 0 replies; 9+ messages in thread
From: Patrick McHardy @ 2015-04-11 9:46 UTC (permalink / raw)
To: pablo; +Cc: netfilter-devel
Support instantiating stateful expressions based on a template that
are associated with dynamically created set entries. The expressions
are evaluated when adding or updating the set element.
This allows to maintain per flow state using the existing set
infrastructure and expression types, with arbitrary definitions of
a flow.
Usage is currently restricted to anonymous sets, meaning only a single
binding can exist, since the desired semantics of multiple independant
bindings haven't been defined so far.
Examples (userspace syntax is still WIP):
1. Limit the rate of new SSH connections per host, similar to iptables
hashlimit:
# nft filter input tcp dport ssh ct state new \
flow ip saddr timeout 60s \
limit 10/second \
accept
2. Account network traffic between each set of /24 networks:
# nft filter forward \
flow ip saddr & 255.255.255.0 . ip daddr & 255.255.255.0 \
counter
3. Account traffic to each host per user:
# nft filter output \
flow skuid . ip daddr \
counter
4. Account traffic for each combination of source address and TCP flags:
# nft filter input \
flow ip saddr . tcp flags \
counter
The resulting set content after a Xmas-scan look like this:
{
192.168.122.1 . fin | psh | urg : counter packets 1001 bytes 40040,
192.168.122.1 . ack : counter packets 74 bytes 3848,
192.168.122.1 . psh | ack : counter packets 35 bytes 3144
}
Signed-off-by: Patrick McHardy <kaber@trash.net>
---
include/uapi/linux/netfilter/nf_tables.h | 2 ++
net/netfilter/nft_dynset.c | 52 +++++++++++++++++++++++++++++---
2 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 4894238..5fa1cd0 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -567,6 +567,7 @@ enum nft_dynset_ops {
* @NFTA_DYNSET_SREG_KEY: source register of the key (NLA_U32)
* @NFTA_DYNSET_SREG_DATA: source register of the data (NLA_U32)
* @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64)
+ * @NFTA_DYNSET_EXPR: expression (NLA_NESTED: nft_expr_attributes)
*/
enum nft_dynset_attributes {
NFTA_DYNSET_UNSPEC,
@@ -576,6 +577,7 @@ enum nft_dynset_attributes {
NFTA_DYNSET_SREG_KEY,
NFTA_DYNSET_SREG_DATA,
NFTA_DYNSET_TIMEOUT,
+ NFTA_DYNSET_EXPR,
__NFTA_DYNSET_MAX,
};
#define NFTA_DYNSET_MAX (__NFTA_DYNSET_MAX - 1)
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index 03699d5..311e21c 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -23,6 +23,7 @@ struct nft_dynset {
enum nft_registers sreg_key:8;
enum nft_registers sreg_data:8;
u64 timeout;
+ struct nft_expr *expr;
struct nft_set_binding binding;
};
@@ -30,6 +31,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
struct nft_regs *regs)
{
const struct nft_dynset *priv = nft_expr_priv(expr);
+ struct nft_set_ext *ext;
u64 timeout;
void *elem;
@@ -44,7 +46,13 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
if (elem == NULL) {
if (set->size)
atomic_dec(&set->nelems);
+ return NULL;
}
+
+ ext = nft_set_elem_ext(set, elem);
+ if (priv->expr != NULL)
+ nft_expr_clone(nft_set_ext_expr(ext), priv->expr);
+
return elem;
}
@@ -55,18 +63,25 @@ static void nft_dynset_eval(const struct nft_expr *expr,
const struct nft_dynset *priv = nft_expr_priv(expr);
struct nft_set *set = priv->set;
const struct nft_set_ext *ext;
+ const struct nft_expr *sexpr;
u64 timeout;
if (set->ops->update(set, ®s->data[priv->sreg_key], nft_dynset_new,
expr, regs, &ext)) {
+ sexpr = nft_set_ext_expr(ext);
+
if (priv->op == NFT_DYNSET_OP_UPDATE &&
nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
timeout = priv->timeout ? : set->timeout;
*nft_set_ext_expiration(ext) = jiffies + timeout;
- return;
- }
- }
+ } else if (sexpr == NULL)
+ goto out;
+ if (sexpr != NULL)
+ sexpr->ops->eval(sexpr, regs, pkt);
+ return;
+ }
+out:
regs->verdict.code = NFT_BREAK;
}
@@ -77,6 +92,7 @@ static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
[NFTA_DYNSET_SREG_KEY] = { .type = NLA_U32 },
[NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 },
[NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 },
+ [NFTA_DYNSET_EXPR] = { .type = NLA_NESTED },
};
static int nft_dynset_init(const struct nft_ctx *ctx,
@@ -142,10 +158,29 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
} else if (set->flags & NFT_SET_MAP)
return -EINVAL;
+ if (tb[NFTA_DYNSET_EXPR] != NULL) {
+ if (!(set->flags & NFT_SET_EVAL))
+ return -EINVAL;
+ if (!(set->flags & NFT_SET_ANONYMOUS))
+ return -EOPNOTSUPP;
+
+ priv->expr = nft_expr_init(ctx, tb[NFTA_DYNSET_EXPR]);
+ if (IS_ERR(priv->expr))
+ return PTR_ERR(priv->expr);
+
+ err = -EOPNOTSUPP;
+ if (!(priv->expr->ops->type->flags & NFT_EXPR_STATEFUL))
+ goto err1;
+ } else if (set->flags & NFT_SET_EVAL)
+ return -EINVAL;
+
nft_set_ext_prepare(&priv->tmpl);
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
if (set->flags & NFT_SET_MAP)
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen);
+ if (priv->expr != NULL)
+ nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPR,
+ priv->expr->ops->size);
if (set->flags & NFT_SET_TIMEOUT) {
if (timeout || set->timeout)
nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
@@ -155,10 +190,15 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
err = nf_tables_bind_set(ctx, set, &priv->binding);
if (err < 0)
- return err;
+ goto err1;
priv->set = set;
return 0;
+
+err1:
+ if (priv->expr != NULL)
+ nft_expr_destroy(ctx, priv->expr);
+ return err;
}
static void nft_dynset_destroy(const struct nft_ctx *ctx,
@@ -167,6 +207,8 @@ static void nft_dynset_destroy(const struct nft_ctx *ctx,
struct nft_dynset *priv = nft_expr_priv(expr);
nf_tables_unbind_set(ctx, priv->set, &priv->binding);
+ if (priv->expr != NULL)
+ nft_expr_destroy(ctx, priv->expr);
}
static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
@@ -184,6 +226,8 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
goto nla_put_failure;
if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout)))
goto nla_put_failure;
+ if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
+ goto nla_put_failure;
return 0;
nla_put_failure:
--
2.1.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 6/5] netfilter: nft_dynset: make sure expr extension exists before using it
2015-04-11 9:46 [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Patrick McHardy
` (4 preceding siblings ...)
2015-04-11 9:46 ` [PATCH 5/5] netfilter: nft_dynset: dynamic stateful expression instantiation Patrick McHardy
@ 2015-04-12 20:26 ` Patrick McHardy
2015-04-13 18:54 ` Pablo Neira Ayuso
2015-04-13 18:54 ` [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Pablo Neira Ayuso
6 siblings, 1 reply; 9+ messages in thread
From: Patrick McHardy @ 2015-04-12 20:26 UTC (permalink / raw)
To: pablo; +Cc: netfilter-devel
This patch fixes a bug in patch 5/5 (netfilter: nft_dynset: dynamic
stateful expression instantiation), we need to make sure the expr
extension exists without using it to avoid a crash with normal
set updates.
If possible, please simply fold it into 5/5.
Thanks!
commit 6609a3aad8ad8c1e60a4b757d626aeb69fae8066
Author: Patrick McHardy <kaber@trash.net>
Date: Sun Apr 12 21:24:31 2015 +0100
netfilter: nft_dynset: make sure expr extension exists before using it
Fix a crash when using dynset without an expression.
Signed-off-by: Patrick McHardy <kaber@trash.net>
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index 311e21c..513a8ef 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -68,7 +68,9 @@ static void nft_dynset_eval(const struct nft_expr *expr,
if (set->ops->update(set, ®s->data[priv->sreg_key], nft_dynset_new,
expr, regs, &ext)) {
- sexpr = nft_set_ext_expr(ext);
+ sexpr = NULL;
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
+ sexpr = nft_set_ext_expr(ext);
if (priv->op == NFT_DYNSET_OP_UPDATE &&
nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation
2015-04-11 9:46 [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Patrick McHardy
` (5 preceding siblings ...)
2015-04-12 20:26 ` [PATCH 6/5] netfilter: nft_dynset: make sure expr extension exists before using it Patrick McHardy
@ 2015-04-13 18:54 ` Pablo Neira Ayuso
6 siblings, 0 replies; 9+ messages in thread
From: Pablo Neira Ayuso @ 2015-04-13 18:54 UTC (permalink / raw)
To: Patrick McHardy; +Cc: netfilter-devel
On Sat, Apr 11, 2015 at 10:46:37AM +0100, Patrick McHardy wrote:
> The following patches are the grand finale of my nf_tables set work,
> using all the building blocks put in place by the previous patches
> to support something like iptables hashlimit, but a lot more powerful.
>
> Sets are extended to allow attaching expressions to set elements.
> The dynset expression dynamically instantiates these expressions
> based on a template when creating new set elements and evaluates
> them for all new or updated set members.
>
> In combination with concatenations this effectively creates state
> tables for arbitrary combinations of keys, using the existing
> expression types to maintain that state. Regular set GC takes care
> of purging expired states.
>
> We currently support two different stateful expressions, counter
> and limit. Using limit as a template we can express the functionality
> of hashlimit, but completely unrestricted in the combination of keys.
> Using counter we can perform accounting for arbitrary flows.
>
> The following examples from patch 5/5 show some possibilities.
> Userspace syntax is still WIP, especially the listing of state
> tables will most likely be seperated from normal set listings
> and use a more structured format:
>
> 1. Limit the rate of new SSH connections per host, similar to iptables
> hashlimit:
>
> # nft filter input tcp dport ssh ct state new \
> flow ip saddr timeout 60s \
> limit 10/second \
> accept
>
> 2. Account network traffic between each set of /24 networks:
>
> # nft filter forward \
> flow ip saddr & 255.255.255.0 . ip daddr & 255.255.255.0 \
> counter
>
> 3. Account traffic to each host per user:
>
> # nft filter output \
> flow skuid . ip daddr \
> counter
>
> 4. Account traffic for each combination of source address and TCP flags:
>
> # nft filter input \
> flow ip saddr . tcp flags \
> counter
>
> The resulting set content after a Xmas-scan look like this:
>
> {
> 192.168.122.1 . fin | psh | urg : counter packets 1001 bytes 40040,
> 192.168.122.1 . ack : counter packets 74 bytes 3848,
> 192.168.122.1 . psh | ack : counter packets 35 bytes 3144
> }
>
> In the future the "expressions attached to elements" will be extended
> to also support user created non-stateful expressions to allow to
> efficiently select beween a set of parameter sets, f.i. a set of log
> statements with different prefixes based on the interface, which currently
> require one rule each. This will most likely have to wait until the next
> kernel version though.
Seried applied, thanks Patrick!
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 6/5] netfilter: nft_dynset: make sure expr extension exists before using it
2015-04-12 20:26 ` [PATCH 6/5] netfilter: nft_dynset: make sure expr extension exists before using it Patrick McHardy
@ 2015-04-13 18:54 ` Pablo Neira Ayuso
0 siblings, 0 replies; 9+ messages in thread
From: Pablo Neira Ayuso @ 2015-04-13 18:54 UTC (permalink / raw)
To: Patrick McHardy; +Cc: netfilter-devel
On Sun, Apr 12, 2015 at 09:26:55PM +0100, Patrick McHardy wrote:
> This patch fixes a bug in patch 5/5 (netfilter: nft_dynset: dynamic
> stateful expression instantiation), we need to make sure the expr
> extension exists without using it to avoid a crash with normal
> set updates.
>
> If possible, please simply fold it into 5/5.
I have fold this into into 5/5. Thanks.
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2015-04-13 18:50 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-04-11 9:46 [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Patrick McHardy
2015-04-11 9:46 ` [PATCH 1/5] netfilter: nf_tables: add helper functions for expression handling Patrick McHardy
2015-04-11 9:46 ` [PATCH 2/5] netfilter: nf_tables: prepare for expressions associated to set elements Patrick McHardy
2015-04-11 9:46 ` [PATCH 3/5] netfilter: nf_tables: mark stateful expressions Patrick McHardy
2015-04-11 9:46 ` [PATCH 4/5] netfilter: nf_tables: add flag to indicate set contains expressions Patrick McHardy
2015-04-11 9:46 ` [PATCH 5/5] netfilter: nft_dynset: dynamic stateful expression instantiation Patrick McHardy
2015-04-12 20:26 ` [PATCH 6/5] netfilter: nft_dynset: make sure expr extension exists before using it Patrick McHardy
2015-04-13 18:54 ` Pablo Neira Ayuso
2015-04-13 18:54 ` [PATCH 0/5] netfilter: nf_tables: dynamic stateful expression instantiation Pablo Neira Ayuso
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).