1/*
2 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
3 * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
4 * Copyright (c) 2012 Intel Corporation
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 */
11
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/skbuff.h>
15#include <linux/ip.h>
16#include <linux/string.h>
17#include <linux/netlink.h>
18#include <linux/netfilter.h>
19#include <linux/netfilter_ipv4.h>
20#include <linux/netfilter/nfnetlink.h>
21#include <linux/netfilter/nf_tables.h>
22#include <net/netfilter/nf_conntrack.h>
23#include <net/netfilter/nf_nat.h>
24#include <net/netfilter/nf_nat_core.h>
25#include <net/netfilter/nf_tables.h>
26#include <net/netfilter/nf_nat_l3proto.h>
27#include <net/ip.h>
28
29struct nft_nat {
30	enum nft_registers      sreg_addr_min:8;
31	enum nft_registers      sreg_addr_max:8;
32	enum nft_registers      sreg_proto_min:8;
33	enum nft_registers      sreg_proto_max:8;
34	enum nf_nat_manip_type  type:8;
35	u8			family;
36	u16			flags;
37};
38
39static void nft_nat_eval(const struct nft_expr *expr,
40			 struct nft_regs *regs,
41			 const struct nft_pktinfo *pkt)
42{
43	const struct nft_nat *priv = nft_expr_priv(expr);
44	enum ip_conntrack_info ctinfo;
45	struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo);
46	struct nf_nat_range range;
47
48	memset(&range, 0, sizeof(range));
49	if (priv->sreg_addr_min) {
50		if (priv->family == AF_INET) {
51			range.min_addr.ip = (__force __be32)
52					regs->data[priv->sreg_addr_min];
53			range.max_addr.ip = (__force __be32)
54					regs->data[priv->sreg_addr_max];
55
56		} else {
57			memcpy(range.min_addr.ip6,
58			       &regs->data[priv->sreg_addr_min],
59			       sizeof(range.min_addr.ip6));
60			memcpy(range.max_addr.ip6,
61			       &regs->data[priv->sreg_addr_max],
62			       sizeof(range.max_addr.ip6));
63		}
64		range.flags |= NF_NAT_RANGE_MAP_IPS;
65	}
66
67	if (priv->sreg_proto_min) {
68		range.min_proto.all =
69			*(__be16 *)&regs->data[priv->sreg_proto_min];
70		range.max_proto.all =
71			*(__be16 *)&regs->data[priv->sreg_proto_max];
72		range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
73	}
74
75	range.flags |= priv->flags;
76
77	regs->verdict.code = nf_nat_setup_info(ct, &range, priv->type);
78}
79
80static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = {
81	[NFTA_NAT_TYPE]		 = { .type = NLA_U32 },
82	[NFTA_NAT_FAMILY]	 = { .type = NLA_U32 },
83	[NFTA_NAT_REG_ADDR_MIN]	 = { .type = NLA_U32 },
84	[NFTA_NAT_REG_ADDR_MAX]	 = { .type = NLA_U32 },
85	[NFTA_NAT_REG_PROTO_MIN] = { .type = NLA_U32 },
86	[NFTA_NAT_REG_PROTO_MAX] = { .type = NLA_U32 },
87	[NFTA_NAT_FLAGS]	 = { .type = NLA_U32 },
88};
89
90static int nft_nat_validate(const struct nft_ctx *ctx,
91			    const struct nft_expr *expr,
92			    const struct nft_data **data)
93{
94	struct nft_nat *priv = nft_expr_priv(expr);
95	int err;
96
97	err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
98	if (err < 0)
99		return err;
100
101	switch (priv->type) {
102	case NFT_NAT_SNAT:
103		err = nft_chain_validate_hooks(ctx->chain,
104					       (1 << NF_INET_POST_ROUTING) |
105					       (1 << NF_INET_LOCAL_IN));
106		break;
107	case NFT_NAT_DNAT:
108		err = nft_chain_validate_hooks(ctx->chain,
109					       (1 << NF_INET_PRE_ROUTING) |
110					       (1 << NF_INET_LOCAL_OUT));
111		break;
112	}
113
114	return err;
115}
116
117static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
118			const struct nlattr * const tb[])
119{
120	struct nft_nat *priv = nft_expr_priv(expr);
121	unsigned int alen, plen;
122	u32 family;
123	int err;
124
125	if (tb[NFTA_NAT_TYPE] == NULL ||
126	    (tb[NFTA_NAT_REG_ADDR_MIN] == NULL &&
127	     tb[NFTA_NAT_REG_PROTO_MIN] == NULL))
128		return -EINVAL;
129
130	switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) {
131	case NFT_NAT_SNAT:
132		priv->type = NF_NAT_MANIP_SRC;
133		break;
134	case NFT_NAT_DNAT:
135		priv->type = NF_NAT_MANIP_DST;
136		break;
137	default:
138		return -EINVAL;
139	}
140
141	err = nft_nat_validate(ctx, expr, NULL);
142	if (err < 0)
143		return err;
144
145	if (tb[NFTA_NAT_FAMILY] == NULL)
146		return -EINVAL;
147
148	family = ntohl(nla_get_be32(tb[NFTA_NAT_FAMILY]));
149	if (family != ctx->afi->family)
150		return -EOPNOTSUPP;
151
152	switch (family) {
153	case NFPROTO_IPV4:
154		alen = FIELD_SIZEOF(struct nf_nat_range, min_addr.ip);
155		break;
156	case NFPROTO_IPV6:
157		alen = FIELD_SIZEOF(struct nf_nat_range, min_addr.ip6);
158		break;
159	default:
160		return -EAFNOSUPPORT;
161	}
162	priv->family = family;
163
164	if (tb[NFTA_NAT_REG_ADDR_MIN]) {
165		priv->sreg_addr_min =
166			nft_parse_register(tb[NFTA_NAT_REG_ADDR_MIN]);
167		err = nft_validate_register_load(priv->sreg_addr_min, alen);
168		if (err < 0)
169			return err;
170
171		if (tb[NFTA_NAT_REG_ADDR_MAX]) {
172			priv->sreg_addr_max =
173				nft_parse_register(tb[NFTA_NAT_REG_ADDR_MAX]);
174
175			err = nft_validate_register_load(priv->sreg_addr_max,
176							 alen);
177			if (err < 0)
178				return err;
179		} else {
180			priv->sreg_addr_max = priv->sreg_addr_min;
181		}
182	}
183
184	plen = FIELD_SIZEOF(struct nf_nat_range, min_addr.all);
185	if (tb[NFTA_NAT_REG_PROTO_MIN]) {
186		priv->sreg_proto_min =
187			nft_parse_register(tb[NFTA_NAT_REG_PROTO_MIN]);
188
189		err = nft_validate_register_load(priv->sreg_proto_min, plen);
190		if (err < 0)
191			return err;
192
193		if (tb[NFTA_NAT_REG_PROTO_MAX]) {
194			priv->sreg_proto_max =
195				nft_parse_register(tb[NFTA_NAT_REG_PROTO_MAX]);
196
197			err = nft_validate_register_load(priv->sreg_proto_max,
198							 plen);
199			if (err < 0)
200				return err;
201		} else {
202			priv->sreg_proto_max = priv->sreg_proto_min;
203		}
204	}
205
206	if (tb[NFTA_NAT_FLAGS]) {
207		priv->flags = ntohl(nla_get_be32(tb[NFTA_NAT_FLAGS]));
208		if (priv->flags & ~NF_NAT_RANGE_MASK)
209			return -EINVAL;
210	}
211
212	return 0;
213}
214
215static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr)
216{
217	const struct nft_nat *priv = nft_expr_priv(expr);
218
219	switch (priv->type) {
220	case NF_NAT_MANIP_SRC:
221		if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT)))
222			goto nla_put_failure;
223		break;
224	case NF_NAT_MANIP_DST:
225		if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT)))
226			goto nla_put_failure;
227		break;
228	}
229
230	if (nla_put_be32(skb, NFTA_NAT_FAMILY, htonl(priv->family)))
231		goto nla_put_failure;
232
233	if (priv->sreg_addr_min) {
234		if (nft_dump_register(skb, NFTA_NAT_REG_ADDR_MIN,
235				      priv->sreg_addr_min) ||
236		    nft_dump_register(skb, NFTA_NAT_REG_ADDR_MAX,
237				      priv->sreg_addr_max))
238			goto nla_put_failure;
239	}
240
241	if (priv->sreg_proto_min) {
242		if (nft_dump_register(skb, NFTA_NAT_REG_PROTO_MIN,
243				      priv->sreg_proto_min) ||
244		    nft_dump_register(skb, NFTA_NAT_REG_PROTO_MAX,
245				      priv->sreg_proto_max))
246			goto nla_put_failure;
247	}
248
249	if (priv->flags != 0) {
250		if (nla_put_be32(skb, NFTA_NAT_FLAGS, htonl(priv->flags)))
251			goto nla_put_failure;
252	}
253
254	return 0;
255
256nla_put_failure:
257	return -1;
258}
259
260static struct nft_expr_type nft_nat_type;
261static const struct nft_expr_ops nft_nat_ops = {
262	.type           = &nft_nat_type,
263	.size           = NFT_EXPR_SIZE(sizeof(struct nft_nat)),
264	.eval           = nft_nat_eval,
265	.init           = nft_nat_init,
266	.dump           = nft_nat_dump,
267	.validate	= nft_nat_validate,
268};
269
270static struct nft_expr_type nft_nat_type __read_mostly = {
271	.name           = "nat",
272	.ops            = &nft_nat_ops,
273	.policy         = nft_nat_policy,
274	.maxattr        = NFTA_NAT_MAX,
275	.owner          = THIS_MODULE,
276};
277
278static int __init nft_nat_module_init(void)
279{
280	return nft_register_expr(&nft_nat_type);
281}
282
283static void __exit nft_nat_module_exit(void)
284{
285	nft_unregister_expr(&nft_nat_type);
286}
287
288module_init(nft_nat_module_init);
289module_exit(nft_nat_module_exit);
290
291MODULE_LICENSE("GPL");
292MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>");
293MODULE_ALIAS_NFT_EXPR("nat");
294