Skip to content

Commit 3e135cd

Browse files
kaberummakynes
authored andcommitted
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 <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 7c6c6e9 commit 3e135cd

File tree

2 files changed

+52
-4
lines changed

2 files changed

+52
-4
lines changed

include/uapi/linux/netfilter/nf_tables.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ enum nft_dynset_ops {
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 {
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

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ struct nft_dynset {
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

2930
static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
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,
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,
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] = {
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,
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,
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,
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)
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:

0 commit comments

Comments
 (0)