aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2015-04-11 05:46:42 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2015-04-13 14:19:55 -0400
commit3e135cd499bfbec15684fe9c756162d58df4dc77 (patch)
treeca4e87c29d799cdcc1e7698e7b2b9d75d6935d35
parent7c6c6e95a12e46f499749bdd496e53d03950f377 (diff)
netfilter: nft_dynset: dynamic stateful expression instantiation
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: flow ip saddr timeout 60s \ limit 10/second \ accept 2. Account network traffic between each set of /24 networks: flow ip saddr & 255.255.255.0 . ip daddr & 255.255.255.0 \ counter 3. Account traffic to each host per user: flow skuid . ip daddr \ counter 4. Account traffic for each combination of source address and TCP flags: 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> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/uapi/linux/netfilter/nf_tables.h2
-rw-r--r--net/netfilter/nft_dynset.c54
2 files changed, 52 insertions, 4 deletions
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 48942381d02f..5fa1cd04762e 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 {
567 * @NFTA_DYNSET_SREG_KEY: source register of the key (NLA_U32) 567 * @NFTA_DYNSET_SREG_KEY: source register of the key (NLA_U32)
568 * @NFTA_DYNSET_SREG_DATA: source register of the data (NLA_U32) 568 * @NFTA_DYNSET_SREG_DATA: source register of the data (NLA_U32)
569 * @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64) 569 * @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64)
570 * @NFTA_DYNSET_EXPR: expression (NLA_NESTED: nft_expr_attributes)
570 */ 571 */
571enum nft_dynset_attributes { 572enum nft_dynset_attributes {
572 NFTA_DYNSET_UNSPEC, 573 NFTA_DYNSET_UNSPEC,
@@ -576,6 +577,7 @@ enum nft_dynset_attributes {
576 NFTA_DYNSET_SREG_KEY, 577 NFTA_DYNSET_SREG_KEY,
577 NFTA_DYNSET_SREG_DATA, 578 NFTA_DYNSET_SREG_DATA,
578 NFTA_DYNSET_TIMEOUT, 579 NFTA_DYNSET_TIMEOUT,
580 NFTA_DYNSET_EXPR,
579 __NFTA_DYNSET_MAX, 581 __NFTA_DYNSET_MAX,
580}; 582};
581#define NFTA_DYNSET_MAX (__NFTA_DYNSET_MAX - 1) 583#define NFTA_DYNSET_MAX (__NFTA_DYNSET_MAX - 1)
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index 03699d5c0b4b..513a8ef60a59 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -23,6 +23,7 @@ struct nft_dynset {
23 enum nft_registers sreg_key:8; 23 enum nft_registers sreg_key:8;
24 enum nft_registers sreg_data:8; 24 enum nft_registers sreg_data:8;
25 u64 timeout; 25 u64 timeout;
26 struct nft_expr *expr;
26 struct nft_set_binding binding; 27 struct nft_set_binding binding;
27}; 28};
28 29
@@ -30,6 +31,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
30 struct nft_regs *regs) 31 struct nft_regs *regs)
31{ 32{
32 const struct nft_dynset *priv = nft_expr_priv(expr); 33 const struct nft_dynset *priv = nft_expr_priv(expr);
34 struct nft_set_ext *ext;
33 u64 timeout; 35 u64 timeout;
34 void *elem; 36 void *elem;
35 37
@@ -44,7 +46,13 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
44 if (elem == NULL) { 46 if (elem == NULL) {
45 if (set->size) 47 if (set->size)
46 atomic_dec(&set->nelems); 48 atomic_dec(&set->nelems);
49 return NULL;
47 } 50 }
51
52 ext = nft_set_elem_ext(set, elem);
53 if (priv->expr != NULL)
54 nft_expr_clone(nft_set_ext_expr(ext), priv->expr);
55
48 return elem; 56 return elem;
49} 57}
50 58
@@ -55,18 +63,27 @@ static void nft_dynset_eval(const struct nft_expr *expr,
55 const struct nft_dynset *priv = nft_expr_priv(expr); 63 const struct nft_dynset *priv = nft_expr_priv(expr);
56 struct nft_set *set = priv->set; 64 struct nft_set *set = priv->set;
57 const struct nft_set_ext *ext; 65 const struct nft_set_ext *ext;
66 const struct nft_expr *sexpr;
58 u64 timeout; 67 u64 timeout;
59 68
60 if (set->ops->update(set, &regs->data[priv->sreg_key], nft_dynset_new, 69 if (set->ops->update(set, &regs->data[priv->sreg_key], nft_dynset_new,
61 expr, regs, &ext)) { 70 expr, regs, &ext)) {
71 sexpr = NULL;
72 if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
73 sexpr = nft_set_ext_expr(ext);
74
62 if (priv->op == NFT_DYNSET_OP_UPDATE && 75 if (priv->op == NFT_DYNSET_OP_UPDATE &&
63 nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) { 76 nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
64 timeout = priv->timeout ? : set->timeout; 77 timeout = priv->timeout ? : set->timeout;
65 *nft_set_ext_expiration(ext) = jiffies + timeout; 78 *nft_set_ext_expiration(ext) = jiffies + timeout;
66 return; 79 } else if (sexpr == NULL)
67 } 80 goto out;
68 }
69 81
82 if (sexpr != NULL)
83 sexpr->ops->eval(sexpr, regs, pkt);
84 return;
85 }
86out:
70 regs->verdict.code = NFT_BREAK; 87 regs->verdict.code = NFT_BREAK;
71} 88}
72 89
@@ -77,6 +94,7 @@ static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
77 [NFTA_DYNSET_SREG_KEY] = { .type = NLA_U32 }, 94 [NFTA_DYNSET_SREG_KEY] = { .type = NLA_U32 },
78 [NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 }, 95 [NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 },
79 [NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 }, 96 [NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 },
97 [NFTA_DYNSET_EXPR] = { .type = NLA_NESTED },
80}; 98};
81 99
82static int nft_dynset_init(const struct nft_ctx *ctx, 100static int nft_dynset_init(const struct nft_ctx *ctx,
@@ -142,10 +160,29 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
142 } else if (set->flags & NFT_SET_MAP) 160 } else if (set->flags & NFT_SET_MAP)
143 return -EINVAL; 161 return -EINVAL;
144 162
163 if (tb[NFTA_DYNSET_EXPR] != NULL) {
164 if (!(set->flags & NFT_SET_EVAL))
165 return -EINVAL;
166 if (!(set->flags & NFT_SET_ANONYMOUS))
167 return -EOPNOTSUPP;
168
169 priv->expr = nft_expr_init(ctx, tb[NFTA_DYNSET_EXPR]);
170 if (IS_ERR(priv->expr))
171 return PTR_ERR(priv->expr);
172
173 err = -EOPNOTSUPP;
174 if (!(priv->expr->ops->type->flags & NFT_EXPR_STATEFUL))
175 goto err1;
176 } else if (set->flags & NFT_SET_EVAL)
177 return -EINVAL;
178
145 nft_set_ext_prepare(&priv->tmpl); 179 nft_set_ext_prepare(&priv->tmpl);
146 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen); 180 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
147 if (set->flags & NFT_SET_MAP) 181 if (set->flags & NFT_SET_MAP)
148 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen); 182 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen);
183 if (priv->expr != NULL)
184 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPR,
185 priv->expr->ops->size);
149 if (set->flags & NFT_SET_TIMEOUT) { 186 if (set->flags & NFT_SET_TIMEOUT) {
150 if (timeout || set->timeout) 187 if (timeout || set->timeout)
151 nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION); 188 nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
@@ -155,10 +192,15 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
155 192
156 err = nf_tables_bind_set(ctx, set, &priv->binding); 193 err = nf_tables_bind_set(ctx, set, &priv->binding);
157 if (err < 0) 194 if (err < 0)
158 return err; 195 goto err1;
159 196
160 priv->set = set; 197 priv->set = set;
161 return 0; 198 return 0;
199
200err1:
201 if (priv->expr != NULL)
202 nft_expr_destroy(ctx, priv->expr);
203 return err;
162} 204}
163 205
164static void nft_dynset_destroy(const struct nft_ctx *ctx, 206static void nft_dynset_destroy(const struct nft_ctx *ctx,
@@ -167,6 +209,8 @@ static void nft_dynset_destroy(const struct nft_ctx *ctx,
167 struct nft_dynset *priv = nft_expr_priv(expr); 209 struct nft_dynset *priv = nft_expr_priv(expr);
168 210
169 nf_tables_unbind_set(ctx, priv->set, &priv->binding); 211 nf_tables_unbind_set(ctx, priv->set, &priv->binding);
212 if (priv->expr != NULL)
213 nft_expr_destroy(ctx, priv->expr);
170} 214}
171 215
172static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr) 216static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
@@ -184,6 +228,8 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
184 goto nla_put_failure; 228 goto nla_put_failure;
185 if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout))) 229 if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout)))
186 goto nla_put_failure; 230 goto nla_put_failure;
231 if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
232 goto nla_put_failure;
187 return 0; 233 return 0;
188 234
189nla_put_failure: 235nla_put_failure: