Blame view

net/netfilter/nft_compat.c 22 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
2
3
4
  /*
   * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
   *
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   * This software has been sponsored by Sophos Astaro <http://www.sophos.com>
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/netlink.h>
  #include <linux/netfilter.h>
  #include <linux/netfilter/nfnetlink.h>
  #include <linux/netfilter/nf_tables.h>
  #include <linux/netfilter/nf_tables_compat.h>
  #include <linux/netfilter/x_tables.h>
  #include <linux/netfilter_ipv4/ip_tables.h>
  #include <linux/netfilter_ipv6/ip6_tables.h>
5191f4d82   Arturo Borrero   netfilter: nft_co...
19
  #include <linux/netfilter_bridge/ebtables.h>
5f1589394   Arturo Borrero   netfilter: nft_co...
20
  #include <linux/netfilter_arp/arp_tables.h>
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
21
  #include <net/netfilter/nf_tables.h>
4b512e1c1   Liping Zhang   netfilter: nft_co...
22

732a8049f   Florian Westphal   netfilter: nft_co...
23
24
25
26
27
28
  /* Used for matches where *info is larger than X byte */
  #define NFT_MATCH_LARGE_THRESH	192
  
  struct nft_xt_match_priv {
  	void *info;
  };
e4844c9c6   Florian Westphal   netfilter: nft_co...
29
30
  static int nft_compat_chain_validate_dependency(const struct nft_ctx *ctx,
  						const char *tablename)
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
31
  {
e4844c9c6   Florian Westphal   netfilter: nft_co...
32
33
  	enum nft_chain_types type = NFT_CHAIN_T_DEFAULT;
  	const struct nft_chain *chain = ctx->chain;
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
34
  	const struct nft_base_chain *basechain;
f323d9546   Pablo Neira Ayuso   netfilter: nf_tab...
35
36
  	if (!tablename ||
  	    !nft_is_base_chain(chain))
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
37
  		return 0;
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
38
  	basechain = nft_base_chain(chain);
e4844c9c6   Florian Westphal   netfilter: nft_co...
39
40
41
42
43
44
  	if (strcmp(tablename, "nat") == 0) {
  		if (ctx->family != NFPROTO_BRIDGE)
  			type = NFT_CHAIN_T_NAT;
  		if (basechain->type->type != type)
  			return -EINVAL;
  	}
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
45
46
47
  
  	return 0;
  }
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
48
49
50
  union nft_entry {
  	struct ipt_entry e4;
  	struct ip6t_entry e6;
5191f4d82   Arturo Borrero   netfilter: nft_co...
51
  	struct ebt_entry ebt;
5f1589394   Arturo Borrero   netfilter: nft_co...
52
  	struct arpt_entry arp;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
53
54
55
56
57
58
59
60
61
  };
  
  static inline void
  nft_compat_set_par(struct xt_action_param *par, void *xt, const void *xt_info)
  {
  	par->target	= xt;
  	par->targinfo	= xt_info;
  	par->hotdrop	= false;
  }
5191f4d82   Arturo Borrero   netfilter: nft_co...
62
  static void nft_target_eval_xt(const struct nft_expr *expr,
a55e22e92   Patrick McHardy   netfilter: nf_tab...
63
  			       struct nft_regs *regs,
5191f4d82   Arturo Borrero   netfilter: nft_co...
64
  			       const struct nft_pktinfo *pkt)
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
65
66
67
68
69
70
71
72
73
74
75
76
  {
  	void *info = nft_expr_priv(expr);
  	struct xt_target *target = expr->ops->data;
  	struct sk_buff *skb = pkt->skb;
  	int ret;
  
  	nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info);
  
  	ret = target->target(skb, &pkt->xt);
  
  	if (pkt->xt.hotdrop)
  		ret = NF_DROP;
5191f4d82   Arturo Borrero   netfilter: nft_co...
77
  	switch (ret) {
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
78
  	case XT_CONTINUE:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
79
  		regs->verdict.code = NFT_CONTINUE;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
80
81
  		break;
  	default:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
82
  		regs->verdict.code = ret;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
83
84
  		break;
  	}
5191f4d82   Arturo Borrero   netfilter: nft_co...
85
86
87
  }
  
  static void nft_target_eval_bridge(const struct nft_expr *expr,
a55e22e92   Patrick McHardy   netfilter: nf_tab...
88
  				   struct nft_regs *regs,
5191f4d82   Arturo Borrero   netfilter: nft_co...
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  				   const struct nft_pktinfo *pkt)
  {
  	void *info = nft_expr_priv(expr);
  	struct xt_target *target = expr->ops->data;
  	struct sk_buff *skb = pkt->skb;
  	int ret;
  
  	nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info);
  
  	ret = target->target(skb, &pkt->xt);
  
  	if (pkt->xt.hotdrop)
  		ret = NF_DROP;
  
  	switch (ret) {
  	case EBT_ACCEPT:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
105
  		regs->verdict.code = NF_ACCEPT;
5191f4d82   Arturo Borrero   netfilter: nft_co...
106
107
  		break;
  	case EBT_DROP:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
108
  		regs->verdict.code = NF_DROP;
5191f4d82   Arturo Borrero   netfilter: nft_co...
109
110
  		break;
  	case EBT_CONTINUE:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
111
  		regs->verdict.code = NFT_CONTINUE;
5191f4d82   Arturo Borrero   netfilter: nft_co...
112
113
  		break;
  	case EBT_RETURN:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
114
  		regs->verdict.code = NFT_RETURN;
5191f4d82   Arturo Borrero   netfilter: nft_co...
115
116
  		break;
  	default:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
117
  		regs->verdict.code = ret;
5191f4d82   Arturo Borrero   netfilter: nft_co...
118
119
  		break;
  	}
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
120
121
122
123
124
125
126
127
128
129
130
131
  }
  
  static const struct nla_policy nft_target_policy[NFTA_TARGET_MAX + 1] = {
  	[NFTA_TARGET_NAME]	= { .type = NLA_NUL_STRING },
  	[NFTA_TARGET_REV]	= { .type = NLA_U32 },
  	[NFTA_TARGET_INFO]	= { .type = NLA_BINARY },
  };
  
  static void
  nft_target_set_tgchk_param(struct xt_tgchk_param *par,
  			   const struct nft_ctx *ctx,
  			   struct xt_target *target, void *info,
2156d321b   Arturo Borrero   netfilter: nft_co...
132
  			   union nft_entry *entry, u16 proto, bool inv)
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
133
  {
2daf1b4d1   Pablo Neira Ayuso   netfilter: nft_co...
134
  	par->net	= ctx->net;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
135
  	par->table	= ctx->table->name;
36596dadf   Pablo Neira Ayuso   netfilter: nf_tab...
136
  	switch (ctx->family) {
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
137
138
139
140
141
  	case AF_INET:
  		entry->e4.ip.proto = proto;
  		entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0;
  		break;
  	case AF_INET6:
749177ccc   Pablo Neira Ayuso   netfilter: nft_co...
142
143
  		if (proto)
  			entry->e6.ipv6.flags |= IP6T_F_PROTO;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
144
145
146
  		entry->e6.ipv6.proto = proto;
  		entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0;
  		break;
5191f4d82   Arturo Borrero   netfilter: nft_co...
147
  	case NFPROTO_BRIDGE:
2156d321b   Arturo Borrero   netfilter: nft_co...
148
  		entry->ebt.ethproto = (__force __be16)proto;
5191f4d82   Arturo Borrero   netfilter: nft_co...
149
150
  		entry->ebt.invflags = inv ? EBT_IPROTO : 0;
  		break;
5f1589394   Arturo Borrero   netfilter: nft_co...
151
152
  	case NFPROTO_ARP:
  		break;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
153
154
155
156
  	}
  	par->entryinfo	= entry;
  	par->target	= target;
  	par->targinfo	= info;
f323d9546   Pablo Neira Ayuso   netfilter: nf_tab...
157
  	if (nft_is_base_chain(ctx->chain)) {
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
158
159
  		const struct nft_base_chain *basechain =
  						nft_base_chain(ctx->chain);
c974a3a36   Pablo Neira Ayuso   netfilter: nf_tab...
160
  		const struct nf_hook_ops *ops = &basechain->ops;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
161
162
  
  		par->hook_mask = 1 << ops->hooknum;
493618a92   Pablo Neira Ayuso   netfilter: nft_co...
163
164
  	} else {
  		par->hook_mask = 0;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
165
  	}
36596dadf   Pablo Neira Ayuso   netfilter: nf_tab...
166
  	par->family	= ctx->family;
55917a21d   Pablo Neira Ayuso   netfilter: x_tabl...
167
  	par->nft_compat = true;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
168
169
170
171
  }
  
  static void target_compat_from_user(struct xt_target *t, void *in, void *out)
  {
756c1b1a7   Pablo Neira Ayuso   netfilter: nft_co...
172
  	int pad;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
173

756c1b1a7   Pablo Neira Ayuso   netfilter: nft_co...
174
175
176
177
  	memcpy(out, in, t->targetsize);
  	pad = XT_ALIGN(t->targetsize) - t->targetsize;
  	if (pad > 0)
  		memset(out + t->targetsize, 0, pad);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
178
179
180
181
182
183
  }
  
  static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1] = {
  	[NFTA_RULE_COMPAT_PROTO]	= { .type = NLA_U32 },
  	[NFTA_RULE_COMPAT_FLAGS]	= { .type = NLA_U32 },
  };
2156d321b   Arturo Borrero   netfilter: nft_co...
184
  static int nft_parse_compat(const struct nlattr *attr, u16 *proto, bool *inv)
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
185
186
187
188
  {
  	struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1];
  	u32 flags;
  	int err;
8cb081746   Johannes Berg   netlink: make val...
189
190
  	err = nla_parse_nested_deprecated(tb, NFTA_RULE_COMPAT_MAX, attr,
  					  nft_rule_compat_policy, NULL);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
191
192
193
194
195
196
197
198
199
200
201
  	if (err < 0)
  		return err;
  
  	if (!tb[NFTA_RULE_COMPAT_PROTO] || !tb[NFTA_RULE_COMPAT_FLAGS])
  		return -EINVAL;
  
  	flags = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_FLAGS]));
  	if (flags & ~NFT_RULE_COMPAT_F_MASK)
  		return -EINVAL;
  	if (flags & NFT_RULE_COMPAT_F_INV)
  		*inv = true;
8691a9a33   Pablo Neira Ayuso   netfilter: nft_co...
202
203
  	*proto = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO]));
  	return 0;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
204
  }
2f941622f   Florian Westphal   netfilter: nft_co...
205
206
207
208
209
210
211
212
213
214
  static void nft_compat_wait_for_destructors(void)
  {
  	/* xtables matches or targets can have side effects, e.g.
  	 * creation/destruction of /proc files.
  	 * The xt ->destroy functions are run asynchronously from
  	 * work queue.  If we have pending invocations we thus
  	 * need to wait for those to finish.
  	 */
  	nf_tables_trans_destroy_flush_work();
  }
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
215
216
217
218
219
220
221
222
  static int
  nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
  		const struct nlattr * const tb[])
  {
  	void *info = nft_expr_priv(expr);
  	struct xt_target *target = expr->ops->data;
  	struct xt_tgchk_param par;
  	size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO]));
2156d321b   Arturo Borrero   netfilter: nft_co...
223
  	u16 proto = 0;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
224
225
226
227
228
  	bool inv = false;
  	union nft_entry e = {};
  	int ret;
  
  	target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info);
8691a9a33   Pablo Neira Ayuso   netfilter: nft_co...
229
230
231
  	if (ctx->nla[NFTA_RULE_COMPAT]) {
  		ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
  		if (ret < 0)
b8e9dc1c7   Florian Westphal   netfilter: nf_tab...
232
  			return ret;
8691a9a33   Pablo Neira Ayuso   netfilter: nft_co...
233
  	}
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
234
235
  
  	nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
2f941622f   Florian Westphal   netfilter: nft_co...
236
  	nft_compat_wait_for_destructors();
ffe8923f1   Florian Westphal   netfilter: nft_co...
237

0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
238
239
  	ret = xt_check_target(&par, size, proto, inv);
  	if (ret < 0)
b8e9dc1c7   Florian Westphal   netfilter: nf_tab...
240
  		return ret;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
241
242
  
  	/* The standard target cannot be used */
b8e9dc1c7   Florian Westphal   netfilter: nf_tab...
243
244
  	if (!target->target)
  		return -EINVAL;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
245
246
  
  	return 0;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
247
  }
ffe8923f1   Florian Westphal   netfilter: nft_co...
248
249
  static void __nft_mt_tg_destroy(struct module *me, const struct nft_expr *expr)
  {
ffe8923f1   Florian Westphal   netfilter: nft_co...
250
251
252
  	module_put(me);
  	kfree(expr->ops);
  }
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
253
  static void
62472bcef   Patrick McHardy   netfilter: nf_tab...
254
  nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
255
256
  {
  	struct xt_target *target = expr->ops->data;
3d9b14213   Pablo Neira Ayuso   netfilter: nft_co...
257
  	void *info = nft_expr_priv(expr);
753c111f6   Pablo Neira Ayuso   netfilter: nft_co...
258
  	struct module *me = target->me;
3d9b14213   Pablo Neira Ayuso   netfilter: nft_co...
259
260
261
262
263
  	struct xt_tgdtor_param par;
  
  	par.net = ctx->net;
  	par.target = target;
  	par.targinfo = info;
36596dadf   Pablo Neira Ayuso   netfilter: nf_tab...
264
  	par.family = ctx->family;
3d9b14213   Pablo Neira Ayuso   netfilter: nft_co...
265
266
  	if (par.target->destroy != NULL)
  		par.target->destroy(&par);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
267

ffe8923f1   Florian Westphal   netfilter: nft_co...
268
  	__nft_mt_tg_destroy(me, expr);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
269
  }
d701d8117   Pablo Neira Ayuso   netfilter: nft_co...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
  static int nft_extension_dump_info(struct sk_buff *skb, int attr,
  				   const void *info,
  				   unsigned int size, unsigned int user_size)
  {
  	unsigned int info_size, aligned_size = XT_ALIGN(size);
  	struct nlattr *nla;
  
  	nla = nla_reserve(skb, attr, aligned_size);
  	if (!nla)
  		return -1;
  
  	info_size = user_size ? : size;
  	memcpy(nla_data(nla), info, info_size);
  	memset(nla_data(nla) + info_size, 0, aligned_size - info_size);
  
  	return 0;
  }
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
287
288
289
290
291
292
293
  static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr)
  {
  	const struct xt_target *target = expr->ops->data;
  	void *info = nft_expr_priv(expr);
  
  	if (nla_put_string(skb, NFTA_TARGET_NAME, target->name) ||
  	    nla_put_be32(skb, NFTA_TARGET_REV, htonl(target->revision)) ||
d701d8117   Pablo Neira Ayuso   netfilter: nft_co...
294
295
  	    nft_extension_dump_info(skb, NFTA_TARGET_INFO, info,
  				    target->targetsize, target->usersize))
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  		goto nla_put_failure;
  
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
  
  static int nft_target_validate(const struct nft_ctx *ctx,
  			       const struct nft_expr *expr,
  			       const struct nft_data **data)
  {
  	struct xt_target *target = expr->ops->data;
  	unsigned int hook_mask = 0;
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
310
  	int ret;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
311

f323d9546   Pablo Neira Ayuso   netfilter: nf_tab...
312
  	if (nft_is_base_chain(ctx->chain)) {
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
313
314
  		const struct nft_base_chain *basechain =
  						nft_base_chain(ctx->chain);
c974a3a36   Pablo Neira Ayuso   netfilter: nf_tab...
315
  		const struct nf_hook_ops *ops = &basechain->ops;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
316
317
  
  		hook_mask = 1 << ops->hooknum;
f7fb77fc1   Pablo Neira Ayuso   netfilter: nft_co...
318
  		if (target->hooks && !(hook_mask & target->hooks))
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
319
  			return -EINVAL;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
320

e4844c9c6   Florian Westphal   netfilter: nft_co...
321
  		ret = nft_compat_chain_validate_dependency(ctx, target->table);
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
322
323
  		if (ret < 0)
  			return ret;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
324
325
326
  	}
  	return 0;
  }
8bdf16474   Florian Westphal   netfilter: nft_co...
327
328
329
330
  static void __nft_match_eval(const struct nft_expr *expr,
  			     struct nft_regs *regs,
  			     const struct nft_pktinfo *pkt,
  			     void *info)
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
331
  {
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
332
333
334
335
336
337
338
339
340
  	struct xt_match *match = expr->ops->data;
  	struct sk_buff *skb = pkt->skb;
  	bool ret;
  
  	nft_compat_set_par((struct xt_action_param *)&pkt->xt, match, info);
  
  	ret = match->match(skb, (struct xt_action_param *)&pkt->xt);
  
  	if (pkt->xt.hotdrop) {
a55e22e92   Patrick McHardy   netfilter: nf_tab...
341
  		regs->verdict.code = NF_DROP;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
342
343
  		return;
  	}
c1f866767   David Miller   netfilter: Fix sw...
344
345
  	switch (ret ? 1 : 0) {
  	case 1:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
346
  		regs->verdict.code = NFT_CONTINUE;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
347
  		break;
c1f866767   David Miller   netfilter: Fix sw...
348
  	case 0:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
349
  		regs->verdict.code = NFT_BREAK;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
350
351
352
  		break;
  	}
  }
732a8049f   Florian Westphal   netfilter: nft_co...
353
354
355
356
357
358
359
360
  static void nft_match_large_eval(const struct nft_expr *expr,
  				 struct nft_regs *regs,
  				 const struct nft_pktinfo *pkt)
  {
  	struct nft_xt_match_priv *priv = nft_expr_priv(expr);
  
  	__nft_match_eval(expr, regs, pkt, priv->info);
  }
8bdf16474   Florian Westphal   netfilter: nft_co...
361
362
363
364
365
366
  static void nft_match_eval(const struct nft_expr *expr,
  			   struct nft_regs *regs,
  			   const struct nft_pktinfo *pkt)
  {
  	__nft_match_eval(expr, regs, pkt, nft_expr_priv(expr));
  }
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
367
368
369
370
371
372
373
374
375
376
  static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = {
  	[NFTA_MATCH_NAME]	= { .type = NLA_NUL_STRING },
  	[NFTA_MATCH_REV]	= { .type = NLA_U32 },
  	[NFTA_MATCH_INFO]	= { .type = NLA_BINARY },
  };
  
  /* struct xt_mtchk_param and xt_tgchk_param look very similar */
  static void
  nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
  			  struct xt_match *match, void *info,
2156d321b   Arturo Borrero   netfilter: nft_co...
377
  			  union nft_entry *entry, u16 proto, bool inv)
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
378
  {
2daf1b4d1   Pablo Neira Ayuso   netfilter: nft_co...
379
  	par->net	= ctx->net;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
380
  	par->table	= ctx->table->name;
36596dadf   Pablo Neira Ayuso   netfilter: nf_tab...
381
  	switch (ctx->family) {
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
382
383
384
385
386
  	case AF_INET:
  		entry->e4.ip.proto = proto;
  		entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0;
  		break;
  	case AF_INET6:
749177ccc   Pablo Neira Ayuso   netfilter: nft_co...
387
388
  		if (proto)
  			entry->e6.ipv6.flags |= IP6T_F_PROTO;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
389
390
391
  		entry->e6.ipv6.proto = proto;
  		entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0;
  		break;
5191f4d82   Arturo Borrero   netfilter: nft_co...
392
  	case NFPROTO_BRIDGE:
2156d321b   Arturo Borrero   netfilter: nft_co...
393
  		entry->ebt.ethproto = (__force __be16)proto;
5191f4d82   Arturo Borrero   netfilter: nft_co...
394
395
  		entry->ebt.invflags = inv ? EBT_IPROTO : 0;
  		break;
5f1589394   Arturo Borrero   netfilter: nft_co...
396
397
  	case NFPROTO_ARP:
  		break;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
398
399
400
401
  	}
  	par->entryinfo	= entry;
  	par->match	= match;
  	par->matchinfo	= info;
f323d9546   Pablo Neira Ayuso   netfilter: nf_tab...
402
  	if (nft_is_base_chain(ctx->chain)) {
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
403
404
  		const struct nft_base_chain *basechain =
  						nft_base_chain(ctx->chain);
c974a3a36   Pablo Neira Ayuso   netfilter: nf_tab...
405
  		const struct nf_hook_ops *ops = &basechain->ops;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
406
407
  
  		par->hook_mask = 1 << ops->hooknum;
493618a92   Pablo Neira Ayuso   netfilter: nft_co...
408
409
  	} else {
  		par->hook_mask = 0;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
410
  	}
36596dadf   Pablo Neira Ayuso   netfilter: nf_tab...
411
  	par->family	= ctx->family;
55917a21d   Pablo Neira Ayuso   netfilter: x_tabl...
412
  	par->nft_compat = true;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
413
414
415
416
  }
  
  static void match_compat_from_user(struct xt_match *m, void *in, void *out)
  {
756c1b1a7   Pablo Neira Ayuso   netfilter: nft_co...
417
418
419
420
421
422
  	int pad;
  
  	memcpy(out, in, m->matchsize);
  	pad = XT_ALIGN(m->matchsize) - m->matchsize;
  	if (pad > 0)
  		memset(out + m->matchsize, 0, pad);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
423
424
425
  }
  
  static int
8bdf16474   Florian Westphal   netfilter: nft_co...
426
427
428
  __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
  		 const struct nlattr * const tb[],
  		 void *info)
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
429
  {
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
430
431
432
  	struct xt_match *match = expr->ops->data;
  	struct xt_mtchk_param par;
  	size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO]));
2156d321b   Arturo Borrero   netfilter: nft_co...
433
  	u16 proto = 0;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
434
435
436
437
438
  	bool inv = false;
  	union nft_entry e = {};
  	int ret;
  
  	match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info);
8691a9a33   Pablo Neira Ayuso   netfilter: nft_co...
439
440
441
  	if (ctx->nla[NFTA_RULE_COMPAT]) {
  		ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
  		if (ret < 0)
b8e9dc1c7   Florian Westphal   netfilter: nf_tab...
442
  			return ret;
8691a9a33   Pablo Neira Ayuso   netfilter: nft_co...
443
  	}
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
444
445
  
  	nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
2f941622f   Florian Westphal   netfilter: nft_co...
446
  	nft_compat_wait_for_destructors();
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
447
  	return xt_check_match(&par, size, proto, inv);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
448
  }
8bdf16474   Florian Westphal   netfilter: nft_co...
449
450
451
452
453
454
  static int
  nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
  	       const struct nlattr * const tb[])
  {
  	return __nft_match_init(ctx, expr, tb, nft_expr_priv(expr));
  }
732a8049f   Florian Westphal   netfilter: nft_co...
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
  static int
  nft_match_large_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
  		     const struct nlattr * const tb[])
  {
  	struct nft_xt_match_priv *priv = nft_expr_priv(expr);
  	struct xt_match *m = expr->ops->data;
  	int ret;
  
  	priv->info = kmalloc(XT_ALIGN(m->matchsize), GFP_KERNEL);
  	if (!priv->info)
  		return -ENOMEM;
  
  	ret = __nft_match_init(ctx, expr, tb, priv->info);
  	if (ret)
  		kfree(priv->info);
  	return ret;
  }
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
472
  static void
8bdf16474   Florian Westphal   netfilter: nft_co...
473
474
  __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,
  		    void *info)
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
475
476
  {
  	struct xt_match *match = expr->ops->data;
29e388010   Florian Westphal   netfilter: nf_tab...
477
  	struct module *me = match->me;
3d9b14213   Pablo Neira Ayuso   netfilter: nft_co...
478
479
480
481
482
  	struct xt_mtdtor_param par;
  
  	par.net = ctx->net;
  	par.match = match;
  	par.matchinfo = info;
36596dadf   Pablo Neira Ayuso   netfilter: nf_tab...
483
  	par.family = ctx->family;
3d9b14213   Pablo Neira Ayuso   netfilter: nft_co...
484
485
  	if (par.match->destroy != NULL)
  		par.match->destroy(&par);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
486

ffe8923f1   Florian Westphal   netfilter: nft_co...
487
  	__nft_mt_tg_destroy(me, expr);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
488
  }
8bdf16474   Florian Westphal   netfilter: nft_co...
489
490
491
492
493
  static void
  nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
  {
  	__nft_match_destroy(ctx, expr, nft_expr_priv(expr));
  }
732a8049f   Florian Westphal   netfilter: nft_co...
494
495
496
497
498
499
500
501
  static void
  nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
  {
  	struct nft_xt_match_priv *priv = nft_expr_priv(expr);
  
  	__nft_match_destroy(ctx, expr, priv->info);
  	kfree(priv->info);
  }
8bdf16474   Florian Westphal   netfilter: nft_co...
502
503
  static int __nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr,
  			    void *info)
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
504
  {
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
505
506
507
508
  	struct xt_match *match = expr->ops->data;
  
  	if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) ||
  	    nla_put_be32(skb, NFTA_MATCH_REV, htonl(match->revision)) ||
d701d8117   Pablo Neira Ayuso   netfilter: nft_co...
509
510
  	    nft_extension_dump_info(skb, NFTA_MATCH_INFO, info,
  				    match->matchsize, match->usersize))
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
511
512
513
514
515
516
517
  		goto nla_put_failure;
  
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
8bdf16474   Florian Westphal   netfilter: nft_co...
518
519
520
521
  static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr)
  {
  	return __nft_match_dump(skb, expr, nft_expr_priv(expr));
  }
732a8049f   Florian Westphal   netfilter: nft_co...
522
523
524
525
526
527
  static int nft_match_large_dump(struct sk_buff *skb, const struct nft_expr *e)
  {
  	struct nft_xt_match_priv *priv = nft_expr_priv(e);
  
  	return __nft_match_dump(skb, e, priv->info);
  }
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
528
529
530
531
532
533
  static int nft_match_validate(const struct nft_ctx *ctx,
  			      const struct nft_expr *expr,
  			      const struct nft_data **data)
  {
  	struct xt_match *match = expr->ops->data;
  	unsigned int hook_mask = 0;
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
534
  	int ret;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
535

f323d9546   Pablo Neira Ayuso   netfilter: nf_tab...
536
  	if (nft_is_base_chain(ctx->chain)) {
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
537
538
  		const struct nft_base_chain *basechain =
  						nft_base_chain(ctx->chain);
c974a3a36   Pablo Neira Ayuso   netfilter: nf_tab...
539
  		const struct nf_hook_ops *ops = &basechain->ops;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
540
541
  
  		hook_mask = 1 << ops->hooknum;
f7fb77fc1   Pablo Neira Ayuso   netfilter: nft_co...
542
  		if (match->hooks && !(hook_mask & match->hooks))
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
543
  			return -EINVAL;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
544

e4844c9c6   Florian Westphal   netfilter: nft_co...
545
  		ret = nft_compat_chain_validate_dependency(ctx, match->table);
f3f5ddedd   Pablo Neira Ayuso   netfilter: nft_co...
546
547
  		if (ret < 0)
  			return ret;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
548
549
550
551
552
553
554
555
556
557
558
559
  	}
  	return 0;
  }
  
  static int
  nfnl_compat_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
  		      int event, u16 family, const char *name,
  		      int rev, int target)
  {
  	struct nlmsghdr *nlh;
  	struct nfgenmsg *nfmsg;
  	unsigned int flags = portid ? NLM_F_MULTI : 0;
dedb67c4b   Pablo Neira Ayuso   netfilter: Add nf...
560
  	event = nfnl_msg_type(NFNL_SUBSYS_NFT_COMPAT, event);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
  	if (nlh == NULL)
  		goto nlmsg_failure;
  
  	nfmsg = nlmsg_data(nlh);
  	nfmsg->nfgen_family = family;
  	nfmsg->version = NFNETLINK_V0;
  	nfmsg->res_id = 0;
  
  	if (nla_put_string(skb, NFTA_COMPAT_NAME, name) ||
  	    nla_put_be32(skb, NFTA_COMPAT_REV, htonl(rev)) ||
  	    nla_put_be32(skb, NFTA_COMPAT_TYPE, htonl(target)))
  		goto nla_put_failure;
  
  	nlmsg_end(skb, nlh);
  	return skb->len;
  
  nlmsg_failure:
  nla_put_failure:
  	nlmsg_cancel(skb, nlh);
  	return -1;
  }
eb1fb1479   Florian Westphal   netfilter: nft_co...
583
584
585
586
  static int nfnl_compat_get_rcu(struct net *net, struct sock *nfnl,
  			       struct sk_buff *skb, const struct nlmsghdr *nlh,
  			       const struct nlattr * const tb[],
  			       struct netlink_ext_ack *extack)
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
  {
  	int ret = 0, target;
  	struct nfgenmsg *nfmsg;
  	const char *fmt;
  	const char *name;
  	u32 rev;
  	struct sk_buff *skb2;
  
  	if (tb[NFTA_COMPAT_NAME] == NULL ||
  	    tb[NFTA_COMPAT_REV] == NULL ||
  	    tb[NFTA_COMPAT_TYPE] == NULL)
  		return -EINVAL;
  
  	name = nla_data(tb[NFTA_COMPAT_NAME]);
  	rev = ntohl(nla_get_be32(tb[NFTA_COMPAT_REV]));
  	target = ntohl(nla_get_be32(tb[NFTA_COMPAT_TYPE]));
  
  	nfmsg = nlmsg_data(nlh);
  
  	switch(nfmsg->nfgen_family) {
  	case AF_INET:
  		fmt = "ipt_%s";
  		break;
  	case AF_INET6:
  		fmt = "ip6t_%s";
  		break;
5191f4d82   Arturo Borrero   netfilter: nft_co...
613
614
615
  	case NFPROTO_BRIDGE:
  		fmt = "ebt_%s";
  		break;
5f1589394   Arturo Borrero   netfilter: nft_co...
616
617
618
  	case NFPROTO_ARP:
  		fmt = "arpt_%s";
  		break;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
619
620
621
622
623
624
  	default:
  		pr_err("nft_compat: unsupported protocol %d
  ",
  			nfmsg->nfgen_family);
  		return -EINVAL;
  	}
eb1fb1479   Florian Westphal   netfilter: nft_co...
625
626
627
628
  	if (!try_module_get(THIS_MODULE))
  		return -EINVAL;
  
  	rcu_read_unlock();
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
629
630
631
  	try_then_request_module(xt_find_revision(nfmsg->nfgen_family, name,
  						 rev, target, &ret),
  						 fmt, name);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
632
  	if (ret < 0)
eb1fb1479   Florian Westphal   netfilter: nft_co...
633
  		goto out_put;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
634
635
  
  	skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
eb1fb1479   Florian Westphal   netfilter: nft_co...
636
637
638
639
  	if (skb2 == NULL) {
  		ret = -ENOMEM;
  		goto out_put;
  	}
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
640
641
642
643
644
645
646
647
648
  
  	/* include the best revision for this extension in the message */
  	if (nfnl_compat_fill_info(skb2, NETLINK_CB(skb).portid,
  				  nlh->nlmsg_seq,
  				  NFNL_MSG_TYPE(nlh->nlmsg_type),
  				  NFNL_MSG_COMPAT_GET,
  				  nfmsg->nfgen_family,
  				  name, ret, target) <= 0) {
  		kfree_skb(skb2);
eb1fb1479   Florian Westphal   netfilter: nft_co...
649
  		goto out_put;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
650
651
652
653
654
655
  	}
  
  	ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid,
  				MSG_DONTWAIT);
  	if (ret > 0)
  		ret = 0;
eb1fb1479   Florian Westphal   netfilter: nft_co...
656
657
658
  out_put:
  	rcu_read_lock();
  	module_put(THIS_MODULE);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
659
660
661
662
663
664
665
666
667
668
669
  	return ret == -EAGAIN ? -ENOBUFS : ret;
  }
  
  static const struct nla_policy nfnl_compat_policy_get[NFTA_COMPAT_MAX+1] = {
  	[NFTA_COMPAT_NAME]	= { .type = NLA_NUL_STRING,
  				    .len = NFT_COMPAT_NAME_MAX-1 },
  	[NFTA_COMPAT_REV]	= { .type = NLA_U32 },
  	[NFTA_COMPAT_TYPE]	= { .type = NLA_U32 },
  };
  
  static const struct nfnl_callback nfnl_nft_compat_cb[NFNL_MSG_COMPAT_MAX] = {
eb1fb1479   Florian Westphal   netfilter: nft_co...
670
  	[NFNL_MSG_COMPAT_GET]		= { .call_rcu = nfnl_compat_get_rcu,
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
671
672
673
674
675
676
677
678
679
680
  					    .attr_count = NFTA_COMPAT_MAX,
  					    .policy = nfnl_compat_policy_get },
  };
  
  static const struct nfnetlink_subsystem nfnl_compat_subsys = {
  	.name		= "nft-compat",
  	.subsys_id	= NFNL_SUBSYS_NFT_COMPAT,
  	.cb_count	= NFNL_MSG_COMPAT_MAX,
  	.cb		= nfnl_nft_compat_cb,
  };
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
681
  static struct nft_expr_type nft_match_type;
ba378ca9c   Pablo Neira Ayuso   netfilter: nft_co...
682

0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
683
684
685
686
  static const struct nft_expr_ops *
  nft_match_select_ops(const struct nft_ctx *ctx,
  		     const struct nlattr * const tb[])
  {
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
687
  	struct nft_expr_ops *ops;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
688
  	struct xt_match *match;
732a8049f   Florian Westphal   netfilter: nft_co...
689
  	unsigned int matchsize;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
690
  	char *mt_name;
ba378ca9c   Pablo Neira Ayuso   netfilter: nft_co...
691
  	u32 rev, family;
2bf4fade5   Liping Zhang   netfilter: nft_co...
692
  	int err;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
693
694
695
696
697
698
699
700
  
  	if (tb[NFTA_MATCH_NAME] == NULL ||
  	    tb[NFTA_MATCH_REV] == NULL ||
  	    tb[NFTA_MATCH_INFO] == NULL)
  		return ERR_PTR(-EINVAL);
  
  	mt_name = nla_data(tb[NFTA_MATCH_NAME]);
  	rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV]));
36596dadf   Pablo Neira Ayuso   netfilter: nf_tab...
701
  	family = ctx->family;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
702

0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
703
704
705
  	match = xt_request_find_match(family, mt_name, rev);
  	if (IS_ERR(match))
  		return ERR_PTR(-ENOENT);
2bf4fade5   Liping Zhang   netfilter: nft_co...
706
707
708
709
  	if (match->matchsize > nla_len(tb[NFTA_MATCH_INFO])) {
  		err = -EINVAL;
  		goto err;
  	}
f0716cd6e   Florian Westphal   netfilter: nft_co...
710

b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
711
712
  	ops = kzalloc(sizeof(struct nft_expr_ops), GFP_KERNEL);
  	if (!ops) {
2bf4fade5   Liping Zhang   netfilter: nft_co...
713
714
715
  		err = -ENOMEM;
  		goto err;
  	}
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
716

b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
717
718
719
720
721
722
723
  	ops->type = &nft_match_type;
  	ops->eval = nft_match_eval;
  	ops->init = nft_match_init;
  	ops->destroy = nft_match_destroy;
  	ops->dump = nft_match_dump;
  	ops->validate = nft_match_validate;
  	ops->data = match;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
724

732a8049f   Florian Westphal   netfilter: nft_co...
725
726
727
  	matchsize = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize));
  	if (matchsize > NFT_MATCH_LARGE_THRESH) {
  		matchsize = NFT_EXPR_SIZE(sizeof(struct nft_xt_match_priv));
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
728
729
730
731
  		ops->eval = nft_match_large_eval;
  		ops->init = nft_match_large_init;
  		ops->destroy = nft_match_large_destroy;
  		ops->dump = nft_match_large_dump;
732a8049f   Florian Westphal   netfilter: nft_co...
732
  	}
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
733
  	ops->size = matchsize;
732a8049f   Florian Westphal   netfilter: nft_co...
734

b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
735
  	return ops;
2bf4fade5   Liping Zhang   netfilter: nft_co...
736
737
738
  err:
  	module_put(match->me);
  	return ERR_PTR(err);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
739
  }
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
740
741
742
743
744
745
746
  static void nft_match_release_ops(const struct nft_expr_ops *ops)
  {
  	struct xt_match *match = ops->data;
  
  	module_put(match->me);
  	kfree(ops);
  }
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
747
748
749
  static struct nft_expr_type nft_match_type __read_mostly = {
  	.name		= "match",
  	.select_ops	= nft_match_select_ops,
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
750
  	.release_ops	= nft_match_release_ops,
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
751
752
753
754
  	.policy		= nft_match_policy,
  	.maxattr	= NFTA_MATCH_MAX,
  	.owner		= THIS_MODULE,
  };
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
755
  static struct nft_expr_type nft_target_type;
ba378ca9c   Pablo Neira Ayuso   netfilter: nft_co...
756

0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
757
758
759
760
  static const struct nft_expr_ops *
  nft_target_select_ops(const struct nft_ctx *ctx,
  		      const struct nlattr * const tb[])
  {
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
761
  	struct nft_expr_ops *ops;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
762
763
  	struct xt_target *target;
  	char *tg_name;
ba378ca9c   Pablo Neira Ayuso   netfilter: nft_co...
764
  	u32 rev, family;
2bf4fade5   Liping Zhang   netfilter: nft_co...
765
  	int err;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
766
767
768
769
770
771
772
773
  
  	if (tb[NFTA_TARGET_NAME] == NULL ||
  	    tb[NFTA_TARGET_REV] == NULL ||
  	    tb[NFTA_TARGET_INFO] == NULL)
  		return ERR_PTR(-EINVAL);
  
  	tg_name = nla_data(tb[NFTA_TARGET_NAME]);
  	rev = ntohl(nla_get_be32(tb[NFTA_TARGET_REV]));
36596dadf   Pablo Neira Ayuso   netfilter: nf_tab...
774
  	family = ctx->family;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
775

21d5e0781   Florian Westphal   netfilter: nft_co...
776
777
778
779
  	if (strcmp(tg_name, XT_ERROR_TARGET) == 0 ||
  	    strcmp(tg_name, XT_STANDARD_TARGET) == 0 ||
  	    strcmp(tg_name, "standard") == 0)
  		return ERR_PTR(-EINVAL);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
780
781
782
  	target = xt_request_find_target(family, tg_name, rev);
  	if (IS_ERR(target))
  		return ERR_PTR(-ENOENT);
21d5e0781   Florian Westphal   netfilter: nft_co...
783
784
785
786
  	if (!target->target) {
  		err = -EINVAL;
  		goto err;
  	}
2bf4fade5   Liping Zhang   netfilter: nft_co...
787
788
789
790
  	if (target->targetsize > nla_len(tb[NFTA_TARGET_INFO])) {
  		err = -EINVAL;
  		goto err;
  	}
f0716cd6e   Florian Westphal   netfilter: nft_co...
791

b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
792
793
  	ops = kzalloc(sizeof(struct nft_expr_ops), GFP_KERNEL);
  	if (!ops) {
2bf4fade5   Liping Zhang   netfilter: nft_co...
794
795
796
  		err = -ENOMEM;
  		goto err;
  	}
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
797

b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
798
799
800
801
802
803
804
  	ops->type = &nft_target_type;
  	ops->size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
  	ops->init = nft_target_init;
  	ops->destroy = nft_target_destroy;
  	ops->dump = nft_target_dump;
  	ops->validate = nft_target_validate;
  	ops->data = target;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
805

5191f4d82   Arturo Borrero   netfilter: nft_co...
806
  	if (family == NFPROTO_BRIDGE)
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
807
  		ops->eval = nft_target_eval_bridge;
5191f4d82   Arturo Borrero   netfilter: nft_co...
808
  	else
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
809
  		ops->eval = nft_target_eval_xt;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
810

b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
811
  	return ops;
2bf4fade5   Liping Zhang   netfilter: nft_co...
812
813
814
  err:
  	module_put(target->me);
  	return ERR_PTR(err);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
815
  }
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
816
817
818
819
820
821
822
  static void nft_target_release_ops(const struct nft_expr_ops *ops)
  {
  	struct xt_target *target = ops->data;
  
  	module_put(target->me);
  	kfree(ops);
  }
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
823
824
825
  static struct nft_expr_type nft_target_type __read_mostly = {
  	.name		= "target",
  	.select_ops	= nft_target_select_ops,
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
826
  	.release_ops	= nft_target_release_ops,
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
827
828
829
830
831
832
833
834
835
836
837
  	.policy		= nft_target_policy,
  	.maxattr	= NFTA_TARGET_MAX,
  	.owner		= THIS_MODULE,
  };
  
  static int __init nft_compat_module_init(void)
  {
  	int ret;
  
  	ret = nft_register_expr(&nft_match_type);
  	if (ret < 0)
b8e204006   Pablo Neira Ayuso   netfilter: nft_co...
838
  		return ret;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
839
840
841
842
843
844
845
846
847
848
849
  
  	ret = nft_register_expr(&nft_target_type);
  	if (ret < 0)
  		goto err_match;
  
  	ret = nfnetlink_subsys_register(&nfnl_compat_subsys);
  	if (ret < 0) {
  		pr_err("nft_compat: cannot register with nfnetlink.
  ");
  		goto err_target;
  	}
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
850
  	return ret;
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
851
852
853
854
855
856
857
858
859
860
861
862
  err_target:
  	nft_unregister_expr(&nft_target_type);
  err_match:
  	nft_unregister_expr(&nft_match_type);
  	return ret;
  }
  
  static void __exit nft_compat_module_exit(void)
  {
  	nfnetlink_subsys_unregister(&nfnl_compat_subsys);
  	nft_unregister_expr(&nft_target_type);
  	nft_unregister_expr(&nft_match_type);
0ca743a55   Pablo Neira Ayuso   netfilter: nf_tab...
863
864
865
866
867
868
869
870
871
872
873
  }
  
  MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT);
  
  module_init(nft_compat_module_init);
  module_exit(nft_compat_module_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
  MODULE_ALIAS_NFT_EXPR("match");
  MODULE_ALIAS_NFT_EXPR("target");
4cacc3951   Rob Gill   netfilter: Add MO...
874
  MODULE_DESCRIPTION("x_tables over nftables support");