blob: 50ff49e518bcd846613731c7482c96657bac51bd [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 */
90 return -1;
91 bad:
92 return -1;
93}
94
Linus Torvalds1da177e2005-04-16 15:20:36 -070095/*
96 * Parsing tlv encoded headers.
97 *
98 * Parsing function "func" returns 1, if parsing succeed
99 * and 0, if it failed.
100 * It MUST NOT touch skb->h.
101 */
102
103struct tlvtype_proc {
104 int type;
105 int (*func)(struct sk_buff *skb, int offset);
106};
107
108/*********************
109 Generic functions
110 *********************/
111
112/* An unknown option is detected, decide what to do */
113
114static int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff)
115{
116 switch ((skb->nh.raw[optoff] & 0xC0) >> 6) {
117 case 0: /* ignore */
118 return 1;
119
120 case 1: /* drop packet */
121 break;
122
123 case 3: /* Send ICMP if not a multicast address and drop packet */
124 /* Actually, it is redundant check. icmp_send
125 will recheck in any case.
126 */
127 if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
128 break;
129 case 2: /* send ICMP PARM PROB regardless and drop packet */
130 icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
131 return 0;
132 };
133
134 kfree_skb(skb);
135 return 0;
136}
137
138/* Parse tlv encoded option header (hop-by-hop or destination) */
139
140static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb)
141{
142 struct tlvtype_proc *curr;
143 int off = skb->h.raw - skb->nh.raw;
144 int len = ((skb->h.raw[1]+1)<<3);
145
146 if ((skb->h.raw + len) - skb->data > skb_headlen(skb))
147 goto bad;
148
149 off += 2;
150 len -= 2;
151
152 while (len > 0) {
153 int optlen = skb->nh.raw[off+1]+2;
154
155 switch (skb->nh.raw[off]) {
156 case IPV6_TLV_PAD0:
157 optlen = 1;
158 break;
159
160 case IPV6_TLV_PADN:
161 break;
162
163 default: /* Other TLV code so scan list */
164 if (optlen > len)
165 goto bad;
166 for (curr=procs; curr->type >= 0; curr++) {
167 if (curr->type == skb->nh.raw[off]) {
168 /* type specific length/alignment
169 checks will be performed in the
170 func(). */
171 if (curr->func(skb, off) == 0)
172 return 0;
173 break;
174 }
175 }
176 if (curr->type < 0) {
177 if (ip6_tlvopt_unknown(skb, off) == 0)
178 return 0;
179 }
180 break;
181 }
182 off += optlen;
183 len -= optlen;
184 }
185 if (len == 0)
186 return 1;
187bad:
188 kfree_skb(skb);
189 return 0;
190}
191
192/*****************************
193 Destination options header.
194 *****************************/
195
196static struct tlvtype_proc tlvprocdestopt_lst[] = {
197 /* No destination options are defined now */
198 {-1, NULL}
199};
200
Patrick McHardy951dbc82006-01-06 23:02:34 -0800201static int ipv6_destopt_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202{
203 struct sk_buff *skb = *skbp;
204 struct inet6_skb_parm *opt = IP6CB(skb);
205
206 if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
207 !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
208 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
209 kfree_skb(skb);
210 return -1;
211 }
212
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900213 opt->lastopt = skb->h.raw - skb->nh.raw;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 opt->dst1 = skb->h.raw - skb->nh.raw;
215
216 if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
217 skb->h.raw += ((skb->h.raw[1]+1)<<3);
Patrick McHardy951dbc82006-01-06 23:02:34 -0800218 opt->nhoff = opt->dst1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 return 1;
220 }
221
222 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
223 return -1;
224}
225
226static struct inet6_protocol destopt_protocol = {
227 .handler = ipv6_destopt_rcv,
Herbert Xuadcfc7d2006-06-30 13:36:15 -0700228 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229};
230
231void __init ipv6_destopt_init(void)
232{
233 if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0)
234 printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");
235}
236
237/********************************
238 NONE header. No data in packet.
239 ********************************/
240
Patrick McHardy951dbc82006-01-06 23:02:34 -0800241static int ipv6_nodata_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
243 struct sk_buff *skb = *skbp;
244
245 kfree_skb(skb);
246 return 0;
247}
248
249static struct inet6_protocol nodata_protocol = {
250 .handler = ipv6_nodata_rcv,
251 .flags = INET6_PROTO_NOPOLICY,
252};
253
254void __init ipv6_nodata_init(void)
255{
256 if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0)
257 printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");
258}
259
260/********************************
261 Routing header.
262 ********************************/
263
Patrick McHardy951dbc82006-01-06 23:02:34 -0800264static int ipv6_rthdr_rcv(struct sk_buff **skbp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
266 struct sk_buff *skb = *skbp;
267 struct inet6_skb_parm *opt = IP6CB(skb);
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700268 struct in6_addr *addr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 struct in6_addr daddr;
270 int n, i;
271
272 struct ipv6_rt_hdr *hdr;
273 struct rt0_hdr *rthdr;
274
275 if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
276 !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
277 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
278 kfree_skb(skb);
279 return -1;
280 }
281
282 hdr = (struct ipv6_rt_hdr *) skb->h.raw;
283
284 if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) ||
285 skb->pkt_type != PACKET_HOST) {
286 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
287 kfree_skb(skb);
288 return -1;
289 }
290
291looped_back:
292 if (hdr->segments_left == 0) {
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700293 switch (hdr->type) {
294#ifdef CONFIG_IPV6_MIP6
295 case IPV6_SRCRT_TYPE_2:
296 /* Silently discard type 2 header unless it was
297 * processed by own
298 */
299 if (!addr) {
300 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
301 kfree_skb(skb);
302 return -1;
303 }
304 break;
305#endif
306 default:
307 break;
308 }
309
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900310 opt->lastopt = skb->h.raw - skb->nh.raw;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 opt->srcrt = skb->h.raw - skb->nh.raw;
312 skb->h.raw += (hdr->hdrlen + 1) << 3;
313 opt->dst0 = opt->dst1;
314 opt->dst1 = 0;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800315 opt->nhoff = (&hdr->nexthdr) - skb->nh.raw;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 return 1;
317 }
318
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700319 switch (hdr->type) {
320 case IPV6_SRCRT_TYPE_0:
321 if (hdr->hdrlen & 0x01) {
322 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
323 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw);
324 return -1;
325 }
326 break;
327#ifdef CONFIG_IPV6_MIP6
328 case IPV6_SRCRT_TYPE_2:
329 /* Silently discard invalid RTH type 2 */
330 if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
331 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
332 kfree_skb(skb);
333 return -1;
334 }
335 break;
336#endif
337 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
339 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);
340 return -1;
341 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
343 /*
344 * This is the routing header forwarding algorithm from
345 * RFC 2460, page 16.
346 */
347
348 n = hdr->hdrlen >> 1;
349
350 if (hdr->segments_left > n) {
351 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
352 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);
353 return -1;
354 }
355
356 /* We are about to mangle packet header. Be careful!
357 Do not damage packets queued somewhere.
358 */
359 if (skb_cloned(skb)) {
360 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
361 kfree_skb(skb);
362 /* the copy is a forwarded packet */
363 if (skb2 == NULL) {
364 IP6_INC_STATS_BH(IPSTATS_MIB_OUTDISCARDS);
365 return -1;
366 }
367 *skbp = skb = skb2;
368 opt = IP6CB(skb2);
369 hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
370 }
371
Patrick McHardy84fa7932006-08-29 16:44:56 -0700372 if (skb->ip_summed == CHECKSUM_COMPLETE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 skb->ip_summed = CHECKSUM_NONE;
374
375 i = n - --hdr->segments_left;
376
377 rthdr = (struct rt0_hdr *) hdr;
378 addr = rthdr->addr;
379 addr += i - 1;
380
Masahide NAKAMURA65d4ed92006-08-23 19:16:22 -0700381 switch (hdr->type) {
382#ifdef CONFIG_IPV6_MIP6
383 case IPV6_SRCRT_TYPE_2:
384 if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
385 (xfrm_address_t *)&skb->nh.ipv6h->saddr,
386 IPPROTO_ROUTING) < 0) {
387 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
388 kfree_skb(skb);
389 return -1;
390 }
391 if (!ipv6_chk_home_addr(addr)) {
392 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
393 kfree_skb(skb);
394 return -1;
395 }
396 break;
397#endif
398 default:
399 break;
400 }
401
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 if (ipv6_addr_is_multicast(addr)) {
403 IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
404 kfree_skb(skb);
405 return -1;
406 }
407
408 ipv6_addr_copy(&daddr, addr);
409 ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
410 ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
411
412 dst_release(xchg(&skb->dst, NULL));
413 ip6_route_input(skb);
414 if (skb->dst->error) {
415 skb_push(skb, skb->data - skb->nh.raw);
416 dst_input(skb);
417 return -1;
418 }
419
420 if (skb->dst->dev->flags&IFF_LOOPBACK) {
421 if (skb->nh.ipv6h->hop_limit <= 1) {
422 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
423 icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
424 0, skb->dev);
425 kfree_skb(skb);
426 return -1;
427 }
428 skb->nh.ipv6h->hop_limit--;
429 goto looped_back;
430 }
431
432 skb_push(skb, skb->data - skb->nh.raw);
433 dst_input(skb);
434 return -1;
435}
436
437static struct inet6_protocol rthdr_protocol = {
438 .handler = ipv6_rthdr_rcv,
Herbert Xuadcfc7d2006-06-30 13:36:15 -0700439 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440};
441
442void __init ipv6_rthdr_init(void)
443{
444 if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
445 printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
446};
447
448/*
449 This function inverts received rthdr.
450 NOTE: specs allow to make it automatically only if
451 packet authenticated.
452
453 I will not discuss it here (though, I am really pissed off at
454 this stupid requirement making rthdr idea useless)
455
456 Actually, it creates severe problems for us.
457 Embryonic requests has no associated sockets,
458 so that user have no control over it and
459 cannot not only to set reply options, but
460 even to know, that someone wants to connect
461 without success. :-(
462
463 For now we need to test the engine, so that I created
464 temporary (or permanent) backdoor.
465 If listening socket set IPV6_RTHDR to 2, then we invert header.
466 --ANK (980729)
467 */
468
469struct ipv6_txoptions *
470ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
471{
472 /* Received rthdr:
473
474 [ H1 -> H2 -> ... H_prev ] daddr=ME
475
476 Inverted result:
477 [ H_prev -> ... -> H1 ] daddr =sender
478
479 Note, that IP output engine will rewrite this rthdr
480 by rotating it left by one addr.
481 */
482
483 int n, i;
484 struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
485 struct rt0_hdr *irthdr;
486 struct ipv6_txoptions *opt;
487 int hdrlen = ipv6_optlen(hdr);
488
489 if (hdr->segments_left ||
490 hdr->type != IPV6_SRCRT_TYPE_0 ||
491 hdr->hdrlen & 0x01)
492 return NULL;
493
494 n = hdr->hdrlen >> 1;
495 opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
496 if (opt == NULL)
497 return NULL;
498 memset(opt, 0, sizeof(*opt));
499 opt->tot_len = sizeof(*opt) + hdrlen;
500 opt->srcrt = (void*)(opt+1);
501 opt->opt_nflen = hdrlen;
502
503 memcpy(opt->srcrt, hdr, sizeof(*hdr));
504 irthdr = (struct rt0_hdr*)opt->srcrt;
Brian Haleye6df4392005-09-10 00:15:06 -0700505 irthdr->reserved = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 opt->srcrt->segments_left = n;
507 for (i=0; i<n; i++)
508 memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
509 return opt;
510}
511
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800512EXPORT_SYMBOL_GPL(ipv6_invert_rthdr);
513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514/**********************************
515 Hop-by-hop options.
516 **********************************/
517
518/* Router Alert as of RFC 2711 */
519
520static int ipv6_hop_ra(struct sk_buff *skb, int optoff)
521{
522 if (skb->nh.raw[optoff+1] == 2) {
523 IP6CB(skb)->ra = optoff;
524 return 1;
525 }
Patrick McHardy64ce2072005-08-09 20:50:53 -0700526 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
527 skb->nh.raw[optoff+1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 kfree_skb(skb);
529 return 0;
530}
531
532/* Jumbo payload */
533
534static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
535{
536 u32 pkt_len;
537
538 if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) {
Patrick McHardy64ce2072005-08-09 20:50:53 -0700539 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
540 skb->nh.raw[optoff+1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
542 goto drop;
543 }
544
545 pkt_len = ntohl(*(u32*)(skb->nh.raw+optoff+2));
546 if (pkt_len <= IPV6_MAXPLEN) {
547 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
548 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
549 return 0;
550 }
551 if (skb->nh.ipv6h->payload_len) {
552 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
553 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
554 return 0;
555 }
556
557 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
558 IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
559 goto drop;
560 }
Stephen Hemminger42ca89c2005-09-08 12:57:43 -0700561
562 if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
563 goto drop;
564
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 return 1;
566
567drop:
568 kfree_skb(skb);
569 return 0;
570}
571
572static struct tlvtype_proc tlvprochopopt_lst[] = {
573 {
574 .type = IPV6_TLV_ROUTERALERT,
575 .func = ipv6_hop_ra,
576 },
577 {
578 .type = IPV6_TLV_JUMBO,
579 .func = ipv6_hop_jumbo,
580 },
581 { -1, }
582};
583
YOSHIFUJI Hideakib8097392006-04-18 14:48:45 -0700584int ipv6_parse_hopopts(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585{
Patrick McHardy951dbc82006-01-06 23:02:34 -0800586 struct inet6_skb_parm *opt = IP6CB(skb);
587
YOSHIFUJI Hideakiec670092006-04-18 14:46:26 -0700588 /*
589 * skb->nh.raw is equal to skb->data, and
590 * skb->h.raw - skb->nh.raw is always equal to
591 * sizeof(struct ipv6hdr) by definition of
592 * hop-by-hop options.
593 */
594 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
595 !pskb_may_pull(skb, sizeof(struct ipv6hdr) + ((skb->h.raw[1] + 1) << 3))) {
596 kfree_skb(skb);
597 return -1;
598 }
599
Patrick McHardy951dbc82006-01-06 23:02:34 -0800600 opt->hop = sizeof(struct ipv6hdr);
601 if (ip6_parse_tlv(tlvprochopopt_lst, skb)) {
602 skb->h.raw += (skb->h.raw[1]+1)<<3;
603 opt->nhoff = sizeof(struct ipv6hdr);
YOSHIFUJI Hideakib8097392006-04-18 14:48:45 -0700604 return 1;
Patrick McHardy951dbc82006-01-06 23:02:34 -0800605 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 return -1;
607}
608
609/*
610 * Creating outbound headers.
611 *
612 * "build" functions work when skb is filled from head to tail (datagram)
613 * "push" functions work when headers are added from tail to head (tcp)
614 *
615 * In both cases we assume, that caller reserved enough room
616 * for headers.
617 */
618
619static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
620 struct ipv6_rt_hdr *opt,
621 struct in6_addr **addr_p)
622{
623 struct rt0_hdr *phdr, *ihdr;
624 int hops;
625
626 ihdr = (struct rt0_hdr *) opt;
627
628 phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
629 memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
630
631 hops = ihdr->rt_hdr.hdrlen >> 1;
632
633 if (hops > 1)
634 memcpy(phdr->addr, ihdr->addr + 1,
635 (hops - 1) * sizeof(struct in6_addr));
636
637 ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
638 *addr_p = ihdr->addr;
639
640 phdr->rt_hdr.nexthdr = *proto;
641 *proto = NEXTHDR_ROUTING;
642}
643
644static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
645{
646 struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
647
648 memcpy(h, opt, ipv6_optlen(opt));
649 h->nexthdr = *proto;
650 *proto = type;
651}
652
653void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
654 u8 *proto,
655 struct in6_addr **daddr)
656{
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900657 if (opt->srcrt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900659 /*
660 * IPV6_RTHDRDSTOPTS is ignored
661 * unless IPV6_RTHDR is set (RFC3542).
662 */
663 if (opt->dst0opt)
664 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
665 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 if (opt->hopopt)
667 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
668}
669
670void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
671{
672 if (opt->dst1opt)
673 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
674}
675
676struct ipv6_txoptions *
677ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
678{
679 struct ipv6_txoptions *opt2;
680
681 opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
682 if (opt2) {
683 long dif = (char*)opt2 - (char*)opt;
684 memcpy(opt2, opt, opt->tot_len);
685 if (opt2->hopopt)
686 *((char**)&opt2->hopopt) += dif;
687 if (opt2->dst0opt)
688 *((char**)&opt2->dst0opt) += dif;
689 if (opt2->dst1opt)
690 *((char**)&opt2->dst1opt) += dif;
691 if (opt2->srcrt)
692 *((char**)&opt2->srcrt) += dif;
693 }
694 return opt2;
695}
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900696
Arnaldo Carvalho de Melo3cf3dc62005-12-13 23:23:20 -0800697EXPORT_SYMBOL_GPL(ipv6_dup_options);
698
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900699static int ipv6_renew_option(void *ohdr,
700 struct ipv6_opt_hdr __user *newopt, int newoptlen,
701 int inherit,
702 struct ipv6_opt_hdr **hdr,
703 char **p)
704{
705 if (inherit) {
706 if (ohdr) {
707 memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
708 *hdr = (struct ipv6_opt_hdr *)*p;
709 *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
710 }
711 } else {
712 if (newopt) {
713 if (copy_from_user(*p, newopt, newoptlen))
714 return -EFAULT;
715 *hdr = (struct ipv6_opt_hdr *)*p;
716 if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
717 return -EINVAL;
718 *p += CMSG_ALIGN(newoptlen);
719 }
720 }
721 return 0;
722}
723
724struct ipv6_txoptions *
725ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
726 int newtype,
727 struct ipv6_opt_hdr __user *newopt, int newoptlen)
728{
729 int tot_len = 0;
730 char *p;
731 struct ipv6_txoptions *opt2;
732 int err;
733
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700734 if (opt) {
735 if (newtype != IPV6_HOPOPTS && opt->hopopt)
736 tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
737 if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
738 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
739 if (newtype != IPV6_RTHDR && opt->srcrt)
740 tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
741 if (newtype != IPV6_DSTOPTS && opt->dst1opt)
742 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
743 }
744
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900745 if (newopt && newoptlen)
746 tot_len += CMSG_ALIGN(newoptlen);
747
748 if (!tot_len)
749 return NULL;
750
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900751 tot_len += sizeof(*opt2);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900752 opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
753 if (!opt2)
754 return ERR_PTR(-ENOBUFS);
755
756 memset(opt2, 0, tot_len);
757
758 opt2->tot_len = tot_len;
759 p = (char *)(opt2 + 1);
760
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700761 err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900762 newtype != IPV6_HOPOPTS,
763 &opt2->hopopt, &p);
764 if (err)
765 goto out;
766
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700767 err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900768 newtype != IPV6_RTHDRDSTOPTS,
769 &opt2->dst0opt, &p);
770 if (err)
771 goto out;
772
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700773 err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900774 newtype != IPV6_RTHDR,
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700775 (struct ipv6_opt_hdr **)&opt2->srcrt, &p);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900776 if (err)
777 goto out;
778
YOSHIFUJI Hideaki99c7bc02006-08-31 14:52:17 -0700779 err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900780 newtype != IPV6_DSTOPTS,
781 &opt2->dst1opt, &p);
782 if (err)
783 goto out;
784
785 opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
786 (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
787 (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
788 opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
789
790 return opt2;
791out:
YOSHIFUJI Hideaki8b8aa4b2005-11-20 12:18:17 +0900792 sock_kfree_s(sk, opt2, opt2->tot_len);
YOSHIFUJI Hideaki333fad52005-09-08 09:59:17 +0900793 return ERR_PTR(err);
794}
795
YOSHIFUJI Hideakidf9890c2005-11-20 12:23:18 +0900796struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
797 struct ipv6_txoptions *opt)
798{
799 /*
800 * ignore the dest before srcrt unless srcrt is being included.
801 * --yoshfuji
802 */
803 if (opt && opt->dst0opt && !opt->srcrt) {
804 if (opt_space != opt) {
805 memcpy(opt_space, opt, sizeof(*opt_space));
806 opt = opt_space;
807 }
808 opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
809 opt->dst0opt = NULL;
810 }
811
812 return opt;
813}
814