Blame view

net/netfilter/nft_dynset.c 7.33 KB
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  /*
   * Copyright (c) 2015 Patrick McHardy <kaber@trash.net>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/netlink.h>
  #include <linux/netfilter.h>
  #include <linux/netfilter/nf_tables.h>
  #include <net/netfilter/nf_tables.h>
  #include <net/netfilter/nf_tables_core.h>
  
  struct nft_dynset {
  	struct nft_set			*set;
  	struct nft_set_ext_tmpl		tmpl;
  	enum nft_dynset_ops		op:8;
  	enum nft_registers		sreg_key:8;
  	enum nft_registers		sreg_data:8;
dbd2be064   Pablo Neira Ayuso   netfilter: nft_dy...
25
  	bool				invert;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
26
  	u64				timeout;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
27
  	struct nft_expr			*expr;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
28
29
30
31
  	struct nft_set_binding		binding;
  };
  
  static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
a55e22e92   Patrick McHardy   netfilter: nf_tab...
32
  			    struct nft_regs *regs)
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
33
34
  {
  	const struct nft_dynset *priv = nft_expr_priv(expr);
3e135cd49   Patrick McHardy   netfilter: nft_dy...
35
  	struct nft_set_ext *ext;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
36
37
38
39
40
41
42
43
  	u64 timeout;
  	void *elem;
  
  	if (set->size && !atomic_add_unless(&set->nelems, 1, set->size))
  		return NULL;
  
  	timeout = priv->timeout ? : set->timeout;
  	elem = nft_set_elem_init(set, &priv->tmpl,
a55e22e92   Patrick McHardy   netfilter: nf_tab...
44
45
  				 &regs->data[priv->sreg_key],
  				 &regs->data[priv->sreg_data],
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
46
  				 timeout, GFP_ATOMIC);
61f9e2924   Liping Zhang   netfilter: nf_tab...
47
48
  	if (elem == NULL)
  		goto err1;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
49
50
  
  	ext = nft_set_elem_ext(set, elem);
086f33216   Pablo Neira Ayuso   netfilter: nf_tab...
51
52
  	if (priv->expr != NULL &&
  	    nft_expr_clone(nft_set_ext_expr(ext), priv->expr) < 0)
61f9e2924   Liping Zhang   netfilter: nf_tab...
53
  		goto err2;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
54

22fe54d5f   Patrick McHardy   netfilter: nf_tab...
55
  	return elem;
61f9e2924   Liping Zhang   netfilter: nf_tab...
56
57
58
59
60
61
62
  
  err2:
  	nft_set_elem_destroy(set, elem, false);
  err1:
  	if (set->size)
  		atomic_dec(&set->nelems);
  	return NULL;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
63
64
65
  }
  
  static void nft_dynset_eval(const struct nft_expr *expr,
a55e22e92   Patrick McHardy   netfilter: nf_tab...
66
  			    struct nft_regs *regs,
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
67
68
69
70
71
  			    const struct nft_pktinfo *pkt)
  {
  	const struct nft_dynset *priv = nft_expr_priv(expr);
  	struct nft_set *set = priv->set;
  	const struct nft_set_ext *ext;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
72
  	const struct nft_expr *sexpr;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
73
  	u64 timeout;
a55e22e92   Patrick McHardy   netfilter: nf_tab...
74
75
  	if (set->ops->update(set, &regs->data[priv->sreg_key], nft_dynset_new,
  			     expr, regs, &ext)) {
3e135cd49   Patrick McHardy   netfilter: nft_dy...
76
77
78
  		sexpr = NULL;
  		if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
  			sexpr = nft_set_ext_expr(ext);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
79
80
81
82
  		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;
277a29283   Liping Zhang   netfilter: nft_dy...
83
  		}
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
84

3e135cd49   Patrick McHardy   netfilter: nft_dy...
85
86
  		if (sexpr != NULL)
  			sexpr->ops->eval(sexpr, regs, pkt);
dbd2be064   Pablo Neira Ayuso   netfilter: nft_dy...
87
88
89
  
  		if (priv->invert)
  			regs->verdict.code = NFT_BREAK;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
90
91
  		return;
  	}
277a29283   Liping Zhang   netfilter: nft_dy...
92

dbd2be064   Pablo Neira Ayuso   netfilter: nft_dy...
93
94
  	if (!priv->invert)
  		regs->verdict.code = NFT_BREAK;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
95
96
97
  }
  
  static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
b2fbd0449   Liping Zhang   netfilter: nf_tab...
98
99
  	[NFTA_DYNSET_SET_NAME]	= { .type = NLA_STRING,
  				    .len = NFT_SET_MAXNAMELEN - 1 },
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
100
101
102
103
104
  	[NFTA_DYNSET_SET_ID]	= { .type = NLA_U32 },
  	[NFTA_DYNSET_OP]	= { .type = NLA_U32 },
  	[NFTA_DYNSET_SREG_KEY]	= { .type = NLA_U32 },
  	[NFTA_DYNSET_SREG_DATA]	= { .type = NLA_U32 },
  	[NFTA_DYNSET_TIMEOUT]	= { .type = NLA_U64 },
3e135cd49   Patrick McHardy   netfilter: nft_dy...
105
  	[NFTA_DYNSET_EXPR]	= { .type = NLA_NESTED },
dbd2be064   Pablo Neira Ayuso   netfilter: nft_dy...
106
  	[NFTA_DYNSET_FLAGS]	= { .type = NLA_U32 },
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
107
108
109
110
111
112
113
  };
  
  static int nft_dynset_init(const struct nft_ctx *ctx,
  			   const struct nft_expr *expr,
  			   const struct nlattr * const tb[])
  {
  	struct nft_dynset *priv = nft_expr_priv(expr);
37a9cc525   Pablo Neira Ayuso   netfilter: nf_tab...
114
  	u8 genmask = nft_genmask_next(ctx->net);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
115
116
117
118
119
120
121
122
  	struct nft_set *set;
  	u64 timeout;
  	int err;
  
  	if (tb[NFTA_DYNSET_SET_NAME] == NULL ||
  	    tb[NFTA_DYNSET_OP] == NULL ||
  	    tb[NFTA_DYNSET_SREG_KEY] == NULL)
  		return -EINVAL;
dbd2be064   Pablo Neira Ayuso   netfilter: nft_dy...
123
124
125
126
127
128
129
130
  	if (tb[NFTA_DYNSET_FLAGS]) {
  		u32 flags = ntohl(nla_get_be32(tb[NFTA_DYNSET_FLAGS]));
  
  		if (flags & ~NFT_DYNSET_F_INV)
  			return -EINVAL;
  		if (flags & NFT_DYNSET_F_INV)
  			priv->invert = true;
  	}
c7a72e3fd   Pablo Neira Ayuso   netfilter: nf_tab...
131
132
133
134
  	set = nft_set_lookup(ctx->net, ctx->table, tb[NFTA_DYNSET_SET_NAME],
  			     tb[NFTA_DYNSET_SET_ID], genmask);
  	if (IS_ERR(set))
  		return PTR_ERR(set);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
135

bb6a6e8e0   Liping Zhang   netfilter: nft_dy...
136
137
  	if (set->ops->update == NULL)
  		return -EOPNOTSUPP;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  	if (set->flags & NFT_SET_CONSTANT)
  		return -EBUSY;
  
  	priv->op = ntohl(nla_get_be32(tb[NFTA_DYNSET_OP]));
  	switch (priv->op) {
  	case NFT_DYNSET_OP_ADD:
  		break;
  	case NFT_DYNSET_OP_UPDATE:
  		if (!(set->flags & NFT_SET_TIMEOUT))
  			return -EOPNOTSUPP;
  		break;
  	default:
  		return -EOPNOTSUPP;
  	}
  
  	timeout = 0;
  	if (tb[NFTA_DYNSET_TIMEOUT] != NULL) {
  		if (!(set->flags & NFT_SET_TIMEOUT))
  			return -EINVAL;
a8b1e36d0   Anders K. Pedersen   netfilter: nft_dy...
157
158
  		timeout = msecs_to_jiffies(be64_to_cpu(nla_get_be64(
  						tb[NFTA_DYNSET_TIMEOUT])));
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
159
  	}
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
160
  	priv->sreg_key = nft_parse_register(tb[NFTA_DYNSET_SREG_KEY]);
d07db9884   Patrick McHardy   netfilter: nf_tab...
161
  	err = nft_validate_register_load(priv->sreg_key, set->klen);;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
162
163
164
165
166
167
168
169
  	if (err < 0)
  		return err;
  
  	if (tb[NFTA_DYNSET_SREG_DATA] != NULL) {
  		if (!(set->flags & NFT_SET_MAP))
  			return -EINVAL;
  		if (set->dtype == NFT_DATA_VERDICT)
  			return -EOPNOTSUPP;
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
170
  		priv->sreg_data = nft_parse_register(tb[NFTA_DYNSET_SREG_DATA]);
d07db9884   Patrick McHardy   netfilter: nf_tab...
171
  		err = nft_validate_register_load(priv->sreg_data, set->dlen);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
172
173
174
175
  		if (err < 0)
  			return err;
  	} else if (set->flags & NFT_SET_MAP)
  		return -EINVAL;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  	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;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
191
192
193
194
  	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);
3e135cd49   Patrick McHardy   netfilter: nft_dy...
195
196
197
  	if (priv->expr != NULL)
  		nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPR,
  				       priv->expr->ops->size);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
198
199
200
201
202
203
204
205
206
  	if (set->flags & NFT_SET_TIMEOUT) {
  		if (timeout || set->timeout)
  			nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
  	}
  
  	priv->timeout = timeout;
  
  	err = nf_tables_bind_set(ctx, set, &priv->binding);
  	if (err < 0)
3e135cd49   Patrick McHardy   netfilter: nft_dy...
207
  		goto err1;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
208
209
210
  
  	priv->set = set;
  	return 0;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
211
212
213
214
215
  
  err1:
  	if (priv->expr != NULL)
  		nft_expr_destroy(ctx, priv->expr);
  	return err;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
216
217
218
219
220
221
222
223
  }
  
  static void nft_dynset_destroy(const struct nft_ctx *ctx,
  			       const struct nft_expr *expr)
  {
  	struct nft_dynset *priv = nft_expr_priv(expr);
  
  	nf_tables_unbind_set(ctx, priv->set, &priv->binding);
3e135cd49   Patrick McHardy   netfilter: nft_dy...
224
225
  	if (priv->expr != NULL)
  		nft_expr_destroy(ctx, priv->expr);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
226
227
228
229
230
  }
  
  static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
  {
  	const struct nft_dynset *priv = nft_expr_priv(expr);
dbd2be064   Pablo Neira Ayuso   netfilter: nft_dy...
231
  	u32 flags = priv->invert ? NFT_DYNSET_F_INV : 0;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
232

b1c96ed37   Patrick McHardy   netfilter: nf_tab...
233
  	if (nft_dump_register(skb, NFTA_DYNSET_SREG_KEY, priv->sreg_key))
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
234
235
  		goto nla_put_failure;
  	if (priv->set->flags & NFT_SET_MAP &&
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
236
  	    nft_dump_register(skb, NFTA_DYNSET_SREG_DATA, priv->sreg_data))
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
237
238
239
240
241
  		goto nla_put_failure;
  	if (nla_put_be32(skb, NFTA_DYNSET_OP, htonl(priv->op)))
  		goto nla_put_failure;
  	if (nla_put_string(skb, NFTA_DYNSET_SET_NAME, priv->set->name))
  		goto nla_put_failure;
a8b1e36d0   Anders K. Pedersen   netfilter: nft_dy...
242
243
  	if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT,
  			 cpu_to_be64(jiffies_to_msecs(priv->timeout)),
b46f6ded9   Nicolas Dichtel   libnl: nla_put_be...
244
  			 NFTA_DYNSET_PAD))
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
245
  		goto nla_put_failure;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
246
247
  	if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
  		goto nla_put_failure;
dbd2be064   Pablo Neira Ayuso   netfilter: nft_dy...
248
249
  	if (nla_put_be32(skb, NFTA_DYNSET_FLAGS, htonl(flags)))
  		goto nla_put_failure;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
250
251
252
253
254
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
255
256
257
258
259
260
261
262
  static const struct nft_expr_ops nft_dynset_ops = {
  	.type		= &nft_dynset_type,
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_dynset)),
  	.eval		= nft_dynset_eval,
  	.init		= nft_dynset_init,
  	.destroy	= nft_dynset_destroy,
  	.dump		= nft_dynset_dump,
  };
4e24877e6   Liping Zhang   netfilter: nf_tab...
263
  struct nft_expr_type nft_dynset_type __read_mostly = {
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
264
265
266
267
268
269
  	.name		= "dynset",
  	.ops		= &nft_dynset_ops,
  	.policy		= nft_dynset_policy,
  	.maxattr	= NFTA_DYNSET_MAX,
  	.owner		= THIS_MODULE,
  };