blob: 88c96b10684c92efb7591aea9f584a604478ffae [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Extension Header handling for IPv6
3 * Linux INET6 implementation
4 *
5 * Authors:
6 * Pedro Roque <roque@di.fc.ul.pt>
7 * Andi Kleen <ak@muc.de>
8 * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
9 *
10 * $Id: exthdrs.c,v 1.13 2001/06/19 15:58:56 davem Exp $
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
16 */
17
18/* Changes:
19 * yoshfuji : ensure not to overrun while parsing
20 * tlv options.
21 * Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs().
22 * YOSHIFUJI Hideaki @USAGI Register inbound extension header
23 * handlers as inet6_protocol{}.
24 */
25
26#include <linux/errno.h>
27#include <linux/types.h>
28#include <linux/socket.h>
29#include <linux/sockios.h>
30#include <linux/sched.h>
31#include <linux/net.h>
32#include <linux/netdevice.h>
33#include <linux/in6.h>
34#include <linux/icmpv6.h>
35
36#include <net/sock.h>
37#include <net/snmp.h>
38
39#include <net/ipv6.h>
40#include <net/protocol.h>
41#include <net/transp_v6.h>
42#include <net/rawv6.h>
43#include <net/ndisc.h>
44#include <net/ip6_route.h>
45#include <net/addrconf.h>
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -070046#ifdef CONFIG_IPV6_MIP6
47#include <net/xfrm.h>
48#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
50#include <asm/uaccess.h>
51
Masahide NAKAMURAc61a4042006-08-23 19:18:35 -070052int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
53{
54 int packet_len = skb->tail - skb->nh.raw;
55 struct ipv6_opt_hdr *hdr;
56 int len;
57
58 if (offset + 2 > packet_len)
59 goto bad;
60 hdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
61 len = ((hdr->hdrlen + 1) << 3);
62
63 if (offset + len > packet_len)
64 goto bad;
65
66 offset += 2;
67 len -= 2;
68
69 while (len > 0) {
70 int opttype = skb->nh.raw[offset];
71 int optlen;
72
73 if (opttype == type)
74 return offset;
75
76 switch (opttype) {
77 case IPV6_TLV_PAD0:
78 optlen = 1;
79 break;
80 default:
81 optlen = skb->nh.raw[offset + 1] + 2;
82 if (optlen > len)
83 goto bad;
84 break;
85 }
86 offset += optlen;
87 len -= optlen;
88 }
89 /* not_found */
Masahide NAKAMURAc61a4042006-08-23 19:18:35 -070090 bad:
91 return -1;
92}
93
Linus Torvalds1da177e2005-04-16 15:20:36 -070094/*
95 * Parsing tlv encoded headers.
96 *
97 * Parsing function "func" returns 1, if parsing succeed
98 * and 0, if it failed.
99 * It MUST NOT touch skb->h.
100 */
101
102struct tlvtype_proc {
103 int type;
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700104 int (*func)(struct sk_buff **skbp, int offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105};
106
107/*********************
108 Generic functions
109 *********************/
110
111/* An unknown option is detected, decide what to do */
112
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700113static int ip6_tlvopt_unknown(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700115 struct sk_buff *skb = *skbp;
116
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 switch ((skb->nh.raw[optoff] & 0xC0) >> 6) {
118 case 0: /* ignore */
119 return 1;
120
121 case 1: /* drop packet */
122 break;
123
124 case 3: /* Send ICMP if not a multicast address and drop packet */
125 /* Actually, it is redundant check. icmp_send
126 will recheck in any case.
127 */
128 if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
129 break;
130 case 2: /* send ICMP PARM PROB regardless and drop packet */
131 icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
132 return 0;
133 };
134
135 kfree_skb(skb);
136 return 0;
137}
138
139/* Parse tlv encoded option header (hop-by-hop or destination) */
140
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700141static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700143 struct sk_buff *skb = *skbp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 struct tlvtype_proc *curr;
145 int off = skb->h.raw - skb->nh.raw;
146 int len = ((skb->h.raw[1]+1)<<3);
147
148 if ((skb->h.raw + len) - skb->data > skb_headlen(skb))
149 goto bad;
150
151 off += 2;
152 len -= 2;
153
154 while (len > 0) {
155 int optlen = skb->nh.raw[off+1]+2;
156
157 switch (skb->nh.raw[off]) {
158 case IPV6_TLV_PAD0:
159 optlen = 1;
160 break;
161
162 case IPV6_TLV_PADN:
163 break;
164
165 default: /* Other TLV code so scan list */
166 if (optlen > len)
167 goto bad;
168 for (curr=procs; curr->type >= 0; curr++) {
169 if (curr->type == skb->nh.raw[off]) {
170 /* type specific length/alignment
171 checks will be performed in the
172 func(). */
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700173 if (curr->func(skbp, off) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 return 0;
175 break;
176 }
177 }
178 if (curr->type < 0) {
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700179 if (ip6_tlvopt_unknown(skbp, off) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 return 0;
181 }
182 break;
183 }
184 off += optlen;
185 len -= optlen;
186 }
187 if (len == 0)
188 return 1;
189bad:
190 kfree_skb(skb);
191 return 0;
192}
193
194/*****************************
195 Destination options header.
196 *****************************/
197
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700198#ifdef CONFIG_IPV6_MIP6
199static int ipv6_dest_hao(struct sk_buff **skbp, int optoff)
200{
201 struct sk_buff *skb = *skbp;
202 struct ipv6_destopt_hao *hao;
203 struct inet6_skb_parm *opt = IP6CB(skb);
204 struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb->nh.raw;
205 struct in6_addr tmp_addr;
206 int ret;
207
208 if (opt->dsthao) {
209 LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n");
210 goto discard;
211 }
212 opt->dsthao = opt->dst1;
213 opt->dst1 = 0;
214
215 hao = (struct ipv6_destopt_hao *)(skb->nh.raw + optoff);
216
217 if (hao->length != 16) {
218 LIMIT_NETDEBUG(
219 KERN_DEBUG "hao invalid option length = %d\n", hao->length);
220 goto discard;
221 }
222
223 if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
224 LIMIT_NETDEBUG(
225 KERN_DEBUG "hao is not an unicast addr: " NIP6_FMT "\n", NIP6(hao->addr));
226 goto discard;
227 }
228
229 ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
230 (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
231 if (unlikely(ret < 0))
232 goto discard;
233
234 if (skb_cloned(skb)) {
235 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700236 struct inet6_skb_parm *opt2;
237
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700238 if (skb2 == NULL)
239 goto discard;
240
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700241 opt2 = IP6CB(skb2);
242 memcpy(opt2, opt, sizeof(*opt2));
243
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700244 kfree_skb(skb);
245
246 /* update all variable using below by copied skbuff */
247 *skbp = skb = skb2;
248 hao = (struct ipv6_destopt_hao *)(skb2->nh.raw + optoff);
249 ipv6h = (struct ipv6hdr *)skb2->nh.raw;
250 }
251
252 if (skb->ip_summed == CHECKSUM_COMPLETE)
253 skb->ip_summed = CHECKSUM_NONE;
254
255 ipv6_addr_copy(&tmp_addr, &ipv6h->saddr);
256 ipv6_addr_copy(&ipv6h->saddr, &hao->addr);
257 ipv6_addr_copy(&hao->addr, &tmp_addr);
258
259 if (skb->tstamp.off_sec == 0)
260 __net_timestamp(skb);
261
262 return 1;
263
264 discard:
265 kfree_skb(skb);
266 return 0;
267}
268#endif
269
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270static struct tlvtype_proc tlvprocdestopt_lst[] = {
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700271#ifdef CONFIG_IPV6_MIP6
272 {
273 .type = IPV6_TLV_HAO,
274 .func = ipv6_dest_hao,
275 },
276#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 {-1, NULL}
278};
279
Patrick McHardy951dbc82006-01-06 23:02:34 -0800280static int ipv6_destopt_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281{
282 struct sk_buff *skb = *skbp;
283 struct inet6_skb_parm *opt = IP6CB(skb);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700284#ifdef CONFIG_IPV6_MIP6
285 __u16 dstbuf;
286#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287
288 if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
289 !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
290 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
291 kfree_skb(skb);
292 return -1;
293 }
294
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900295 opt->lastopt = skb->h.raw - skb->nh.raw;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 opt->dst1 = skb->h.raw - skb->nh.raw;
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700297#ifdef CONFIG_IPV6_MIP6
298 dstbuf = opt->dst1;
299#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700301 if (ip6_parse_tlv(tlvprocdestopt_lst, skbp)) {
302 skb = *skbp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 skb->h.raw += ((skb->h.raw[1]+1)<<3);
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700304 opt = IP6CB(skb);
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700305#ifdef CONFIG_IPV6_MIP6
306 opt->nhoff = dstbuf;
307#else
Patrick McHardy951dbc82006-01-06 23:02:34 -0800308 opt->nhoff = opt->dst1;
Masahide NAKAMURAa831f5b2006-08-23 19:24:48 -0700309#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 return 1;
311 }
312
313 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
314 return -1;
315}
316
317static struct inet6_protocol destopt_protocol = {
318 .handler = ipv6_destopt_rcv,
Herbert Xuadcfc7d2006-06-30 13:36:15 -0700319 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320};
321
322void __init ipv6_destopt_init(void)
323{
324 if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0)
325 printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");
326}
327
328/********************************
329 NONE header. No data in packet.
330 ********************************/
331
Patrick McHardy951dbc82006-01-06 23:02:34 -0800332static int ipv6_nodata_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
334 struct sk_buff *skb = *skbp;
335
336 kfree_skb(skb);
337 return 0;
338}
339
340static struct inet6_protocol nodata_protocol = {
341 .handler = ipv6_nodata_rcv,
342 .flags = INET6_PROTO_NOPOLICY,
343};
344
345void __init ipv6_nodata_init(void)
346{
347 if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0)
348 printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");
349}
350
351/********************************
352 Routing header.
353 ********************************/
354
Patrick McHardy951dbc82006-01-06 23:02:34 -0800355static int ipv6_rthdr_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356{
357 struct sk_buff *skb = *skbp;
358 struct inet6_skb_parm *opt = IP6CB(skb);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700359 struct in6_addr *addr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 struct in6_addr daddr;
361 int n, i;
362
363 struct ipv6_rt_hdr *hdr;
364 struct rt0_hdr *rthdr;
365
366 if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
367 !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
368 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
369 kfree_skb(skb);
370 return -1;
371 }
372
373 hdr = (struct ipv6_rt_hdr *) skb->h.raw;
374
375 if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) ||
376 skb->pkt_type != PACKET_HOST) {
377 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
378 kfree_skb(skb);
379 return -1;
380 }
381
382looped_back:
383 if (hdr->segments_left == 0) {
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700384 switch (hdr->type) {
385#ifdef CONFIG_IPV6_MIP6
386 case IPV6_SRCRT_TYPE_2:
387 /* Silently discard type 2 header unless it was
388 * processed by own
389 */
390 if (!addr) {
391 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
392 kfree_skb(skb);
393 return -1;
394 }
395 break;
396#endif
397 default:
398 break;
399 }
400
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900401 opt->lastopt = skb->h.raw - skb->nh.raw;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 opt->srcrt = skb->h.raw - skb->nh.raw;
403 skb->h.raw += (hdr->hdrlen + 1) << 3;
404 opt->dst0 = opt->dst1;
405 opt->dst1 = 0;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800406 opt->nhoff = (&hdr->nexthdr) - skb->nh.raw;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 return 1;
408 }
409
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700410 switch (hdr->type) {
411 case IPV6_SRCRT_TYPE_0:
412 if (hdr->hdrlen & 0x01) {
413 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
414 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw);
415 return -1;
416 }
417 break;
418#ifdef CONFIG_IPV6_MIP6
419 case IPV6_SRCRT_TYPE_2:
420 /* Silently discard invalid RTH type 2 */
421 if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
422 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
423 kfree_skb(skb);
424 return -1;
425 }
426 break;
427#endif
428 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
430 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);
431 return -1;
432 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
434 /*
435 * This is the routing header forwarding algorithm from
436 * RFC 2460, page 16.
437 */
438
439 n = hdr->hdrlen >> 1;
440
441 if (hdr->segments_left > n) {
442 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
443 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);
444 return -1;
445 }
446
447 /* We are about to mangle packet header. Be careful!
448 Do not damage packets queued somewhere.
449 */
450 if (skb_cloned(skb)) {
451 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
452 kfree_skb(skb);
453 /* the copy is a forwarded packet */
454 if (skb2 == NULL) {
455 IP6_INC_STATS_BH(IPSTATS_MIB_OUTDISCARDS);
456 return -1;
457 }
458 *skbp = skb = skb2;
459 opt = IP6CB(skb2);
460 hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
461 }
462
Patrick McHardy84fa7932006-08-29 16:44:56 -0700463 if (skb->ip_summed == CHECKSUM_COMPLETE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 skb->ip_summed = CHECKSUM_NONE;
465
466 i = n - --hdr->segments_left;
467
468 rthdr = (struct rt0_hdr *) hdr;
469 addr = rthdr->addr;
470 addr += i - 1;
471
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700472 switch (hdr->type) {
473#ifdef CONFIG_IPV6_MIP6
474 case IPV6_SRCRT_TYPE_2:
475 if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
476 (xfrm_address_t *)&skb->nh.ipv6h->saddr,
477 IPPROTO_ROUTING) < 0) {
478 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
479 kfree_skb(skb);
480 return -1;
481 }
482 if (!ipv6_chk_home_addr(addr)) {
483 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
484 kfree_skb(skb);
485 return -1;
486 }
487 break;
488#endif
489 default:
490 break;
491 }
492
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 if (ipv6_addr_is_multicast(addr)) {
494 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
495 kfree_skb(skb);
496 return -1;
497 }
498
499 ipv6_addr_copy(&daddr, addr);
500 ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
501 ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
502
503 dst_release(xchg(&skb->dst, NULL));
504 ip6_route_input(skb);
505 if (skb->dst->error) {
506 skb_push(skb, skb->data - skb->nh.raw);
507 dst_input(skb);
508 return -1;
509 }
510
511 if (skb->dst->dev->flags&IFF_LOOPBACK) {
512 if (skb->nh.ipv6h->hop_limit <= 1) {
513 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
514 icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
515 0, skb->dev);
516 kfree_skb(skb);
517 return -1;
518 }
519 skb->nh.ipv6h->hop_limit--;
520 goto looped_back;
521 }
522
523 skb_push(skb, skb->data - skb->nh.raw);
524 dst_input(skb);
525 return -1;
526}
527
528static struct inet6_protocol rthdr_protocol = {
529 .handler = ipv6_rthdr_rcv,
Herbert Xuadcfc7d2006-06-30 13:36:15 -0700530 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531};
532
533void __init ipv6_rthdr_init(void)
534{
535 if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
536 printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
537};
538
539/*
540 This function inverts received rthdr.
541 NOTE: specs allow to make it automatically only if
542 packet authenticated.
543
544 I will not discuss it here (though, I am really pissed off at
545 this stupid requirement making rthdr idea useless)
546
547 Actually, it creates severe problems for us.
548 Embryonic requests has no associated sockets,
549 so that user have no control over it and
550 cannot not only to set reply options, but
551 even to know, that someone wants to connect
552 without success. :-(
553
554 For now we need to test the engine, so that I created
555 temporary (or permanent) backdoor.
556 If listening socket set IPV6_RTHDR to 2, then we invert header.
557 --ANK (980729)
558 */
559
560struct ipv6_txoptions *
561ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
562{
563 /* Received rthdr:
564
565 [ H1 -> H2 -> ... H_prev ] daddr=ME
566
567 Inverted result:
568 [ H_prev -> ... -> H1 ] daddr =sender
569
570 Note, that IP output engine will rewrite this rthdr
571 by rotating it left by one addr.
572 */
573
574 int n, i;
575 struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
576 struct rt0_hdr *irthdr;
577 struct ipv6_txoptions *opt;
578 int hdrlen = ipv6_optlen(hdr);
579
580 if (hdr->segments_left ||
581 hdr->type != IPV6_SRCRT_TYPE_0 ||
582 hdr->hdrlen & 0x01)
583 return NULL;
584
585 n = hdr->hdrlen >> 1;
586 opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
587 if (opt == NULL)
588 return NULL;
589 memset(opt, 0, sizeof(*opt));
590 opt->tot_len = sizeof(*opt) + hdrlen;
591 opt->srcrt = (void*)(opt+1);
592 opt->opt_nflen = hdrlen;
593
594 memcpy(opt->srcrt, hdr, sizeof(*hdr));
595 irthdr = (struct rt0_hdr*)opt->srcrt;
Brian Haleye6df4392005-09-10 00:15:06 -0700596 irthdr->reserved = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 opt->srcrt->segments_left = n;
598 for (i=0; i<n; i++)
599 memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
600 return opt;
601}
602
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800603EXPORT_SYMBOL_GPL(ipv6_invert_rthdr);
604
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605/**********************************
606 Hop-by-hop options.
607 **********************************/
608
609/* Router Alert as of RFC 2711 */
610
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700611static int ipv6_hop_ra(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700613 struct sk_buff *skb = *skbp;
614
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 if (skb->nh.raw[optoff+1] == 2) {
616 IP6CB(skb)->ra = optoff;
617 return 1;
618 }
Patrick McHardy64ce2072005-08-09 20:50:53 -0700619 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
620 skb->nh.raw[optoff+1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 kfree_skb(skb);
622 return 0;
623}
624
625/* Jumbo payload */
626
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700627static int ipv6_hop_jumbo(struct sk_buff **skbp, int optoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700629 struct sk_buff *skb = *skbp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 u32 pkt_len;
631
632 if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) {
Patrick McHardy64ce2072005-08-09 20:50:53 -0700633 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
634 skb->nh.raw[optoff+1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
636 goto drop;
637 }
638
639 pkt_len = ntohl(*(u32*)(skb->nh.raw+optoff+2));
640 if (pkt_len <= IPV6_MAXPLEN) {
641 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
642 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
643 return 0;
644 }
645 if (skb->nh.ipv6h->payload_len) {
646 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
647 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
648 return 0;
649 }
650
651 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
652 IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
653 goto drop;
654 }
Stephen Hemminger42ca89c2005-09-08 12:57:43 -0700655
656 if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
657 goto drop;
658
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 return 1;
660
661drop:
662 kfree_skb(skb);
663 return 0;
664}
665
666static struct tlvtype_proc tlvprochopopt_lst[] = {
667 {
668 .type = IPV6_TLV_ROUTERALERT,
669 .func = ipv6_hop_ra,
670 },
671 {
672 .type = IPV6_TLV_JUMBO,
673 .func = ipv6_hop_jumbo,
674 },
675 { -1, }
676};
677
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700678int ipv6_parse_hopopts(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679{
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700680 struct sk_buff *skb = *skbp;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800681 struct inet6_skb_parm *opt = IP6CB(skb);
682
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700683 /*
684 * skb->nh.raw is equal to skb->data, and
685 * skb->h.raw - skb->nh.raw is always equal to
686 * sizeof(struct ipv6hdr) by definition of
687 * hop-by-hop options.
688 */
689 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
690 !pskb_may_pull(skb, sizeof(struct ipv6hdr) + ((skb->h.raw[1] + 1) << 3))) {
691 kfree_skb(skb);
692 return -1;
693 }
694
Patrick McHardy951dbc82006-01-06 23:02:34 -0800695 opt->hop = sizeof(struct ipv6hdr);
Masahide NAKAMURAa80ff032006-08-23 19:19:50 -0700696 if (ip6_parse_tlv(tlvprochopopt_lst, skbp)) {
697 skb = *skbp;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800698 skb->h.raw += (skb->h.raw[1]+1)<<3;
Masahide NAKAMURAdc435e62006-08-31 15:18:49 -0700699 opt = IP6CB(skb);
Patrick McHardy951dbc82006-01-06 23:02:34 -0800700 opt->nhoff = sizeof(struct ipv6hdr);
YOSHIFUJI Hideakib8097392006-04-18 14:48:45 -0700701 return 1;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800702 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 return -1;
704}
705
706/*
707 * Creating outbound headers.
708 *
709 * "build" functions work when skb is filled from head to tail (datagram)
710 * "push" functions work when headers are added from tail to head (tcp)
711 *
712 * In both cases we assume, that caller reserved enough room
713 * for headers.
714 */
715
716static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
717 struct ipv6_rt_hdr *opt,
718 struct in6_addr **addr_p)
719{
720 struct rt0_hdr *phdr, *ihdr;
721 int hops;
722
723 ihdr = (struct rt0_hdr *) opt;
724
725 phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
726 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
727
728 hops = ihdr->rt_hdr.hdrlen >> 1;
729
730 if (hops > 1)
731 memcpy(phdr->addr, ihdr->addr + 1,
732 (hops - 1) * sizeof(struct in6_addr));
733
734 ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
735 *addr_p = ihdr->addr;
736
737 phdr->rt_hdr.nexthdr = *proto;
738 *proto = NEXTHDR_ROUTING;
739}
740
741static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
742{
743 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
744
745 memcpy(h, opt, ipv6_optlen(opt));
746 h->nexthdr = *proto;
747 *proto = type;
748}
749
750void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
751 u8 *proto,
752 struct in6_addr **daddr)
753{
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900754 if (opt->srcrt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900756 /*
757 * IPV6_RTHDRDSTOPTS is ignored
758 * unless IPV6_RTHDR is set (RFC3542).
759 */
760 if (opt->dst0opt)
761 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
762 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 if (opt->hopopt)
764 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
765}
766
767void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
768{
769 if (opt->dst1opt)
770 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
771}
772
773struct ipv6_txoptions *
774ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
775{
776 struct ipv6_txoptions *opt2;
777
778 opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
779 if (opt2) {
780 long dif = (char*)opt2 - (char*)opt;
781 memcpy(opt2, opt, opt->tot_len);
782 if (opt2->hopopt)
783 *((char**)&opt2->hopopt) += dif;
784 if (opt2->dst0opt)
785 *((char**)&opt2->dst0opt) += dif;
786 if (opt2->dst1opt)
787 *((char**)&opt2->dst1opt) += dif;
788 if (opt2->srcrt)
789 *((char**)&opt2->srcrt) += dif;
790 }
791 return opt2;
792}
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900793
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800794EXPORT_SYMBOL_GPL(ipv6_dup_options);
795
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900796static int ipv6_renew_option(void *ohdr,
797 struct ipv6_opt_hdr __user *newopt, int newoptlen,
798 int inherit,
799 struct ipv6_opt_hdr **hdr,
800 char **p)
801{
802 if (inherit) {
803 if (ohdr) {
804 memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
805 *hdr = (struct ipv6_opt_hdr *)*p;
806 *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
807 }
808 } else {
809 if (newopt) {
810 if (copy_from_user(*p, newopt, newoptlen))
811 return -EFAULT;
812 *hdr = (struct ipv6_opt_hdr *)*p;
813 if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
814 return -EINVAL;
815 *p += CMSG_ALIGN(newoptlen);
816 }
817 }
818 return 0;
819}
820
821struct ipv6_txoptions *
822ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
823 int newtype,
824 struct ipv6_opt_hdr __user *newopt, int newoptlen)
825{
826 int tot_len = 0;
827 char *p;
828 struct ipv6_txoptions *opt2;
829 int err;
830
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700831 if (opt) {
832 if (newtype != IPV6_HOPOPTS && opt->hopopt)
833 tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
834 if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
835 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
836 if (newtype != IPV6_RTHDR && opt->srcrt)
837 tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
838 if (newtype != IPV6_DSTOPTS && opt->dst1opt)
839 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
840 }
841
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900842 if (newopt && newoptlen)
843 tot_len += CMSG_ALIGN(newoptlen);
844
845 if (!tot_len)
846 return NULL;
847
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900848 tot_len += sizeof(*opt2);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900849 opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
850 if (!opt2)
851 return ERR_PTR(-ENOBUFS);
852
853 memset(opt2, 0, tot_len);
854
855 opt2->tot_len = tot_len;
856 p = (char *)(opt2 + 1);
857
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700858 err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900859 newtype != IPV6_HOPOPTS,
860 &opt2->hopopt, &p);
861 if (err)
862 goto out;
863
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700864 err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900865 newtype != IPV6_RTHDRDSTOPTS,
866 &opt2->dst0opt, &p);
867 if (err)
868 goto out;
869
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700870 err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900871 newtype != IPV6_RTHDR,
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700872 (struct ipv6_opt_hdr **)&opt2->srcrt, &p);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900873 if (err)
874 goto out;
875
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700876 err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900877 newtype != IPV6_DSTOPTS,
878 &opt2->dst1opt, &p);
879 if (err)
880 goto out;
881
882 opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
883 (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
884 (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
885 opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
886
887 return opt2;
888out:
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900889 sock_kfree_s(sk, opt2, opt2->tot_len);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900890 return ERR_PTR(err);
891}
892
YOSHIFUJI Hideakidf9890c2005-11-20 12:23:18 +0900893struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
894 struct ipv6_txoptions *opt)
895{
896 /*
897 * ignore the dest before srcrt unless srcrt is being included.
898 * --yoshfuji
899 */
900 if (opt && opt->dst0opt && !opt->srcrt) {
901 if (opt_space != opt) {
902 memcpy(opt_space, opt, sizeof(*opt_space));
903 opt = opt_space;
904 }
905 opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
906 opt->dst0opt = NULL;
907 }
908
909 return opt;
910}
911