]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/ipv6/netfilter/nft_chain_nat_ipv6.c
Merge remote-tracking branch 'net-next/master'
[karo-tx-linux.git] / net / ipv6 / netfilter / nft_chain_nat_ipv6.c
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");