diff options
Diffstat (limited to 'net/ipv6/exthdrs.c')
-rw-r--r-- | net/ipv6/exthdrs.c | 119 |
1 files changed, 109 insertions, 10 deletions
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 5be6da2584ee..922549581abc 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; |
@@ -404,8 +406,7 @@ ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr) | |||
404 | 406 | ||
405 | memcpy(opt->srcrt, hdr, sizeof(*hdr)); | 407 | memcpy(opt->srcrt, hdr, sizeof(*hdr)); |
406 | irthdr = (struct rt0_hdr*)opt->srcrt; | 408 | irthdr = (struct rt0_hdr*)opt->srcrt; |
407 | /* Obsolete field, MBZ, when originated by us */ | 409 | irthdr->reserved = 0; |
408 | irthdr->bitmap = 0; | ||
409 | opt->srcrt->segments_left = n; | 410 | opt->srcrt->segments_left = n; |
410 | for (i=0; i<n; i++) | 411 | for (i=0; i<n; i++) |
411 | memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16); | 412 | memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16); |
@@ -459,11 +460,10 @@ static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff) | |||
459 | IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS); | 460 | IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS); |
460 | goto drop; | 461 | goto drop; |
461 | } | 462 | } |
462 | if (pkt_len + sizeof(struct ipv6hdr) < skb->len) { | 463 | |
463 | __pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr)); | 464 | if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) |
464 | if (skb->ip_summed == CHECKSUM_HW) | 465 | goto drop; |
465 | skb->ip_summed = CHECKSUM_NONE; | 466 | |
466 | } | ||
467 | return 1; | 467 | return 1; |
468 | 468 | ||
469 | drop: | 469 | drop: |
@@ -539,10 +539,15 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, | |||
539 | u8 *proto, | 539 | u8 *proto, |
540 | struct in6_addr **daddr) | 540 | struct in6_addr **daddr) |
541 | { | 541 | { |
542 | if (opt->srcrt) | 542 | if (opt->srcrt) { |
543 | ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); | 543 | ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); |
544 | if (opt->dst0opt) | 544 | /* |
545 | ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); | 545 | * IPV6_RTHDRDSTOPTS is ignored |
546 | * unless IPV6_RTHDR is set (RFC3542). | ||
547 | */ | ||
548 | if (opt->dst0opt) | ||
549 | ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); | ||
550 | } | ||
546 | if (opt->hopopt) | 551 | if (opt->hopopt) |
547 | ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); | 552 | ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); |
548 | } | 553 | } |
@@ -573,3 +578,97 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) | |||
573 | } | 578 | } |
574 | return opt2; | 579 | return opt2; |
575 | } | 580 | } |
581 | |||
582 | static int ipv6_renew_option(void *ohdr, | ||
583 | struct ipv6_opt_hdr __user *newopt, int newoptlen, | ||
584 | int inherit, | ||
585 | struct ipv6_opt_hdr **hdr, | ||
586 | char **p) | ||
587 | { | ||
588 | if (inherit) { | ||
589 | if (ohdr) { | ||
590 | memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr)); | ||
591 | *hdr = (struct ipv6_opt_hdr *)*p; | ||
592 | *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr)); | ||
593 | } | ||
594 | } else { | ||
595 | if (newopt) { | ||
596 | if (copy_from_user(*p, newopt, newoptlen)) | ||
597 | return -EFAULT; | ||
598 | *hdr = (struct ipv6_opt_hdr *)*p; | ||
599 | if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen) | ||
600 | return -EINVAL; | ||
601 | *p += CMSG_ALIGN(newoptlen); | ||
602 | } | ||
603 | } | ||
604 | return 0; | ||
605 | } | ||
606 | |||
607 | struct ipv6_txoptions * | ||
608 | ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, | ||
609 | int newtype, | ||
610 | struct ipv6_opt_hdr __user *newopt, int newoptlen) | ||
611 | { | ||
612 | int tot_len = 0; | ||
613 | char *p; | ||
614 | struct ipv6_txoptions *opt2; | ||
615 | int err; | ||
616 | |||
617 | if (newtype != IPV6_HOPOPTS && opt->hopopt) | ||
618 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt)); | ||
619 | if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt) | ||
620 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt)); | ||
621 | if (newtype != IPV6_RTHDR && opt->srcrt) | ||
622 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt)); | ||
623 | if (newtype != IPV6_DSTOPTS && opt->dst1opt) | ||
624 | tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt)); | ||
625 | if (newopt && newoptlen) | ||
626 | tot_len += CMSG_ALIGN(newoptlen); | ||
627 | |||
628 | if (!tot_len) | ||
629 | return NULL; | ||
630 | |||
631 | opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC); | ||
632 | if (!opt2) | ||
633 | return ERR_PTR(-ENOBUFS); | ||
634 | |||
635 | memset(opt2, 0, tot_len); | ||
636 | |||
637 | opt2->tot_len = tot_len; | ||
638 | p = (char *)(opt2 + 1); | ||
639 | |||
640 | err = ipv6_renew_option(opt->hopopt, newopt, newoptlen, | ||
641 | newtype != IPV6_HOPOPTS, | ||
642 | &opt2->hopopt, &p); | ||
643 | if (err) | ||
644 | goto out; | ||
645 | |||
646 | err = ipv6_renew_option(opt->dst0opt, newopt, newoptlen, | ||
647 | newtype != IPV6_RTHDRDSTOPTS, | ||
648 | &opt2->dst0opt, &p); | ||
649 | if (err) | ||
650 | goto out; | ||
651 | |||
652 | err = ipv6_renew_option(opt->srcrt, newopt, newoptlen, | ||
653 | newtype != IPV6_RTHDR, | ||
654 | (struct ipv6_opt_hdr **)opt2->srcrt, &p); | ||
655 | if (err) | ||
656 | goto out; | ||
657 | |||
658 | err = ipv6_renew_option(opt->dst1opt, newopt, newoptlen, | ||
659 | newtype != IPV6_DSTOPTS, | ||
660 | &opt2->dst1opt, &p); | ||
661 | if (err) | ||
662 | goto out; | ||
663 | |||
664 | opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) + | ||
665 | (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) + | ||
666 | (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0); | ||
667 | opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0); | ||
668 | |||
669 | return opt2; | ||
670 | out: | ||
671 | sock_kfree_s(sk, p, tot_len); | ||
672 | return ERR_PTR(err); | ||
673 | } | ||
674 | |||