]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/ipv6/mip6.c
a8adf891fe0e46aaac823c2b0603294966f6177c
[karo-tx-linux.git] / net / ipv6 / mip6.c
1 /*
2  * Copyright (C)2003-2006 Helsinki University of Technology
3  * Copyright (C)2003-2006 USAGI/WIDE Project
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 /*
20  * Authors:
21  *      Noriaki TAKAMIYA @USAGI
22  *      Masahide NAKAMURA @USAGI
23  */
24
25 #include <linux/config.h>
26 #include <linux/module.h>
27 #include <linux/skbuff.h>
28 #include <linux/ipv6.h>
29 #include <net/ipv6.h>
30 #include <net/xfrm.h>
31 #include <net/mip6.h>
32
33 static xfrm_address_t *mip6_xfrm_addr(struct xfrm_state *x, xfrm_address_t *addr)
34 {
35         return x->coaddr;
36 }
37
38 static inline unsigned int calc_padlen(unsigned int len, unsigned int n)
39 {
40         return (n - len + 16) & 0x7;
41 }
42
43 static inline void *mip6_padn(__u8 *data, __u8 padlen)
44 {
45         if (!data)
46                 return NULL;
47         if (padlen == 1) {
48                 data[0] = MIP6_OPT_PAD_1;
49         } else if (padlen > 1) {
50                 data[0] = MIP6_OPT_PAD_N;
51                 data[1] = padlen - 2;
52                 if (padlen > 2)
53                         memset(data+2, 0, data[1]);
54         }
55         return data + padlen;
56 }
57
58 static int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb)
59 {
60         struct ipv6hdr *iph = skb->nh.ipv6h;
61         struct ipv6_destopt_hdr *destopt = (struct ipv6_destopt_hdr *)skb->data;
62
63         if (!ipv6_addr_equal(&iph->saddr, (struct in6_addr *)x->coaddr) &&
64             !ipv6_addr_any((struct in6_addr *)x->coaddr))
65                 return -ENOENT;
66
67         return destopt->nexthdr;
68 }
69
70 /* Destination Option Header is inserted.
71  * IP Header's src address is replaced with Home Address Option in
72  * Destination Option Header.
73  */
74 static int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb)
75 {
76         struct ipv6hdr *iph;
77         struct ipv6_destopt_hdr *dstopt;
78         struct ipv6_destopt_hao *hao;
79         u8 nexthdr;
80         int len;
81
82         iph = (struct ipv6hdr *)skb->data;
83         iph->payload_len = htons(skb->len - sizeof(*iph));
84
85         nexthdr = *skb->nh.raw;
86         *skb->nh.raw = IPPROTO_DSTOPTS;
87
88         dstopt = (struct ipv6_destopt_hdr *)skb->h.raw;
89         dstopt->nexthdr = nexthdr;
90
91         hao = mip6_padn((char *)(dstopt + 1),
92                         calc_padlen(sizeof(*dstopt), 6));
93
94         hao->type = IPV6_TLV_HAO;
95         hao->length = sizeof(*hao) - 2;
96         BUG_TRAP(hao->length == 16);
97
98         len = ((char *)hao - (char *)dstopt) + sizeof(*hao);
99
100         memcpy(&hao->addr, &iph->saddr, sizeof(hao->addr));
101         memcpy(&iph->saddr, x->coaddr, sizeof(iph->saddr));
102
103         BUG_TRAP(len == x->props.header_len);
104         dstopt->hdrlen = (x->props.header_len >> 3) - 1;
105
106         return 0;
107 }
108
109 static int mip6_destopt_offset(struct xfrm_state *x, struct sk_buff *skb,
110                                u8 **nexthdr)
111 {
112         u16 offset = sizeof(struct ipv6hdr);
113         struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1);
114         unsigned int packet_len = skb->tail - skb->nh.raw;
115         int found_rhdr = 0;
116
117         *nexthdr = &skb->nh.ipv6h->nexthdr;
118
119         while (offset + 1 <= packet_len) {
120
121                 switch (**nexthdr) {
122                 case NEXTHDR_HOP:
123                         break;
124                 case NEXTHDR_ROUTING:
125                         found_rhdr = 1;
126                         break;
127                 case NEXTHDR_DEST:
128                         /*
129                          * HAO MUST NOT appear more than once.
130                          * XXX: It is better to try to find by the end of
131                          * XXX: packet if HAO exists.
132                          */
133                         if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) {
134                                 LIMIT_NETDEBUG(KERN_WARNING "mip6: hao exists already, override\n");
135                                 return offset;
136                         }
137
138                         if (found_rhdr)
139                                 return offset;
140
141                         break;
142                 default:
143                         return offset;
144                 }
145
146                 offset += ipv6_optlen(exthdr);
147                 *nexthdr = &exthdr->nexthdr;
148                 exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
149         }
150
151         return offset;
152 }
153
154 static int mip6_destopt_init_state(struct xfrm_state *x)
155 {
156         if (x->id.spi) {
157                 printk(KERN_INFO "%s: spi is not 0: %u\n", __FUNCTION__,
158                        x->id.spi);
159                 return -EINVAL;
160         }
161         if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) {
162                 printk(KERN_INFO "%s: state's mode is not %u: %u\n",
163                        __FUNCTION__, XFRM_MODE_ROUTEOPTIMIZATION, x->props.mode);
164                 return -EINVAL;
165         }
166
167         x->props.header_len = sizeof(struct ipv6_destopt_hdr) +
168                 calc_padlen(sizeof(struct ipv6_destopt_hdr), 6) +
169                 sizeof(struct ipv6_destopt_hao);
170         BUG_TRAP(x->props.header_len == 24);
171
172         return 0;
173 }
174
175 /*
176  * Do nothing about destroying since it has no specific operation for
177  * destination options header unlike IPsec protocols.
178  */
179 static void mip6_destopt_destroy(struct xfrm_state *x)
180 {
181 }
182
183 static struct xfrm_type mip6_destopt_type =
184 {
185         .description    = "MIP6DESTOPT",
186         .owner          = THIS_MODULE,
187         .proto          = IPPROTO_DSTOPTS,
188         .flags          = XFRM_TYPE_NON_FRAGMENT,
189         .init_state     = mip6_destopt_init_state,
190         .destructor     = mip6_destopt_destroy,
191         .input          = mip6_destopt_input,
192         .output         = mip6_destopt_output,
193         .hdr_offset     = mip6_destopt_offset,
194         .local_addr     = mip6_xfrm_addr,
195 };
196
197 static int mip6_rthdr_input(struct xfrm_state *x, struct sk_buff *skb)
198 {
199         struct rt2_hdr *rt2 = (struct rt2_hdr *)skb->data;
200
201         if (!ipv6_addr_equal(&rt2->addr, (struct in6_addr *)x->coaddr) &&
202             !ipv6_addr_any((struct in6_addr *)x->coaddr))
203                 return -ENOENT;
204
205         return rt2->rt_hdr.nexthdr;
206 }
207
208 /* Routing Header type 2 is inserted.
209  * IP Header's dst address is replaced with Routing Header's Home Address.
210  */
211 static int mip6_rthdr_output(struct xfrm_state *x, struct sk_buff *skb)
212 {
213         struct ipv6hdr *iph;
214         struct rt2_hdr *rt2;
215         u8 nexthdr;
216
217         iph = (struct ipv6hdr *)skb->data;
218         iph->payload_len = htons(skb->len - sizeof(*iph));
219
220         nexthdr = *skb->nh.raw;
221         *skb->nh.raw = IPPROTO_ROUTING;
222
223         rt2 = (struct rt2_hdr *)skb->h.raw;
224         rt2->rt_hdr.nexthdr = nexthdr;
225         rt2->rt_hdr.hdrlen = (x->props.header_len >> 3) - 1;
226         rt2->rt_hdr.type = IPV6_SRCRT_TYPE_2;
227         rt2->rt_hdr.segments_left = 1;
228         memset(&rt2->reserved, 0, sizeof(rt2->reserved));
229
230         BUG_TRAP(rt2->rt_hdr.hdrlen == 2);
231
232         memcpy(&rt2->addr, &iph->daddr, sizeof(rt2->addr));
233         memcpy(&iph->daddr, x->coaddr, sizeof(iph->daddr));
234
235         return 0;
236 }
237
238 static int mip6_rthdr_offset(struct xfrm_state *x, struct sk_buff *skb,
239                              u8 **nexthdr)
240 {
241         u16 offset = sizeof(struct ipv6hdr);
242         struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1);
243         unsigned int packet_len = skb->tail - skb->nh.raw;
244         int found_rhdr = 0;
245
246         *nexthdr = &skb->nh.ipv6h->nexthdr;
247
248         while (offset + 1 <= packet_len) {
249
250                 switch (**nexthdr) {
251                 case NEXTHDR_HOP:
252                         break;
253                 case NEXTHDR_ROUTING:
254                         if (offset + 3 <= packet_len) {
255                                 struct ipv6_rt_hdr *rt;
256                                 rt = (struct ipv6_rt_hdr *)(skb->nh.raw + offset);
257                                 if (rt->type != 0)
258                                         return offset;
259                         }
260                         found_rhdr = 1;
261                         break;
262                 case NEXTHDR_DEST:
263                         if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
264                                 return offset;
265
266                         if (found_rhdr)
267                                 return offset;
268
269                         break;
270                 default:
271                         return offset;
272                 }
273
274                 offset += ipv6_optlen(exthdr);
275                 *nexthdr = &exthdr->nexthdr;
276                 exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
277         }
278
279         return offset;
280 }
281
282 static int mip6_rthdr_init_state(struct xfrm_state *x)
283 {
284         if (x->id.spi) {
285                 printk(KERN_INFO "%s: spi is not 0: %u\n", __FUNCTION__,
286                        x->id.spi);
287                 return -EINVAL;
288         }
289         if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) {
290                 printk(KERN_INFO "%s: state's mode is not %u: %u\n",
291                        __FUNCTION__, XFRM_MODE_ROUTEOPTIMIZATION, x->props.mode);
292                 return -EINVAL;
293         }
294
295         x->props.header_len = sizeof(struct rt2_hdr);
296
297         return 0;
298 }
299
300 /*
301  * Do nothing about destroying since it has no specific operation for routing
302  * header type 2 unlike IPsec protocols.
303  */
304 static void mip6_rthdr_destroy(struct xfrm_state *x)
305 {
306 }
307
308 static struct xfrm_type mip6_rthdr_type =
309 {
310         .description    = "MIP6RT",
311         .owner          = THIS_MODULE,
312         .proto          = IPPROTO_ROUTING,
313         .flags          = XFRM_TYPE_NON_FRAGMENT,
314         .init_state     = mip6_rthdr_init_state,
315         .destructor     = mip6_rthdr_destroy,
316         .input          = mip6_rthdr_input,
317         .output         = mip6_rthdr_output,
318         .hdr_offset     = mip6_rthdr_offset,
319         .remote_addr    = mip6_xfrm_addr,
320 };
321
322 int __init mip6_init(void)
323 {
324         printk(KERN_INFO "Mobile IPv6\n");
325
326         if (xfrm_register_type(&mip6_destopt_type, AF_INET6) < 0) {
327                 printk(KERN_INFO "%s: can't add xfrm type(destopt)\n", __FUNCTION__);
328                 goto mip6_destopt_xfrm_fail;
329         }
330         if (xfrm_register_type(&mip6_rthdr_type, AF_INET6) < 0) {
331                 printk(KERN_INFO "%s: can't add xfrm type(rthdr)\n", __FUNCTION__);
332                 goto mip6_rthdr_xfrm_fail;
333         }
334         return 0;
335
336  mip6_rthdr_xfrm_fail:
337         xfrm_unregister_type(&mip6_destopt_type, AF_INET6);
338  mip6_destopt_xfrm_fail:
339         return -EAGAIN;
340 }
341
342 void __exit mip6_fini(void)
343 {
344         if (xfrm_unregister_type(&mip6_rthdr_type, AF_INET6) < 0)
345                 printk(KERN_INFO "%s: can't remove xfrm type(rthdr)\n", __FUNCTION__);
346         if (xfrm_unregister_type(&mip6_destopt_type, AF_INET6) < 0)
347                 printk(KERN_INFO "%s: can't remove xfrm type(destopt)\n", __FUNCTION__);
348 }