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 5be6da2584ee..ffcda45e2c1e 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; |
@@ -539,10 +541,15 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, | |||
539 | u8 *proto, | 541 | u8 *proto, |
540 | struct in6_addr **daddr) | 542 | struct in6_addr **daddr) |
541 | { | 543 | { |
542 | if (opt->srcrt) | 544 | if (opt->srcrt) { |
543 | ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); | 545 | ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); |
544 | if (opt->dst0opt) | 546 | /* |
545 | ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); | 547 | * IPV6_RTHDRDSTOPTS is ignored |
548 | * unless IPV6_RTHDR is set (RFC3542). | ||
549 | */ | ||
550 | if (opt->dst0opt) | ||
551 | ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); | ||
552 | } | ||
546 | if (opt->hopopt) | 553 | if (opt->hopopt) |
547 | ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); | 554 | ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); |
548 | } | 555 | } |
@@ -573,3 +580,97 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) | |||
573 | } | 580 | } |
574 | return opt2; | 581 | return opt2; |
575 | } | 582 | } |
583 | |||
584 | static int ipv6_renew_option(void *ohdr, | ||
585 | struct ipv6_opt_hdr __user *newopt, int newoptlen, | ||
586 | int inherit, | ||
587 | struct ipv6_opt_hdr **hdr, | ||
588 | char **p) | ||
589 | { | ||
590 | if (inherit) { | ||
591 | if (ohdr) { | ||
592 | memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr)); | ||
593 | *hdr = (struct ipv6_opt_hdr *)*p; | ||
594 | *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr)); | ||
595 | } | ||
596 | } else { | ||
597 | if (newopt) { | ||
598 | if (copy_from_user(*p, newopt, newoptlen)) | ||
599 | return -EFAULT; | ||
600 | *hdr = (struct ipv6_opt_hdr *)*p; | ||
601 | if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen) | ||
602 | return -EINVAL; | ||
603 | *p += CMSG_ALIGN(newoptlen); | ||
604 | } | ||
605 | } | ||
606 | return 0; | ||
607 | } | ||
608 | |||
609 | struct ipv6_txoptions * | ||
610 | ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, | ||
611 | int newtype, | ||
612 | struct ipv6_opt_hdr __user *newopt, int newoptlen) | ||
613 | { | ||
614 | int tot_len = 0; | ||
615 | char *p; | ||
616 | struct ipv6_txoptions *opt2; | ||
617 | int err; | ||
618 | |||
619 | if (newtype != IPV6_HOPOPTS && opt->hopopt) | ||
620 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt)); | ||
621 | if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt) | ||
622 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt)); | ||
623 | if (newtype != IPV6_RTHDR && opt->srcrt) | ||
624 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt)); | ||
625 | if (newtype != IPV6_DSTOPTS && opt->dst1opt) | ||
626 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt)); | ||
627 | if (newopt && newoptlen) | ||
628 | tot_len += CMSG_ALIGN(newoptlen); | ||
629 | |||
630 | if (!tot_len) | ||
631 | return NULL; | ||
632 | |||
633 | opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC); | ||
634 | if (!opt2) | ||
635 | return ERR_PTR(-ENOBUFS); | ||
636 | |||
637 | memset(opt2, 0, tot_len); | ||
638 | |||
639 | opt2->tot_len = tot_len; | ||
640 | p = (char *)(opt2 + 1); | ||
641 | |||
642 | err = ipv6_renew_option(opt->hopopt, newopt, newoptlen, | ||
643 | newtype != IPV6_HOPOPTS, | ||
644 | &opt2->hopopt, &p); | ||
645 | if (err) | ||
646 | goto out; | ||
647 | |||
648 | err = ipv6_renew_option(opt->dst0opt, newopt, newoptlen, | ||
649 | newtype != IPV6_RTHDRDSTOPTS, | ||
650 | &opt2->dst0opt, &p); | ||
651 | if (err) | ||
652 | goto out; | ||
653 | |||
654 | err = ipv6_renew_option(opt->srcrt, newopt, newoptlen, | ||
655 | newtype != IPV6_RTHDR, | ||
656 | (struct ipv6_opt_hdr **)opt2->srcrt, &p); | ||
657 | if (err) | ||
658 | goto out; | ||
659 | |||
660 | err = ipv6_renew_option(opt->dst1opt, newopt, newoptlen, | ||
661 | newtype != IPV6_DSTOPTS, | ||
662 | &opt2->dst1opt, &p); | ||
663 | if (err) | ||
664 | goto out; | ||
665 | |||
666 | opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) + | ||
667 | (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) + | ||
668 | (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0); | ||
669 | opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0); | ||
670 | |||
671 | return opt2; | ||
672 | out: | ||
673 | sock_kfree_s(sk, p, tot_len); | ||
674 | return ERR_PTR(err); | ||
675 | } | ||
676 | |||