Commit 58a317f1061c894d2344c0b6a18ab4a64b69b815

Authored by Patrick McHardy
Committed by Pablo Neira Ayuso
1 parent 2cf545e835

netfilter: ipv6: add IPv6 NAT support

Signed-off-by: Patrick McHardy <kaber@trash.net>

Showing 13 changed files with 764 additions and 2 deletions Side-by-side Diff

include/linux/netfilter/nfnetlink_conntrack.h
... ... @@ -147,6 +147,8 @@
147 147 CTA_NAT_V4_MAXIP,
148 148 #define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP
149 149 CTA_NAT_PROTO,
  150 + CTA_NAT_V6_MINIP,
  151 + CTA_NAT_V6_MAXIP,
150 152 __CTA_NAT_MAX
151 153 };
152 154 #define CTA_NAT_MAX (__CTA_NAT_MAX - 1)
include/net/netfilter/nf_nat_l3proto.h
... ... @@ -43,6 +43,11 @@
43 43 struct nf_conn *ct,
44 44 enum ip_conntrack_info ctinfo,
45 45 unsigned int hooknum);
  46 +extern int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
  47 + struct nf_conn *ct,
  48 + enum ip_conntrack_info ctinfo,
  49 + unsigned int hooknum,
  50 + unsigned int hdrlen);
46 51  
47 52 #endif /* _NF_NAT_L3PROTO_H */
include/net/netfilter/nf_nat_l4proto.h
... ... @@ -51,6 +51,7 @@
51 51 extern const struct nf_nat_l4proto nf_nat_l4proto_tcp;
52 52 extern const struct nf_nat_l4proto nf_nat_l4proto_udp;
53 53 extern const struct nf_nat_l4proto nf_nat_l4proto_icmp;
  54 +extern const struct nf_nat_l4proto nf_nat_l4proto_icmpv6;
54 55 extern const struct nf_nat_l4proto nf_nat_l4proto_unknown;
55 56  
56 57 extern bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple,
include/net/netns/ipv6.h
... ... @@ -42,6 +42,7 @@
42 42 #ifdef CONFIG_SECURITY
43 43 struct xt_table *ip6table_security;
44 44 #endif
  45 + struct xt_table *ip6table_nat;
45 46 #endif
46 47 struct rt6_info *ip6_null_entry;
47 48 struct rt6_statistics *rt6_stats;
net/core/secure_seq.c
... ... @@ -76,6 +76,7 @@
76 76  
77 77 return hash[0];
78 78 }
  79 +EXPORT_SYMBOL(secure_ipv6_port_ephemeral);
79 80 #endif
80 81  
81 82 #ifdef CONFIG_INET
net/ipv6/netfilter/Kconfig
... ... @@ -25,6 +25,18 @@
25 25  
26 26 To compile it as a module, choose M here. If unsure, say N.
27 27  
  28 +config NF_NAT_IPV6
  29 + tristate "IPv6 NAT"
  30 + depends on NF_CONNTRACK_IPV6
  31 + depends on NETFILTER_ADVANCED
  32 + select NF_NAT
  33 + help
  34 + The IPv6 NAT option allows masquerading, port forwarding and other
  35 + forms of full Network Address Port Translation. It is controlled by
  36 + the `nat' table in ip6tables, see the man page for ip6tables(8).
  37 +
  38 + To compile it as a module, choose M here. If unsure, say N.
  39 +
28 40 config IP6_NF_IPTABLES
29 41 tristate "IP6 tables support (required for filtering)"
30 42 depends on INET && IPV6
net/ipv6/netfilter/Makefile
... ... @@ -8,12 +8,16 @@
8 8 obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o
9 9 obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o
10 10 obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o
  11 +obj-$(CONFIG_NF_NAT_IPV6) += ip6table_nat.o
11 12  
12 13 # objects for l3 independent conntrack
13 14 nf_conntrack_ipv6-y := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o
14 15  
15 16 # l3 independent conntrack
16 17 obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o nf_defrag_ipv6.o
  18 +
  19 +nf_nat_ipv6-y := nf_nat_l3proto_ipv6.o nf_nat_proto_icmpv6.o
  20 +obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o
17 21  
18 22 # defrag
19 23 nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o
net/ipv6/netfilter/ip6table_nat.c
  1 +/*
  2 + * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
  3 + *
  4 + * This program is free software; you can redistribute it and/or modify
  5 + * it under the terms of the GNU General Public License version 2 as
  6 + * published by the Free Software Foundation.
  7 + *
  8 + * Based on Rusty Russell's IPv4 NAT code. Development of IPv6 NAT
  9 + * funded by Astaro.
  10 + */
  11 +
  12 +#include <linux/module.h>
  13 +#include <linux/netfilter.h>
  14 +#include <linux/netfilter_ipv6.h>
  15 +#include <linux/netfilter_ipv6/ip6_tables.h>
  16 +#include <linux/ipv6.h>
  17 +#include <net/ipv6.h>
  18 +
  19 +#include <net/netfilter/nf_nat.h>
  20 +#include <net/netfilter/nf_nat_core.h>
  21 +#include <net/netfilter/nf_nat_l3proto.h>
  22 +
  23 +static const struct xt_table nf_nat_ipv6_table = {
  24 + .name = "nat",
  25 + .valid_hooks = (1 << NF_INET_PRE_ROUTING) |
  26 + (1 << NF_INET_POST_ROUTING) |
  27 + (1 << NF_INET_LOCAL_OUT) |
  28 + (1 << NF_INET_LOCAL_IN),
  29 + .me = THIS_MODULE,
  30 + .af = NFPROTO_IPV6,
  31 +};
  32 +
  33 +static unsigned int alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
  34 +{
  35 + /* Force range to this IP; let proto decide mapping for
  36 + * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
  37 + */
  38 + struct nf_nat_range range;
  39 +
  40 + range.flags = 0;
  41 + pr_debug("Allocating NULL binding for %p (%pI6)\n", ct,
  42 + HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ?
  43 + &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6 :
  44 + &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6);
  45 +
  46 + return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
  47 +}
  48 +
  49 +static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum,
  50 + const struct net_device *in,
  51 + const struct net_device *out,
  52 + struct nf_conn *ct)
  53 +{
  54 + struct net *net = nf_ct_net(ct);
  55 + unsigned int ret;
  56 +
  57 + ret = ip6t_do_table(skb, hooknum, in, out, net->ipv6.ip6table_nat);
  58 + if (ret == NF_ACCEPT) {
  59 + if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum)))
  60 + ret = alloc_null_binding(ct, hooknum);
  61 + }
  62 + return ret;
  63 +}
  64 +
  65 +static unsigned int
  66 +nf_nat_ipv6_fn(unsigned int hooknum,
  67 + struct sk_buff *skb,
  68 + const struct net_device *in,
  69 + const struct net_device *out,
  70 + int (*okfn)(struct sk_buff *))
  71 +{
  72 + struct nf_conn *ct;
  73 + enum ip_conntrack_info ctinfo;
  74 + struct nf_conn_nat *nat;
  75 + enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
  76 + __be16 frag_off;
  77 + int hdrlen;
  78 + u8 nexthdr;
  79 +
  80 + ct = nf_ct_get(skb, &ctinfo);
  81 + /* Can't track? It's not due to stress, or conntrack would
  82 + * have dropped it. Hence it's the user's responsibilty to
  83 + * packet filter it out, or implement conntrack/NAT for that
  84 + * protocol. 8) --RR
  85 + */
  86 + if (!ct)
  87 + return NF_ACCEPT;
  88 +
  89 + /* Don't try to NAT if this packet is not conntracked */
  90 + if (nf_ct_is_untracked(ct))
  91 + return NF_ACCEPT;
  92 +
  93 + nat = nfct_nat(ct);
  94 + if (!nat) {
  95 + /* NAT module was loaded late. */
  96 + if (nf_ct_is_confirmed(ct))
  97 + return NF_ACCEPT;
  98 + nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
  99 + if (nat == NULL) {
  100 + pr_debug("failed to add NAT extension\n");
  101 + return NF_ACCEPT;
  102 + }
  103 + }
  104 +
  105 + switch (ctinfo) {
  106 + case IP_CT_RELATED:
  107 + case IP_CT_RELATED_REPLY:
  108 + nexthdr = ipv6_hdr(skb)->nexthdr;
  109 + hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
  110 + &nexthdr, &frag_off);
  111 +
  112 + if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
  113 + if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
  114 + hooknum, hdrlen))
  115 + return NF_DROP;
  116 + else
  117 + return NF_ACCEPT;
  118 + }
  119 + /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
  120 + case IP_CT_NEW:
  121 + /* Seen it before? This can happen for loopback, retrans,
  122 + * or local packets.
  123 + */
  124 + if (!nf_nat_initialized(ct, maniptype)) {
  125 + unsigned int ret;
  126 +
  127 + ret = nf_nat_rule_find(skb, hooknum, in, out, ct);
  128 + if (ret != NF_ACCEPT)
  129 + return ret;
  130 + } else
  131 + pr_debug("Already setup manip %s for ct %p\n",
  132 + maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
  133 + ct);
  134 + break;
  135 +
  136 + default:
  137 + /* ESTABLISHED */
  138 + NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
  139 + ctinfo == IP_CT_ESTABLISHED_REPLY);
  140 + }
  141 +
  142 + return nf_nat_packet(ct, ctinfo, hooknum, skb);
  143 +}
  144 +
  145 +static unsigned int
  146 +nf_nat_ipv6_in(unsigned int hooknum,
  147 + struct sk_buff *skb,
  148 + const struct net_device *in,
  149 + const struct net_device *out,
  150 + int (*okfn)(struct sk_buff *))
  151 +{
  152 + unsigned int ret;
  153 + struct in6_addr daddr = ipv6_hdr(skb)->daddr;
  154 +
  155 + ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
  156 + if (ret != NF_DROP && ret != NF_STOLEN &&
  157 + ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
  158 + skb_dst_drop(skb);
  159 +
  160 + return ret;
  161 +}
  162 +
  163 +static unsigned int
  164 +nf_nat_ipv6_out(unsigned int hooknum,
  165 + struct sk_buff *skb,
  166 + const struct net_device *in,
  167 + const struct net_device *out,
  168 + int (*okfn)(struct sk_buff *))
  169 +{
  170 +#ifdef CONFIG_XFRM
  171 + const struct nf_conn *ct;
  172 + enum ip_conntrack_info ctinfo;
  173 +#endif
  174 + unsigned int ret;
  175 +
  176 + /* root is playing with raw sockets. */
  177 + if (skb->len < sizeof(struct ipv6hdr))
  178 + return NF_ACCEPT;
  179 +
  180 + ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
  181 +#ifdef CONFIG_XFRM
  182 + if (ret != NF_DROP && ret != NF_STOLEN &&
  183 + !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
  184 + (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
  185 + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
  186 +
  187 + if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
  188 + &ct->tuplehash[!dir].tuple.dst.u3) ||
  189 + (ct->tuplehash[dir].tuple.src.u.all !=
  190 + ct->tuplehash[!dir].tuple.dst.u.all))
  191 + if (nf_xfrm_me_harder(skb, AF_INET6) < 0)
  192 + ret = NF_DROP;
  193 + }
  194 +#endif
  195 + return ret;
  196 +}
  197 +
  198 +static unsigned int
  199 +nf_nat_ipv6_local_fn(unsigned int hooknum,
  200 + struct sk_buff *skb,
  201 + const struct net_device *in,
  202 + const struct net_device *out,
  203 + int (*okfn)(struct sk_buff *))
  204 +{
  205 + const struct nf_conn *ct;
  206 + enum ip_conntrack_info ctinfo;
  207 + unsigned int ret;
  208 +
  209 + /* root is playing with raw sockets. */
  210 + if (skb->len < sizeof(struct ipv6hdr))
  211 + return NF_ACCEPT;
  212 +
  213 + ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
  214 + if (ret != NF_DROP && ret != NF_STOLEN &&
  215 + (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
  216 + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
  217 +
  218 + if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
  219 + &ct->tuplehash[!dir].tuple.src.u3)) {
  220 + if (ip6_route_me_harder(skb))
  221 + ret = NF_DROP;
  222 + }
  223 +#ifdef CONFIG_XFRM
  224 + else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
  225 + ct->tuplehash[dir].tuple.dst.u.all !=
  226 + ct->tuplehash[!dir].tuple.src.u.all)
  227 + if (nf_xfrm_me_harder(skb, AF_INET6))
  228 + ret = NF_DROP;
  229 +#endif
  230 + }
  231 + return ret;
  232 +}
  233 +
  234 +static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
  235 + /* Before packet filtering, change destination */
  236 + {
  237 + .hook = nf_nat_ipv6_in,
  238 + .owner = THIS_MODULE,
  239 + .pf = NFPROTO_IPV6,
  240 + .hooknum = NF_INET_PRE_ROUTING,
  241 + .priority = NF_IP6_PRI_NAT_DST,
  242 + },
  243 + /* After packet filtering, change source */
  244 + {
  245 + .hook = nf_nat_ipv6_out,
  246 + .owner = THIS_MODULE,
  247 + .pf = NFPROTO_IPV6,
  248 + .hooknum = NF_INET_POST_ROUTING,
  249 + .priority = NF_IP6_PRI_NAT_SRC,
  250 + },
  251 + /* Before packet filtering, change destination */
  252 + {
  253 + .hook = nf_nat_ipv6_local_fn,
  254 + .owner = THIS_MODULE,
  255 + .pf = NFPROTO_IPV6,
  256 + .hooknum = NF_INET_LOCAL_OUT,
  257 + .priority = NF_IP6_PRI_NAT_DST,
  258 + },
  259 + /* After packet filtering, change source */
  260 + {
  261 + .hook = nf_nat_ipv6_fn,
  262 + .owner = THIS_MODULE,
  263 + .pf = NFPROTO_IPV6,
  264 + .hooknum = NF_INET_LOCAL_IN,
  265 + .priority = NF_IP6_PRI_NAT_SRC,
  266 + },
  267 +};
  268 +
  269 +static int __net_init ip6table_nat_net_init(struct net *net)
  270 +{
  271 + struct ip6t_replace *repl;
  272 +
  273 + repl = ip6t_alloc_initial_table(&nf_nat_ipv6_table);
  274 + if (repl == NULL)
  275 + return -ENOMEM;
  276 + net->ipv6.ip6table_nat = ip6t_register_table(net, &nf_nat_ipv6_table, repl);
  277 + kfree(repl);
  278 + if (IS_ERR(net->ipv6.ip6table_nat))
  279 + return PTR_ERR(net->ipv6.ip6table_nat);
  280 + return 0;
  281 +}
  282 +
  283 +static void __net_exit ip6table_nat_net_exit(struct net *net)
  284 +{
  285 + ip6t_unregister_table(net, net->ipv6.ip6table_nat);
  286 +}
  287 +
  288 +static struct pernet_operations ip6table_nat_net_ops = {
  289 + .init = ip6table_nat_net_init,
  290 + .exit = ip6table_nat_net_exit,
  291 +};
  292 +
  293 +static int __init ip6table_nat_init(void)
  294 +{
  295 + int err;
  296 +
  297 + err = register_pernet_subsys(&ip6table_nat_net_ops);
  298 + if (err < 0)
  299 + goto err1;
  300 +
  301 + err = nf_register_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops));
  302 + if (err < 0)
  303 + goto err2;
  304 + return 0;
  305 +
  306 +err2:
  307 + unregister_pernet_subsys(&ip6table_nat_net_ops);
  308 +err1:
  309 + return err;
  310 +}
  311 +
  312 +static void __exit ip6table_nat_exit(void)
  313 +{
  314 + nf_unregister_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops));
  315 + unregister_pernet_subsys(&ip6table_nat_net_ops);
  316 +}
  317 +
  318 +module_init(ip6table_nat_init);
  319 +module_exit(ip6table_nat_exit);
  320 +
  321 +MODULE_LICENSE("GPL");
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
... ... @@ -28,6 +28,7 @@
28 28 #include <net/netfilter/nf_conntrack_core.h>
29 29 #include <net/netfilter/nf_conntrack_zones.h>
30 30 #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
  31 +#include <net/netfilter/nf_nat_helper.h>
31 32 #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
32 33 #include <net/netfilter/nf_log.h>
33 34  
... ... @@ -142,6 +143,36 @@
142 143 const struct net_device *out,
143 144 int (*okfn)(struct sk_buff *))
144 145 {
  146 + struct nf_conn *ct;
  147 + enum ip_conntrack_info ctinfo;
  148 + unsigned char pnum = ipv6_hdr(skb)->nexthdr;
  149 + int protoff;
  150 + __be16 frag_off;
  151 +
  152 + ct = nf_ct_get(skb, &ctinfo);
  153 + if (!ct || ctinfo == IP_CT_RELATED_REPLY)
  154 + goto out;
  155 +
  156 + protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum,
  157 + &frag_off);
  158 + if (protoff < 0 || (frag_off & htons(~0x7)) != 0) {
  159 + pr_debug("proto header not found\n");
  160 + goto out;
  161 + }
  162 +
  163 + /* adjust seqs for loopback traffic only in outgoing direction */
  164 + if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
  165 + !nf_is_loopback_packet(skb)) {
  166 + typeof(nf_nat_seq_adjust_hook) seq_adjust;
  167 +
  168 + seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook);
  169 + if (!seq_adjust ||
  170 + !seq_adjust(skb, ct, ctinfo, protoff)) {
  171 + NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
  172 + return NF_DROP;
  173 + }
  174 + }
  175 +out:
145 176 /* We've seen it coming out the other side: confirm it */
146 177 return nf_conntrack_confirm(skb);
147 178 }
148 179  
... ... @@ -170,12 +201,14 @@
170 201 }
171 202  
172 203 /* Conntrack helpers need the entire reassembled packet in the
173   - * POST_ROUTING hook.
  204 + * POST_ROUTING hook. In case of unconfirmed connections NAT
  205 + * might reassign a helper, so the entire packet is also
  206 + * required.
174 207 */
175 208 ct = nf_ct_get(reasm, &ctinfo);
176 209 if (ct != NULL && !nf_ct_is_untracked(ct)) {
177 210 help = nfct_help(ct);
178   - if (help && help->helper) {
  211 + if ((help && help->helper) || !nf_ct_is_confirmed(ct)) {
179 212 nf_conntrack_get_reasm(skb);
180 213 NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm,
181 214 (struct net_device *)in,
net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
  1 +/*
  2 + * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
  3 + *
  4 + * This program is free software; you can redistribute it and/or modify
  5 + * it under the terms of the GNU General Public License version 2 as
  6 + * published by the Free Software Foundation.
  7 + *
  8 + * Development of IPv6 NAT funded by Astaro.
  9 + */
  10 +#include <linux/types.h>
  11 +#include <linux/module.h>
  12 +#include <linux/skbuff.h>
  13 +#include <linux/ipv6.h>
  14 +#include <linux/netfilter.h>
  15 +#include <linux/netfilter_ipv6.h>
  16 +#include <net/secure_seq.h>
  17 +#include <net/checksum.h>
  18 +#include <net/ip6_route.h>
  19 +#include <net/ipv6.h>
  20 +
  21 +#include <net/netfilter/nf_conntrack_core.h>
  22 +#include <net/netfilter/nf_conntrack.h>
  23 +#include <net/netfilter/nf_nat_core.h>
  24 +#include <net/netfilter/nf_nat_l3proto.h>
  25 +#include <net/netfilter/nf_nat_l4proto.h>
  26 +
  27 +static const struct nf_nat_l3proto nf_nat_l3proto_ipv6;
  28 +
  29 +#ifdef CONFIG_XFRM
  30 +static void nf_nat_ipv6_decode_session(struct sk_buff *skb,
  31 + const struct nf_conn *ct,
  32 + enum ip_conntrack_dir dir,
  33 + unsigned long statusbit,
  34 + struct flowi *fl)
  35 +{
  36 + const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple;
  37 + struct flowi6 *fl6 = &fl->u.ip6;
  38 +
  39 + if (ct->status & statusbit) {
  40 + fl6->daddr = t->dst.u3.in6;
  41 + if (t->dst.protonum == IPPROTO_TCP ||
  42 + t->dst.protonum == IPPROTO_UDP ||
  43 + t->dst.protonum == IPPROTO_UDPLITE ||
  44 + t->dst.protonum == IPPROTO_DCCP ||
  45 + t->dst.protonum == IPPROTO_SCTP)
  46 + fl6->fl6_dport = t->dst.u.all;
  47 + }
  48 +
  49 + statusbit ^= IPS_NAT_MASK;
  50 +
  51 + if (ct->status & statusbit) {
  52 + fl6->saddr = t->src.u3.in6;
  53 + if (t->dst.protonum == IPPROTO_TCP ||
  54 + t->dst.protonum == IPPROTO_UDP ||
  55 + t->dst.protonum == IPPROTO_UDPLITE ||
  56 + t->dst.protonum == IPPROTO_DCCP ||
  57 + t->dst.protonum == IPPROTO_SCTP)
  58 + fl6->fl6_sport = t->src.u.all;
  59 + }
  60 +}
  61 +#endif
  62 +
  63 +static bool nf_nat_ipv6_in_range(const struct nf_conntrack_tuple *t,
  64 + const struct nf_nat_range *range)
  65 +{
  66 + return ipv6_addr_cmp(&t->src.u3.in6, &range->min_addr.in6) >= 0 &&
  67 + ipv6_addr_cmp(&t->src.u3.in6, &range->max_addr.in6) <= 0;
  68 +}
  69 +
  70 +static u32 nf_nat_ipv6_secure_port(const struct nf_conntrack_tuple *t,
  71 + __be16 dport)
  72 +{
  73 + return secure_ipv6_port_ephemeral(t->src.u3.ip6, t->dst.u3.ip6, dport);
  74 +}
  75 +
  76 +static bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb,
  77 + unsigned int iphdroff,
  78 + const struct nf_nat_l4proto *l4proto,
  79 + const struct nf_conntrack_tuple *target,
  80 + enum nf_nat_manip_type maniptype)
  81 +{
  82 + struct ipv6hdr *ipv6h;
  83 + __be16 frag_off;
  84 + int hdroff;
  85 + u8 nexthdr;
  86 +
  87 + if (!skb_make_writable(skb, iphdroff + sizeof(*ipv6h)))
  88 + return false;
  89 +
  90 + ipv6h = (void *)skb->data + iphdroff;
  91 + nexthdr = ipv6h->nexthdr;
  92 + hdroff = ipv6_skip_exthdr(skb, iphdroff + sizeof(*ipv6h),
  93 + &nexthdr, &frag_off);
  94 + if (hdroff < 0)
  95 + goto manip_addr;
  96 +
  97 + if ((frag_off & htons(~0x7)) == 0 &&
  98 + !l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv6, iphdroff, hdroff,
  99 + target, maniptype))
  100 + return false;
  101 +manip_addr:
  102 + if (maniptype == NF_NAT_MANIP_SRC)
  103 + ipv6h->saddr = target->src.u3.in6;
  104 + else
  105 + ipv6h->daddr = target->dst.u3.in6;
  106 +
  107 + return true;
  108 +}
  109 +
  110 +static void nf_nat_ipv6_csum_update(struct sk_buff *skb,
  111 + unsigned int iphdroff, __sum16 *check,
  112 + const struct nf_conntrack_tuple *t,
  113 + enum nf_nat_manip_type maniptype)
  114 +{
  115 + const struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + iphdroff);
  116 + const struct in6_addr *oldip, *newip;
  117 +
  118 + if (maniptype == NF_NAT_MANIP_SRC) {
  119 + oldip = &ipv6h->saddr;
  120 + newip = &t->src.u3.in6;
  121 + } else {
  122 + oldip = &ipv6h->daddr;
  123 + newip = &t->dst.u3.in6;
  124 + }
  125 + inet_proto_csum_replace16(check, skb, oldip->s6_addr32,
  126 + newip->s6_addr32, 1);
  127 +}
  128 +
  129 +static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
  130 + u8 proto, void *data, __sum16 *check,
  131 + int datalen, int oldlen)
  132 +{
  133 + const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
  134 + struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
  135 +
  136 + if (skb->ip_summed != CHECKSUM_PARTIAL) {
  137 + if (!(rt->rt6i_flags & RTF_LOCAL) &&
  138 + (!skb->dev || skb->dev->features & NETIF_F_V6_CSUM)) {
  139 + skb->ip_summed = CHECKSUM_PARTIAL;
  140 + skb->csum_start = skb_headroom(skb) +
  141 + skb_network_offset(skb) +
  142 + (data - (void *)skb->data);
  143 + skb->csum_offset = (void *)check - data;
  144 + *check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
  145 + datalen, proto, 0);
  146 + } else {
  147 + *check = 0;
  148 + *check = csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
  149 + datalen, proto,
  150 + csum_partial(data, datalen,
  151 + 0));
  152 + if (proto == IPPROTO_UDP && !*check)
  153 + *check = CSUM_MANGLED_0;
  154 + }
  155 + } else
  156 + inet_proto_csum_replace2(check, skb,
  157 + htons(oldlen), htons(datalen), 1);
  158 +}
  159 +
  160 +static int nf_nat_ipv6_nlattr_to_range(struct nlattr *tb[],
  161 + struct nf_nat_range *range)
  162 +{
  163 + if (tb[CTA_NAT_V6_MINIP]) {
  164 + nla_memcpy(&range->min_addr.ip6, tb[CTA_NAT_V6_MINIP],
  165 + sizeof(struct in6_addr));
  166 + range->flags |= NF_NAT_RANGE_MAP_IPS;
  167 + }
  168 +
  169 + if (tb[CTA_NAT_V6_MAXIP])
  170 + nla_memcpy(&range->max_addr.ip6, tb[CTA_NAT_V6_MAXIP],
  171 + sizeof(struct in6_addr));
  172 + else
  173 + range->max_addr = range->min_addr;
  174 +
  175 + return 0;
  176 +}
  177 +
  178 +static const struct nf_nat_l3proto nf_nat_l3proto_ipv6 = {
  179 + .l3proto = NFPROTO_IPV6,
  180 + .secure_port = nf_nat_ipv6_secure_port,
  181 + .in_range = nf_nat_ipv6_in_range,
  182 + .manip_pkt = nf_nat_ipv6_manip_pkt,
  183 + .csum_update = nf_nat_ipv6_csum_update,
  184 + .csum_recalc = nf_nat_ipv6_csum_recalc,
  185 + .nlattr_to_range = nf_nat_ipv6_nlattr_to_range,
  186 +#ifdef CONFIG_XFRM
  187 + .decode_session = nf_nat_ipv6_decode_session,
  188 +#endif
  189 +};
  190 +
  191 +int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
  192 + struct nf_conn *ct,
  193 + enum ip_conntrack_info ctinfo,
  194 + unsigned int hooknum,
  195 + unsigned int hdrlen)
  196 +{
  197 + struct {
  198 + struct icmp6hdr icmp6;
  199 + struct ipv6hdr ip6;
  200 + } *inside;
  201 + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
  202 + enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
  203 + const struct nf_nat_l4proto *l4proto;
  204 + struct nf_conntrack_tuple target;
  205 + unsigned long statusbit;
  206 +
  207 + NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY);
  208 +
  209 + if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
  210 + return 0;
  211 + if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6))
  212 + return 0;
  213 +
  214 + inside = (void *)skb->data + hdrlen;
  215 + if (inside->icmp6.icmp6_type == NDISC_REDIRECT) {
  216 + if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
  217 + return 0;
  218 + if (ct->status & IPS_NAT_MASK)
  219 + return 0;
  220 + }
  221 +
  222 + if (manip == NF_NAT_MANIP_SRC)
  223 + statusbit = IPS_SRC_NAT;
  224 + else
  225 + statusbit = IPS_DST_NAT;
  226 +
  227 + /* Invert if this is reply direction */
  228 + if (dir == IP_CT_DIR_REPLY)
  229 + statusbit ^= IPS_NAT_MASK;
  230 +
  231 + if (!(ct->status & statusbit))
  232 + return 1;
  233 +
  234 + l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, inside->ip6.nexthdr);
  235 + if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6),
  236 + l4proto, &ct->tuplehash[!dir].tuple, !manip))
  237 + return 0;
  238 +
  239 + if (skb->ip_summed != CHECKSUM_PARTIAL) {
  240 + struct ipv6hdr *ipv6h = ipv6_hdr(skb);
  241 + inside = (void *)skb->data + hdrlen;
  242 + inside->icmp6.icmp6_cksum = 0;
  243 + inside->icmp6.icmp6_cksum =
  244 + csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
  245 + skb->len - hdrlen, IPPROTO_ICMPV6,
  246 + csum_partial(&inside->icmp6,
  247 + skb->len - hdrlen, 0));
  248 + }
  249 +
  250 + nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
  251 + l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, IPPROTO_ICMPV6);
  252 + if (!nf_nat_ipv6_manip_pkt(skb, 0, l4proto, &target, manip))
  253 + return 0;
  254 +
  255 + return 1;
  256 +}
  257 +EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation);
  258 +
  259 +static int __init nf_nat_l3proto_ipv6_init(void)
  260 +{
  261 + int err;
  262 +
  263 + err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
  264 + if (err < 0)
  265 + goto err1;
  266 + err = nf_nat_l3proto_register(&nf_nat_l3proto_ipv6);
  267 + if (err < 0)
  268 + goto err2;
  269 + return err;
  270 +
  271 +err2:
  272 + nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
  273 +err1:
  274 + return err;
  275 +}
  276 +
  277 +static void __exit nf_nat_l3proto_ipv6_exit(void)
  278 +{
  279 + nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv6);
  280 + nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
  281 +}
  282 +
  283 +MODULE_LICENSE("GPL");
  284 +MODULE_ALIAS("nf-nat-" __stringify(AF_INET6));
  285 +
  286 +module_init(nf_nat_l3proto_ipv6_init);
  287 +module_exit(nf_nat_l3proto_ipv6_exit);
net/ipv6/netfilter/nf_nat_proto_icmpv6.c
  1 +/*
  2 + * Copyright (c) 2011 Patrick Mchardy <kaber@trash.net>
  3 + *
  4 + * This program is free software; you can redistribute it and/or modify
  5 + * it under the terms of the GNU General Public License version 2 as
  6 + * published by the Free Software Foundation.
  7 + *
  8 + * Based on Rusty Russell's IPv4 ICMP NAT code. Development of IPv6
  9 + * NAT funded by Astaro.
  10 + */
  11 +
  12 +#include <linux/types.h>
  13 +#include <linux/init.h>
  14 +#include <linux/icmpv6.h>
  15 +
  16 +#include <linux/netfilter.h>
  17 +#include <net/netfilter/nf_nat.h>
  18 +#include <net/netfilter/nf_nat_core.h>
  19 +#include <net/netfilter/nf_nat_l3proto.h>
  20 +#include <net/netfilter/nf_nat_l4proto.h>
  21 +
  22 +static bool
  23 +icmpv6_in_range(const struct nf_conntrack_tuple *tuple,
  24 + enum nf_nat_manip_type maniptype,
  25 + const union nf_conntrack_man_proto *min,
  26 + const union nf_conntrack_man_proto *max)
  27 +{
  28 + return ntohs(tuple->src.u.icmp.id) >= ntohs(min->icmp.id) &&
  29 + ntohs(tuple->src.u.icmp.id) <= ntohs(max->icmp.id);
  30 +}
  31 +
  32 +static void
  33 +icmpv6_unique_tuple(const struct nf_nat_l3proto *l3proto,
  34 + struct nf_conntrack_tuple *tuple,
  35 + const struct nf_nat_range *range,
  36 + enum nf_nat_manip_type maniptype,
  37 + const struct nf_conn *ct)
  38 +{
  39 + static u16 id;
  40 + unsigned int range_size;
  41 + unsigned int i;
  42 +
  43 + range_size = ntohs(range->max_proto.icmp.id) -
  44 + ntohs(range->min_proto.icmp.id) + 1;
  45 +
  46 + if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED))
  47 + range_size = 0xffff;
  48 +
  49 + for (i = 0; ; ++id) {
  50 + tuple->src.u.icmp.id = htons(ntohs(range->min_proto.icmp.id) +
  51 + (id % range_size));
  52 + if (++i == range_size || !nf_nat_used_tuple(tuple, ct))
  53 + return;
  54 + }
  55 +}
  56 +
  57 +static bool
  58 +icmpv6_manip_pkt(struct sk_buff *skb,
  59 + const struct nf_nat_l3proto *l3proto,
  60 + unsigned int iphdroff, unsigned int hdroff,
  61 + const struct nf_conntrack_tuple *tuple,
  62 + enum nf_nat_manip_type maniptype)
  63 +{
  64 + struct icmp6hdr *hdr;
  65 +
  66 + if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
  67 + return false;
  68 +
  69 + hdr = (struct icmp6hdr *)(skb->data + hdroff);
  70 + l3proto->csum_update(skb, iphdroff, &hdr->icmp6_cksum,
  71 + tuple, maniptype);
  72 + if (hdr->icmp6_code == ICMPV6_ECHO_REQUEST ||
  73 + hdr->icmp6_code == ICMPV6_ECHO_REPLY) {
  74 + inet_proto_csum_replace2(&hdr->icmp6_cksum, skb,
  75 + hdr->icmp6_identifier,
  76 + tuple->src.u.icmp.id, 0);
  77 + hdr->icmp6_identifier = tuple->src.u.icmp.id;
  78 + }
  79 + return true;
  80 +}
  81 +
  82 +const struct nf_nat_l4proto nf_nat_l4proto_icmpv6 = {
  83 + .l4proto = IPPROTO_ICMPV6,
  84 + .manip_pkt = icmpv6_manip_pkt,
  85 + .in_range = icmpv6_in_range,
  86 + .unique_tuple = icmpv6_unique_tuple,
  87 +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
  88 + .nlattr_to_range = nf_nat_l4proto_nlattr_to_range,
  89 +#endif
  90 +};
net/netfilter/nf_nat_core.c
... ... @@ -696,6 +696,8 @@
696 696 static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = {
697 697 [CTA_NAT_V4_MINIP] = { .type = NLA_U32 },
698 698 [CTA_NAT_V4_MAXIP] = { .type = NLA_U32 },
  699 + [CTA_NAT_V6_MINIP] = { .len = sizeof(struct in6_addr) },
  700 + [CTA_NAT_V6_MAXIP] = { .len = sizeof(struct in6_addr) },
699 701 [CTA_NAT_PROTO] = { .type = NLA_NESTED },
700 702 };
701 703  
net/netfilter/xt_nat.c
... ... @@ -163,6 +163,9 @@
163 163 module_exit(xt_nat_exit);
164 164  
165 165 MODULE_LICENSE("GPL");
  166 +MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
166 167 MODULE_ALIAS("ipt_SNAT");
167 168 MODULE_ALIAS("ipt_DNAT");
  169 +MODULE_ALIAS("ip6t_SNAT");
  170 +MODULE_ALIAS("ip6t_DNAT");