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.c119
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
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;
@@ -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
469drop: 469drop:
@@ -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
582static 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
607struct ipv6_txoptions *
608ipv6_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;
670out:
671 sock_kfree_s(sk, p, tot_len);
672 return ERR_PTR(err);
673}
674