History

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

Tomasz Bursztyka committed 13 years ago

Commit eb316281 parent 9ddf6329 files · +457 −162Browse files

Unified · Split

9 files
  1. include/uapi/linux/netfilter/nf_tables.h +10 −8
  2. net/ipv4/netfilter/Kconfig +1 −0
  3. net/ipv4/netfilter/nft_chain_nat_ipv4.c +2 −154
  4. net/ipv6/netfilter/Kconfig +5 −0
  5. net/ipv6/netfilter/Makefile +1 −0
  6. net/ipv6/netfilter/nft_chain_nat_ipv6.c +211 −0
  7. net/netfilter/Kconfig +6 −0
  8. net/netfilter/Makefile +1 −0
  9. net/netfilter/nft_nat.c +220 −0
include/uapi/linux/netfilter/nf_tables.h+10−8
@@ -695,18 +695,20 @@ enum nft_nat_types { · 694 unmodified lines
695695 * enum nft_nat_attributes - nf_tables nat expression netlink attributes
696696 *
697697 * @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)
699− * @NFTA_NAT_ADDR_MAX: source register of address range end (NLA_U32: nft_registers)
700− * @NFTA_NAT_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers)
701− * @NFTA_NAT_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers)
698+ * @NFTA_NAT_FAMILY: NAT family (NLA_U32)
699+ * @NFTA_NAT_REG_ADDR_MIN: source register of address 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_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)
702703 */
703704 enum nft_nat_attributes {
704705 NFTA_NAT_UNSPEC,
705706 NFTA_NAT_TYPE,
706− NFTA_NAT_ADDR_MIN,
707− NFTA_NAT_ADDR_MAX,
708− NFTA_NAT_PROTO_MIN,
709− NFTA_NAT_PROTO_MAX,
707+ NFTA_NAT_FAMILY,
708+ NFTA_NAT_REG_ADDR_MIN,
709+ NFTA_NAT_REG_ADDR_MAX,
710+ NFTA_NAT_REG_PROTO_MIN,
711+ NFTA_NAT_REG_PROTO_MAX,
710712 __NFTA_NAT_MAX
711713 };
712714 #define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1)
net/ipv4/netfilter/Kconfig+1−0
@@ -50,6 +50,7 @@ config NFT_CHAIN_ROUTE_IPV4 · 49 unmodified lines
5050
5151 config NFT_CHAIN_NAT_IPV4
5252 depends on NF_TABLES_IPV4
53+ depends on NF_NAT_IPV4 && NFT_NAT
5354 tristate "IPv4 nf_tables nat chain support"
5455
5556 config IP_NF_IPTABLES
net/ipv4/netfilter/nft_chain_nat_ipv4.c+2−154
@@ -1,6 +1,7 @@
11 /*
22 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
33 * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
4+ * Copyright (c) 2012 Intel Corporation
45 *
56 * This program is free software; you can redistribute it and/or modify
67 * it under the terms of the GNU General Public License version 2 as
@@ -14,10 +15,8 @@ · 7 unmodified lines
1415 #include <linux/list.h>
1516 #include <linux/skbuff.h>
1617 #include <linux/ip.h>
17−#include <linux/netlink.h>
1818 #include <linux/netfilter.h>
1919 #include <linux/netfilter_ipv4.h>
20−#include <linux/netfilter/nfnetlink.h>
2120 #include <linux/netfilter/nf_tables.h>
2221 #include <net/netfilter/nf_conntrack.h>
2322 #include <net/netfilter/nf_nat.h>
@@ -27,147 +26,6 @@ · 3 unmodified lines
2726 #include <net/netfilter/nf_nat_l3proto.h>
2827 #include <net/ip.h>
2928
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−
17129 /*
17230 * NAT chains
17331 */
@@ -306,7 +164,7 @@ static unsigned int nf_nat_output(const struct nf_hook_ops *ops, · 132 unmodified lines
306164 return ret;
307165 }
308166
309−struct nf_chain_type nft_chain_nat_ipv4 = {
167+static struct nf_chain_type nft_chain_nat_ipv4 = {
310168 .family = NFPROTO_IPV4,
311169 .name = "nat",
312170 .type = NFT_CHAIN_T_NAT,
@@ -331,20 +189,11 @@ static int __init nft_chain_nat_init(void) · 18 unmodified lines
331189 if (err < 0)
332190 return err;
333191
334− err = nft_register_expr(&nft_nat_type);
335− if (err < 0)
336− goto err;
337−
338192 return 0;
339−
340−err:
341− nft_unregister_chain_type(&nft_chain_nat_ipv4);
342− return err;
343193 }
344194
345195 static void __exit nft_chain_nat_exit(void)
346196 {
347− nft_unregister_expr(&nft_nat_type);
348197 nft_unregister_chain_type(&nft_chain_nat_ipv4);
349198 }
350199
@@ -354,4 +203,3 @@ module_exit(nft_chain_nat_exit); · 3 unmodified lines
354203 MODULE_LICENSE("GPL");
355204 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
356205 MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat");
357−MODULE_ALIAS_NFT_EXPR("nat");
net/ipv6/netfilter/Kconfig+5−0
@@ -33,6 +33,11 @@ config NFT_CHAIN_ROUTE_IPV6 · 32 unmodified lines
3333 depends on NF_TABLES_IPV6
3434 tristate "IPv6 nf_tables route chain support"
3535
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+
3641 config IP6_NF_IPTABLES
3742 tristate "IP6 tables support (required for filtering)"
3843 depends on INET && IPV6
net/ipv6/netfilter/Makefile+1−0
@@ -26,6 +26,7 @@ obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o · 25 unmodified lines
2626 # nf_tables
2727 obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o
2828 obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
29+obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o
2930
3031 # matches
3132 obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
net/ipv6/netfilter/nft_chain_nat_ipv6.c+211−0
@@ -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");
net/netfilter/Kconfig+6−0
@@ -450,6 +450,12 @@ config NFT_LIMIT · 449 unmodified lines
450450 depends on NF_TABLES
451451 tristate "Netfilter nf_tables limit module"
452452
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+
453459 config NFT_COMPAT
454460 depends on NF_TABLES
455461 depends on NETFILTER_XTABLES
net/netfilter/Makefile+1−0
@@ -75,6 +75,7 @@ obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o · 74 unmodified lines
7575 obj-$(CONFIG_NFT_META) += nft_meta.o
7676 obj-$(CONFIG_NFT_CT) += nft_ct.o
7777 obj-$(CONFIG_NFT_LIMIT) += nft_limit.o
78+obj-$(CONFIG_NFT_NAT) += nft_nat.o
7879 #nf_tables-objs += nft_meta_target.o
7980 obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o
8081 obj-$(CONFIG_NFT_HASH) += nft_hash.o
net/netfilter/nft_nat.c+220−0
@@ -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");