aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/exthdrs.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/exthdrs.c')
-rw-r--r--net/ipv6/exthdrs.c107
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
244looped_back: 245looped_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
584static 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
609struct ipv6_txoptions *
610ipv6_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;
672out:
673 sock_kfree_s(sk, p, tot_len);
674 return ERR_PTR(err);
675}
676