History

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 }

Patrick McHardy committed 11 years ago

Commit 3e135cd1 parent 7c6c6e92 files · +52 −4Browse files

Unified · Split

2 files
  1. include/uapi/linux/netfilter/nf_tables.h +2 −0
  2. net/netfilter/nft_dynset.c +50 −4
include/uapi/linux/netfilter/nf_tables.h+2−0
@@ -567,6 +567,7 @@ enum nft_dynset_ops { · 566 unmodified lines
567567 * @NFTA_DYNSET_SREG_KEY: source register of the key (NLA_U32)
568568 * @NFTA_DYNSET_SREG_DATA: source register of the data (NLA_U32)
569569 * @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64)
570+ * @NFTA_DYNSET_EXPR: expression (NLA_NESTED: nft_expr_attributes)
570571 */
571572 enum nft_dynset_attributes {
572573 NFTA_DYNSET_UNSPEC,
@@ -576,6 +577,7 @@ enum nft_dynset_attributes { · 3 unmodified lines
576577 NFTA_DYNSET_SREG_KEY,
577578 NFTA_DYNSET_SREG_DATA,
578579 NFTA_DYNSET_TIMEOUT,
580+ NFTA_DYNSET_EXPR,
579581 __NFTA_DYNSET_MAX,
580582 };
581583 #define NFTA_DYNSET_MAX (__NFTA_DYNSET_MAX - 1)
net/netfilter/nft_dynset.c+50−4
@@ -23,6 +23,7 @@ struct nft_dynset { · 22 unmodified lines
2323 enum nft_registers sreg_key:8;
2424 enum nft_registers sreg_data:8;
2525 u64 timeout;
26+ struct nft_expr *expr;
2627 struct nft_set_binding binding;
2728 };
2829
@@ -30,6 +31,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, · 1 unmodified line
3031 struct nft_regs *regs)
3132 {
3233 const struct nft_dynset *priv = nft_expr_priv(expr);
34+ struct nft_set_ext *ext;
3335 u64 timeout;
3436 void *elem;
3537
@@ -44,7 +46,13 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, · 8 unmodified lines
4446 if (elem == NULL) {
4547 if (set->size)
4648 atomic_dec(&set->nelems);
49+ return NULL;
4750 }
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+
4856 return elem;
4957 }
5058
@@ -55,18 +63,27 @@ static void nft_dynset_eval(const struct nft_expr *expr, · 4 unmodified lines
5563 const struct nft_dynset *priv = nft_expr_priv(expr);
5664 struct nft_set *set = priv->set;
5765 const struct nft_set_ext *ext;
66+ const struct nft_expr *sexpr;
5867 u64 timeout;
5968
6069 if (set->ops->update(set, &regs->data[priv->sreg_key], nft_dynset_new,
6170 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+
6275 if (priv->op == NFT_DYNSET_OP_UPDATE &&
6376 nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
6477 timeout = priv->timeout ? : set->timeout;
6578 *nft_set_ext_expiration(ext) = jiffies + timeout;
66− return;
67− }
68− }
79+ } else if (sexpr == NULL)
80+ goto out;
6981
82+ if (sexpr != NULL)
83+ sexpr->ops->eval(sexpr, regs, pkt);
84+ return;
85+ }
86+out:
7087 regs->verdict.code = NFT_BREAK;
7188 }
7289
@@ -77,6 +94,7 @@ static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = { · 4 unmodified lines
7794 [NFTA_DYNSET_SREG_KEY] = { .type = NLA_U32 },
7895 [NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 },
7996 [NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 },
97+ [NFTA_DYNSET_EXPR] = { .type = NLA_NESTED },
8098 };
8199
82100 static int nft_dynset_init(const struct nft_ctx *ctx,
@@ -142,10 +160,29 @@ static int nft_dynset_init(const struct nft_ctx *ctx, · 59 unmodified lines
142160 } else if (set->flags & NFT_SET_MAP)
143161 return -EINVAL;
144162
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+
145179 nft_set_ext_prepare(&priv->tmpl);
146180 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
147181 if (set->flags & NFT_SET_MAP)
148182 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);
149186 if (set->flags & NFT_SET_TIMEOUT) {
150187 if (timeout || set->timeout)
151188 nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
@@ -155,10 +192,15 @@ static int nft_dynset_init(const struct nft_ctx *ctx, · 3 unmodified lines
155192
156193 err = nf_tables_bind_set(ctx, set, &priv->binding);
157194 if (err < 0)
158− return err;
195+ goto err1;
159196
160197 priv->set = set;
161198 return 0;
199+
200+err1:
201+ if (priv->expr != NULL)
202+ nft_expr_destroy(ctx, priv->expr);
203+ return err;
162204 }
163205
164206 static void nft_dynset_destroy(const struct nft_ctx *ctx,
@@ -167,6 +209,8 @@ static void nft_dynset_destroy(const struct nft_ctx *ctx, · 2 unmodified lines
167209 struct nft_dynset *priv = nft_expr_priv(expr);
168210
169211 nf_tables_unbind_set(ctx, priv->set, &priv->binding);
212+ if (priv->expr != NULL)
213+ nft_expr_destroy(ctx, priv->expr);
170214 }
171215
172216 static 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) · 11 unmodified lines
184228 goto nla_put_failure;
185229 if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout)))
186230 goto nla_put_failure;
231+ if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
232+ goto nla_put_failure;
187233 return 0;
188234
189235 nla_put_failure: