]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - net/ipv6/exthdrs.c
[IPV6]: Find option offset by type.
[mirror_ubuntu-jammy-kernel.git] / net / ipv6 / exthdrs.c
CommitLineData
1da177e4
LT
1/*
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>
65d4ed92
MN
46#ifdef CONFIG_IPV6_MIP6
47#include <net/xfrm.h>
48#endif
1da177e4
LT
49
50#include <asm/uaccess.h>
51
c61a4043
MN
52int 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
1da177e4
LT
95/*
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
951dbc8a 201static int ipv6_destopt_rcv(struct sk_buff **skbp)
1da177e4
LT
202{
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
333fad53 213 opt->lastopt = skb->h.raw - skb->nh.raw;
1da177e4
LT
214 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);
951dbc8a 218 opt->nhoff = opt->dst1;
1da177e4
LT
219 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,
adcfc7d0 228 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
1da177e4
LT
229};
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
951dbc8a 241static int ipv6_nodata_rcv(struct sk_buff **skbp)
1da177e4
LT
242{
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
951dbc8a 264static int ipv6_rthdr_rcv(struct sk_buff **skbp)
1da177e4
LT
265{
266 struct sk_buff *skb = *skbp;
267 struct inet6_skb_parm *opt = IP6CB(skb);
65d4ed92 268 struct in6_addr *addr = NULL;
1da177e4
LT
269 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) {
65d4ed92
MN
293 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
333fad53 310 opt->lastopt = skb->h.raw - skb->nh.raw;
1da177e4
LT
311 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;
951dbc8a 315 opt->nhoff = (&hdr->nexthdr) - skb->nh.raw;
1da177e4
LT
316 return 1;
317 }
318
65d4ed92
MN
319 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:
1da177e4
LT
338 IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
339 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);
340 return -1;
341 }
1da177e4
LT
342
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
84fa7933 372 if (skb->ip_summed == CHECKSUM_COMPLETE)
1da177e4
LT
373 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
65d4ed92
MN
381 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
1da177e4
LT
402 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,
adcfc7d0 439 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
1da177e4
LT
440};
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;
e6df439b 505 irthdr->reserved = 0;
1da177e4
LT
506 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
3cf3dc6c
ACM
512EXPORT_SYMBOL_GPL(ipv6_invert_rthdr);
513
1da177e4
LT
514/**********************************
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 }
64ce2073
PM
526 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
527 skb->nh.raw[optoff+1]);
1da177e4
LT
528 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) {
64ce2073
PM
539 LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
540 skb->nh.raw[optoff+1]);
1da177e4
LT
541 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 }
42ca89c1
SH
561
562 if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
563 goto drop;
564
1da177e4
LT
565 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
b809739a 584int ipv6_parse_hopopts(struct sk_buff *skb)
1da177e4 585{
951dbc8a
PM
586 struct inet6_skb_parm *opt = IP6CB(skb);
587
ec670095
YH
588 /*
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
951dbc8a
PM
600 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);
b809739a 604 return 1;
951dbc8a 605 }
1da177e4
LT
606 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{
333fad53 657 if (opt->srcrt) {
1da177e4 658 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
333fad53
YH
659 /*
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 }
1da177e4
LT
666 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}
333fad53 696
3cf3dc6c
ACM
697EXPORT_SYMBOL_GPL(ipv6_dup_options);
698
333fad53
YH
699static 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
99c7bc01
YH
734 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
333fad53
YH
745 if (newopt && newoptlen)
746 tot_len += CMSG_ALIGN(newoptlen);
747
748 if (!tot_len)
749 return NULL;
750
8b8aa4b5 751 tot_len += sizeof(*opt2);
333fad53
YH
752 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
99c7bc01 761 err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
333fad53
YH
762 newtype != IPV6_HOPOPTS,
763 &opt2->hopopt, &p);
764 if (err)
765 goto out;
766
99c7bc01 767 err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
333fad53
YH
768 newtype != IPV6_RTHDRDSTOPTS,
769 &opt2->dst0opt, &p);
770 if (err)
771 goto out;
772
99c7bc01 773 err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
333fad53 774 newtype != IPV6_RTHDR,
99c7bc01 775 (struct ipv6_opt_hdr **)&opt2->srcrt, &p);
333fad53
YH
776 if (err)
777 goto out;
778
99c7bc01 779 err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
333fad53
YH
780 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:
8b8aa4b5 792 sock_kfree_s(sk, opt2, opt2->tot_len);
333fad53
YH
793 return ERR_PTR(err);
794}
795
df9890c3
YH
796struct 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