diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/datagram.c | 110 | ||||
-rw-r--r-- | net/ipv6/exthdrs.c | 107 | ||||
-rw-r--r-- | net/ipv6/ip6_flowlabel.c | 12 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 164 | ||||
-rw-r--r-- | net/ipv6/raw.c | 3 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 21 | ||||
-rw-r--r-- | net/ipv6/udp.c | 3 |
7 files changed, 379 insertions, 41 deletions
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 01468fab3d3d..832476bbc5cb 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c | |||
@@ -394,21 +394,85 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) | |||
394 | u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK; | 394 | u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK; |
395 | put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo); | 395 | put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo); |
396 | } | 396 | } |
397 | |||
398 | /* HbH is allowed only once */ | ||
397 | if (np->rxopt.bits.hopopts && opt->hop) { | 399 | if (np->rxopt.bits.hopopts && opt->hop) { |
398 | u8 *ptr = skb->nh.raw + opt->hop; | 400 | u8 *ptr = skb->nh.raw + opt->hop; |
399 | put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr); | 401 | put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr); |
400 | } | 402 | } |
401 | if (np->rxopt.bits.dstopts && opt->dst0) { | 403 | |
404 | if (opt->lastopt && | ||
405 | (np->rxopt.bits.dstopts || np->rxopt.bits.srcrt)) { | ||
406 | /* | ||
407 | * Silly enough, but we need to reparse in order to | ||
408 | * report extension headers (except for HbH) | ||
409 | * in order. | ||
410 | * | ||
411 | * Also note that IPV6_RECVRTHDRDSTOPTS is NOT | ||
412 | * (and WILL NOT be) defined because | ||
413 | * IPV6_RECVDSTOPTS is more generic. --yoshfuji | ||
414 | */ | ||
415 | unsigned int off = sizeof(struct ipv6hdr); | ||
416 | u8 nexthdr = skb->nh.ipv6h->nexthdr; | ||
417 | |||
418 | while (off <= opt->lastopt) { | ||
419 | unsigned len; | ||
420 | u8 *ptr = skb->nh.raw + off; | ||
421 | |||
422 | switch(nexthdr) { | ||
423 | case IPPROTO_DSTOPTS: | ||
424 | nexthdr = ptr[0]; | ||
425 | len = (ptr[1] + 1) << 3; | ||
426 | if (np->rxopt.bits.dstopts) | ||
427 | put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr); | ||
428 | break; | ||
429 | case IPPROTO_ROUTING: | ||
430 | nexthdr = ptr[0]; | ||
431 | len = (ptr[1] + 1) << 3; | ||
432 | if (np->rxopt.bits.srcrt) | ||
433 | put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr); | ||
434 | break; | ||
435 | case IPPROTO_AH: | ||
436 | nexthdr = ptr[0]; | ||
437 | len = (ptr[1] + 1) << 2; | ||
438 | break; | ||
439 | default: | ||
440 | nexthdr = ptr[0]; | ||
441 | len = (ptr[1] + 1) << 3; | ||
442 | break; | ||
443 | } | ||
444 | |||
445 | off += len; | ||
446 | } | ||
447 | } | ||
448 | |||
449 | /* socket options in old style */ | ||
450 | if (np->rxopt.bits.rxoinfo) { | ||
451 | struct in6_pktinfo src_info; | ||
452 | |||
453 | src_info.ipi6_ifindex = opt->iif; | ||
454 | ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr); | ||
455 | put_cmsg(msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info); | ||
456 | } | ||
457 | if (np->rxopt.bits.rxohlim) { | ||
458 | int hlim = skb->nh.ipv6h->hop_limit; | ||
459 | put_cmsg(msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim); | ||
460 | } | ||
461 | if (np->rxopt.bits.ohopopts && opt->hop) { | ||
462 | u8 *ptr = skb->nh.raw + opt->hop; | ||
463 | put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr); | ||
464 | } | ||
465 | if (np->rxopt.bits.odstopts && opt->dst0) { | ||
402 | u8 *ptr = skb->nh.raw + opt->dst0; | 466 | u8 *ptr = skb->nh.raw + opt->dst0; |
403 | put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); | 467 | put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr); |
404 | } | 468 | } |
405 | if (np->rxopt.bits.srcrt && opt->srcrt) { | 469 | if (np->rxopt.bits.osrcrt && opt->srcrt) { |
406 | struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt); | 470 | struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt); |
407 | put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr); | 471 | put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr); |
408 | } | 472 | } |
409 | if (np->rxopt.bits.dstopts && opt->dst1) { | 473 | if (np->rxopt.bits.odstopts && opt->dst1) { |
410 | u8 *ptr = skb->nh.raw + opt->dst1; | 474 | u8 *ptr = skb->nh.raw + opt->dst1; |
411 | put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); | 475 | put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr); |
412 | } | 476 | } |
413 | return 0; | 477 | return 0; |
414 | } | 478 | } |
@@ -438,6 +502,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, | |||
438 | 502 | ||
439 | switch (cmsg->cmsg_type) { | 503 | switch (cmsg->cmsg_type) { |
440 | case IPV6_PKTINFO: | 504 | case IPV6_PKTINFO: |
505 | case IPV6_2292PKTINFO: | ||
441 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) { | 506 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) { |
442 | err = -EINVAL; | 507 | err = -EINVAL; |
443 | goto exit_f; | 508 | goto exit_f; |
@@ -492,6 +557,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, | |||
492 | fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg); | 557 | fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg); |
493 | break; | 558 | break; |
494 | 559 | ||
560 | case IPV6_2292HOPOPTS: | ||
495 | case IPV6_HOPOPTS: | 561 | case IPV6_HOPOPTS: |
496 | if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { | 562 | if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { |
497 | err = -EINVAL; | 563 | err = -EINVAL; |
@@ -512,7 +578,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, | |||
512 | opt->hopopt = hdr; | 578 | opt->hopopt = hdr; |
513 | break; | 579 | break; |
514 | 580 | ||
515 | case IPV6_DSTOPTS: | 581 | case IPV6_2292DSTOPTS: |
516 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { | 582 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { |
517 | err = -EINVAL; | 583 | err = -EINVAL; |
518 | goto exit_f; | 584 | goto exit_f; |
@@ -536,6 +602,33 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, | |||
536 | opt->dst1opt = hdr; | 602 | opt->dst1opt = hdr; |
537 | break; | 603 | break; |
538 | 604 | ||
605 | case IPV6_DSTOPTS: | ||
606 | case IPV6_RTHDRDSTOPTS: | ||
607 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { | ||
608 | err = -EINVAL; | ||
609 | goto exit_f; | ||
610 | } | ||
611 | |||
612 | hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg); | ||
613 | len = ((hdr->hdrlen + 1) << 3); | ||
614 | if (cmsg->cmsg_len < CMSG_LEN(len)) { | ||
615 | err = -EINVAL; | ||
616 | goto exit_f; | ||
617 | } | ||
618 | if (!capable(CAP_NET_RAW)) { | ||
619 | err = -EPERM; | ||
620 | goto exit_f; | ||
621 | } | ||
622 | if (cmsg->cmsg_type == IPV6_DSTOPTS) { | ||
623 | opt->opt_flen += len; | ||
624 | opt->dst1opt = hdr; | ||
625 | } else { | ||
626 | opt->opt_nflen += len; | ||
627 | opt->dst0opt = hdr; | ||
628 | } | ||
629 | break; | ||
630 | |||
631 | case IPV6_2292RTHDR: | ||
539 | case IPV6_RTHDR: | 632 | case IPV6_RTHDR: |
540 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) { | 633 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) { |
541 | err = -EINVAL; | 634 | err = -EINVAL; |
@@ -568,7 +661,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, | |||
568 | opt->opt_nflen += len; | 661 | opt->opt_nflen += len; |
569 | opt->srcrt = rthdr; | 662 | opt->srcrt = rthdr; |
570 | 663 | ||
571 | if (opt->dst1opt) { | 664 | if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) { |
572 | int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3); | 665 | int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3); |
573 | 666 | ||
574 | opt->opt_nflen += dsthdrlen; | 667 | opt->opt_nflen += dsthdrlen; |
@@ -579,6 +672,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, | |||
579 | 672 | ||
580 | break; | 673 | break; |
581 | 674 | ||
675 | case IPV6_2292HOPLIMIT: | ||
582 | case IPV6_HOPLIMIT: | 676 | case IPV6_HOPLIMIT: |
583 | if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { | 677 | if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { |
584 | err = -EINVAL; | 678 | err = -EINVAL; |
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 | |||
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index b6c73da5ff35..2d5ce376c265 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c | |||
@@ -225,16 +225,20 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space, | |||
225 | struct ip6_flowlabel * fl, | 225 | struct ip6_flowlabel * fl, |
226 | struct ipv6_txoptions * fopt) | 226 | struct ipv6_txoptions * fopt) |
227 | { | 227 | { |
228 | struct ipv6_txoptions * fl_opt = fl->opt; | 228 | struct ipv6_txoptions * fl_opt = fl ? fl->opt : NULL; |
229 | 229 | ||
230 | if (fopt == NULL || fopt->opt_flen == 0) | 230 | if (fopt == NULL || fopt->opt_flen == 0) { |
231 | return fl_opt; | 231 | if (!fl_opt || !fl_opt->dst0opt || fl_opt->srcrt) |
232 | return fl_opt; | ||
233 | } | ||
232 | 234 | ||
233 | if (fl_opt != NULL) { | 235 | if (fl_opt != NULL) { |
234 | opt_space->hopopt = fl_opt->hopopt; | 236 | opt_space->hopopt = fl_opt->hopopt; |
235 | opt_space->dst0opt = fl_opt->dst0opt; | 237 | opt_space->dst0opt = fl_opt->srcrt ? fl_opt->dst0opt : NULL; |
236 | opt_space->srcrt = fl_opt->srcrt; | 238 | opt_space->srcrt = fl_opt->srcrt; |
237 | opt_space->opt_nflen = fl_opt->opt_nflen; | 239 | opt_space->opt_nflen = fl_opt->opt_nflen; |
240 | if (fl_opt->dst0opt && !fl_opt->srcrt) | ||
241 | opt_space->opt_nflen -= ipv6_optlen(fl_opt->dst0opt); | ||
238 | } else { | 242 | } else { |
239 | if (fopt->opt_nflen == 0) | 243 | if (fopt->opt_nflen == 0) |
240 | return fopt; | 244 | return fopt; |
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 76466af8331e..dc1d9914bf7d 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -210,39 +210,127 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
210 | retv = 0; | 210 | retv = 0; |
211 | break; | 211 | break; |
212 | 212 | ||
213 | case IPV6_PKTINFO: | 213 | case IPV6_RECVPKTINFO: |
214 | np->rxopt.bits.rxinfo = valbool; | 214 | np->rxopt.bits.rxinfo = valbool; |
215 | retv = 0; | 215 | retv = 0; |
216 | break; | 216 | break; |
217 | |||
218 | case IPV6_2292PKTINFO: | ||
219 | np->rxopt.bits.rxoinfo = valbool; | ||
220 | retv = 0; | ||
221 | break; | ||
217 | 222 | ||
218 | case IPV6_HOPLIMIT: | 223 | case IPV6_RECVHOPLIMIT: |
219 | np->rxopt.bits.rxhlim = valbool; | 224 | np->rxopt.bits.rxhlim = valbool; |
220 | retv = 0; | 225 | retv = 0; |
221 | break; | 226 | break; |
222 | 227 | ||
223 | case IPV6_RTHDR: | 228 | case IPV6_2292HOPLIMIT: |
229 | np->rxopt.bits.rxohlim = valbool; | ||
230 | retv = 0; | ||
231 | break; | ||
232 | |||
233 | case IPV6_RECVRTHDR: | ||
224 | if (val < 0 || val > 2) | 234 | if (val < 0 || val > 2) |
225 | goto e_inval; | 235 | goto e_inval; |
226 | np->rxopt.bits.srcrt = val; | 236 | np->rxopt.bits.srcrt = val; |
227 | retv = 0; | 237 | retv = 0; |
228 | break; | 238 | break; |
229 | 239 | ||
230 | case IPV6_HOPOPTS: | 240 | case IPV6_2292RTHDR: |
241 | if (val < 0 || val > 2) | ||
242 | goto e_inval; | ||
243 | np->rxopt.bits.osrcrt = val; | ||
244 | retv = 0; | ||
245 | break; | ||
246 | |||
247 | case IPV6_RECVHOPOPTS: | ||
231 | np->rxopt.bits.hopopts = valbool; | 248 | np->rxopt.bits.hopopts = valbool; |
232 | retv = 0; | 249 | retv = 0; |
233 | break; | 250 | break; |
234 | 251 | ||
235 | case IPV6_DSTOPTS: | 252 | case IPV6_2292HOPOPTS: |
253 | np->rxopt.bits.ohopopts = valbool; | ||
254 | retv = 0; | ||
255 | break; | ||
256 | |||
257 | case IPV6_RECVDSTOPTS: | ||
236 | np->rxopt.bits.dstopts = valbool; | 258 | np->rxopt.bits.dstopts = valbool; |
237 | retv = 0; | 259 | retv = 0; |
238 | break; | 260 | break; |
239 | 261 | ||
262 | case IPV6_2292DSTOPTS: | ||
263 | np->rxopt.bits.odstopts = valbool; | ||
264 | retv = 0; | ||
265 | break; | ||
266 | |||
240 | case IPV6_FLOWINFO: | 267 | case IPV6_FLOWINFO: |
241 | np->rxopt.bits.rxflow = valbool; | 268 | np->rxopt.bits.rxflow = valbool; |
242 | retv = 0; | 269 | retv = 0; |
243 | break; | 270 | break; |
244 | 271 | ||
245 | case IPV6_PKTOPTIONS: | 272 | case IPV6_HOPOPTS: |
273 | case IPV6_RTHDRDSTOPTS: | ||
274 | case IPV6_RTHDR: | ||
275 | case IPV6_DSTOPTS: | ||
276 | { | ||
277 | struct ipv6_txoptions *opt; | ||
278 | if (optlen == 0) | ||
279 | optval = 0; | ||
280 | |||
281 | /* hop-by-hop / destination options are privileged option */ | ||
282 | retv = -EPERM; | ||
283 | if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW)) | ||
284 | break; | ||
285 | |||
286 | retv = -EINVAL; | ||
287 | if (optlen & 0x7 || optlen > 8 * 255) | ||
288 | break; | ||
289 | |||
290 | opt = ipv6_renew_options(sk, np->opt, optname, | ||
291 | (struct ipv6_opt_hdr __user *)optval, | ||
292 | optlen); | ||
293 | if (IS_ERR(opt)) { | ||
294 | retv = PTR_ERR(opt); | ||
295 | break; | ||
296 | } | ||
297 | |||
298 | /* routing header option needs extra check */ | ||
299 | if (optname == IPV6_RTHDR && opt->srcrt) { | ||
300 | struct ipv6_rt_hdr *rthdr = opt->srcrt; | ||
301 | if (rthdr->type) | ||
302 | goto sticky_done; | ||
303 | if ((rthdr->hdrlen & 1) || | ||
304 | (rthdr->hdrlen >> 1) != rthdr->segments_left) | ||
305 | goto sticky_done; | ||
306 | } | ||
307 | |||
308 | retv = 0; | ||
309 | if (sk->sk_type == SOCK_STREAM) { | ||
310 | if (opt) { | ||
311 | struct tcp_sock *tp = tcp_sk(sk); | ||
312 | if (!((1 << sk->sk_state) & | ||
313 | (TCPF_LISTEN | TCPF_CLOSE)) | ||
314 | && inet_sk(sk)->daddr != LOOPBACK4_IPV6) { | ||
315 | tp->ext_header_len = opt->opt_flen + opt->opt_nflen; | ||
316 | tcp_sync_mss(sk, tp->pmtu_cookie); | ||
317 | } | ||
318 | } | ||
319 | opt = xchg(&np->opt, opt); | ||
320 | sk_dst_reset(sk); | ||
321 | } else { | ||
322 | write_lock(&sk->sk_dst_lock); | ||
323 | opt = xchg(&np->opt, opt); | ||
324 | write_unlock(&sk->sk_dst_lock); | ||
325 | sk_dst_reset(sk); | ||
326 | } | ||
327 | sticky_done: | ||
328 | if (opt) | ||
329 | sock_kfree_s(sk, opt, opt->tot_len); | ||
330 | break; | ||
331 | } | ||
332 | |||
333 | case IPV6_2292PKTOPTIONS: | ||
246 | { | 334 | { |
247 | struct ipv6_txoptions *opt = NULL; | 335 | struct ipv6_txoptions *opt = NULL; |
248 | struct msghdr msg; | 336 | struct msghdr msg; |
@@ -529,6 +617,17 @@ e_inval: | |||
529 | return -EINVAL; | 617 | return -EINVAL; |
530 | } | 618 | } |
531 | 619 | ||
620 | int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_opt_hdr *hdr, | ||
621 | char __user *optval, int len) | ||
622 | { | ||
623 | if (!hdr) | ||
624 | return 0; | ||
625 | len = min_t(int, len, ipv6_optlen(hdr)); | ||
626 | if (copy_to_user(optval, hdr, ipv6_optlen(hdr))) | ||
627 | return -EFAULT; | ||
628 | return len; | ||
629 | } | ||
630 | |||
532 | int ipv6_getsockopt(struct sock *sk, int level, int optname, | 631 | int ipv6_getsockopt(struct sock *sk, int level, int optname, |
533 | char __user *optval, int __user *optlen) | 632 | char __user *optval, int __user *optlen) |
534 | { | 633 | { |
@@ -567,7 +666,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
567 | return err; | 666 | return err; |
568 | } | 667 | } |
569 | 668 | ||
570 | case IPV6_PKTOPTIONS: | 669 | case IPV6_2292PKTOPTIONS: |
571 | { | 670 | { |
572 | struct msghdr msg; | 671 | struct msghdr msg; |
573 | struct sk_buff *skb; | 672 | struct sk_buff *skb; |
@@ -601,6 +700,16 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
601 | int hlim = np->mcast_hops; | 700 | int hlim = np->mcast_hops; |
602 | put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); | 701 | put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); |
603 | } | 702 | } |
703 | if (np->rxopt.bits.rxoinfo) { | ||
704 | struct in6_pktinfo src_info; | ||
705 | src_info.ipi6_ifindex = np->mcast_oif; | ||
706 | ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr); | ||
707 | put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info); | ||
708 | } | ||
709 | if (np->rxopt.bits.rxohlim) { | ||
710 | int hlim = np->mcast_hops; | ||
711 | put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim); | ||
712 | } | ||
604 | } | 713 | } |
605 | len -= msg.msg_controllen; | 714 | len -= msg.msg_controllen; |
606 | return put_user(len, optlen); | 715 | return put_user(len, optlen); |
@@ -625,26 +734,59 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
625 | val = np->ipv6only; | 734 | val = np->ipv6only; |
626 | break; | 735 | break; |
627 | 736 | ||
628 | case IPV6_PKTINFO: | 737 | case IPV6_RECVPKTINFO: |
629 | val = np->rxopt.bits.rxinfo; | 738 | val = np->rxopt.bits.rxinfo; |
630 | break; | 739 | break; |
631 | 740 | ||
632 | case IPV6_HOPLIMIT: | 741 | case IPV6_2292PKTINFO: |
742 | val = np->rxopt.bits.rxoinfo; | ||
743 | break; | ||
744 | |||
745 | case IPV6_RECVHOPLIMIT: | ||
633 | val = np->rxopt.bits.rxhlim; | 746 | val = np->rxopt.bits.rxhlim; |
634 | break; | 747 | break; |
635 | 748 | ||
636 | case IPV6_RTHDR: | 749 | case IPV6_2292HOPLIMIT: |
750 | val = np->rxopt.bits.rxohlim; | ||
751 | break; | ||
752 | |||
753 | case IPV6_RECVRTHDR: | ||
637 | val = np->rxopt.bits.srcrt; | 754 | val = np->rxopt.bits.srcrt; |
638 | break; | 755 | break; |
639 | 756 | ||
757 | case IPV6_2292RTHDR: | ||
758 | val = np->rxopt.bits.osrcrt; | ||
759 | break; | ||
760 | |||
640 | case IPV6_HOPOPTS: | 761 | case IPV6_HOPOPTS: |
762 | case IPV6_RTHDRDSTOPTS: | ||
763 | case IPV6_RTHDR: | ||
764 | case IPV6_DSTOPTS: | ||
765 | { | ||
766 | |||
767 | lock_sock(sk); | ||
768 | len = ipv6_getsockopt_sticky(sk, np->opt->hopopt, | ||
769 | optval, len); | ||
770 | release_sock(sk); | ||
771 | return put_user(len, optlen); | ||
772 | } | ||
773 | |||
774 | case IPV6_RECVHOPOPTS: | ||
641 | val = np->rxopt.bits.hopopts; | 775 | val = np->rxopt.bits.hopopts; |
642 | break; | 776 | break; |
643 | 777 | ||
644 | case IPV6_DSTOPTS: | 778 | case IPV6_2292HOPOPTS: |
779 | val = np->rxopt.bits.ohopopts; | ||
780 | break; | ||
781 | |||
782 | case IPV6_RECVDSTOPTS: | ||
645 | val = np->rxopt.bits.dstopts; | 783 | val = np->rxopt.bits.dstopts; |
646 | break; | 784 | break; |
647 | 785 | ||
786 | case IPV6_2292DSTOPTS: | ||
787 | val = np->rxopt.bits.odstopts; | ||
788 | break; | ||
789 | |||
648 | case IPV6_FLOWINFO: | 790 | case IPV6_FLOWINFO: |
649 | val = np->rxopt.bits.rxflow; | 791 | val = np->rxopt.bits.rxflow; |
650 | break; | 792 | break; |
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index ed3a76b30fd9..e527a1652d7c 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c | |||
@@ -755,8 +755,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, | |||
755 | } | 755 | } |
756 | if (opt == NULL) | 756 | if (opt == NULL) |
757 | opt = np->opt; | 757 | opt = np->opt; |
758 | if (flowlabel) | 758 | opt = fl6_merge_options(&opt_space, flowlabel, opt); |
759 | opt = fl6_merge_options(&opt_space, flowlabel, opt); | ||
760 | 759 | ||
761 | fl.proto = proto; | 760 | fl.proto = proto; |
762 | rawv6_probe_proto_opt(&fl, msg); | 761 | rawv6_probe_proto_opt(&fl, msg); |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 794734f1d230..246414b27d0e 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -849,7 +849,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req, | |||
849 | if (dst == NULL) { | 849 | if (dst == NULL) { |
850 | opt = np->opt; | 850 | opt = np->opt; |
851 | if (opt == NULL && | 851 | if (opt == NULL && |
852 | np->rxopt.bits.srcrt == 2 && | 852 | np->rxopt.bits.osrcrt == 2 && |
853 | treq->pktopts) { | 853 | treq->pktopts) { |
854 | struct sk_buff *pktopts = treq->pktopts; | 854 | struct sk_buff *pktopts = treq->pktopts; |
855 | struct inet6_skb_parm *rxopt = IP6CB(pktopts); | 855 | struct inet6_skb_parm *rxopt = IP6CB(pktopts); |
@@ -915,11 +915,10 @@ static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb) | |||
915 | struct inet6_skb_parm *opt = IP6CB(skb); | 915 | struct inet6_skb_parm *opt = IP6CB(skb); |
916 | 916 | ||
917 | if (np->rxopt.all) { | 917 | if (np->rxopt.all) { |
918 | if ((opt->hop && np->rxopt.bits.hopopts) || | 918 | if ((opt->hop && (np->rxopt.bits.hopopts || np->rxopt.bits.ohopopts)) || |
919 | ((IPV6_FLOWINFO_MASK&*(u32*)skb->nh.raw) && | 919 | ((IPV6_FLOWINFO_MASK & *(u32*)skb->nh.raw) && np->rxopt.bits.rxflow) || |
920 | np->rxopt.bits.rxflow) || | 920 | (opt->srcrt && (np->rxopt.bits.srcrt || np->rxopt.bits.osrcrt)) || |
921 | (opt->srcrt && np->rxopt.bits.srcrt) || | 921 | ((opt->dst1 || opt->dst0) && (np->rxopt.bits.dstopts || np->rxopt.bits.odstopts))) |
922 | ((opt->dst1 || opt->dst0) && np->rxopt.bits.dstopts)) | ||
923 | return 1; | 922 | return 1; |
924 | } | 923 | } |
925 | return 0; | 924 | return 0; |
@@ -1190,8 +1189,8 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) | |||
1190 | TCP_ECN_create_request(req, skb->h.th); | 1189 | TCP_ECN_create_request(req, skb->h.th); |
1191 | treq->pktopts = NULL; | 1190 | treq->pktopts = NULL; |
1192 | if (ipv6_opt_accepted(sk, skb) || | 1191 | if (ipv6_opt_accepted(sk, skb) || |
1193 | np->rxopt.bits.rxinfo || | 1192 | np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || |
1194 | np->rxopt.bits.rxhlim) { | 1193 | np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) { |
1195 | atomic_inc(&skb->users); | 1194 | atomic_inc(&skb->users); |
1196 | treq->pktopts = skb; | 1195 | treq->pktopts = skb; |
1197 | } | 1196 | } |
@@ -1288,7 +1287,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
1288 | if (sk_acceptq_is_full(sk)) | 1287 | if (sk_acceptq_is_full(sk)) |
1289 | goto out_overflow; | 1288 | goto out_overflow; |
1290 | 1289 | ||
1291 | if (np->rxopt.bits.srcrt == 2 && | 1290 | if (np->rxopt.bits.osrcrt == 2 && |
1292 | opt == NULL && treq->pktopts) { | 1291 | opt == NULL && treq->pktopts) { |
1293 | struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts); | 1292 | struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts); |
1294 | if (rxopt->srcrt) | 1293 | if (rxopt->srcrt) |
@@ -1544,9 +1543,9 @@ ipv6_pktoptions: | |||
1544 | tp = tcp_sk(sk); | 1543 | tp = tcp_sk(sk); |
1545 | if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt && | 1544 | if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt && |
1546 | !((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) { | 1545 | !((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) { |
1547 | if (np->rxopt.bits.rxinfo) | 1546 | if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo) |
1548 | np->mcast_oif = inet6_iif(opt_skb); | 1547 | np->mcast_oif = inet6_iif(opt_skb); |
1549 | if (np->rxopt.bits.rxhlim) | 1548 | if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) |
1550 | np->mcast_hops = opt_skb->nh.ipv6h->hop_limit; | 1549 | np->mcast_hops = opt_skb->nh.ipv6h->hop_limit; |
1551 | if (ipv6_opt_accepted(sk, opt_skb)) { | 1550 | if (ipv6_opt_accepted(sk, opt_skb)) { |
1552 | skb_set_owner_r(opt_skb, sk); | 1551 | skb_set_owner_r(opt_skb, sk); |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 390d750449ce..aa6eaf3f18a6 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -773,8 +773,7 @@ do_udp_sendmsg: | |||
773 | } | 773 | } |
774 | if (opt == NULL) | 774 | if (opt == NULL) |
775 | opt = np->opt; | 775 | opt = np->opt; |
776 | if (flowlabel) | 776 | opt = fl6_merge_options(&opt_space, flowlabel, opt); |
777 | opt = fl6_merge_options(&opt_space, flowlabel, opt); | ||
778 | 777 | ||
779 | fl->proto = IPPROTO_UDP; | 778 | fl->proto = IPPROTO_UDP; |
780 | ipv6_addr_copy(&fl->fl6_dst, daddr); | 779 | ipv6_addr_copy(&fl->fl6_dst, daddr); |