Blame view

net/netfilter/nft_dynset.c 7.94 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
  	u64 timeout;
  	void *elem;
99a0efbee   Florian Westphal   netfilter: nf_tab...
38
  	if (!atomic_add_unless(&set->nelems, 1, set->size))
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
39
40
41
42
  		return NULL;
  
  	timeout = priv->timeout ? : set->timeout;
  	elem = nft_set_elem_init(set, &priv->tmpl,
a55e22e92   Patrick McHardy   netfilter: nf_tab...
43
44
  				 &regs->data[priv->sreg_key],
  				 &regs->data[priv->sreg_data],
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
45
  				 timeout, GFP_ATOMIC);
61f9e2924   Liping Zhang   netfilter: nf_tab...
46
47
  	if (elem == NULL)
  		goto err1;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
48
49
  
  	ext = nft_set_elem_ext(set, elem);
086f33216   Pablo Neira Ayuso   netfilter: nf_tab...
50
51
  	if (priv->expr != NULL &&
  	    nft_expr_clone(nft_set_ext_expr(ext), priv->expr) < 0)
61f9e2924   Liping Zhang   netfilter: nf_tab...
52
  		goto err2;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
53

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

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

dbd2be064   Pablo Neira Ayuso   netfilter: nft_dy...
90
91
  	if (!priv->invert)
  		regs->verdict.code = NFT_BREAK;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
92
93
94
  }
  
  static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
b2fbd0449   Liping Zhang   netfilter: nf_tab...
95
96
  	[NFTA_DYNSET_SET_NAME]	= { .type = NLA_STRING,
  				    .len = NFT_SET_MAXNAMELEN - 1 },
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
97
98
99
100
101
  	[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...
102
  	[NFTA_DYNSET_EXPR]	= { .type = NLA_NESTED },
dbd2be064   Pablo Neira Ayuso   netfilter: nft_dy...
103
  	[NFTA_DYNSET_FLAGS]	= { .type = NLA_U32 },
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
104
105
106
107
108
109
110
  };
  
  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...
111
  	u8 genmask = nft_genmask_next(ctx->net);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
112
113
114
  	struct nft_set *set;
  	u64 timeout;
  	int err;
f102d66b3   Florian Westphal   netfilter: nf_tab...
115
  	lockdep_assert_held(&ctx->net->nft.commit_mutex);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
116
117
118
119
  	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...
120
121
122
123
124
125
126
127
  	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;
  	}
10659cbab   Pablo Neira Ayuso   netfilter: nf_tab...
128
129
130
  	set = nft_set_lookup_global(ctx->net, ctx->table,
  				    tb[NFTA_DYNSET_SET_NAME],
  				    tb[NFTA_DYNSET_SET_ID], genmask);
c7a72e3fd   Pablo Neira Ayuso   netfilter: nf_tab...
131
132
  	if (IS_ERR(set))
  		return PTR_ERR(set);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
133

bb6a6e8e0   Liping Zhang   netfilter: nft_dy...
134
135
  	if (set->ops->update == NULL)
  		return -EOPNOTSUPP;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  	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...
155
156
  		timeout = msecs_to_jiffies(be64_to_cpu(nla_get_be64(
  						tb[NFTA_DYNSET_TIMEOUT])));
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
157
  	}
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
158
  	priv->sreg_key = nft_parse_register(tb[NFTA_DYNSET_SREG_KEY]);
b0c3dc65e   Luis de Bethencourt   netfilter: nf_tab...
159
  	err = nft_validate_register_load(priv->sreg_key, set->klen);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
160
161
162
163
164
165
166
167
  	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...
168
  		priv->sreg_data = nft_parse_register(tb[NFTA_DYNSET_SREG_DATA]);
d07db9884   Patrick McHardy   netfilter: nf_tab...
169
  		err = nft_validate_register_load(priv->sreg_data, set->dlen);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
170
171
172
173
  		if (err < 0)
  			return err;
  	} else if (set->flags & NFT_SET_MAP)
  		return -EINVAL;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
174
175
176
  	if (tb[NFTA_DYNSET_EXPR] != NULL) {
  		if (!(set->flags & NFT_SET_EVAL))
  			return -EINVAL;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
177
178
179
180
181
182
183
184
  
  		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;
79b174ade   Pablo Neira Ayuso   netfilter: nf_tab...
185
186
187
188
189
190
191
192
  
  		if (priv->expr->ops->type->flags & NFT_EXPR_GC) {
  			if (set->flags & NFT_SET_TIMEOUT)
  				goto err1;
  			if (!set->ops->gc_init)
  				goto err1;
  			set->ops->gc_init(set);
  		}
215a31f19   Pablo Neira Ayuso   netfilter: nft_dy...
193
  	}
3e135cd49   Patrick McHardy   netfilter: nft_dy...
194

22fe54d5f   Patrick McHardy   netfilter: nf_tab...
195
196
197
198
  	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...
199
200
201
  	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...
202
203
204
205
206
207
208
209
210
  	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...
211
  		goto err1;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
212

99a0efbee   Florian Westphal   netfilter: nf_tab...
213
214
  	if (set->size == 0)
  		set->size = 0xffff;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
215
216
  	priv->set = set;
  	return 0;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
217
218
219
220
221
  
  err1:
  	if (priv->expr != NULL)
  		nft_expr_destroy(ctx, priv->expr);
  	return err;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
222
  }
cd5125d8f   Florian Westphal   netfilter: nf_tab...
223
  static void nft_dynset_deactivate(const struct nft_ctx *ctx,
f6ac85858   Pablo Neira Ayuso   netfilter: nf_tab...
224
225
  				  const struct nft_expr *expr,
  				  enum nft_trans_phase phase)
cd5125d8f   Florian Westphal   netfilter: nf_tab...
226
227
  {
  	struct nft_dynset *priv = nft_expr_priv(expr);
273fe3f10   Pablo Neira Ayuso   netfilter: nf_tab...
228
229
230
231
232
233
234
  	nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
  }
  
  static void nft_dynset_activate(const struct nft_ctx *ctx,
  				const struct nft_expr *expr)
  {
  	struct nft_dynset *priv = nft_expr_priv(expr);
f6ac85858   Pablo Neira Ayuso   netfilter: nf_tab...
235

273fe3f10   Pablo Neira Ayuso   netfilter: nf_tab...
236
  	priv->set->use++;
cd5125d8f   Florian Westphal   netfilter: nf_tab...
237
  }
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
238
239
240
241
  static void nft_dynset_destroy(const struct nft_ctx *ctx,
  			       const struct nft_expr *expr)
  {
  	struct nft_dynset *priv = nft_expr_priv(expr);
3e135cd49   Patrick McHardy   netfilter: nft_dy...
242
243
  	if (priv->expr != NULL)
  		nft_expr_destroy(ctx, priv->expr);
cd5125d8f   Florian Westphal   netfilter: nf_tab...
244
245
  
  	nf_tables_destroy_set(ctx, priv->set);
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
246
247
248
249
250
  }
  
  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...
251
  	u32 flags = priv->invert ? NFT_DYNSET_F_INV : 0;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
252

b1c96ed37   Patrick McHardy   netfilter: nf_tab...
253
  	if (nft_dump_register(skb, NFTA_DYNSET_SREG_KEY, priv->sreg_key))
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
254
255
  		goto nla_put_failure;
  	if (priv->set->flags & NFT_SET_MAP &&
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
256
  	    nft_dump_register(skb, NFTA_DYNSET_SREG_DATA, priv->sreg_data))
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
257
258
259
260
261
  		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...
262
263
  	if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT,
  			 cpu_to_be64(jiffies_to_msecs(priv->timeout)),
b46f6ded9   Nicolas Dichtel   libnl: nla_put_be...
264
  			 NFTA_DYNSET_PAD))
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
265
  		goto nla_put_failure;
3e135cd49   Patrick McHardy   netfilter: nft_dy...
266
267
  	if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
  		goto nla_put_failure;
dbd2be064   Pablo Neira Ayuso   netfilter: nft_dy...
268
269
  	if (nla_put_be32(skb, NFTA_DYNSET_FLAGS, htonl(flags)))
  		goto nla_put_failure;
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
270
271
272
273
274
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
275
276
277
278
279
280
  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,
273fe3f10   Pablo Neira Ayuso   netfilter: nf_tab...
281
  	.activate	= nft_dynset_activate,
cd5125d8f   Florian Westphal   netfilter: nf_tab...
282
  	.deactivate	= nft_dynset_deactivate,
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
283
284
  	.dump		= nft_dynset_dump,
  };
4e24877e6   Liping Zhang   netfilter: nf_tab...
285
  struct nft_expr_type nft_dynset_type __read_mostly = {
22fe54d5f   Patrick McHardy   netfilter: nf_tab...
286
287
288
289
290
291
  	.name		= "dynset",
  	.ops		= &nft_dynset_ops,
  	.policy		= nft_dynset_policy,
  	.maxattr	= NFTA_DYNSET_MAX,
  	.owner		= THIS_MODULE,
  };