diff options
Diffstat (limited to 'net/ipv6/exthdrs.c')
-rw-r--r-- | net/ipv6/exthdrs.c | 107 |
1 files changed, 104 insertions, 3 deletions
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 3b9fa900a4bf..47122728212a 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c | |||
@@ -164,6 +164,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp) | |||
164 | return -1; | 164 | return -1; |
165 | } | 165 | } |
166 | 166 | ||
167 | opt->lastopt = skb->h.raw - skb->nh.raw; | ||
167 | opt->dst1 = skb->h.raw - skb->nh.raw; | 168 | opt->dst1 = skb->h.raw - skb->nh.raw; |
168 | 169 | ||
169 | if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { | 170 | if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { |
@@ -243,6 +244,7 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp) | |||
243 | 244 | ||
244 | looped_back: | 245 | looped_back: |
245 | if (hdr->segments_left == 0) { | 246 | if (hdr->segments_left == 0) { |
247 | opt->lastopt = skb->h.raw - skb->nh.raw; | ||
246 | opt->srcrt = skb->h.raw - skb->nh.raw; | 248 | opt->srcrt = skb->h.raw - skb->nh.raw; |
247 | skb->h.raw += (hdr->hdrlen + 1) << 3; | 249 | skb->h.raw += (hdr->hdrlen + 1) << 3; |
248 | opt->dst0 = opt->dst1; | 250 | opt->dst0 = opt->dst1; |
@@ -538,10 +540,15 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, | |||
538 | u8 *proto, | 540 | u8 *proto, |
539 | struct in6_addr **daddr) | 541 | struct in6_addr **daddr) |
540 | { | 542 | { |
541 | if (opt->srcrt) | 543 | if (opt->srcrt) { |
542 | ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); | 544 | ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); |
543 | if (opt->dst0opt) | 545 | /* |
544 | ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); | 546 | * IPV6_RTHDRDSTOPTS is ignored |
547 | * unless IPV6_RTHDR is set (RFC3542). | ||
548 | */ | ||
549 | if (opt->dst0opt) | ||
550 | ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); | ||
551 | } | ||
545 | if (opt->hopopt) | 552 | if (opt->hopopt) |
546 | ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); | 553 | ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); |
547 | } | 554 | } |
@@ -572,3 +579,97 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) | |||
572 | } | 579 | } |
573 | return opt2; | 580 | return opt2; |
574 | } | 581 | } |
582 | |||
583 | static int ipv6_renew_option(void *ohdr, | ||
584 | struct ipv6_opt_hdr __user *newopt, int newoptlen, | ||
585 | int inherit, | ||
586 | struct ipv6_opt_hdr **hdr, | ||
587 | char **p) | ||
588 | { | ||
589 | if (inherit) { | ||
590 | if (ohdr) { | ||
591 | memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr)); | ||
592 | *hdr = (struct ipv6_opt_hdr *)*p; | ||
593 | *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr)); | ||
594 | } | ||
595 | } else { | ||
596 | if (newopt) { | ||
597 | if (copy_from_user(*p, newopt, newoptlen)) | ||
598 | return -EFAULT; | ||
599 | *hdr = (struct ipv6_opt_hdr *)*p; | ||
600 | if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen) | ||
601 | return -EINVAL; | ||
602 | *p += CMSG_ALIGN(newoptlen); | ||
603 | } | ||
604 | } | ||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | struct ipv6_txoptions * | ||
609 | ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, | ||
610 | int newtype, | ||
611 | struct ipv6_opt_hdr __user *newopt, int newoptlen) | ||
612 | { | ||
613 | int tot_len = 0; | ||
614 | char *p; | ||
615 | struct ipv6_txoptions *opt2; | ||
616 | int err; | ||
617 | |||
618 | if (newtype != IPV6_HOPOPTS && opt->hopopt) | ||
619 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt)); | ||
620 | if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt) | ||
621 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt)); | ||
622 | if (newtype != IPV6_RTHDR && opt->srcrt) | ||
623 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt)); | ||
624 | if (newtype != IPV6_DSTOPTS && opt->dst1opt) | ||
625 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt)); | ||
626 | if (newopt && newoptlen) | ||
627 | tot_len += CMSG_ALIGN(newoptlen); | ||
628 | |||
629 | if (!tot_len) | ||
630 | return NULL; | ||
631 | |||
632 | opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC); | ||
633 | if (!opt2) | ||
634 | return ERR_PTR(-ENOBUFS); | ||
635 | |||
636 | memset(opt2, 0, tot_len); | ||
637 | |||
638 | opt2->tot_len = tot_len; | ||
639 | p = (char *)(opt2 + 1); | ||
640 | |||
641 | err = ipv6_renew_option(opt->hopopt, newopt, newoptlen, | ||
642 | newtype != IPV6_HOPOPTS, | ||
643 | &opt2->hopopt, &p); | ||
644 | if (err) | ||
645 | goto out; | ||
646 | |||
647 | err = ipv6_renew_option(opt->dst0opt, newopt, newoptlen, | ||
648 | newtype != IPV6_RTHDRDSTOPTS, | ||
649 | &opt2->dst0opt, &p); | ||
650 | if (err) | ||
651 | goto out; | ||
652 | |||
653 | err = ipv6_renew_option(opt->srcrt, newopt, newoptlen, | ||
654 | newtype != IPV6_RTHDR, | ||
655 | (struct ipv6_opt_hdr **)opt2->srcrt, &p); | ||
656 | if (err) | ||
657 | goto out; | ||
658 | |||
659 | err = ipv6_renew_option(opt->dst1opt, newopt, newoptlen, | ||
660 | newtype != IPV6_DSTOPTS, | ||
661 | &opt2->dst1opt, &p); | ||
662 | if (err) | ||
663 | goto out; | ||
664 | |||
665 | opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) + | ||
666 | (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) + | ||
667 | (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0); | ||
668 | opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0); | ||
669 | |||
670 | return opt2; | ||
671 | out: | ||
672 | sock_kfree_s(sk, p, tot_len); | ||
673 | return ERR_PTR(err); | ||
674 | } | ||
675 | |||