Blame view

net/netfilter/nft_nat.c 9.96 KB
75a6faf61   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
2
3
4
5
  /*
   * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
   * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
   * Copyright (c) 2012 Intel Corporation
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/skbuff.h>
  #include <linux/ip.h>
  #include <linux/string.h>
  #include <linux/netlink.h>
  #include <linux/netfilter.h>
  #include <linux/netfilter_ipv4.h>
  #include <linux/netfilter/nfnetlink.h>
  #include <linux/netfilter/nf_tables.h>
  #include <net/netfilter/nf_conntrack.h>
  #include <net/netfilter/nf_nat.h>
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
20
  #include <net/netfilter/nf_tables.h>
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
21
22
23
  #include <net/ip.h>
  
  struct nft_nat {
4f16d25c6   Pablo Neira Ayuso   netfilter: nftabl...
24
25
26
27
  	u8			sreg_addr_min;
  	u8			sreg_addr_max;
  	u8			sreg_proto_min;
  	u8			sreg_proto_max;
a4c2e8beb   Patrick McHardy   netfilter: nft_na...
28
29
  	enum nf_nat_manip_type  type:8;
  	u8			family;
e42eff8a3   Arturo Borrero   netfilter: nft_na...
30
  	u16			flags;
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
31
  };
acd766e31   Pablo Neira Ayuso   netfilter: nft_na...
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
  static void nft_nat_setup_addr(struct nf_nat_range2 *range,
  			       const struct nft_regs *regs,
  			       const struct nft_nat *priv)
  {
  	switch (priv->family) {
  	case AF_INET:
  		range->min_addr.ip = (__force __be32)
  				regs->data[priv->sreg_addr_min];
  		range->max_addr.ip = (__force __be32)
  				regs->data[priv->sreg_addr_max];
  		break;
  	case AF_INET6:
  		memcpy(range->min_addr.ip6, &regs->data[priv->sreg_addr_min],
  		       sizeof(range->min_addr.ip6));
  		memcpy(range->max_addr.ip6, &regs->data[priv->sreg_addr_max],
  		       sizeof(range->max_addr.ip6));
  		break;
  	}
  }
  
  static void nft_nat_setup_proto(struct nf_nat_range2 *range,
  				const struct nft_regs *regs,
  				const struct nft_nat *priv)
  {
  	range->min_proto.all = (__force __be16)
  		nft_reg_load16(&regs->data[priv->sreg_proto_min]);
  	range->max_proto.all = (__force __be16)
  		nft_reg_load16(&regs->data[priv->sreg_proto_max]);
  }
3ff7ddb13   Pablo Neira Ayuso   netfilter: nft_na...
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  static void nft_nat_setup_netmap(struct nf_nat_range2 *range,
  				 const struct nft_pktinfo *pkt,
  				 const struct nft_nat *priv)
  {
  	struct sk_buff *skb = pkt->skb;
  	union nf_inet_addr new_addr;
  	__be32 netmask;
  	int i, len = 0;
  
  	switch (priv->type) {
  	case NFT_NAT_SNAT:
  		if (nft_pf(pkt) == NFPROTO_IPV4) {
  			new_addr.ip = ip_hdr(skb)->saddr;
  			len = sizeof(struct in_addr);
  		} else {
  			new_addr.in6 = ipv6_hdr(skb)->saddr;
  			len = sizeof(struct in6_addr);
  		}
  		break;
  	case NFT_NAT_DNAT:
  		if (nft_pf(pkt) == NFPROTO_IPV4) {
  			new_addr.ip = ip_hdr(skb)->daddr;
  			len = sizeof(struct in_addr);
  		} else {
  			new_addr.in6 = ipv6_hdr(skb)->daddr;
  			len = sizeof(struct in6_addr);
  		}
  		break;
  	}
  
  	for (i = 0; i < len / sizeof(__be32); i++) {
  		netmask = ~(range->min_addr.ip6[i] ^ range->max_addr.ip6[i]);
  		new_addr.ip6[i] &= ~netmask;
  		new_addr.ip6[i] |= range->min_addr.ip6[i] & netmask;
  	}
  
  	range->min_addr = new_addr;
  	range->max_addr = new_addr;
  }
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
100
  static void nft_nat_eval(const struct nft_expr *expr,
a55e22e92   Patrick McHardy   netfilter: nf_tab...
101
  			 struct nft_regs *regs,
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
102
103
104
105
106
  			 const struct nft_pktinfo *pkt)
  {
  	const struct nft_nat *priv = nft_expr_priv(expr);
  	enum ip_conntrack_info ctinfo;
  	struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo);
2eb0f624b   Thierry Du Tre   netfilter: add NA...
107
  	struct nf_nat_range2 range;
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
108
109
  
  	memset(&range, 0, sizeof(range));
3ff7ddb13   Pablo Neira Ayuso   netfilter: nft_na...
110
111
  
  	if (priv->sreg_addr_min) {
acd766e31   Pablo Neira Ayuso   netfilter: nft_na...
112
  		nft_nat_setup_addr(&range, regs, priv);
3ff7ddb13   Pablo Neira Ayuso   netfilter: nft_na...
113
114
115
  		if (priv->flags & NF_NAT_RANGE_NETMAP)
  			nft_nat_setup_netmap(&range, pkt, priv);
  	}
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
116

acd766e31   Pablo Neira Ayuso   netfilter: nft_na...
117
118
  	if (priv->sreg_proto_min)
  		nft_nat_setup_proto(&range, regs, priv);
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
119

4566aa440   Pablo Neira Ayuso   netfilter: nft_na...
120
  	range.flags = priv->flags;
e42eff8a3   Arturo Borrero   netfilter: nft_na...
121

a55e22e92   Patrick McHardy   netfilter: nf_tab...
122
  	regs->verdict.code = nf_nat_setup_info(ct, &range, priv->type);
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
123
124
125
126
127
128
129
130
131
  }
  
  static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = {
  	[NFTA_NAT_TYPE]		 = { .type = NLA_U32 },
  	[NFTA_NAT_FAMILY]	 = { .type = NLA_U32 },
  	[NFTA_NAT_REG_ADDR_MIN]	 = { .type = NLA_U32 },
  	[NFTA_NAT_REG_ADDR_MAX]	 = { .type = NLA_U32 },
  	[NFTA_NAT_REG_PROTO_MIN] = { .type = NLA_U32 },
  	[NFTA_NAT_REG_PROTO_MAX] = { .type = NLA_U32 },
e42eff8a3   Arturo Borrero   netfilter: nft_na...
132
  	[NFTA_NAT_FLAGS]	 = { .type = NLA_U32 },
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
133
  };
75e8d06d4   Pablo Neira Ayuso   netfilter: nf_tab...
134
135
136
  static int nft_nat_validate(const struct nft_ctx *ctx,
  			    const struct nft_expr *expr,
  			    const struct nft_data **data)
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
137
138
139
  {
  	struct nft_nat *priv = nft_expr_priv(expr);
  	int err;
7210e4e38   Pablo Neira Ayuso   netfilter: nf_tab...
140
141
142
  	err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
  	if (err < 0)
  		return err;
75e8d06d4   Pablo Neira Ayuso   netfilter: nf_tab...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  	switch (priv->type) {
  	case NFT_NAT_SNAT:
  		err = nft_chain_validate_hooks(ctx->chain,
  					       (1 << NF_INET_POST_ROUTING) |
  					       (1 << NF_INET_LOCAL_IN));
  		break;
  	case NFT_NAT_DNAT:
  		err = nft_chain_validate_hooks(ctx->chain,
  					       (1 << NF_INET_PRE_ROUTING) |
  					       (1 << NF_INET_LOCAL_OUT));
  		break;
  	}
  
  	return err;
  }
  
  static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
  			const struct nlattr * const tb[])
  {
  	struct nft_nat *priv = nft_expr_priv(expr);
d07db9884   Patrick McHardy   netfilter: nf_tab...
163
  	unsigned int alen, plen;
75e8d06d4   Pablo Neira Ayuso   netfilter: nf_tab...
164
165
  	u32 family;
  	int err;
5c819a397   Pablo Neira Ayuso   netfilter: nft_na...
166
167
168
  	if (tb[NFTA_NAT_TYPE] == NULL ||
  	    (tb[NFTA_NAT_REG_ADDR_MIN] == NULL &&
  	     tb[NFTA_NAT_REG_PROTO_MIN] == NULL))
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
169
170
171
172
173
174
175
176
177
178
  		return -EINVAL;
  
  	switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) {
  	case NFT_NAT_SNAT:
  		priv->type = NF_NAT_MANIP_SRC;
  		break;
  	case NFT_NAT_DNAT:
  		priv->type = NF_NAT_MANIP_DST;
  		break;
  	default:
0d7c83463   Pablo Neira Ayuso   netfilter: nft_na...
179
  		return -EOPNOTSUPP;
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
180
181
182
183
  	}
  
  	if (tb[NFTA_NAT_FAMILY] == NULL)
  		return -EINVAL;
a4c2e8beb   Patrick McHardy   netfilter: nft_na...
184
  	family = ntohl(nla_get_be32(tb[NFTA_NAT_FAMILY]));
d164385ec   Florian Westphal   netfilter: nat: a...
185
  	if (ctx->family != NFPROTO_INET && ctx->family != family)
a4c2e8beb   Patrick McHardy   netfilter: nft_na...
186
  		return -EOPNOTSUPP;
d07db9884   Patrick McHardy   netfilter: nf_tab...
187
188
189
  
  	switch (family) {
  	case NFPROTO_IPV4:
c593642c8   Pankaj Bharadiya   treewide: Use siz...
190
  		alen = sizeof_field(struct nf_nat_range, min_addr.ip);
d07db9884   Patrick McHardy   netfilter: nf_tab...
191
192
  		break;
  	case NFPROTO_IPV6:
c593642c8   Pankaj Bharadiya   treewide: Use siz...
193
  		alen = sizeof_field(struct nf_nat_range, min_addr.ip6);
d07db9884   Patrick McHardy   netfilter: nf_tab...
194
195
  		break;
  	default:
a33f387ec   Pablo Neira Ayuso   netfilter: nft_na...
196
197
198
  		if (tb[NFTA_NAT_REG_ADDR_MIN])
  			return -EAFNOSUPPORT;
  		break;
d07db9884   Patrick McHardy   netfilter: nf_tab...
199
  	}
a4c2e8beb   Patrick McHardy   netfilter: nft_na...
200
  	priv->family = family;
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
201
202
  
  	if (tb[NFTA_NAT_REG_ADDR_MIN]) {
4f16d25c6   Pablo Neira Ayuso   netfilter: nftabl...
203
204
  		err = nft_parse_register_load(tb[NFTA_NAT_REG_ADDR_MIN],
  					      &priv->sreg_addr_min, alen);
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
205
206
  		if (err < 0)
  			return err;
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
207

61cfac6b4   Pablo Neira Ayuso   netfilter: nft_na...
208
  		if (tb[NFTA_NAT_REG_ADDR_MAX]) {
4f16d25c6   Pablo Neira Ayuso   netfilter: nftabl...
209
210
211
  			err = nft_parse_register_load(tb[NFTA_NAT_REG_ADDR_MAX],
  						      &priv->sreg_addr_max,
  						      alen);
61cfac6b4   Pablo Neira Ayuso   netfilter: nft_na...
212
213
214
215
216
  			if (err < 0)
  				return err;
  		} else {
  			priv->sreg_addr_max = priv->sreg_addr_min;
  		}
4566aa440   Pablo Neira Ayuso   netfilter: nft_na...
217
218
  
  		priv->flags |= NF_NAT_RANGE_MAP_IPS;
61cfac6b4   Pablo Neira Ayuso   netfilter: nft_na...
219
  	}
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
220

c593642c8   Pankaj Bharadiya   treewide: Use siz...
221
  	plen = sizeof_field(struct nf_nat_range, min_addr.all);
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
222
  	if (tb[NFTA_NAT_REG_PROTO_MIN]) {
4f16d25c6   Pablo Neira Ayuso   netfilter: nftabl...
223
224
  		err = nft_parse_register_load(tb[NFTA_NAT_REG_PROTO_MIN],
  					      &priv->sreg_proto_min, plen);
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
225
226
  		if (err < 0)
  			return err;
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
227

61cfac6b4   Pablo Neira Ayuso   netfilter: nft_na...
228
  		if (tb[NFTA_NAT_REG_PROTO_MAX]) {
4f16d25c6   Pablo Neira Ayuso   netfilter: nftabl...
229
230
231
  			err = nft_parse_register_load(tb[NFTA_NAT_REG_PROTO_MAX],
  						      &priv->sreg_proto_max,
  						      plen);
61cfac6b4   Pablo Neira Ayuso   netfilter: nft_na...
232
233
234
235
236
  			if (err < 0)
  				return err;
  		} else {
  			priv->sreg_proto_max = priv->sreg_proto_min;
  		}
4566aa440   Pablo Neira Ayuso   netfilter: nft_na...
237
238
  
  		priv->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
61cfac6b4   Pablo Neira Ayuso   netfilter: nft_na...
239
  	}
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
240

e42eff8a3   Arturo Borrero   netfilter: nft_na...
241
  	if (tb[NFTA_NAT_FLAGS]) {
4566aa440   Pablo Neira Ayuso   netfilter: nft_na...
242
  		priv->flags |= ntohl(nla_get_be32(tb[NFTA_NAT_FLAGS]));
e42eff8a3   Arturo Borrero   netfilter: nft_na...
243
  		if (priv->flags & ~NF_NAT_RANGE_MASK)
0d7c83463   Pablo Neira Ayuso   netfilter: nft_na...
244
  			return -EOPNOTSUPP;
e42eff8a3   Arturo Borrero   netfilter: nft_na...
245
  	}
20afd4239   Florian Westphal   netfilter: nf_tab...
246
  	return nf_ct_netns_get(ctx->net, family);
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  }
  
  static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr)
  {
  	const struct nft_nat *priv = nft_expr_priv(expr);
  
  	switch (priv->type) {
  	case NF_NAT_MANIP_SRC:
  		if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT)))
  			goto nla_put_failure;
  		break;
  	case NF_NAT_MANIP_DST:
  		if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT)))
  			goto nla_put_failure;
  		break;
  	}
  
  	if (nla_put_be32(skb, NFTA_NAT_FAMILY, htonl(priv->family)))
  		goto nla_put_failure;
1e2d56a5d   Pablo Neira Ayuso   netfilter: nft_na...
266
267
  
  	if (priv->sreg_addr_min) {
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
268
269
270
271
  		if (nft_dump_register(skb, NFTA_NAT_REG_ADDR_MIN,
  				      priv->sreg_addr_min) ||
  		    nft_dump_register(skb, NFTA_NAT_REG_ADDR_MAX,
  				      priv->sreg_addr_max))
1e2d56a5d   Pablo Neira Ayuso   netfilter: nft_na...
272
273
  			goto nla_put_failure;
  	}
915136065   Pablo Neira Ayuso   netfilter: nft_na...
274
  	if (priv->sreg_proto_min) {
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
275
276
277
278
  		if (nft_dump_register(skb, NFTA_NAT_REG_PROTO_MIN,
  				      priv->sreg_proto_min) ||
  		    nft_dump_register(skb, NFTA_NAT_REG_PROTO_MAX,
  				      priv->sreg_proto_max))
915136065   Pablo Neira Ayuso   netfilter: nft_na...
279
280
  			goto nla_put_failure;
  	}
e42eff8a3   Arturo Borrero   netfilter: nft_na...
281
282
283
284
285
  
  	if (priv->flags != 0) {
  		if (nla_put_be32(skb, NFTA_NAT_FLAGS, htonl(priv->flags)))
  			goto nla_put_failure;
  	}
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
286
287
288
289
290
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
20afd4239   Florian Westphal   netfilter: nf_tab...
291
292
293
294
295
296
297
  static void
  nft_nat_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
  {
  	const struct nft_nat *priv = nft_expr_priv(expr);
  
  	nf_ct_netns_put(ctx->net, priv->family);
  }
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
298
299
300
301
302
303
  static struct nft_expr_type nft_nat_type;
  static const struct nft_expr_ops nft_nat_ops = {
  	.type           = &nft_nat_type,
  	.size           = NFT_EXPR_SIZE(sizeof(struct nft_nat)),
  	.eval           = nft_nat_eval,
  	.init           = nft_nat_init,
20afd4239   Florian Westphal   netfilter: nf_tab...
304
  	.destroy        = nft_nat_destroy,
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
305
  	.dump           = nft_nat_dump,
7210e4e38   Pablo Neira Ayuso   netfilter: nf_tab...
306
  	.validate	= nft_nat_validate,
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
307
308
309
310
311
312
313
314
315
  };
  
  static struct nft_expr_type nft_nat_type __read_mostly = {
  	.name           = "nat",
  	.ops            = &nft_nat_ops,
  	.policy         = nft_nat_policy,
  	.maxattr        = NFTA_NAT_MAX,
  	.owner          = THIS_MODULE,
  };
d164385ec   Florian Westphal   netfilter: nat: a...
316
317
318
319
320
321
  #ifdef CONFIG_NF_TABLES_INET
  static void nft_nat_inet_eval(const struct nft_expr *expr,
  			      struct nft_regs *regs,
  			      const struct nft_pktinfo *pkt)
  {
  	const struct nft_nat *priv = nft_expr_priv(expr);
bf65364cd   Florian Westphal   netfilter: nat: r...
322
323
  	if (priv->family == nft_pf(pkt) ||
  	    priv->family == NFPROTO_INET)
d164385ec   Florian Westphal   netfilter: nat: a...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
  		nft_nat_eval(expr, regs, pkt);
  }
  
  static const struct nft_expr_ops nft_nat_inet_ops = {
  	.type           = &nft_nat_type,
  	.size           = NFT_EXPR_SIZE(sizeof(struct nft_nat)),
  	.eval           = nft_nat_inet_eval,
  	.init           = nft_nat_init,
  	.destroy        = nft_nat_destroy,
  	.dump           = nft_nat_dump,
  	.validate	= nft_nat_validate,
  };
  
  static struct nft_expr_type nft_inet_nat_type __read_mostly = {
  	.name           = "nat",
  	.family		= NFPROTO_INET,
  	.ops            = &nft_nat_inet_ops,
  	.policy         = nft_nat_policy,
  	.maxattr        = NFTA_NAT_MAX,
  	.owner          = THIS_MODULE,
  };
  
  static int nft_nat_inet_module_init(void)
  {
  	return nft_register_expr(&nft_inet_nat_type);
  }
  
  static void nft_nat_inet_module_exit(void)
  {
  	nft_unregister_expr(&nft_inet_nat_type);
  }
  #else
  static int nft_nat_inet_module_init(void) { return 0; }
  static void nft_nat_inet_module_exit(void) { }
  #endif
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
359
360
  static int __init nft_nat_module_init(void)
  {
d164385ec   Florian Westphal   netfilter: nat: a...
361
362
363
364
365
366
367
368
369
370
  	int ret = nft_nat_inet_module_init();
  
  	if (ret)
  		return ret;
  
  	ret = nft_register_expr(&nft_nat_type);
  	if (ret)
  		nft_nat_inet_module_exit();
  
  	return ret;
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
371
372
373
374
  }
  
  static void __exit nft_nat_module_exit(void)
  {
d164385ec   Florian Westphal   netfilter: nat: a...
375
  	nft_nat_inet_module_exit();
eb31628e3   Tomasz Bursztyka   netfilter: nf_tab...
376
377
378
379
380
381
382
383
384
  	nft_unregister_expr(&nft_nat_type);
  }
  
  module_init(nft_nat_module_init);
  module_exit(nft_nat_module_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>");
  MODULE_ALIAS_NFT_EXPR("nat");
4cacc3951   Rob Gill   netfilter: Add MO...
385
  MODULE_DESCRIPTION("Network Address Translation support");