diff options
author | Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com> | 2013-10-10 07:39:19 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-10-14 12:00:58 -0400 |
commit | eb31628e37a0a4e01fffd79dcc7f815d2357f53a (patch) | |
tree | 8f78d641ddc8817109b55cedb114e4a4a8d9c045 | |
parent | 9ddf63235749a9efa1fad2eeb74be2ee9b580f8d (diff) |
netfilter: nf_tables: Add support for IPv6 NAT
This patch generalizes the NAT expression to support both IPv4 and IPv6
using the existing IPv4/IPv6 NAT infrastructure. This also adds the
NAT chain type for IPv6.
This patch collapses the following patches that were posted to the
netfilter-devel mailing list, from Tomasz:
* nf_tables: Change NFTA_NAT_ attributes to better semantic significance
* nf_tables: Split IPv4 NAT into NAT expression and IPv4 NAT chain
* nf_tables: Add support for IPv6 NAT expression
* nf_tables: Add support for IPv6 NAT chain
* nf_tables: Fix up build issue on IPv6 NAT support
And, from Pablo Neira Ayuso:
* fix missing dependencies in nft_chain_nat
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | include/uapi/linux/netfilter/nf_tables.h | 18 | ||||
-rw-r--r-- | net/ipv4/netfilter/Kconfig | 1 | ||||
-rw-r--r-- | net/ipv4/netfilter/nft_chain_nat_ipv4.c | 156 | ||||
-rw-r--r-- | net/ipv6/netfilter/Kconfig | 5 | ||||
-rw-r--r-- | net/ipv6/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/ipv6/netfilter/nft_chain_nat_ipv6.c | 211 | ||||
-rw-r--r-- | net/netfilter/Kconfig | 6 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/nft_nat.c | 220 |
9 files changed, 457 insertions, 162 deletions
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index a9c4bce1988f..7d4a1992f89c 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h | |||
@@ -695,18 +695,20 @@ enum nft_nat_types { | |||
695 | * enum nft_nat_attributes - nf_tables nat expression netlink attributes | 695 | * enum nft_nat_attributes - nf_tables nat expression netlink attributes |
696 | * | 696 | * |
697 | * @NFTA_NAT_TYPE: NAT type (NLA_U32: nft_nat_types) | 697 | * @NFTA_NAT_TYPE: NAT type (NLA_U32: nft_nat_types) |
698 | * @NFTA_NAT_ADDR_MIN: source register of address range start (NLA_U32: nft_registers) | 698 | * @NFTA_NAT_FAMILY: NAT family (NLA_U32) |
699 | * @NFTA_NAT_ADDR_MAX: source register of address range end (NLA_U32: nft_registers) | 699 | * @NFTA_NAT_REG_ADDR_MIN: source register of address range start (NLA_U32: nft_registers) |
700 | * @NFTA_NAT_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers) | 700 | * @NFTA_NAT_REG_ADDR_MAX: source register of address range end (NLA_U32: nft_registers) |
701 | * @NFTA_NAT_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers) | 701 | * @NFTA_NAT_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers) |
702 | * @NFTA_NAT_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers) | ||
702 | */ | 703 | */ |
703 | enum nft_nat_attributes { | 704 | enum nft_nat_attributes { |
704 | NFTA_NAT_UNSPEC, | 705 | NFTA_NAT_UNSPEC, |
705 | NFTA_NAT_TYPE, | 706 | NFTA_NAT_TYPE, |
706 | NFTA_NAT_ADDR_MIN, | 707 | NFTA_NAT_FAMILY, |
707 | NFTA_NAT_ADDR_MAX, | 708 | NFTA_NAT_REG_ADDR_MIN, |
708 | NFTA_NAT_PROTO_MIN, | 709 | NFTA_NAT_REG_ADDR_MAX, |
709 | NFTA_NAT_PROTO_MAX, | 710 | NFTA_NAT_REG_PROTO_MIN, |
711 | NFTA_NAT_REG_PROTO_MAX, | ||
710 | __NFTA_NAT_MAX | 712 | __NFTA_NAT_MAX |
711 | }; | 713 | }; |
712 | #define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1) | 714 | #define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1) |
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index ae65fe98bfbe..1f37ef67f1ac 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig | |||
@@ -50,6 +50,7 @@ config NFT_CHAIN_ROUTE_IPV4 | |||
50 | 50 | ||
51 | config NFT_CHAIN_NAT_IPV4 | 51 | config NFT_CHAIN_NAT_IPV4 |
52 | depends on NF_TABLES_IPV4 | 52 | depends on NF_TABLES_IPV4 |
53 | depends on NF_NAT_IPV4 && NFT_NAT | ||
53 | tristate "IPv4 nf_tables nat chain support" | 54 | tristate "IPv4 nf_tables nat chain support" |
54 | 55 | ||
55 | config IP_NF_IPTABLES | 56 | config IP_NF_IPTABLES |
diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index e09c201adf84..cf2c792cd971 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c | |||
@@ -1,6 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> | 2 | * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> |
3 | * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org> | 3 | * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org> |
4 | * Copyright (c) 2012 Intel Corporation | ||
4 | * | 5 | * |
5 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 7 | * it under the terms of the GNU General Public License version 2 as |
@@ -14,10 +15,8 @@ | |||
14 | #include <linux/list.h> | 15 | #include <linux/list.h> |
15 | #include <linux/skbuff.h> | 16 | #include <linux/skbuff.h> |
16 | #include <linux/ip.h> | 17 | #include <linux/ip.h> |
17 | #include <linux/netlink.h> | ||
18 | #include <linux/netfilter.h> | 18 | #include <linux/netfilter.h> |
19 | #include <linux/netfilter_ipv4.h> | 19 | #include <linux/netfilter_ipv4.h> |
20 | #include <linux/netfilter/nfnetlink.h> | ||
21 | #include <linux/netfilter/nf_tables.h> | 20 | #include <linux/netfilter/nf_tables.h> |
22 | #include <net/netfilter/nf_conntrack.h> | 21 | #include <net/netfilter/nf_conntrack.h> |
23 | #include <net/netfilter/nf_nat.h> | 22 | #include <net/netfilter/nf_nat.h> |
@@ -27,147 +26,6 @@ | |||
27 | #include <net/netfilter/nf_nat_l3proto.h> | 26 | #include <net/netfilter/nf_nat_l3proto.h> |
28 | #include <net/ip.h> | 27 | #include <net/ip.h> |
29 | 28 | ||
30 | struct nft_nat { | ||
31 | enum nft_registers sreg_addr_min:8; | ||
32 | enum nft_registers sreg_addr_max:8; | ||
33 | enum nft_registers sreg_proto_min:8; | ||
34 | enum nft_registers sreg_proto_max:8; | ||
35 | enum nf_nat_manip_type type; | ||
36 | }; | ||
37 | |||
38 | static void nft_nat_eval(const struct nft_expr *expr, | ||
39 | struct nft_data data[NFT_REG_MAX + 1], | ||
40 | const struct nft_pktinfo *pkt) | ||
41 | { | ||
42 | const struct nft_nat *priv = nft_expr_priv(expr); | ||
43 | enum ip_conntrack_info ctinfo; | ||
44 | struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo); | ||
45 | struct nf_nat_range range; | ||
46 | |||
47 | memset(&range, 0, sizeof(range)); | ||
48 | if (priv->sreg_addr_min) { | ||
49 | range.min_addr.ip = data[priv->sreg_addr_min].data[0]; | ||
50 | range.max_addr.ip = data[priv->sreg_addr_max].data[0]; | ||
51 | range.flags |= NF_NAT_RANGE_MAP_IPS; | ||
52 | } | ||
53 | |||
54 | if (priv->sreg_proto_min) { | ||
55 | range.min_proto.all = data[priv->sreg_proto_min].data[0]; | ||
56 | range.max_proto.all = data[priv->sreg_proto_max].data[0]; | ||
57 | range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; | ||
58 | } | ||
59 | |||
60 | data[NFT_REG_VERDICT].verdict = | ||
61 | nf_nat_setup_info(ct, &range, priv->type); | ||
62 | } | ||
63 | |||
64 | static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = { | ||
65 | [NFTA_NAT_ADDR_MIN] = { .type = NLA_U32 }, | ||
66 | [NFTA_NAT_ADDR_MAX] = { .type = NLA_U32 }, | ||
67 | [NFTA_NAT_PROTO_MIN] = { .type = NLA_U32 }, | ||
68 | [NFTA_NAT_PROTO_MAX] = { .type = NLA_U32 }, | ||
69 | [NFTA_NAT_TYPE] = { .type = NLA_U32 }, | ||
70 | }; | ||
71 | |||
72 | static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | ||
73 | const struct nlattr * const tb[]) | ||
74 | { | ||
75 | struct nft_nat *priv = nft_expr_priv(expr); | ||
76 | int err; | ||
77 | |||
78 | if (tb[NFTA_NAT_TYPE] == NULL) | ||
79 | return -EINVAL; | ||
80 | |||
81 | switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) { | ||
82 | case NFT_NAT_SNAT: | ||
83 | priv->type = NF_NAT_MANIP_SRC; | ||
84 | break; | ||
85 | case NFT_NAT_DNAT: | ||
86 | priv->type = NF_NAT_MANIP_DST; | ||
87 | break; | ||
88 | default: | ||
89 | return -EINVAL; | ||
90 | } | ||
91 | |||
92 | if (tb[NFTA_NAT_ADDR_MIN]) { | ||
93 | priv->sreg_addr_min = ntohl(nla_get_be32(tb[NFTA_NAT_ADDR_MIN])); | ||
94 | err = nft_validate_input_register(priv->sreg_addr_min); | ||
95 | if (err < 0) | ||
96 | return err; | ||
97 | } | ||
98 | |||
99 | if (tb[NFTA_NAT_ADDR_MAX]) { | ||
100 | priv->sreg_addr_max = ntohl(nla_get_be32(tb[NFTA_NAT_ADDR_MAX])); | ||
101 | err = nft_validate_input_register(priv->sreg_addr_max); | ||
102 | if (err < 0) | ||
103 | return err; | ||
104 | } else | ||
105 | priv->sreg_addr_max = priv->sreg_addr_min; | ||
106 | |||
107 | if (tb[NFTA_NAT_PROTO_MIN]) { | ||
108 | priv->sreg_proto_min = ntohl(nla_get_be32(tb[NFTA_NAT_PROTO_MIN])); | ||
109 | err = nft_validate_input_register(priv->sreg_proto_min); | ||
110 | if (err < 0) | ||
111 | return err; | ||
112 | } | ||
113 | |||
114 | if (tb[NFTA_NAT_PROTO_MAX]) { | ||
115 | priv->sreg_proto_max = ntohl(nla_get_be32(tb[NFTA_NAT_PROTO_MAX])); | ||
116 | err = nft_validate_input_register(priv->sreg_proto_max); | ||
117 | if (err < 0) | ||
118 | return err; | ||
119 | } else | ||
120 | priv->sreg_proto_max = priv->sreg_proto_min; | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr) | ||
126 | { | ||
127 | const struct nft_nat *priv = nft_expr_priv(expr); | ||
128 | |||
129 | switch (priv->type) { | ||
130 | case NF_NAT_MANIP_SRC: | ||
131 | if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT))) | ||
132 | goto nla_put_failure; | ||
133 | break; | ||
134 | case NF_NAT_MANIP_DST: | ||
135 | if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT))) | ||
136 | goto nla_put_failure; | ||
137 | break; | ||
138 | } | ||
139 | |||
140 | if (nla_put_be32(skb, NFTA_NAT_ADDR_MIN, htonl(priv->sreg_addr_min))) | ||
141 | goto nla_put_failure; | ||
142 | if (nla_put_be32(skb, NFTA_NAT_ADDR_MAX, htonl(priv->sreg_addr_max))) | ||
143 | goto nla_put_failure; | ||
144 | if (nla_put_be32(skb, NFTA_NAT_PROTO_MIN, htonl(priv->sreg_proto_min))) | ||
145 | goto nla_put_failure; | ||
146 | if (nla_put_be32(skb, NFTA_NAT_PROTO_MAX, htonl(priv->sreg_proto_max))) | ||
147 | goto nla_put_failure; | ||
148 | return 0; | ||
149 | |||
150 | nla_put_failure: | ||
151 | return -1; | ||
152 | } | ||
153 | |||
154 | static struct nft_expr_type nft_nat_type; | ||
155 | static const struct nft_expr_ops nft_nat_ops = { | ||
156 | .type = &nft_nat_type, | ||
157 | .size = NFT_EXPR_SIZE(sizeof(struct nft_nat)), | ||
158 | .eval = nft_nat_eval, | ||
159 | .init = nft_nat_init, | ||
160 | .dump = nft_nat_dump, | ||
161 | }; | ||
162 | |||
163 | static struct nft_expr_type nft_nat_type __read_mostly = { | ||
164 | .name = "nat", | ||
165 | .ops = &nft_nat_ops, | ||
166 | .policy = nft_nat_policy, | ||
167 | .maxattr = NFTA_NAT_MAX, | ||
168 | .owner = THIS_MODULE, | ||
169 | }; | ||
170 | |||
171 | /* | 29 | /* |
172 | * NAT chains | 30 | * NAT chains |
173 | */ | 31 | */ |
@@ -306,7 +164,7 @@ static unsigned int nf_nat_output(const struct nf_hook_ops *ops, | |||
306 | return ret; | 164 | return ret; |
307 | } | 165 | } |
308 | 166 | ||
309 | struct nf_chain_type nft_chain_nat_ipv4 = { | 167 | static struct nf_chain_type nft_chain_nat_ipv4 = { |
310 | .family = NFPROTO_IPV4, | 168 | .family = NFPROTO_IPV4, |
311 | .name = "nat", | 169 | .name = "nat", |
312 | .type = NFT_CHAIN_T_NAT, | 170 | .type = NFT_CHAIN_T_NAT, |
@@ -331,20 +189,11 @@ static int __init nft_chain_nat_init(void) | |||
331 | if (err < 0) | 189 | if (err < 0) |
332 | return err; | 190 | return err; |
333 | 191 | ||
334 | err = nft_register_expr(&nft_nat_type); | ||
335 | if (err < 0) | ||
336 | goto err; | ||
337 | |||
338 | return 0; | 192 | return 0; |
339 | |||
340 | err: | ||
341 | nft_unregister_chain_type(&nft_chain_nat_ipv4); | ||
342 | return err; | ||
343 | } | 193 | } |
344 | 194 | ||
345 | static void __exit nft_chain_nat_exit(void) | 195 | static void __exit nft_chain_nat_exit(void) |
346 | { | 196 | { |
347 | nft_unregister_expr(&nft_nat_type); | ||
348 | nft_unregister_chain_type(&nft_chain_nat_ipv4); | 197 | nft_unregister_chain_type(&nft_chain_nat_ipv4); |
349 | } | 198 | } |
350 | 199 | ||
@@ -354,4 +203,3 @@ module_exit(nft_chain_nat_exit); | |||
354 | MODULE_LICENSE("GPL"); | 203 | MODULE_LICENSE("GPL"); |
355 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | 204 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); |
356 | MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat"); | 205 | MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat"); |
357 | MODULE_ALIAS_NFT_EXPR("nat"); | ||
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 23833064b7b5..7702f9e90a04 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig | |||
@@ -33,6 +33,11 @@ config NFT_CHAIN_ROUTE_IPV6 | |||
33 | depends on NF_TABLES_IPV6 | 33 | depends on NF_TABLES_IPV6 |
34 | tristate "IPv6 nf_tables route chain support" | 34 | tristate "IPv6 nf_tables route chain support" |
35 | 35 | ||
36 | config NFT_CHAIN_NAT_IPV6 | ||
37 | depends on NF_TABLES_IPV6 | ||
38 | depends on NF_NAT_IPV6 && NFT_NAT | ||
39 | tristate "IPv6 nf_tables nat chain support" | ||
40 | |||
36 | config IP6_NF_IPTABLES | 41 | config IP6_NF_IPTABLES |
37 | tristate "IP6 tables support (required for filtering)" | 42 | tristate "IP6 tables support (required for filtering)" |
38 | depends on INET && IPV6 | 43 | depends on INET && IPV6 |
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index be4913aa524d..d1b4928f34f7 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile | |||
@@ -26,6 +26,7 @@ obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o | |||
26 | # nf_tables | 26 | # nf_tables |
27 | obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o | 27 | obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o |
28 | obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o | 28 | obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o |
29 | obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o | ||
29 | 30 | ||
30 | # matches | 31 | # matches |
31 | obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o | 32 | obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o |
diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c new file mode 100644 index 000000000000..e86dcd70dc76 --- /dev/null +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Patrick McHardy <kaber@trash.net> | ||
3 | * Copyright (c) 2012 Intel Corporation | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/list.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/ip.h> | ||
16 | #include <linux/netfilter.h> | ||
17 | #include <linux/netfilter_ipv6.h> | ||
18 | #include <linux/netfilter/nf_tables.h> | ||
19 | #include <net/netfilter/nf_conntrack.h> | ||
20 | #include <net/netfilter/nf_nat.h> | ||
21 | #include <net/netfilter/nf_nat_core.h> | ||
22 | #include <net/netfilter/nf_tables.h> | ||
23 | #include <net/netfilter/nf_tables_ipv6.h> | ||
24 | #include <net/netfilter/nf_nat_l3proto.h> | ||
25 | #include <net/ipv6.h> | ||
26 | |||
27 | /* | ||
28 | * IPv6 NAT chains | ||
29 | */ | ||
30 | |||
31 | static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, | ||
32 | struct sk_buff *skb, | ||
33 | const struct net_device *in, | ||
34 | const struct net_device *out, | ||
35 | int (*okfn)(struct sk_buff *)) | ||
36 | { | ||
37 | enum ip_conntrack_info ctinfo; | ||
38 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
39 | struct nf_conn_nat *nat; | ||
40 | enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); | ||
41 | __be16 frag_off; | ||
42 | int hdrlen; | ||
43 | u8 nexthdr; | ||
44 | struct nft_pktinfo pkt; | ||
45 | unsigned int ret; | ||
46 | |||
47 | if (ct == NULL || nf_ct_is_untracked(ct)) | ||
48 | return NF_ACCEPT; | ||
49 | |||
50 | nat = nfct_nat(ct); | ||
51 | if (nat == NULL) { | ||
52 | /* Conntrack module was loaded late, can't add extension. */ | ||
53 | if (nf_ct_is_confirmed(ct)) | ||
54 | return NF_ACCEPT; | ||
55 | nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | ||
56 | if (nat == NULL) | ||
57 | return NF_ACCEPT; | ||
58 | } | ||
59 | |||
60 | switch (ctinfo) { | ||
61 | case IP_CT_RELATED: | ||
62 | case IP_CT_RELATED + IP_CT_IS_REPLY: | ||
63 | nexthdr = ipv6_hdr(skb)->nexthdr; | ||
64 | hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), | ||
65 | &nexthdr, &frag_off); | ||
66 | |||
67 | if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { | ||
68 | if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo, | ||
69 | ops->hooknum, | ||
70 | hdrlen)) | ||
71 | return NF_DROP; | ||
72 | else | ||
73 | return NF_ACCEPT; | ||
74 | } | ||
75 | /* Fall through */ | ||
76 | case IP_CT_NEW: | ||
77 | if (nf_nat_initialized(ct, maniptype)) | ||
78 | break; | ||
79 | |||
80 | nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out); | ||
81 | |||
82 | ret = nft_do_chain_pktinfo(&pkt, ops); | ||
83 | if (ret != NF_ACCEPT) | ||
84 | return ret; | ||
85 | if (!nf_nat_initialized(ct, maniptype)) { | ||
86 | ret = nf_nat_alloc_null_binding(ct, ops->hooknum); | ||
87 | if (ret != NF_ACCEPT) | ||
88 | return ret; | ||
89 | } | ||
90 | default: | ||
91 | break; | ||
92 | } | ||
93 | |||
94 | return nf_nat_packet(ct, ctinfo, ops->hooknum, skb); | ||
95 | } | ||
96 | |||
97 | static unsigned int nf_nat_ipv6_prerouting(const struct nf_hook_ops *ops, | ||
98 | struct sk_buff *skb, | ||
99 | const struct net_device *in, | ||
100 | const struct net_device *out, | ||
101 | int (*okfn)(struct sk_buff *)) | ||
102 | { | ||
103 | struct in6_addr daddr = ipv6_hdr(skb)->daddr; | ||
104 | unsigned int ret; | ||
105 | |||
106 | ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); | ||
107 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
108 | ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr)) | ||
109 | skb_dst_drop(skb); | ||
110 | |||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | static unsigned int nf_nat_ipv6_postrouting(const struct nf_hook_ops *ops, | ||
115 | struct sk_buff *skb, | ||
116 | const struct net_device *in, | ||
117 | const struct net_device *out, | ||
118 | int (*okfn)(struct sk_buff *)) | ||
119 | { | ||
120 | enum ip_conntrack_info ctinfo __maybe_unused; | ||
121 | const struct nf_conn *ct __maybe_unused; | ||
122 | unsigned int ret; | ||
123 | |||
124 | ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); | ||
125 | #ifdef CONFIG_XFRM | ||
126 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
127 | !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && | ||
128 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
129 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
130 | |||
131 | if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, | ||
132 | &ct->tuplehash[!dir].tuple.dst.u3) || | ||
133 | (ct->tuplehash[dir].tuple.src.u.all != | ||
134 | ct->tuplehash[!dir].tuple.dst.u.all)) | ||
135 | if (nf_xfrm_me_harder(skb, AF_INET6) < 0) | ||
136 | ret = NF_DROP; | ||
137 | } | ||
138 | #endif | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | static unsigned int nf_nat_ipv6_output(const struct nf_hook_ops *ops, | ||
143 | struct sk_buff *skb, | ||
144 | const struct net_device *in, | ||
145 | const struct net_device *out, | ||
146 | int (*okfn)(struct sk_buff *)) | ||
147 | { | ||
148 | enum ip_conntrack_info ctinfo; | ||
149 | const struct nf_conn *ct; | ||
150 | unsigned int ret; | ||
151 | |||
152 | ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); | ||
153 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
154 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
155 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
156 | |||
157 | if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, | ||
158 | &ct->tuplehash[!dir].tuple.src.u3)) { | ||
159 | if (ip6_route_me_harder(skb)) | ||
160 | ret = NF_DROP; | ||
161 | } | ||
162 | #ifdef CONFIG_XFRM | ||
163 | else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && | ||
164 | ct->tuplehash[dir].tuple.dst.u.all != | ||
165 | ct->tuplehash[!dir].tuple.src.u.all) | ||
166 | if (nf_xfrm_me_harder(skb, AF_INET6)) | ||
167 | ret = NF_DROP; | ||
168 | #endif | ||
169 | } | ||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | static struct nf_chain_type nft_chain_nat_ipv6 = { | ||
174 | .family = NFPROTO_IPV6, | ||
175 | .name = "nat", | ||
176 | .type = NFT_CHAIN_T_NAT, | ||
177 | .hook_mask = (1 << NF_INET_PRE_ROUTING) | | ||
178 | (1 << NF_INET_POST_ROUTING) | | ||
179 | (1 << NF_INET_LOCAL_OUT) | | ||
180 | (1 << NF_INET_LOCAL_IN), | ||
181 | .fn = { | ||
182 | [NF_INET_PRE_ROUTING] = nf_nat_ipv6_prerouting, | ||
183 | [NF_INET_POST_ROUTING] = nf_nat_ipv6_postrouting, | ||
184 | [NF_INET_LOCAL_OUT] = nf_nat_ipv6_output, | ||
185 | [NF_INET_LOCAL_IN] = nf_nat_ipv6_fn, | ||
186 | }, | ||
187 | .me = THIS_MODULE, | ||
188 | }; | ||
189 | |||
190 | static int __init nft_chain_nat_ipv6_init(void) | ||
191 | { | ||
192 | int err; | ||
193 | |||
194 | err = nft_register_chain_type(&nft_chain_nat_ipv6); | ||
195 | if (err < 0) | ||
196 | return err; | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static void __exit nft_chain_nat_ipv6_exit(void) | ||
202 | { | ||
203 | nft_unregister_chain_type(&nft_chain_nat_ipv6); | ||
204 | } | ||
205 | |||
206 | module_init(nft_chain_nat_ipv6_init); | ||
207 | module_exit(nft_chain_nat_ipv6_exit); | ||
208 | |||
209 | MODULE_LICENSE("GPL"); | ||
210 | MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>"); | ||
211 | MODULE_ALIAS_NFT_CHAIN(AF_INET6, "nat"); | ||
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 49e362707379..48acec17e27a 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -450,6 +450,12 @@ config NFT_LIMIT | |||
450 | depends on NF_TABLES | 450 | depends on NF_TABLES |
451 | tristate "Netfilter nf_tables limit module" | 451 | tristate "Netfilter nf_tables limit module" |
452 | 452 | ||
453 | config NFT_NAT | ||
454 | depends on NF_TABLES | ||
455 | depends on NF_CONNTRACK | ||
456 | depends on NF_NAT | ||
457 | tristate "Netfilter nf_tables nat module" | ||
458 | |||
453 | config NFT_COMPAT | 459 | config NFT_COMPAT |
454 | depends on NF_TABLES | 460 | depends on NF_TABLES |
455 | depends on NETFILTER_XTABLES | 461 | depends on NETFILTER_XTABLES |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index a6781450b6fb..394483b2c193 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -75,6 +75,7 @@ obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o | |||
75 | obj-$(CONFIG_NFT_META) += nft_meta.o | 75 | obj-$(CONFIG_NFT_META) += nft_meta.o |
76 | obj-$(CONFIG_NFT_CT) += nft_ct.o | 76 | obj-$(CONFIG_NFT_CT) += nft_ct.o |
77 | obj-$(CONFIG_NFT_LIMIT) += nft_limit.o | 77 | obj-$(CONFIG_NFT_LIMIT) += nft_limit.o |
78 | obj-$(CONFIG_NFT_NAT) += nft_nat.o | ||
78 | #nf_tables-objs += nft_meta_target.o | 79 | #nf_tables-objs += nft_meta_target.o |
79 | obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o | 80 | obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o |
80 | obj-$(CONFIG_NFT_HASH) += nft_hash.o | 81 | obj-$(CONFIG_NFT_HASH) += nft_hash.o |
diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c new file mode 100644 index 000000000000..b0b87b2d2411 --- /dev/null +++ b/net/netfilter/nft_nat.c | |||
@@ -0,0 +1,220 @@ | |||
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 | |||
29 | struct 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 | int family; | ||
35 | enum nf_nat_manip_type type; | ||
36 | }; | ||
37 | |||
38 | static void nft_nat_eval(const struct nft_expr *expr, | ||
39 | struct nft_data data[NFT_REG_MAX + 1], | ||
40 | const struct nft_pktinfo *pkt) | ||
41 | { | ||
42 | const struct nft_nat *priv = nft_expr_priv(expr); | ||
43 | enum ip_conntrack_info ctinfo; | ||
44 | struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo); | ||
45 | struct nf_nat_range range; | ||
46 | |||
47 | memset(&range, 0, sizeof(range)); | ||
48 | if (priv->sreg_addr_min) { | ||
49 | if (priv->family == AF_INET) { | ||
50 | range.min_addr.ip = data[priv->sreg_addr_min].data[0]; | ||
51 | range.max_addr.ip = data[priv->sreg_addr_max].data[0]; | ||
52 | |||
53 | } else { | ||
54 | memcpy(range.min_addr.ip6, | ||
55 | data[priv->sreg_addr_min].data, | ||
56 | sizeof(struct nft_data)); | ||
57 | memcpy(range.max_addr.ip6, | ||
58 | data[priv->sreg_addr_max].data, | ||
59 | sizeof(struct nft_data)); | ||
60 | } | ||
61 | range.flags |= NF_NAT_RANGE_MAP_IPS; | ||
62 | } | ||
63 | |||
64 | if (priv->sreg_proto_min) { | ||
65 | range.min_proto.all = data[priv->sreg_proto_min].data[0]; | ||
66 | range.max_proto.all = data[priv->sreg_proto_max].data[0]; | ||
67 | range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; | ||
68 | } | ||
69 | |||
70 | data[NFT_REG_VERDICT].verdict = | ||
71 | nf_nat_setup_info(ct, &range, priv->type); | ||
72 | } | ||
73 | |||
74 | static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = { | ||
75 | [NFTA_NAT_TYPE] = { .type = NLA_U32 }, | ||
76 | [NFTA_NAT_FAMILY] = { .type = NLA_U32 }, | ||
77 | [NFTA_NAT_REG_ADDR_MIN] = { .type = NLA_U32 }, | ||
78 | [NFTA_NAT_REG_ADDR_MAX] = { .type = NLA_U32 }, | ||
79 | [NFTA_NAT_REG_PROTO_MIN] = { .type = NLA_U32 }, | ||
80 | [NFTA_NAT_REG_PROTO_MAX] = { .type = NLA_U32 }, | ||
81 | }; | ||
82 | |||
83 | static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | ||
84 | const struct nlattr * const tb[]) | ||
85 | { | ||
86 | struct nft_nat *priv = nft_expr_priv(expr); | ||
87 | int err; | ||
88 | |||
89 | if (tb[NFTA_NAT_TYPE] == NULL) | ||
90 | return -EINVAL; | ||
91 | |||
92 | switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) { | ||
93 | case NFT_NAT_SNAT: | ||
94 | priv->type = NF_NAT_MANIP_SRC; | ||
95 | break; | ||
96 | case NFT_NAT_DNAT: | ||
97 | priv->type = NF_NAT_MANIP_DST; | ||
98 | break; | ||
99 | default: | ||
100 | return -EINVAL; | ||
101 | } | ||
102 | |||
103 | if (tb[NFTA_NAT_FAMILY] == NULL) | ||
104 | return -EINVAL; | ||
105 | |||
106 | priv->family = ntohl(nla_get_be32(tb[NFTA_NAT_FAMILY])); | ||
107 | if (priv->family != AF_INET && priv->family != AF_INET6) | ||
108 | return -EINVAL; | ||
109 | |||
110 | if (tb[NFTA_NAT_REG_ADDR_MIN]) { | ||
111 | priv->sreg_addr_min = ntohl(nla_get_be32( | ||
112 | tb[NFTA_NAT_REG_ADDR_MIN])); | ||
113 | err = nft_validate_input_register(priv->sreg_addr_min); | ||
114 | if (err < 0) | ||
115 | return err; | ||
116 | } | ||
117 | |||
118 | if (tb[NFTA_NAT_REG_ADDR_MAX]) { | ||
119 | priv->sreg_addr_max = ntohl(nla_get_be32( | ||
120 | tb[NFTA_NAT_REG_ADDR_MAX])); | ||
121 | err = nft_validate_input_register(priv->sreg_addr_max); | ||
122 | if (err < 0) | ||
123 | return err; | ||
124 | } else | ||
125 | priv->sreg_addr_max = priv->sreg_addr_min; | ||
126 | |||
127 | if (tb[NFTA_NAT_REG_PROTO_MIN]) { | ||
128 | priv->sreg_proto_min = ntohl(nla_get_be32( | ||
129 | tb[NFTA_NAT_REG_PROTO_MIN])); | ||
130 | err = nft_validate_input_register(priv->sreg_proto_min); | ||
131 | if (err < 0) | ||
132 | return err; | ||
133 | } | ||
134 | |||
135 | if (tb[NFTA_NAT_REG_PROTO_MAX]) { | ||
136 | priv->sreg_proto_max = ntohl(nla_get_be32( | ||
137 | tb[NFTA_NAT_REG_PROTO_MAX])); | ||
138 | err = nft_validate_input_register(priv->sreg_proto_max); | ||
139 | if (err < 0) | ||
140 | return err; | ||
141 | } else | ||
142 | priv->sreg_proto_max = priv->sreg_proto_min; | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr) | ||
148 | { | ||
149 | const struct nft_nat *priv = nft_expr_priv(expr); | ||
150 | |||
151 | switch (priv->type) { | ||
152 | case NF_NAT_MANIP_SRC: | ||
153 | if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT))) | ||
154 | goto nla_put_failure; | ||
155 | break; | ||
156 | case NF_NAT_MANIP_DST: | ||
157 | if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT))) | ||
158 | goto nla_put_failure; | ||
159 | break; | ||
160 | } | ||
161 | |||
162 | if (nla_put_be32(skb, NFTA_NAT_FAMILY, htonl(priv->family))) | ||
163 | goto nla_put_failure; | ||
164 | if (nla_put_be32(skb, | ||
165 | NFTA_NAT_REG_ADDR_MIN, htonl(priv->sreg_addr_min))) | ||
166 | goto nla_put_failure; | ||
167 | if (nla_put_be32(skb, | ||
168 | NFTA_NAT_REG_ADDR_MAX, htonl(priv->sreg_addr_max))) | ||
169 | goto nla_put_failure; | ||
170 | if (nla_put_be32(skb, | ||
171 | NFTA_NAT_REG_PROTO_MIN, htonl(priv->sreg_proto_min))) | ||
172 | goto nla_put_failure; | ||
173 | if (nla_put_be32(skb, | ||
174 | NFTA_NAT_REG_PROTO_MAX, htonl(priv->sreg_proto_max))) | ||
175 | goto nla_put_failure; | ||
176 | return 0; | ||
177 | |||
178 | nla_put_failure: | ||
179 | return -1; | ||
180 | } | ||
181 | |||
182 | static struct nft_expr_type nft_nat_type; | ||
183 | static const struct nft_expr_ops nft_nat_ops = { | ||
184 | .type = &nft_nat_type, | ||
185 | .size = NFT_EXPR_SIZE(sizeof(struct nft_nat)), | ||
186 | .eval = nft_nat_eval, | ||
187 | .init = nft_nat_init, | ||
188 | .dump = nft_nat_dump, | ||
189 | }; | ||
190 | |||
191 | static struct nft_expr_type nft_nat_type __read_mostly = { | ||
192 | .name = "nat", | ||
193 | .ops = &nft_nat_ops, | ||
194 | .policy = nft_nat_policy, | ||
195 | .maxattr = NFTA_NAT_MAX, | ||
196 | .owner = THIS_MODULE, | ||
197 | }; | ||
198 | |||
199 | static int __init nft_nat_module_init(void) | ||
200 | { | ||
201 | int err; | ||
202 | |||
203 | err = nft_register_expr(&nft_nat_type); | ||
204 | if (err < 0) | ||
205 | return err; | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static void __exit nft_nat_module_exit(void) | ||
211 | { | ||
212 | nft_unregister_expr(&nft_nat_type); | ||
213 | } | ||
214 | |||
215 | module_init(nft_nat_module_init); | ||
216 | module_exit(nft_nat_module_exit); | ||
217 | |||
218 | MODULE_LICENSE("GPL"); | ||
219 | MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>"); | ||
220 | MODULE_ALIAS_NFT_EXPR("nat"); | ||