]> Pileus Git - ~andy/linux/blob - net/sched/act_nat.c
Merge tag 'batman-adv-for-davem' of git://git.open-mesh.org/linux-merge
[~andy/linux] / net / sched / act_nat.c
1 /*
2  * Stateless NAT actions
3  *
4  * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  */
11
12 #include <linux/errno.h>
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/netfilter.h>
17 #include <linux/rtnetlink.h>
18 #include <linux/skbuff.h>
19 #include <linux/slab.h>
20 #include <linux/spinlock.h>
21 #include <linux/string.h>
22 #include <linux/tc_act/tc_nat.h>
23 #include <net/act_api.h>
24 #include <net/icmp.h>
25 #include <net/ip.h>
26 #include <net/netlink.h>
27 #include <net/tc_act/tc_nat.h>
28 #include <net/tcp.h>
29 #include <net/udp.h>
30
31
32 #define NAT_TAB_MASK    15
33
34 static struct tcf_hashinfo nat_hash_info;
35
36 static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = {
37         [TCA_NAT_PARMS] = { .len = sizeof(struct tc_nat) },
38 };
39
40 static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
41                         struct tc_action *a, int ovr, int bind)
42 {
43         struct nlattr *tb[TCA_NAT_MAX + 1];
44         struct tc_nat *parm;
45         int ret = 0, err;
46         struct tcf_nat *p;
47         struct tcf_common *pc;
48
49         if (nla == NULL)
50                 return -EINVAL;
51
52         err = nla_parse_nested(tb, TCA_NAT_MAX, nla, nat_policy);
53         if (err < 0)
54                 return err;
55
56         if (tb[TCA_NAT_PARMS] == NULL)
57                 return -EINVAL;
58         parm = nla_data(tb[TCA_NAT_PARMS]);
59
60         pc = tcf_hash_check(parm->index, a, bind, &nat_hash_info);
61         if (!pc) {
62                 pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind,
63                                      &nat_hash_info);
64                 if (IS_ERR(pc))
65                         return PTR_ERR(pc);
66                 ret = ACT_P_CREATED;
67         } else {
68                 if (bind)
69                         return 0;
70                 tcf_hash_release(pc, bind, &nat_hash_info);
71                 if (!ovr)
72                         return -EEXIST;
73         }
74         p = to_tcf_nat(pc);
75
76         spin_lock_bh(&p->tcf_lock);
77         p->old_addr = parm->old_addr;
78         p->new_addr = parm->new_addr;
79         p->mask = parm->mask;
80         p->flags = parm->flags;
81
82         p->tcf_action = parm->action;
83         spin_unlock_bh(&p->tcf_lock);
84
85         if (ret == ACT_P_CREATED)
86                 tcf_hash_insert(pc, &nat_hash_info);
87
88         return ret;
89 }
90
91 static int tcf_nat_cleanup(struct tc_action *a, int bind)
92 {
93         struct tcf_nat *p = a->priv;
94
95         return tcf_hash_release(&p->common, bind, &nat_hash_info);
96 }
97
98 static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
99                    struct tcf_result *res)
100 {
101         struct tcf_nat *p = a->priv;
102         struct iphdr *iph;
103         __be32 old_addr;
104         __be32 new_addr;
105         __be32 mask;
106         __be32 addr;
107         int egress;
108         int action;
109         int ihl;
110         int noff;
111
112         spin_lock(&p->tcf_lock);
113
114         p->tcf_tm.lastuse = jiffies;
115         old_addr = p->old_addr;
116         new_addr = p->new_addr;
117         mask = p->mask;
118         egress = p->flags & TCA_NAT_FLAG_EGRESS;
119         action = p->tcf_action;
120
121         bstats_update(&p->tcf_bstats, skb);
122
123         spin_unlock(&p->tcf_lock);
124
125         if (unlikely(action == TC_ACT_SHOT))
126                 goto drop;
127
128         noff = skb_network_offset(skb);
129         if (!pskb_may_pull(skb, sizeof(*iph) + noff))
130                 goto drop;
131
132         iph = ip_hdr(skb);
133
134         if (egress)
135                 addr = iph->saddr;
136         else
137                 addr = iph->daddr;
138
139         if (!((old_addr ^ addr) & mask)) {
140                 if (skb_cloned(skb) &&
141                     !skb_clone_writable(skb, sizeof(*iph) + noff) &&
142                     pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
143                         goto drop;
144
145                 new_addr &= mask;
146                 new_addr |= addr & ~mask;
147
148                 /* Rewrite IP header */
149                 iph = ip_hdr(skb);
150                 if (egress)
151                         iph->saddr = new_addr;
152                 else
153                         iph->daddr = new_addr;
154
155                 csum_replace4(&iph->check, addr, new_addr);
156         } else if ((iph->frag_off & htons(IP_OFFSET)) ||
157                    iph->protocol != IPPROTO_ICMP) {
158                 goto out;
159         }
160
161         ihl = iph->ihl * 4;
162
163         /* It would be nice to share code with stateful NAT. */
164         switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) {
165         case IPPROTO_TCP:
166         {
167                 struct tcphdr *tcph;
168
169                 if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) ||
170                     (skb_cloned(skb) &&
171                      !skb_clone_writable(skb, ihl + sizeof(*tcph) + noff) &&
172                      pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
173                         goto drop;
174
175                 tcph = (void *)(skb_network_header(skb) + ihl);
176                 inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, 1);
177                 break;
178         }
179         case IPPROTO_UDP:
180         {
181                 struct udphdr *udph;
182
183                 if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) ||
184                     (skb_cloned(skb) &&
185                      !skb_clone_writable(skb, ihl + sizeof(*udph) + noff) &&
186                      pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
187                         goto drop;
188
189                 udph = (void *)(skb_network_header(skb) + ihl);
190                 if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
191                         inet_proto_csum_replace4(&udph->check, skb, addr,
192                                                  new_addr, 1);
193                         if (!udph->check)
194                                 udph->check = CSUM_MANGLED_0;
195                 }
196                 break;
197         }
198         case IPPROTO_ICMP:
199         {
200                 struct icmphdr *icmph;
201
202                 if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + noff))
203                         goto drop;
204
205                 icmph = (void *)(skb_network_header(skb) + ihl);
206
207                 if ((icmph->type != ICMP_DEST_UNREACH) &&
208                     (icmph->type != ICMP_TIME_EXCEEDED) &&
209                     (icmph->type != ICMP_PARAMETERPROB))
210                         break;
211
212                 if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph) +
213                                         noff))
214                         goto drop;
215
216                 icmph = (void *)(skb_network_header(skb) + ihl);
217                 iph = (void *)(icmph + 1);
218                 if (egress)
219                         addr = iph->daddr;
220                 else
221                         addr = iph->saddr;
222
223                 if ((old_addr ^ addr) & mask)
224                         break;
225
226                 if (skb_cloned(skb) &&
227                     !skb_clone_writable(skb, ihl + sizeof(*icmph) +
228                                              sizeof(*iph) + noff) &&
229                     pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
230                         goto drop;
231
232                 icmph = (void *)(skb_network_header(skb) + ihl);
233                 iph = (void *)(icmph + 1);
234
235                 new_addr &= mask;
236                 new_addr |= addr & ~mask;
237
238                 /* XXX Fix up the inner checksums. */
239                 if (egress)
240                         iph->daddr = new_addr;
241                 else
242                         iph->saddr = new_addr;
243
244                 inet_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr,
245                                          0);
246                 break;
247         }
248         default:
249                 break;
250         }
251
252 out:
253         return action;
254
255 drop:
256         spin_lock(&p->tcf_lock);
257         p->tcf_qstats.drops++;
258         spin_unlock(&p->tcf_lock);
259         return TC_ACT_SHOT;
260 }
261
262 static int tcf_nat_dump(struct sk_buff *skb, struct tc_action *a,
263                         int bind, int ref)
264 {
265         unsigned char *b = skb_tail_pointer(skb);
266         struct tcf_nat *p = a->priv;
267         struct tc_nat opt = {
268                 .old_addr = p->old_addr,
269                 .new_addr = p->new_addr,
270                 .mask     = p->mask,
271                 .flags    = p->flags,
272
273                 .index    = p->tcf_index,
274                 .action   = p->tcf_action,
275                 .refcnt   = p->tcf_refcnt - ref,
276                 .bindcnt  = p->tcf_bindcnt - bind,
277         };
278         struct tcf_t t;
279
280         if (nla_put(skb, TCA_NAT_PARMS, sizeof(opt), &opt))
281                 goto nla_put_failure;
282         t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install);
283         t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse);
284         t.expires = jiffies_to_clock_t(p->tcf_tm.expires);
285         if (nla_put(skb, TCA_NAT_TM, sizeof(t), &t))
286                 goto nla_put_failure;
287
288         return skb->len;
289
290 nla_put_failure:
291         nlmsg_trim(skb, b);
292         return -1;
293 }
294
295 static struct tc_action_ops act_nat_ops = {
296         .kind           =       "nat",
297         .hinfo          =       &nat_hash_info,
298         .type           =       TCA_ACT_NAT,
299         .capab          =       TCA_CAP_NONE,
300         .owner          =       THIS_MODULE,
301         .act            =       tcf_nat,
302         .dump           =       tcf_nat_dump,
303         .cleanup        =       tcf_nat_cleanup,
304         .init           =       tcf_nat_init,
305 };
306
307 MODULE_DESCRIPTION("Stateless NAT actions");
308 MODULE_LICENSE("GPL");
309
310 static int __init nat_init_module(void)
311 {
312         int err = tcf_hashinfo_init(&nat_hash_info, NAT_TAB_MASK);
313         if (err)
314                 return err;
315         return tcf_register_action(&act_nat_ops);
316 }
317
318 static void __exit nat_cleanup_module(void)
319 {
320         tcf_unregister_action(&act_nat_ops);
321         tcf_hashinfo_destroy(&nat_hash_info);
322 }
323
324 module_init(nat_init_module);
325 module_exit(nat_cleanup_module);