diff options
Diffstat (limited to 'net/ipv4/ip_sockglue.c')
-rw-r--r-- | net/ipv4/ip_sockglue.c | 1127 |
1 files changed, 563 insertions, 564 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 23048d9f3584..c5e41644c80d 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c | |||
@@ -403,20 +403,20 @@ out: | |||
403 | */ | 403 | */ |
404 | 404 | ||
405 | static int do_ip_setsockopt(struct sock *sk, int level, | 405 | static int do_ip_setsockopt(struct sock *sk, int level, |
406 | int optname, char __user *optval, int optlen) | 406 | int optname, char __user *optval, int optlen) |
407 | { | 407 | { |
408 | struct inet_sock *inet = inet_sk(sk); | 408 | struct inet_sock *inet = inet_sk(sk); |
409 | int val=0,err; | 409 | int val=0,err; |
410 | 410 | ||
411 | if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) | | 411 | if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) | |
412 | (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) | | 412 | (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) | |
413 | (1<<IP_RETOPTS) | (1<<IP_TOS) | | 413 | (1<<IP_RETOPTS) | (1<<IP_TOS) | |
414 | (1<<IP_TTL) | (1<<IP_HDRINCL) | | 414 | (1<<IP_TTL) | (1<<IP_HDRINCL) | |
415 | (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | | 415 | (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | |
416 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) | | 416 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) | |
417 | (1<<IP_PASSSEC))) || | 417 | (1<<IP_PASSSEC))) || |
418 | optname == IP_MULTICAST_TTL || | 418 | optname == IP_MULTICAST_TTL || |
419 | optname == IP_MULTICAST_LOOP) { | 419 | optname == IP_MULTICAST_LOOP) { |
420 | if (optlen >= sizeof(int)) { | 420 | if (optlen >= sizeof(int)) { |
421 | if (get_user(val, (int __user *) optval)) | 421 | if (get_user(val, (int __user *) optval)) |
422 | return -EFAULT; | 422 | return -EFAULT; |
@@ -440,444 +440,444 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
440 | lock_sock(sk); | 440 | lock_sock(sk); |
441 | 441 | ||
442 | switch (optname) { | 442 | switch (optname) { |
443 | case IP_OPTIONS: | 443 | case IP_OPTIONS: |
444 | { | 444 | { |
445 | struct ip_options * opt = NULL; | 445 | struct ip_options * opt = NULL; |
446 | if (optlen > 40 || optlen < 0) | 446 | if (optlen > 40 || optlen < 0) |
447 | goto e_inval; | 447 | goto e_inval; |
448 | err = ip_options_get_from_user(&opt, optval, optlen); | 448 | err = ip_options_get_from_user(&opt, optval, optlen); |
449 | if (err) | 449 | if (err) |
450 | break; | 450 | break; |
451 | if (inet->is_icsk) { | 451 | if (inet->is_icsk) { |
452 | struct inet_connection_sock *icsk = inet_csk(sk); | 452 | struct inet_connection_sock *icsk = inet_csk(sk); |
453 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 453 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
454 | if (sk->sk_family == PF_INET || | 454 | if (sk->sk_family == PF_INET || |
455 | (!((1 << sk->sk_state) & | 455 | (!((1 << sk->sk_state) & |
456 | (TCPF_LISTEN | TCPF_CLOSE)) && | 456 | (TCPF_LISTEN | TCPF_CLOSE)) && |
457 | inet->daddr != LOOPBACK4_IPV6)) { | 457 | inet->daddr != LOOPBACK4_IPV6)) { |
458 | #endif | 458 | #endif |
459 | if (inet->opt) | 459 | if (inet->opt) |
460 | icsk->icsk_ext_hdr_len -= inet->opt->optlen; | 460 | icsk->icsk_ext_hdr_len -= inet->opt->optlen; |
461 | if (opt) | 461 | if (opt) |
462 | icsk->icsk_ext_hdr_len += opt->optlen; | 462 | icsk->icsk_ext_hdr_len += opt->optlen; |
463 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); | 463 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); |
464 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 464 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
465 | } | ||
466 | #endif | ||
467 | } | 465 | } |
468 | opt = xchg(&inet->opt, opt); | 466 | #endif |
469 | kfree(opt); | ||
470 | break; | ||
471 | } | 467 | } |
472 | case IP_PKTINFO: | 468 | opt = xchg(&inet->opt, opt); |
473 | if (val) | 469 | kfree(opt); |
474 | inet->cmsg_flags |= IP_CMSG_PKTINFO; | 470 | break; |
475 | else | 471 | } |
476 | inet->cmsg_flags &= ~IP_CMSG_PKTINFO; | 472 | case IP_PKTINFO: |
477 | break; | 473 | if (val) |
478 | case IP_RECVTTL: | 474 | inet->cmsg_flags |= IP_CMSG_PKTINFO; |
479 | if (val) | 475 | else |
480 | inet->cmsg_flags |= IP_CMSG_TTL; | 476 | inet->cmsg_flags &= ~IP_CMSG_PKTINFO; |
481 | else | 477 | break; |
482 | inet->cmsg_flags &= ~IP_CMSG_TTL; | 478 | case IP_RECVTTL: |
483 | break; | 479 | if (val) |
484 | case IP_RECVTOS: | 480 | inet->cmsg_flags |= IP_CMSG_TTL; |
485 | if (val) | 481 | else |
486 | inet->cmsg_flags |= IP_CMSG_TOS; | 482 | inet->cmsg_flags &= ~IP_CMSG_TTL; |
487 | else | 483 | break; |
488 | inet->cmsg_flags &= ~IP_CMSG_TOS; | 484 | case IP_RECVTOS: |
489 | break; | 485 | if (val) |
490 | case IP_RECVOPTS: | 486 | inet->cmsg_flags |= IP_CMSG_TOS; |
491 | if (val) | 487 | else |
492 | inet->cmsg_flags |= IP_CMSG_RECVOPTS; | 488 | inet->cmsg_flags &= ~IP_CMSG_TOS; |
493 | else | 489 | break; |
494 | inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; | 490 | case IP_RECVOPTS: |
495 | break; | 491 | if (val) |
496 | case IP_RETOPTS: | 492 | inet->cmsg_flags |= IP_CMSG_RECVOPTS; |
497 | if (val) | 493 | else |
498 | inet->cmsg_flags |= IP_CMSG_RETOPTS; | 494 | inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; |
499 | else | 495 | break; |
500 | inet->cmsg_flags &= ~IP_CMSG_RETOPTS; | 496 | case IP_RETOPTS: |
497 | if (val) | ||
498 | inet->cmsg_flags |= IP_CMSG_RETOPTS; | ||
499 | else | ||
500 | inet->cmsg_flags &= ~IP_CMSG_RETOPTS; | ||
501 | break; | ||
502 | case IP_PASSSEC: | ||
503 | if (val) | ||
504 | inet->cmsg_flags |= IP_CMSG_PASSSEC; | ||
505 | else | ||
506 | inet->cmsg_flags &= ~IP_CMSG_PASSSEC; | ||
507 | break; | ||
508 | case IP_TOS: /* This sets both TOS and Precedence */ | ||
509 | if (sk->sk_type == SOCK_STREAM) { | ||
510 | val &= ~3; | ||
511 | val |= inet->tos & 3; | ||
512 | } | ||
513 | if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && | ||
514 | !capable(CAP_NET_ADMIN)) { | ||
515 | err = -EPERM; | ||
501 | break; | 516 | break; |
502 | case IP_PASSSEC: | 517 | } |
503 | if (val) | 518 | if (inet->tos != val) { |
504 | inet->cmsg_flags |= IP_CMSG_PASSSEC; | 519 | inet->tos = val; |
505 | else | 520 | sk->sk_priority = rt_tos2priority(val); |
506 | inet->cmsg_flags &= ~IP_CMSG_PASSSEC; | 521 | sk_dst_reset(sk); |
522 | } | ||
523 | break; | ||
524 | case IP_TTL: | ||
525 | if (optlen<1) | ||
526 | goto e_inval; | ||
527 | if (val != -1 && (val < 1 || val>255)) | ||
528 | goto e_inval; | ||
529 | inet->uc_ttl = val; | ||
530 | break; | ||
531 | case IP_HDRINCL: | ||
532 | if (sk->sk_type != SOCK_RAW) { | ||
533 | err = -ENOPROTOOPT; | ||
507 | break; | 534 | break; |
508 | case IP_TOS: /* This sets both TOS and Precedence */ | 535 | } |
509 | if (sk->sk_type == SOCK_STREAM) { | 536 | inet->hdrincl = val ? 1 : 0; |
510 | val &= ~3; | 537 | break; |
511 | val |= inet->tos & 3; | 538 | case IP_MTU_DISCOVER: |
512 | } | 539 | if (val<0 || val>2) |
513 | if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && | 540 | goto e_inval; |
514 | !capable(CAP_NET_ADMIN)) { | 541 | inet->pmtudisc = val; |
515 | err = -EPERM; | 542 | break; |
543 | case IP_RECVERR: | ||
544 | inet->recverr = !!val; | ||
545 | if (!val) | ||
546 | skb_queue_purge(&sk->sk_error_queue); | ||
547 | break; | ||
548 | case IP_MULTICAST_TTL: | ||
549 | if (sk->sk_type == SOCK_STREAM) | ||
550 | goto e_inval; | ||
551 | if (optlen<1) | ||
552 | goto e_inval; | ||
553 | if (val==-1) | ||
554 | val = 1; | ||
555 | if (val < 0 || val > 255) | ||
556 | goto e_inval; | ||
557 | inet->mc_ttl = val; | ||
558 | break; | ||
559 | case IP_MULTICAST_LOOP: | ||
560 | if (optlen<1) | ||
561 | goto e_inval; | ||
562 | inet->mc_loop = !!val; | ||
563 | break; | ||
564 | case IP_MULTICAST_IF: | ||
565 | { | ||
566 | struct ip_mreqn mreq; | ||
567 | struct net_device *dev = NULL; | ||
568 | |||
569 | if (sk->sk_type == SOCK_STREAM) | ||
570 | goto e_inval; | ||
571 | /* | ||
572 | * Check the arguments are allowable | ||
573 | */ | ||
574 | |||
575 | err = -EFAULT; | ||
576 | if (optlen >= sizeof(struct ip_mreqn)) { | ||
577 | if (copy_from_user(&mreq,optval,sizeof(mreq))) | ||
516 | break; | 578 | break; |
517 | } | 579 | } else { |
518 | if (inet->tos != val) { | 580 | memset(&mreq, 0, sizeof(mreq)); |
519 | inet->tos = val; | 581 | if (optlen >= sizeof(struct in_addr) && |
520 | sk->sk_priority = rt_tos2priority(val); | 582 | copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr))) |
521 | sk_dst_reset(sk); | 583 | break; |
522 | } | 584 | } |
523 | break; | 585 | |
524 | case IP_TTL: | 586 | if (!mreq.imr_ifindex) { |
525 | if (optlen<1) | 587 | if (mreq.imr_address.s_addr == INADDR_ANY) { |
526 | goto e_inval; | 588 | inet->mc_index = 0; |
527 | if (val != -1 && (val < 1 || val>255)) | 589 | inet->mc_addr = 0; |
528 | goto e_inval; | 590 | err = 0; |
529 | inet->uc_ttl = val; | ||
530 | break; | ||
531 | case IP_HDRINCL: | ||
532 | if (sk->sk_type != SOCK_RAW) { | ||
533 | err = -ENOPROTOOPT; | ||
534 | break; | 591 | break; |
535 | } | 592 | } |
536 | inet->hdrincl = val ? 1 : 0; | 593 | dev = ip_dev_find(mreq.imr_address.s_addr); |
537 | break; | 594 | if (dev) { |
538 | case IP_MTU_DISCOVER: | 595 | mreq.imr_ifindex = dev->ifindex; |
539 | if (val<0 || val>2) | 596 | dev_put(dev); |
540 | goto e_inval; | 597 | } |
541 | inet->pmtudisc = val; | 598 | } else |
542 | break; | 599 | dev = __dev_get_by_index(mreq.imr_ifindex); |
543 | case IP_RECVERR: | ||
544 | inet->recverr = !!val; | ||
545 | if (!val) | ||
546 | skb_queue_purge(&sk->sk_error_queue); | ||
547 | break; | ||
548 | case IP_MULTICAST_TTL: | ||
549 | if (sk->sk_type == SOCK_STREAM) | ||
550 | goto e_inval; | ||
551 | if (optlen<1) | ||
552 | goto e_inval; | ||
553 | if (val==-1) | ||
554 | val = 1; | ||
555 | if (val < 0 || val > 255) | ||
556 | goto e_inval; | ||
557 | inet->mc_ttl = val; | ||
558 | break; | ||
559 | case IP_MULTICAST_LOOP: | ||
560 | if (optlen<1) | ||
561 | goto e_inval; | ||
562 | inet->mc_loop = !!val; | ||
563 | break; | ||
564 | case IP_MULTICAST_IF: | ||
565 | { | ||
566 | struct ip_mreqn mreq; | ||
567 | struct net_device *dev = NULL; | ||
568 | 600 | ||
569 | if (sk->sk_type == SOCK_STREAM) | ||
570 | goto e_inval; | ||
571 | /* | ||
572 | * Check the arguments are allowable | ||
573 | */ | ||
574 | 601 | ||
575 | err = -EFAULT; | 602 | err = -EADDRNOTAVAIL; |
576 | if (optlen >= sizeof(struct ip_mreqn)) { | 603 | if (!dev) |
577 | if (copy_from_user(&mreq,optval,sizeof(mreq))) | 604 | break; |
578 | break; | 605 | |
579 | } else { | 606 | err = -EINVAL; |
580 | memset(&mreq, 0, sizeof(mreq)); | 607 | if (sk->sk_bound_dev_if && |
581 | if (optlen >= sizeof(struct in_addr) && | 608 | mreq.imr_ifindex != sk->sk_bound_dev_if) |
582 | copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr))) | 609 | break; |
583 | break; | ||
584 | } | ||
585 | 610 | ||
586 | if (!mreq.imr_ifindex) { | 611 | inet->mc_index = mreq.imr_ifindex; |
587 | if (mreq.imr_address.s_addr == INADDR_ANY) { | 612 | inet->mc_addr = mreq.imr_address.s_addr; |
588 | inet->mc_index = 0; | 613 | err = 0; |
589 | inet->mc_addr = 0; | 614 | break; |
590 | err = 0; | 615 | } |
591 | break; | ||
592 | } | ||
593 | dev = ip_dev_find(mreq.imr_address.s_addr); | ||
594 | if (dev) { | ||
595 | mreq.imr_ifindex = dev->ifindex; | ||
596 | dev_put(dev); | ||
597 | } | ||
598 | } else | ||
599 | dev = __dev_get_by_index(mreq.imr_ifindex); | ||
600 | 616 | ||
617 | case IP_ADD_MEMBERSHIP: | ||
618 | case IP_DROP_MEMBERSHIP: | ||
619 | { | ||
620 | struct ip_mreqn mreq; | ||
601 | 621 | ||
602 | err = -EADDRNOTAVAIL; | 622 | if (optlen < sizeof(struct ip_mreq)) |
603 | if (!dev) | 623 | goto e_inval; |
624 | err = -EFAULT; | ||
625 | if (optlen >= sizeof(struct ip_mreqn)) { | ||
626 | if (copy_from_user(&mreq,optval,sizeof(mreq))) | ||
604 | break; | 627 | break; |
605 | 628 | } else { | |
606 | err = -EINVAL; | 629 | memset(&mreq, 0, sizeof(mreq)); |
607 | if (sk->sk_bound_dev_if && | 630 | if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq))) |
608 | mreq.imr_ifindex != sk->sk_bound_dev_if) | ||
609 | break; | 631 | break; |
632 | } | ||
610 | 633 | ||
611 | inet->mc_index = mreq.imr_ifindex; | 634 | if (optname == IP_ADD_MEMBERSHIP) |
612 | inet->mc_addr = mreq.imr_address.s_addr; | 635 | err = ip_mc_join_group(sk, &mreq); |
613 | err = 0; | 636 | else |
637 | err = ip_mc_leave_group(sk, &mreq); | ||
638 | break; | ||
639 | } | ||
640 | case IP_MSFILTER: | ||
641 | { | ||
642 | extern int sysctl_igmp_max_msf; | ||
643 | struct ip_msfilter *msf; | ||
644 | |||
645 | if (optlen < IP_MSFILTER_SIZE(0)) | ||
646 | goto e_inval; | ||
647 | if (optlen > sysctl_optmem_max) { | ||
648 | err = -ENOBUFS; | ||
614 | break; | 649 | break; |
615 | } | 650 | } |
651 | msf = kmalloc(optlen, GFP_KERNEL); | ||
652 | if (msf == 0) { | ||
653 | err = -ENOBUFS; | ||
654 | break; | ||
655 | } | ||
656 | err = -EFAULT; | ||
657 | if (copy_from_user(msf, optval, optlen)) { | ||
658 | kfree(msf); | ||
659 | break; | ||
660 | } | ||
661 | /* numsrc >= (1G-4) overflow in 32 bits */ | ||
662 | if (msf->imsf_numsrc >= 0x3ffffffcU || | ||
663 | msf->imsf_numsrc > sysctl_igmp_max_msf) { | ||
664 | kfree(msf); | ||
665 | err = -ENOBUFS; | ||
666 | break; | ||
667 | } | ||
668 | if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { | ||
669 | kfree(msf); | ||
670 | err = -EINVAL; | ||
671 | break; | ||
672 | } | ||
673 | err = ip_mc_msfilter(sk, msf, 0); | ||
674 | kfree(msf); | ||
675 | break; | ||
676 | } | ||
677 | case IP_BLOCK_SOURCE: | ||
678 | case IP_UNBLOCK_SOURCE: | ||
679 | case IP_ADD_SOURCE_MEMBERSHIP: | ||
680 | case IP_DROP_SOURCE_MEMBERSHIP: | ||
681 | { | ||
682 | struct ip_mreq_source mreqs; | ||
683 | int omode, add; | ||
616 | 684 | ||
617 | case IP_ADD_MEMBERSHIP: | 685 | if (optlen != sizeof(struct ip_mreq_source)) |
618 | case IP_DROP_MEMBERSHIP: | 686 | goto e_inval; |
619 | { | 687 | if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { |
620 | struct ip_mreqn mreq; | ||
621 | |||
622 | if (optlen < sizeof(struct ip_mreq)) | ||
623 | goto e_inval; | ||
624 | err = -EFAULT; | 688 | err = -EFAULT; |
625 | if (optlen >= sizeof(struct ip_mreqn)) { | ||
626 | if(copy_from_user(&mreq,optval,sizeof(mreq))) | ||
627 | break; | ||
628 | } else { | ||
629 | memset(&mreq, 0, sizeof(mreq)); | ||
630 | if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq))) | ||
631 | break; | ||
632 | } | ||
633 | |||
634 | if (optname == IP_ADD_MEMBERSHIP) | ||
635 | err = ip_mc_join_group(sk, &mreq); | ||
636 | else | ||
637 | err = ip_mc_leave_group(sk, &mreq); | ||
638 | break; | 689 | break; |
639 | } | 690 | } |
640 | case IP_MSFILTER: | 691 | if (optname == IP_BLOCK_SOURCE) { |
641 | { | 692 | omode = MCAST_EXCLUDE; |
642 | extern int sysctl_igmp_max_msf; | 693 | add = 1; |
643 | struct ip_msfilter *msf; | 694 | } else if (optname == IP_UNBLOCK_SOURCE) { |
695 | omode = MCAST_EXCLUDE; | ||
696 | add = 0; | ||
697 | } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { | ||
698 | struct ip_mreqn mreq; | ||
644 | 699 | ||
645 | if (optlen < IP_MSFILTER_SIZE(0)) | 700 | mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; |
646 | goto e_inval; | 701 | mreq.imr_address.s_addr = mreqs.imr_interface; |
647 | if (optlen > sysctl_optmem_max) { | 702 | mreq.imr_ifindex = 0; |
648 | err = -ENOBUFS; | 703 | err = ip_mc_join_group(sk, &mreq); |
649 | break; | 704 | if (err && err != -EADDRINUSE) |
650 | } | ||
651 | msf = kmalloc(optlen, GFP_KERNEL); | ||
652 | if (msf == 0) { | ||
653 | err = -ENOBUFS; | ||
654 | break; | 705 | break; |
655 | } | 706 | omode = MCAST_INCLUDE; |
707 | add = 1; | ||
708 | } else /* IP_DROP_SOURCE_MEMBERSHIP */ { | ||
709 | omode = MCAST_INCLUDE; | ||
710 | add = 0; | ||
711 | } | ||
712 | err = ip_mc_source(add, omode, sk, &mreqs, 0); | ||
713 | break; | ||
714 | } | ||
715 | case MCAST_JOIN_GROUP: | ||
716 | case MCAST_LEAVE_GROUP: | ||
717 | { | ||
718 | struct group_req greq; | ||
719 | struct sockaddr_in *psin; | ||
720 | struct ip_mreqn mreq; | ||
721 | |||
722 | if (optlen < sizeof(struct group_req)) | ||
723 | goto e_inval; | ||
724 | err = -EFAULT; | ||
725 | if (copy_from_user(&greq, optval, sizeof(greq))) | ||
726 | break; | ||
727 | psin = (struct sockaddr_in *)&greq.gr_group; | ||
728 | if (psin->sin_family != AF_INET) | ||
729 | goto e_inval; | ||
730 | memset(&mreq, 0, sizeof(mreq)); | ||
731 | mreq.imr_multiaddr = psin->sin_addr; | ||
732 | mreq.imr_ifindex = greq.gr_interface; | ||
733 | |||
734 | if (optname == MCAST_JOIN_GROUP) | ||
735 | err = ip_mc_join_group(sk, &mreq); | ||
736 | else | ||
737 | err = ip_mc_leave_group(sk, &mreq); | ||
738 | break; | ||
739 | } | ||
740 | case MCAST_JOIN_SOURCE_GROUP: | ||
741 | case MCAST_LEAVE_SOURCE_GROUP: | ||
742 | case MCAST_BLOCK_SOURCE: | ||
743 | case MCAST_UNBLOCK_SOURCE: | ||
744 | { | ||
745 | struct group_source_req greqs; | ||
746 | struct ip_mreq_source mreqs; | ||
747 | struct sockaddr_in *psin; | ||
748 | int omode, add; | ||
749 | |||
750 | if (optlen != sizeof(struct group_source_req)) | ||
751 | goto e_inval; | ||
752 | if (copy_from_user(&greqs, optval, sizeof(greqs))) { | ||
656 | err = -EFAULT; | 753 | err = -EFAULT; |
657 | if (copy_from_user(msf, optval, optlen)) { | ||
658 | kfree(msf); | ||
659 | break; | ||
660 | } | ||
661 | /* numsrc >= (1G-4) overflow in 32 bits */ | ||
662 | if (msf->imsf_numsrc >= 0x3ffffffcU || | ||
663 | msf->imsf_numsrc > sysctl_igmp_max_msf) { | ||
664 | kfree(msf); | ||
665 | err = -ENOBUFS; | ||
666 | break; | ||
667 | } | ||
668 | if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { | ||
669 | kfree(msf); | ||
670 | err = -EINVAL; | ||
671 | break; | ||
672 | } | ||
673 | err = ip_mc_msfilter(sk, msf, 0); | ||
674 | kfree(msf); | ||
675 | break; | 754 | break; |
676 | } | 755 | } |
677 | case IP_BLOCK_SOURCE: | 756 | if (greqs.gsr_group.ss_family != AF_INET || |
678 | case IP_UNBLOCK_SOURCE: | 757 | greqs.gsr_source.ss_family != AF_INET) { |
679 | case IP_ADD_SOURCE_MEMBERSHIP: | 758 | err = -EADDRNOTAVAIL; |
680 | case IP_DROP_SOURCE_MEMBERSHIP: | ||
681 | { | ||
682 | struct ip_mreq_source mreqs; | ||
683 | int omode, add; | ||
684 | |||
685 | if (optlen != sizeof(struct ip_mreq_source)) | ||
686 | goto e_inval; | ||
687 | if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { | ||
688 | err = -EFAULT; | ||
689 | break; | ||
690 | } | ||
691 | if (optname == IP_BLOCK_SOURCE) { | ||
692 | omode = MCAST_EXCLUDE; | ||
693 | add = 1; | ||
694 | } else if (optname == IP_UNBLOCK_SOURCE) { | ||
695 | omode = MCAST_EXCLUDE; | ||
696 | add = 0; | ||
697 | } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { | ||
698 | struct ip_mreqn mreq; | ||
699 | |||
700 | mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; | ||
701 | mreq.imr_address.s_addr = mreqs.imr_interface; | ||
702 | mreq.imr_ifindex = 0; | ||
703 | err = ip_mc_join_group(sk, &mreq); | ||
704 | if (err && err != -EADDRINUSE) | ||
705 | break; | ||
706 | omode = MCAST_INCLUDE; | ||
707 | add = 1; | ||
708 | } else /* IP_DROP_SOURCE_MEMBERSHIP */ { | ||
709 | omode = MCAST_INCLUDE; | ||
710 | add = 0; | ||
711 | } | ||
712 | err = ip_mc_source(add, omode, sk, &mreqs, 0); | ||
713 | break; | 759 | break; |
714 | } | 760 | } |
715 | case MCAST_JOIN_GROUP: | 761 | psin = (struct sockaddr_in *)&greqs.gsr_group; |
716 | case MCAST_LEAVE_GROUP: | 762 | mreqs.imr_multiaddr = psin->sin_addr.s_addr; |
717 | { | 763 | psin = (struct sockaddr_in *)&greqs.gsr_source; |
718 | struct group_req greq; | 764 | mreqs.imr_sourceaddr = psin->sin_addr.s_addr; |
719 | struct sockaddr_in *psin; | 765 | mreqs.imr_interface = 0; /* use index for mc_source */ |
766 | |||
767 | if (optname == MCAST_BLOCK_SOURCE) { | ||
768 | omode = MCAST_EXCLUDE; | ||
769 | add = 1; | ||
770 | } else if (optname == MCAST_UNBLOCK_SOURCE) { | ||
771 | omode = MCAST_EXCLUDE; | ||
772 | add = 0; | ||
773 | } else if (optname == MCAST_JOIN_SOURCE_GROUP) { | ||
720 | struct ip_mreqn mreq; | 774 | struct ip_mreqn mreq; |
721 | 775 | ||
722 | if (optlen < sizeof(struct group_req)) | 776 | psin = (struct sockaddr_in *)&greqs.gsr_group; |
723 | goto e_inval; | ||
724 | err = -EFAULT; | ||
725 | if(copy_from_user(&greq, optval, sizeof(greq))) | ||
726 | break; | ||
727 | psin = (struct sockaddr_in *)&greq.gr_group; | ||
728 | if (psin->sin_family != AF_INET) | ||
729 | goto e_inval; | ||
730 | memset(&mreq, 0, sizeof(mreq)); | ||
731 | mreq.imr_multiaddr = psin->sin_addr; | 777 | mreq.imr_multiaddr = psin->sin_addr; |
732 | mreq.imr_ifindex = greq.gr_interface; | 778 | mreq.imr_address.s_addr = 0; |
733 | 779 | mreq.imr_ifindex = greqs.gsr_interface; | |
734 | if (optname == MCAST_JOIN_GROUP) | 780 | err = ip_mc_join_group(sk, &mreq); |
735 | err = ip_mc_join_group(sk, &mreq); | 781 | if (err && err != -EADDRINUSE) |
736 | else | 782 | break; |
737 | err = ip_mc_leave_group(sk, &mreq); | 783 | greqs.gsr_interface = mreq.imr_ifindex; |
784 | omode = MCAST_INCLUDE; | ||
785 | add = 1; | ||
786 | } else /* MCAST_LEAVE_SOURCE_GROUP */ { | ||
787 | omode = MCAST_INCLUDE; | ||
788 | add = 0; | ||
789 | } | ||
790 | err = ip_mc_source(add, omode, sk, &mreqs, | ||
791 | greqs.gsr_interface); | ||
792 | break; | ||
793 | } | ||
794 | case MCAST_MSFILTER: | ||
795 | { | ||
796 | extern int sysctl_igmp_max_msf; | ||
797 | struct sockaddr_in *psin; | ||
798 | struct ip_msfilter *msf = NULL; | ||
799 | struct group_filter *gsf = NULL; | ||
800 | int msize, i, ifindex; | ||
801 | |||
802 | if (optlen < GROUP_FILTER_SIZE(0)) | ||
803 | goto e_inval; | ||
804 | if (optlen > sysctl_optmem_max) { | ||
805 | err = -ENOBUFS; | ||
738 | break; | 806 | break; |
739 | } | 807 | } |
740 | case MCAST_JOIN_SOURCE_GROUP: | 808 | gsf = kmalloc(optlen,GFP_KERNEL); |
741 | case MCAST_LEAVE_SOURCE_GROUP: | 809 | if (gsf == 0) { |
742 | case MCAST_BLOCK_SOURCE: | 810 | err = -ENOBUFS; |
743 | case MCAST_UNBLOCK_SOURCE: | ||
744 | { | ||
745 | struct group_source_req greqs; | ||
746 | struct ip_mreq_source mreqs; | ||
747 | struct sockaddr_in *psin; | ||
748 | int omode, add; | ||
749 | |||
750 | if (optlen != sizeof(struct group_source_req)) | ||
751 | goto e_inval; | ||
752 | if (copy_from_user(&greqs, optval, sizeof(greqs))) { | ||
753 | err = -EFAULT; | ||
754 | break; | ||
755 | } | ||
756 | if (greqs.gsr_group.ss_family != AF_INET || | ||
757 | greqs.gsr_source.ss_family != AF_INET) { | ||
758 | err = -EADDRNOTAVAIL; | ||
759 | break; | ||
760 | } | ||
761 | psin = (struct sockaddr_in *)&greqs.gsr_group; | ||
762 | mreqs.imr_multiaddr = psin->sin_addr.s_addr; | ||
763 | psin = (struct sockaddr_in *)&greqs.gsr_source; | ||
764 | mreqs.imr_sourceaddr = psin->sin_addr.s_addr; | ||
765 | mreqs.imr_interface = 0; /* use index for mc_source */ | ||
766 | |||
767 | if (optname == MCAST_BLOCK_SOURCE) { | ||
768 | omode = MCAST_EXCLUDE; | ||
769 | add = 1; | ||
770 | } else if (optname == MCAST_UNBLOCK_SOURCE) { | ||
771 | omode = MCAST_EXCLUDE; | ||
772 | add = 0; | ||
773 | } else if (optname == MCAST_JOIN_SOURCE_GROUP) { | ||
774 | struct ip_mreqn mreq; | ||
775 | |||
776 | psin = (struct sockaddr_in *)&greqs.gsr_group; | ||
777 | mreq.imr_multiaddr = psin->sin_addr; | ||
778 | mreq.imr_address.s_addr = 0; | ||
779 | mreq.imr_ifindex = greqs.gsr_interface; | ||
780 | err = ip_mc_join_group(sk, &mreq); | ||
781 | if (err && err != -EADDRINUSE) | ||
782 | break; | ||
783 | greqs.gsr_interface = mreq.imr_ifindex; | ||
784 | omode = MCAST_INCLUDE; | ||
785 | add = 1; | ||
786 | } else /* MCAST_LEAVE_SOURCE_GROUP */ { | ||
787 | omode = MCAST_INCLUDE; | ||
788 | add = 0; | ||
789 | } | ||
790 | err = ip_mc_source(add, omode, sk, &mreqs, | ||
791 | greqs.gsr_interface); | ||
792 | break; | 811 | break; |
793 | } | 812 | } |
794 | case MCAST_MSFILTER: | 813 | err = -EFAULT; |
795 | { | 814 | if (copy_from_user(gsf, optval, optlen)) { |
796 | extern int sysctl_igmp_max_msf; | 815 | goto mc_msf_out; |
797 | struct sockaddr_in *psin; | 816 | } |
798 | struct ip_msfilter *msf = NULL; | 817 | /* numsrc >= (4G-140)/128 overflow in 32 bits */ |
799 | struct group_filter *gsf = NULL; | 818 | if (gsf->gf_numsrc >= 0x1ffffff || |
800 | int msize, i, ifindex; | 819 | gsf->gf_numsrc > sysctl_igmp_max_msf) { |
801 | 820 | err = -ENOBUFS; | |
802 | if (optlen < GROUP_FILTER_SIZE(0)) | 821 | goto mc_msf_out; |
803 | goto e_inval; | 822 | } |
804 | if (optlen > sysctl_optmem_max) { | 823 | if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { |
805 | err = -ENOBUFS; | 824 | err = -EINVAL; |
806 | break; | 825 | goto mc_msf_out; |
807 | } | 826 | } |
808 | gsf = kmalloc(optlen,GFP_KERNEL); | 827 | msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); |
809 | if (gsf == 0) { | 828 | msf = kmalloc(msize,GFP_KERNEL); |
810 | err = -ENOBUFS; | 829 | if (msf == 0) { |
811 | break; | 830 | err = -ENOBUFS; |
812 | } | 831 | goto mc_msf_out; |
813 | err = -EFAULT; | 832 | } |
814 | if (copy_from_user(gsf, optval, optlen)) { | 833 | ifindex = gsf->gf_interface; |
815 | goto mc_msf_out; | 834 | psin = (struct sockaddr_in *)&gsf->gf_group; |
816 | } | 835 | if (psin->sin_family != AF_INET) { |
817 | /* numsrc >= (4G-140)/128 overflow in 32 bits */ | ||
818 | if (gsf->gf_numsrc >= 0x1ffffff || | ||
819 | gsf->gf_numsrc > sysctl_igmp_max_msf) { | ||
820 | err = -ENOBUFS; | ||
821 | goto mc_msf_out; | ||
822 | } | ||
823 | if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { | ||
824 | err = -EINVAL; | ||
825 | goto mc_msf_out; | ||
826 | } | ||
827 | msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); | ||
828 | msf = kmalloc(msize,GFP_KERNEL); | ||
829 | if (msf == 0) { | ||
830 | err = -ENOBUFS; | ||
831 | goto mc_msf_out; | ||
832 | } | ||
833 | ifindex = gsf->gf_interface; | ||
834 | psin = (struct sockaddr_in *)&gsf->gf_group; | ||
835 | if (psin->sin_family != AF_INET) { | ||
836 | err = -EADDRNOTAVAIL; | ||
837 | goto mc_msf_out; | ||
838 | } | ||
839 | msf->imsf_multiaddr = psin->sin_addr.s_addr; | ||
840 | msf->imsf_interface = 0; | ||
841 | msf->imsf_fmode = gsf->gf_fmode; | ||
842 | msf->imsf_numsrc = gsf->gf_numsrc; | ||
843 | err = -EADDRNOTAVAIL; | 836 | err = -EADDRNOTAVAIL; |
844 | for (i=0; i<gsf->gf_numsrc; ++i) { | 837 | goto mc_msf_out; |
845 | psin = (struct sockaddr_in *)&gsf->gf_slist[i]; | ||
846 | |||
847 | if (psin->sin_family != AF_INET) | ||
848 | goto mc_msf_out; | ||
849 | msf->imsf_slist[i] = psin->sin_addr.s_addr; | ||
850 | } | ||
851 | kfree(gsf); | ||
852 | gsf = NULL; | ||
853 | |||
854 | err = ip_mc_msfilter(sk, msf, ifindex); | ||
855 | mc_msf_out: | ||
856 | kfree(msf); | ||
857 | kfree(gsf); | ||
858 | break; | ||
859 | } | 838 | } |
860 | case IP_ROUTER_ALERT: | 839 | msf->imsf_multiaddr = psin->sin_addr.s_addr; |
861 | err = ip_ra_control(sk, val ? 1 : 0, NULL); | 840 | msf->imsf_interface = 0; |
862 | break; | 841 | msf->imsf_fmode = gsf->gf_fmode; |
842 | msf->imsf_numsrc = gsf->gf_numsrc; | ||
843 | err = -EADDRNOTAVAIL; | ||
844 | for (i=0; i<gsf->gf_numsrc; ++i) { | ||
845 | psin = (struct sockaddr_in *)&gsf->gf_slist[i]; | ||
863 | 846 | ||
864 | case IP_FREEBIND: | 847 | if (psin->sin_family != AF_INET) |
865 | if (optlen<1) | 848 | goto mc_msf_out; |
866 | goto e_inval; | 849 | msf->imsf_slist[i] = psin->sin_addr.s_addr; |
867 | inet->freebind = !!val; | 850 | } |
868 | break; | 851 | kfree(gsf); |
869 | 852 | gsf = NULL; | |
870 | case IP_IPSEC_POLICY: | 853 | |
871 | case IP_XFRM_POLICY: | 854 | err = ip_mc_msfilter(sk, msf, ifindex); |
872 | err = -EPERM; | 855 | mc_msf_out: |
873 | if (!capable(CAP_NET_ADMIN)) | 856 | kfree(msf); |
874 | break; | 857 | kfree(gsf); |
875 | err = xfrm_user_policy(sk, optname, optval, optlen); | 858 | break; |
859 | } | ||
860 | case IP_ROUTER_ALERT: | ||
861 | err = ip_ra_control(sk, val ? 1 : 0, NULL); | ||
862 | break; | ||
863 | |||
864 | case IP_FREEBIND: | ||
865 | if (optlen<1) | ||
866 | goto e_inval; | ||
867 | inet->freebind = !!val; | ||
868 | break; | ||
869 | |||
870 | case IP_IPSEC_POLICY: | ||
871 | case IP_XFRM_POLICY: | ||
872 | err = -EPERM; | ||
873 | if (!capable(CAP_NET_ADMIN)) | ||
876 | break; | 874 | break; |
875 | err = xfrm_user_policy(sk, optname, optval, optlen); | ||
876 | break; | ||
877 | 877 | ||
878 | default: | 878 | default: |
879 | err = -ENOPROTOOPT; | 879 | err = -ENOPROTOOPT; |
880 | break; | 880 | break; |
881 | } | 881 | } |
882 | release_sock(sk); | 882 | release_sock(sk); |
883 | return err; | 883 | return err; |
@@ -948,214 +948,213 @@ EXPORT_SYMBOL(compat_ip_setsockopt); | |||
948 | */ | 948 | */ |
949 | 949 | ||
950 | static int do_ip_getsockopt(struct sock *sk, int level, int optname, | 950 | static int do_ip_getsockopt(struct sock *sk, int level, int optname, |
951 | char __user *optval, int __user *optlen) | 951 | char __user *optval, int __user *optlen) |
952 | { | 952 | { |
953 | struct inet_sock *inet = inet_sk(sk); | 953 | struct inet_sock *inet = inet_sk(sk); |
954 | int val; | 954 | int val; |
955 | int len; | 955 | int len; |
956 | 956 | ||
957 | if(level!=SOL_IP) | 957 | if (level != SOL_IP) |
958 | return -EOPNOTSUPP; | 958 | return -EOPNOTSUPP; |
959 | 959 | ||
960 | #ifdef CONFIG_IP_MROUTE | 960 | #ifdef CONFIG_IP_MROUTE |
961 | if(optname>=MRT_BASE && optname <=MRT_BASE+10) | 961 | if (optname >= MRT_BASE && optname <= MRT_BASE+10) { |
962 | { | ||
963 | return ip_mroute_getsockopt(sk,optname,optval,optlen); | 962 | return ip_mroute_getsockopt(sk,optname,optval,optlen); |
964 | } | 963 | } |
965 | #endif | 964 | #endif |
966 | 965 | ||
967 | if(get_user(len,optlen)) | 966 | if (get_user(len,optlen)) |
968 | return -EFAULT; | 967 | return -EFAULT; |
969 | if(len < 0) | 968 | if (len < 0) |
970 | return -EINVAL; | 969 | return -EINVAL; |
971 | 970 | ||
972 | lock_sock(sk); | 971 | lock_sock(sk); |
973 | 972 | ||
974 | switch(optname) { | 973 | switch (optname) { |
975 | case IP_OPTIONS: | 974 | case IP_OPTIONS: |
976 | { | 975 | { |
977 | unsigned char optbuf[sizeof(struct ip_options)+40]; | 976 | unsigned char optbuf[sizeof(struct ip_options)+40]; |
978 | struct ip_options * opt = (struct ip_options*)optbuf; | 977 | struct ip_options * opt = (struct ip_options*)optbuf; |
979 | opt->optlen = 0; | 978 | opt->optlen = 0; |
980 | if (inet->opt) | 979 | if (inet->opt) |
981 | memcpy(optbuf, inet->opt, | 980 | memcpy(optbuf, inet->opt, |
982 | sizeof(struct ip_options)+ | 981 | sizeof(struct ip_options)+ |
983 | inet->opt->optlen); | 982 | inet->opt->optlen); |
984 | release_sock(sk); | 983 | release_sock(sk); |
985 | 984 | ||
986 | if (opt->optlen == 0) | 985 | if (opt->optlen == 0) |
987 | return put_user(0, optlen); | 986 | return put_user(0, optlen); |
988 | 987 | ||
989 | ip_options_undo(opt); | 988 | ip_options_undo(opt); |
990 | 989 | ||
991 | len = min_t(unsigned int, len, opt->optlen); | 990 | len = min_t(unsigned int, len, opt->optlen); |
992 | if(put_user(len, optlen)) | 991 | if (put_user(len, optlen)) |
993 | return -EFAULT; | 992 | return -EFAULT; |
994 | if(copy_to_user(optval, opt->__data, len)) | 993 | if (copy_to_user(optval, opt->__data, len)) |
995 | return -EFAULT; | 994 | return -EFAULT; |
996 | return 0; | 995 | return 0; |
997 | } | 996 | } |
998 | case IP_PKTINFO: | 997 | case IP_PKTINFO: |
999 | val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; | 998 | val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; |
1000 | break; | 999 | break; |
1001 | case IP_RECVTTL: | 1000 | case IP_RECVTTL: |
1002 | val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; | 1001 | val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; |
1003 | break; | 1002 | break; |
1004 | case IP_RECVTOS: | 1003 | case IP_RECVTOS: |
1005 | val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; | 1004 | val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; |
1006 | break; | 1005 | break; |
1007 | case IP_RECVOPTS: | 1006 | case IP_RECVOPTS: |
1008 | val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; | 1007 | val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; |
1009 | break; | 1008 | break; |
1010 | case IP_RETOPTS: | 1009 | case IP_RETOPTS: |
1011 | val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; | 1010 | val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; |
1012 | break; | 1011 | break; |
1013 | case IP_PASSSEC: | 1012 | case IP_PASSSEC: |
1014 | val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; | 1013 | val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; |
1015 | break; | 1014 | break; |
1016 | case IP_TOS: | 1015 | case IP_TOS: |
1017 | val = inet->tos; | 1016 | val = inet->tos; |
1018 | break; | 1017 | break; |
1019 | case IP_TTL: | 1018 | case IP_TTL: |
1020 | val = (inet->uc_ttl == -1 ? | 1019 | val = (inet->uc_ttl == -1 ? |
1021 | sysctl_ip_default_ttl : | 1020 | sysctl_ip_default_ttl : |
1022 | inet->uc_ttl); | 1021 | inet->uc_ttl); |
1023 | break; | 1022 | break; |
1024 | case IP_HDRINCL: | 1023 | case IP_HDRINCL: |
1025 | val = inet->hdrincl; | 1024 | val = inet->hdrincl; |
1026 | break; | 1025 | break; |
1027 | case IP_MTU_DISCOVER: | 1026 | case IP_MTU_DISCOVER: |
1028 | val = inet->pmtudisc; | 1027 | val = inet->pmtudisc; |
1029 | break; | 1028 | break; |
1030 | case IP_MTU: | 1029 | case IP_MTU: |
1031 | { | 1030 | { |
1032 | struct dst_entry *dst; | 1031 | struct dst_entry *dst; |
1033 | val = 0; | 1032 | val = 0; |
1034 | dst = sk_dst_get(sk); | 1033 | dst = sk_dst_get(sk); |
1035 | if (dst) { | 1034 | if (dst) { |
1036 | val = dst_mtu(dst); | 1035 | val = dst_mtu(dst); |
1037 | dst_release(dst); | 1036 | dst_release(dst); |
1038 | } | ||
1039 | if (!val) { | ||
1040 | release_sock(sk); | ||
1041 | return -ENOTCONN; | ||
1042 | } | ||
1043 | break; | ||
1044 | } | 1037 | } |
1045 | case IP_RECVERR: | 1038 | if (!val) { |
1046 | val = inet->recverr; | ||
1047 | break; | ||
1048 | case IP_MULTICAST_TTL: | ||
1049 | val = inet->mc_ttl; | ||
1050 | break; | ||
1051 | case IP_MULTICAST_LOOP: | ||
1052 | val = inet->mc_loop; | ||
1053 | break; | ||
1054 | case IP_MULTICAST_IF: | ||
1055 | { | ||
1056 | struct in_addr addr; | ||
1057 | len = min_t(unsigned int, len, sizeof(struct in_addr)); | ||
1058 | addr.s_addr = inet->mc_addr; | ||
1059 | release_sock(sk); | 1039 | release_sock(sk); |
1060 | 1040 | return -ENOTCONN; | |
1061 | if(put_user(len, optlen)) | ||
1062 | return -EFAULT; | ||
1063 | if(copy_to_user(optval, &addr, len)) | ||
1064 | return -EFAULT; | ||
1065 | return 0; | ||
1066 | } | 1041 | } |
1067 | case IP_MSFILTER: | 1042 | break; |
1068 | { | 1043 | } |
1069 | struct ip_msfilter msf; | 1044 | case IP_RECVERR: |
1070 | int err; | 1045 | val = inet->recverr; |
1046 | break; | ||
1047 | case IP_MULTICAST_TTL: | ||
1048 | val = inet->mc_ttl; | ||
1049 | break; | ||
1050 | case IP_MULTICAST_LOOP: | ||
1051 | val = inet->mc_loop; | ||
1052 | break; | ||
1053 | case IP_MULTICAST_IF: | ||
1054 | { | ||
1055 | struct in_addr addr; | ||
1056 | len = min_t(unsigned int, len, sizeof(struct in_addr)); | ||
1057 | addr.s_addr = inet->mc_addr; | ||
1058 | release_sock(sk); | ||
1071 | 1059 | ||
1072 | if (len < IP_MSFILTER_SIZE(0)) { | 1060 | if (put_user(len, optlen)) |
1073 | release_sock(sk); | 1061 | return -EFAULT; |
1074 | return -EINVAL; | 1062 | if (copy_to_user(optval, &addr, len)) |
1075 | } | 1063 | return -EFAULT; |
1076 | if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { | 1064 | return 0; |
1077 | release_sock(sk); | 1065 | } |
1078 | return -EFAULT; | 1066 | case IP_MSFILTER: |
1079 | } | 1067 | { |
1080 | err = ip_mc_msfget(sk, &msf, | 1068 | struct ip_msfilter msf; |
1081 | (struct ip_msfilter __user *)optval, optlen); | 1069 | int err; |
1070 | |||
1071 | if (len < IP_MSFILTER_SIZE(0)) { | ||
1082 | release_sock(sk); | 1072 | release_sock(sk); |
1083 | return err; | 1073 | return -EINVAL; |
1084 | } | 1074 | } |
1085 | case MCAST_MSFILTER: | 1075 | if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { |
1086 | { | ||
1087 | struct group_filter gsf; | ||
1088 | int err; | ||
1089 | |||
1090 | if (len < GROUP_FILTER_SIZE(0)) { | ||
1091 | release_sock(sk); | ||
1092 | return -EINVAL; | ||
1093 | } | ||
1094 | if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { | ||
1095 | release_sock(sk); | ||
1096 | return -EFAULT; | ||
1097 | } | ||
1098 | err = ip_mc_gsfget(sk, &gsf, | ||
1099 | (struct group_filter __user *)optval, optlen); | ||
1100 | release_sock(sk); | 1076 | release_sock(sk); |
1101 | return err; | 1077 | return -EFAULT; |
1102 | } | 1078 | } |
1103 | case IP_PKTOPTIONS: | 1079 | err = ip_mc_msfget(sk, &msf, |
1104 | { | 1080 | (struct ip_msfilter __user *)optval, optlen); |
1105 | struct msghdr msg; | 1081 | release_sock(sk); |
1082 | return err; | ||
1083 | } | ||
1084 | case MCAST_MSFILTER: | ||
1085 | { | ||
1086 | struct group_filter gsf; | ||
1087 | int err; | ||
1106 | 1088 | ||
1089 | if (len < GROUP_FILTER_SIZE(0)) { | ||
1107 | release_sock(sk); | 1090 | release_sock(sk); |
1091 | return -EINVAL; | ||
1092 | } | ||
1093 | if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { | ||
1094 | release_sock(sk); | ||
1095 | return -EFAULT; | ||
1096 | } | ||
1097 | err = ip_mc_gsfget(sk, &gsf, | ||
1098 | (struct group_filter __user *)optval, optlen); | ||
1099 | release_sock(sk); | ||
1100 | return err; | ||
1101 | } | ||
1102 | case IP_PKTOPTIONS: | ||
1103 | { | ||
1104 | struct msghdr msg; | ||
1105 | |||
1106 | release_sock(sk); | ||
1108 | 1107 | ||
1109 | if (sk->sk_type != SOCK_STREAM) | 1108 | if (sk->sk_type != SOCK_STREAM) |
1110 | return -ENOPROTOOPT; | 1109 | return -ENOPROTOOPT; |
1111 | 1110 | ||
1112 | msg.msg_control = optval; | 1111 | msg.msg_control = optval; |
1113 | msg.msg_controllen = len; | 1112 | msg.msg_controllen = len; |
1114 | msg.msg_flags = 0; | 1113 | msg.msg_flags = 0; |
1115 | 1114 | ||
1116 | if (inet->cmsg_flags & IP_CMSG_PKTINFO) { | 1115 | if (inet->cmsg_flags & IP_CMSG_PKTINFO) { |
1117 | struct in_pktinfo info; | 1116 | struct in_pktinfo info; |
1118 | 1117 | ||
1119 | info.ipi_addr.s_addr = inet->rcv_saddr; | 1118 | info.ipi_addr.s_addr = inet->rcv_saddr; |
1120 | info.ipi_spec_dst.s_addr = inet->rcv_saddr; | 1119 | info.ipi_spec_dst.s_addr = inet->rcv_saddr; |
1121 | info.ipi_ifindex = inet->mc_index; | 1120 | info.ipi_ifindex = inet->mc_index; |
1122 | put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); | 1121 | put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); |
1123 | } | ||
1124 | if (inet->cmsg_flags & IP_CMSG_TTL) { | ||
1125 | int hlim = inet->mc_ttl; | ||
1126 | put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); | ||
1127 | } | ||
1128 | len -= msg.msg_controllen; | ||
1129 | return put_user(len, optlen); | ||
1130 | } | 1122 | } |
1131 | case IP_FREEBIND: | 1123 | if (inet->cmsg_flags & IP_CMSG_TTL) { |
1132 | val = inet->freebind; | 1124 | int hlim = inet->mc_ttl; |
1133 | break; | 1125 | put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); |
1134 | default: | 1126 | } |
1135 | release_sock(sk); | 1127 | len -= msg.msg_controllen; |
1136 | return -ENOPROTOOPT; | 1128 | return put_user(len, optlen); |
1129 | } | ||
1130 | case IP_FREEBIND: | ||
1131 | val = inet->freebind; | ||
1132 | break; | ||
1133 | default: | ||
1134 | release_sock(sk); | ||
1135 | return -ENOPROTOOPT; | ||
1137 | } | 1136 | } |
1138 | release_sock(sk); | 1137 | release_sock(sk); |
1139 | 1138 | ||
1140 | if (len < sizeof(int) && len > 0 && val>=0 && val<255) { | 1139 | if (len < sizeof(int) && len > 0 && val>=0 && val<255) { |
1141 | unsigned char ucval = (unsigned char)val; | 1140 | unsigned char ucval = (unsigned char)val; |
1142 | len = 1; | 1141 | len = 1; |
1143 | if(put_user(len, optlen)) | 1142 | if (put_user(len, optlen)) |
1144 | return -EFAULT; | 1143 | return -EFAULT; |
1145 | if(copy_to_user(optval,&ucval,1)) | 1144 | if (copy_to_user(optval,&ucval,1)) |
1146 | return -EFAULT; | 1145 | return -EFAULT; |
1147 | } else { | 1146 | } else { |
1148 | len = min_t(unsigned int, sizeof(int), len); | 1147 | len = min_t(unsigned int, sizeof(int), len); |
1149 | if(put_user(len, optlen)) | 1148 | if (put_user(len, optlen)) |
1150 | return -EFAULT; | 1149 | return -EFAULT; |
1151 | if(copy_to_user(optval,&val,len)) | 1150 | if (copy_to_user(optval,&val,len)) |
1152 | return -EFAULT; | 1151 | return -EFAULT; |
1153 | } | 1152 | } |
1154 | return 0; | 1153 | return 0; |
1155 | } | 1154 | } |
1156 | 1155 | ||
1157 | int ip_getsockopt(struct sock *sk, int level, | 1156 | int ip_getsockopt(struct sock *sk, int level, |
1158 | int optname, char __user *optval, int __user *optlen) | 1157 | int optname, char __user *optval, int __user *optlen) |
1159 | { | 1158 | { |
1160 | int err; | 1159 | int err; |
1161 | 1160 | ||
@@ -1169,7 +1168,7 @@ int ip_getsockopt(struct sock *sk, int level, | |||
1169 | ) { | 1168 | ) { |
1170 | int len; | 1169 | int len; |
1171 | 1170 | ||
1172 | if(get_user(len,optlen)) | 1171 | if (get_user(len,optlen)) |
1173 | return -EFAULT; | 1172 | return -EFAULT; |
1174 | 1173 | ||
1175 | lock_sock(sk); | 1174 | lock_sock(sk); |