diff options
Diffstat (limited to 'net/wireless/wext.c')
-rw-r--r-- | net/wireless/wext.c | 584 |
1 files changed, 363 insertions, 221 deletions
diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 947188a5b937..df5b3886c36b 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c | |||
@@ -500,7 +500,7 @@ static int call_commit_handler(struct net_device *dev) | |||
500 | /* | 500 | /* |
501 | * Calculate size of private arguments | 501 | * Calculate size of private arguments |
502 | */ | 502 | */ |
503 | static inline int get_priv_size(__u16 args) | 503 | static int get_priv_size(__u16 args) |
504 | { | 504 | { |
505 | int num = args & IW_PRIV_SIZE_MASK; | 505 | int num = args & IW_PRIV_SIZE_MASK; |
506 | int type = (args & IW_PRIV_TYPE_MASK) >> 12; | 506 | int type = (args & IW_PRIV_TYPE_MASK) >> 12; |
@@ -512,10 +512,9 @@ static inline int get_priv_size(__u16 args) | |||
512 | /* | 512 | /* |
513 | * Re-calculate the size of private arguments | 513 | * Re-calculate the size of private arguments |
514 | */ | 514 | */ |
515 | static inline int adjust_priv_size(__u16 args, | 515 | static int adjust_priv_size(__u16 args, struct iw_point *iwp) |
516 | union iwreq_data * wrqu) | ||
517 | { | 516 | { |
518 | int num = wrqu->data.length; | 517 | int num = iwp->length; |
519 | int max = args & IW_PRIV_SIZE_MASK; | 518 | int max = args & IW_PRIV_SIZE_MASK; |
520 | int type = (args & IW_PRIV_TYPE_MASK) >> 12; | 519 | int type = (args & IW_PRIV_TYPE_MASK) >> 12; |
521 | 520 | ||
@@ -695,19 +694,150 @@ void wext_proc_exit(struct net *net) | |||
695 | */ | 694 | */ |
696 | 695 | ||
697 | /* ---------------------------------------------------------------- */ | 696 | /* ---------------------------------------------------------------- */ |
697 | static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, | ||
698 | const struct iw_ioctl_description *descr, | ||
699 | iw_handler handler, struct net_device *dev, | ||
700 | struct iw_request_info *info) | ||
701 | { | ||
702 | int err, extra_size, user_length = 0, essid_compat = 0; | ||
703 | char *extra; | ||
704 | |||
705 | /* Calculate space needed by arguments. Always allocate | ||
706 | * for max space. | ||
707 | */ | ||
708 | extra_size = descr->max_tokens * descr->token_size; | ||
709 | |||
710 | /* Check need for ESSID compatibility for WE < 21 */ | ||
711 | switch (cmd) { | ||
712 | case SIOCSIWESSID: | ||
713 | case SIOCGIWESSID: | ||
714 | case SIOCSIWNICKN: | ||
715 | case SIOCGIWNICKN: | ||
716 | if (iwp->length == descr->max_tokens + 1) | ||
717 | essid_compat = 1; | ||
718 | else if (IW_IS_SET(cmd) && (iwp->length != 0)) { | ||
719 | char essid[IW_ESSID_MAX_SIZE + 1]; | ||
720 | |||
721 | err = copy_from_user(essid, iwp->pointer, | ||
722 | iwp->length * | ||
723 | descr->token_size); | ||
724 | if (err) | ||
725 | return -EFAULT; | ||
726 | |||
727 | if (essid[iwp->length - 1] == '\0') | ||
728 | essid_compat = 1; | ||
729 | } | ||
730 | break; | ||
731 | default: | ||
732 | break; | ||
733 | } | ||
734 | |||
735 | iwp->length -= essid_compat; | ||
736 | |||
737 | /* Check what user space is giving us */ | ||
738 | if (IW_IS_SET(cmd)) { | ||
739 | /* Check NULL pointer */ | ||
740 | if (!iwp->pointer && iwp->length != 0) | ||
741 | return -EFAULT; | ||
742 | /* Check if number of token fits within bounds */ | ||
743 | if (iwp->length > descr->max_tokens) | ||
744 | return -E2BIG; | ||
745 | if (iwp->length < descr->min_tokens) | ||
746 | return -EINVAL; | ||
747 | } else { | ||
748 | /* Check NULL pointer */ | ||
749 | if (!iwp->pointer) | ||
750 | return -EFAULT; | ||
751 | /* Save user space buffer size for checking */ | ||
752 | user_length = iwp->length; | ||
753 | |||
754 | /* Don't check if user_length > max to allow forward | ||
755 | * compatibility. The test user_length < min is | ||
756 | * implied by the test at the end. | ||
757 | */ | ||
758 | |||
759 | /* Support for very large requests */ | ||
760 | if ((descr->flags & IW_DESCR_FLAG_NOMAX) && | ||
761 | (user_length > descr->max_tokens)) { | ||
762 | /* Allow userspace to GET more than max so | ||
763 | * we can support any size GET requests. | ||
764 | * There is still a limit : -ENOMEM. | ||
765 | */ | ||
766 | extra_size = user_length * descr->token_size; | ||
767 | |||
768 | /* Note : user_length is originally a __u16, | ||
769 | * and token_size is controlled by us, | ||
770 | * so extra_size won't get negative and | ||
771 | * won't overflow... | ||
772 | */ | ||
773 | } | ||
774 | } | ||
775 | |||
776 | /* kzalloc() ensures NULL-termination for essid_compat. */ | ||
777 | extra = kzalloc(extra_size, GFP_KERNEL); | ||
778 | if (!extra) | ||
779 | return -ENOMEM; | ||
780 | |||
781 | /* If it is a SET, get all the extra data in here */ | ||
782 | if (IW_IS_SET(cmd) && (iwp->length != 0)) { | ||
783 | if (copy_from_user(extra, iwp->pointer, | ||
784 | iwp->length * | ||
785 | descr->token_size)) { | ||
786 | err = -EFAULT; | ||
787 | goto out; | ||
788 | } | ||
789 | } | ||
790 | |||
791 | err = handler(dev, info, (union iwreq_data *) iwp, extra); | ||
792 | |||
793 | iwp->length += essid_compat; | ||
794 | |||
795 | /* If we have something to return to the user */ | ||
796 | if (!err && IW_IS_GET(cmd)) { | ||
797 | /* Check if there is enough buffer up there */ | ||
798 | if (user_length < iwp->length) { | ||
799 | err = -E2BIG; | ||
800 | goto out; | ||
801 | } | ||
802 | |||
803 | if (copy_to_user(iwp->pointer, extra, | ||
804 | iwp->length * | ||
805 | descr->token_size)) { | ||
806 | err = -EFAULT; | ||
807 | goto out; | ||
808 | } | ||
809 | } | ||
810 | |||
811 | /* Generate an event to notify listeners of the change */ | ||
812 | if ((descr->flags & IW_DESCR_FLAG_EVENT) && err == -EIWCOMMIT) { | ||
813 | union iwreq_data *data = (union iwreq_data *) iwp; | ||
814 | |||
815 | if (descr->flags & IW_DESCR_FLAG_RESTRICT) | ||
816 | /* If the event is restricted, don't | ||
817 | * export the payload. | ||
818 | */ | ||
819 | wireless_send_event(dev, cmd, data, NULL); | ||
820 | else | ||
821 | wireless_send_event(dev, cmd, data, extra); | ||
822 | } | ||
823 | |||
824 | out: | ||
825 | kfree(extra); | ||
826 | return err; | ||
827 | } | ||
828 | |||
698 | /* | 829 | /* |
699 | * Wrapper to call a standard Wireless Extension handler. | 830 | * Wrapper to call a standard Wireless Extension handler. |
700 | * We do various checks and also take care of moving data between | 831 | * We do various checks and also take care of moving data between |
701 | * user space and kernel space. | 832 | * user space and kernel space. |
702 | */ | 833 | */ |
703 | static int ioctl_standard_call(struct net_device * dev, | 834 | static int ioctl_standard_call(struct net_device * dev, |
704 | struct ifreq * ifr, | 835 | struct iwreq *iwr, |
705 | unsigned int cmd, | 836 | unsigned int cmd, |
837 | struct iw_request_info *info, | ||
706 | iw_handler handler) | 838 | iw_handler handler) |
707 | { | 839 | { |
708 | struct iwreq * iwr = (struct iwreq *) ifr; | ||
709 | const struct iw_ioctl_description * descr; | 840 | const struct iw_ioctl_description * descr; |
710 | struct iw_request_info info; | ||
711 | int ret = -EINVAL; | 841 | int ret = -EINVAL; |
712 | 842 | ||
713 | /* Get the description of the IOCTL */ | 843 | /* Get the description of the IOCTL */ |
@@ -715,145 +845,19 @@ static int ioctl_standard_call(struct net_device * dev, | |||
715 | return -EOPNOTSUPP; | 845 | return -EOPNOTSUPP; |
716 | descr = &(standard_ioctl[cmd - SIOCIWFIRST]); | 846 | descr = &(standard_ioctl[cmd - SIOCIWFIRST]); |
717 | 847 | ||
718 | /* Prepare the call */ | ||
719 | info.cmd = cmd; | ||
720 | info.flags = 0; | ||
721 | |||
722 | /* Check if we have a pointer to user space data or not */ | 848 | /* Check if we have a pointer to user space data or not */ |
723 | if (descr->header_type != IW_HEADER_TYPE_POINT) { | 849 | if (descr->header_type != IW_HEADER_TYPE_POINT) { |
724 | 850 | ||
725 | /* No extra arguments. Trivial to handle */ | 851 | /* No extra arguments. Trivial to handle */ |
726 | ret = handler(dev, &info, &(iwr->u), NULL); | 852 | ret = handler(dev, info, &(iwr->u), NULL); |
727 | 853 | ||
728 | /* Generate an event to notify listeners of the change */ | 854 | /* Generate an event to notify listeners of the change */ |
729 | if ((descr->flags & IW_DESCR_FLAG_EVENT) && | 855 | if ((descr->flags & IW_DESCR_FLAG_EVENT) && |
730 | ((ret == 0) || (ret == -EIWCOMMIT))) | 856 | ((ret == 0) || (ret == -EIWCOMMIT))) |
731 | wireless_send_event(dev, cmd, &(iwr->u), NULL); | 857 | wireless_send_event(dev, cmd, &(iwr->u), NULL); |
732 | } else { | 858 | } else { |
733 | char * extra; | 859 | ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, |
734 | int extra_size; | 860 | handler, dev, info); |
735 | int user_length = 0; | ||
736 | int err; | ||
737 | int essid_compat = 0; | ||
738 | |||
739 | /* Calculate space needed by arguments. Always allocate | ||
740 | * for max space. Easier, and won't last long... */ | ||
741 | extra_size = descr->max_tokens * descr->token_size; | ||
742 | |||
743 | /* Check need for ESSID compatibility for WE < 21 */ | ||
744 | switch (cmd) { | ||
745 | case SIOCSIWESSID: | ||
746 | case SIOCGIWESSID: | ||
747 | case SIOCSIWNICKN: | ||
748 | case SIOCGIWNICKN: | ||
749 | if (iwr->u.data.length == descr->max_tokens + 1) | ||
750 | essid_compat = 1; | ||
751 | else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { | ||
752 | char essid[IW_ESSID_MAX_SIZE + 1]; | ||
753 | |||
754 | err = copy_from_user(essid, iwr->u.data.pointer, | ||
755 | iwr->u.data.length * | ||
756 | descr->token_size); | ||
757 | if (err) | ||
758 | return -EFAULT; | ||
759 | |||
760 | if (essid[iwr->u.data.length - 1] == '\0') | ||
761 | essid_compat = 1; | ||
762 | } | ||
763 | break; | ||
764 | default: | ||
765 | break; | ||
766 | } | ||
767 | |||
768 | iwr->u.data.length -= essid_compat; | ||
769 | |||
770 | /* Check what user space is giving us */ | ||
771 | if (IW_IS_SET(cmd)) { | ||
772 | /* Check NULL pointer */ | ||
773 | if ((iwr->u.data.pointer == NULL) && | ||
774 | (iwr->u.data.length != 0)) | ||
775 | return -EFAULT; | ||
776 | /* Check if number of token fits within bounds */ | ||
777 | if (iwr->u.data.length > descr->max_tokens) | ||
778 | return -E2BIG; | ||
779 | if (iwr->u.data.length < descr->min_tokens) | ||
780 | return -EINVAL; | ||
781 | } else { | ||
782 | /* Check NULL pointer */ | ||
783 | if (iwr->u.data.pointer == NULL) | ||
784 | return -EFAULT; | ||
785 | /* Save user space buffer size for checking */ | ||
786 | user_length = iwr->u.data.length; | ||
787 | |||
788 | /* Don't check if user_length > max to allow forward | ||
789 | * compatibility. The test user_length < min is | ||
790 | * implied by the test at the end. */ | ||
791 | |||
792 | /* Support for very large requests */ | ||
793 | if ((descr->flags & IW_DESCR_FLAG_NOMAX) && | ||
794 | (user_length > descr->max_tokens)) { | ||
795 | /* Allow userspace to GET more than max so | ||
796 | * we can support any size GET requests. | ||
797 | * There is still a limit : -ENOMEM. */ | ||
798 | extra_size = user_length * descr->token_size; | ||
799 | /* Note : user_length is originally a __u16, | ||
800 | * and token_size is controlled by us, | ||
801 | * so extra_size won't get negative and | ||
802 | * won't overflow... */ | ||
803 | } | ||
804 | } | ||
805 | |||
806 | /* Create the kernel buffer */ | ||
807 | /* kzalloc ensures NULL-termination for essid_compat */ | ||
808 | extra = kzalloc(extra_size, GFP_KERNEL); | ||
809 | if (extra == NULL) | ||
810 | return -ENOMEM; | ||
811 | |||
812 | /* If it is a SET, get all the extra data in here */ | ||
813 | if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { | ||
814 | err = copy_from_user(extra, iwr->u.data.pointer, | ||
815 | iwr->u.data.length * | ||
816 | descr->token_size); | ||
817 | if (err) { | ||
818 | kfree(extra); | ||
819 | return -EFAULT; | ||
820 | } | ||
821 | } | ||
822 | |||
823 | /* Call the handler */ | ||
824 | ret = handler(dev, &info, &(iwr->u), extra); | ||
825 | |||
826 | iwr->u.data.length += essid_compat; | ||
827 | |||
828 | /* If we have something to return to the user */ | ||
829 | if (!ret && IW_IS_GET(cmd)) { | ||
830 | /* Check if there is enough buffer up there */ | ||
831 | if (user_length < iwr->u.data.length) { | ||
832 | kfree(extra); | ||
833 | return -E2BIG; | ||
834 | } | ||
835 | |||
836 | err = copy_to_user(iwr->u.data.pointer, extra, | ||
837 | iwr->u.data.length * | ||
838 | descr->token_size); | ||
839 | if (err) | ||
840 | ret = -EFAULT; | ||
841 | } | ||
842 | |||
843 | /* Generate an event to notify listeners of the change */ | ||
844 | if ((descr->flags & IW_DESCR_FLAG_EVENT) && | ||
845 | ((ret == 0) || (ret == -EIWCOMMIT))) { | ||
846 | if (descr->flags & IW_DESCR_FLAG_RESTRICT) | ||
847 | /* If the event is restricted, don't | ||
848 | * export the payload */ | ||
849 | wireless_send_event(dev, cmd, &(iwr->u), NULL); | ||
850 | else | ||
851 | wireless_send_event(dev, cmd, &(iwr->u), | ||
852 | extra); | ||
853 | } | ||
854 | |||
855 | /* Cleanup - I told you it wasn't that long ;-) */ | ||
856 | kfree(extra); | ||
857 | } | 861 | } |
858 | 862 | ||
859 | /* Call commit handler if needed and defined */ | 863 | /* Call commit handler if needed and defined */ |
@@ -881,25 +885,22 @@ static int ioctl_standard_call(struct net_device * dev, | |||
881 | * a iw_handler but process it in your ioctl handler (i.e. use the | 885 | * a iw_handler but process it in your ioctl handler (i.e. use the |
882 | * old driver API). | 886 | * old driver API). |
883 | */ | 887 | */ |
884 | static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, | 888 | static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd, |
885 | unsigned int cmd, iw_handler handler) | 889 | const struct iw_priv_args **descrp) |
886 | { | 890 | { |
887 | struct iwreq * iwr = (struct iwreq *) ifr; | 891 | const struct iw_priv_args *descr; |
888 | const struct iw_priv_args * descr = NULL; | 892 | int i, extra_size; |
889 | struct iw_request_info info; | ||
890 | int extra_size = 0; | ||
891 | int i; | ||
892 | int ret = -EINVAL; | ||
893 | 893 | ||
894 | /* Get the description of the IOCTL */ | 894 | descr = NULL; |
895 | for (i = 0; i < dev->wireless_handlers->num_private_args; i++) | 895 | for (i = 0; i < dev->wireless_handlers->num_private_args; i++) { |
896 | if (cmd == dev->wireless_handlers->private_args[i].cmd) { | 896 | if (cmd == dev->wireless_handlers->private_args[i].cmd) { |
897 | descr = &(dev->wireless_handlers->private_args[i]); | 897 | descr = &dev->wireless_handlers->private_args[i]; |
898 | break; | 898 | break; |
899 | } | 899 | } |
900 | } | ||
900 | 901 | ||
901 | /* Compute the size of the set/get arguments */ | 902 | extra_size = 0; |
902 | if (descr != NULL) { | 903 | if (descr) { |
903 | if (IW_IS_SET(cmd)) { | 904 | if (IW_IS_SET(cmd)) { |
904 | int offset = 0; /* For sub-ioctls */ | 905 | int offset = 0; /* For sub-ioctls */ |
905 | /* Check for sub-ioctl handler */ | 906 | /* Check for sub-ioctl handler */ |
@@ -924,72 +925,77 @@ static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, | |||
924 | extra_size = 0; | 925 | extra_size = 0; |
925 | } | 926 | } |
926 | } | 927 | } |
928 | *descrp = descr; | ||
929 | return extra_size; | ||
930 | } | ||
927 | 931 | ||
928 | /* Prepare the call */ | 932 | static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd, |
929 | info.cmd = cmd; | 933 | const struct iw_priv_args *descr, |
930 | info.flags = 0; | 934 | iw_handler handler, struct net_device *dev, |
935 | struct iw_request_info *info, int extra_size) | ||
936 | { | ||
937 | char *extra; | ||
938 | int err; | ||
931 | 939 | ||
932 | /* Check if we have a pointer to user space data or not. */ | 940 | /* Check what user space is giving us */ |
933 | if (extra_size == 0) { | 941 | if (IW_IS_SET(cmd)) { |
934 | /* No extra arguments. Trivial to handle */ | 942 | if (!iwp->pointer && iwp->length != 0) |
935 | ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); | 943 | return -EFAULT; |
936 | } else { | ||
937 | char * extra; | ||
938 | int err; | ||
939 | 944 | ||
940 | /* Check what user space is giving us */ | 945 | if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK)) |
941 | if (IW_IS_SET(cmd)) { | 946 | return -E2BIG; |
942 | /* Check NULL pointer */ | 947 | } else if (!iwp->pointer) |
943 | if ((iwr->u.data.pointer == NULL) && | 948 | return -EFAULT; |
944 | (iwr->u.data.length != 0)) | ||
945 | return -EFAULT; | ||
946 | 949 | ||
947 | /* Does it fits within bounds ? */ | 950 | extra = kmalloc(extra_size, GFP_KERNEL); |
948 | if (iwr->u.data.length > (descr->set_args & | 951 | if (!extra) |
949 | IW_PRIV_SIZE_MASK)) | 952 | return -ENOMEM; |
950 | return -E2BIG; | ||
951 | } else if (iwr->u.data.pointer == NULL) | ||
952 | return -EFAULT; | ||
953 | 953 | ||
954 | /* Always allocate for max space. Easier, and won't last | 954 | /* If it is a SET, get all the extra data in here */ |
955 | * long... */ | 955 | if (IW_IS_SET(cmd) && (iwp->length != 0)) { |
956 | extra = kmalloc(extra_size, GFP_KERNEL); | 956 | if (copy_from_user(extra, iwp->pointer, extra_size)) { |
957 | if (extra == NULL) | 957 | err = -EFAULT; |
958 | return -ENOMEM; | 958 | goto out; |
959 | |||
960 | /* If it is a SET, get all the extra data in here */ | ||
961 | if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { | ||
962 | err = copy_from_user(extra, iwr->u.data.pointer, | ||
963 | extra_size); | ||
964 | if (err) { | ||
965 | kfree(extra); | ||
966 | return -EFAULT; | ||
967 | } | ||
968 | } | 959 | } |
960 | } | ||
969 | 961 | ||
970 | /* Call the handler */ | 962 | /* Call the handler */ |
971 | ret = handler(dev, &info, &(iwr->u), extra); | 963 | err = handler(dev, info, (union iwreq_data *) iwp, extra); |
972 | 964 | ||
973 | /* If we have something to return to the user */ | 965 | /* If we have something to return to the user */ |
974 | if (!ret && IW_IS_GET(cmd)) { | 966 | if (!err && IW_IS_GET(cmd)) { |
967 | /* Adjust for the actual length if it's variable, | ||
968 | * avoid leaking kernel bits outside. | ||
969 | */ | ||
970 | if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) | ||
971 | extra_size = adjust_priv_size(descr->get_args, iwp); | ||
975 | 972 | ||
976 | /* Adjust for the actual length if it's variable, | 973 | if (copy_to_user(iwp->pointer, extra, extra_size)) |
977 | * avoid leaking kernel bits outside. */ | 974 | err = -EFAULT; |
978 | if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { | 975 | } |
979 | extra_size = adjust_priv_size(descr->get_args, | ||
980 | &(iwr->u)); | ||
981 | } | ||
982 | 976 | ||
983 | err = copy_to_user(iwr->u.data.pointer, extra, | 977 | out: |
984 | extra_size); | 978 | kfree(extra); |
985 | if (err) | 979 | return err; |
986 | ret = -EFAULT; | 980 | } |
987 | } | ||
988 | 981 | ||
989 | /* Cleanup - I told you it wasn't that long ;-) */ | 982 | static int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, |
990 | kfree(extra); | 983 | unsigned int cmd, struct iw_request_info *info, |
991 | } | 984 | iw_handler handler) |
985 | { | ||
986 | int extra_size = 0, ret = -EINVAL; | ||
987 | const struct iw_priv_args *descr; | ||
992 | 988 | ||
989 | extra_size = get_priv_descr_and_size(dev, cmd, &descr); | ||
990 | |||
991 | /* Check if we have a pointer to user space data or not. */ | ||
992 | if (extra_size == 0) { | ||
993 | /* No extra arguments. Trivial to handle */ | ||
994 | ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); | ||
995 | } else { | ||
996 | ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, | ||
997 | handler, dev, info, extra_size); | ||
998 | } | ||
993 | 999 | ||
994 | /* Call commit handler if needed and defined */ | 1000 | /* Call commit handler if needed and defined */ |
995 | if (ret == -EIWCOMMIT) | 1001 | if (ret == -EIWCOMMIT) |
@@ -999,12 +1005,21 @@ static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, | |||
999 | } | 1005 | } |
1000 | 1006 | ||
1001 | /* ---------------------------------------------------------------- */ | 1007 | /* ---------------------------------------------------------------- */ |
1008 | typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, | ||
1009 | unsigned int, struct iw_request_info *, | ||
1010 | iw_handler); | ||
1011 | |||
1002 | /* | 1012 | /* |
1003 | * Main IOCTl dispatcher. | 1013 | * Main IOCTl dispatcher. |
1004 | * Check the type of IOCTL and call the appropriate wrapper... | 1014 | * Check the type of IOCTL and call the appropriate wrapper... |
1005 | */ | 1015 | */ |
1006 | static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd) | 1016 | static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, |
1017 | unsigned int cmd, | ||
1018 | struct iw_request_info *info, | ||
1019 | wext_ioctl_func standard, | ||
1020 | wext_ioctl_func private) | ||
1007 | { | 1021 | { |
1022 | struct iwreq *iwr = (struct iwreq *) ifr; | ||
1008 | struct net_device *dev; | 1023 | struct net_device *dev; |
1009 | iw_handler handler; | 1024 | iw_handler handler; |
1010 | 1025 | ||
@@ -1019,12 +1034,12 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned i | |||
1019 | * Note that 'cmd' is already filtered in dev_ioctl() with | 1034 | * Note that 'cmd' is already filtered in dev_ioctl() with |
1020 | * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ | 1035 | * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ |
1021 | if (cmd == SIOCGIWSTATS) | 1036 | if (cmd == SIOCGIWSTATS) |
1022 | return ioctl_standard_call(dev, ifr, cmd, | 1037 | return standard(dev, iwr, cmd, info, |
1023 | &iw_handler_get_iwstats); | 1038 | &iw_handler_get_iwstats); |
1024 | 1039 | ||
1025 | if (cmd == SIOCGIWPRIV && dev->wireless_handlers) | 1040 | if (cmd == SIOCGIWPRIV && dev->wireless_handlers) |
1026 | return ioctl_standard_call(dev, ifr, cmd, | 1041 | return standard(dev, iwr, cmd, info, |
1027 | &iw_handler_get_private); | 1042 | &iw_handler_get_private); |
1028 | 1043 | ||
1029 | /* Basic check */ | 1044 | /* Basic check */ |
1030 | if (!netif_device_present(dev)) | 1045 | if (!netif_device_present(dev)) |
@@ -1035,9 +1050,9 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned i | |||
1035 | if (handler) { | 1050 | if (handler) { |
1036 | /* Standard and private are not the same */ | 1051 | /* Standard and private are not the same */ |
1037 | if (cmd < SIOCIWFIRSTPRIV) | 1052 | if (cmd < SIOCIWFIRSTPRIV) |
1038 | return ioctl_standard_call(dev, ifr, cmd, handler); | 1053 | return standard(dev, iwr, cmd, info, handler); |
1039 | else | 1054 | else |
1040 | return ioctl_private_call(dev, ifr, cmd, handler); | 1055 | return private(dev, iwr, cmd, info, handler); |
1041 | } | 1056 | } |
1042 | /* Old driver API : call driver ioctl handler */ | 1057 | /* Old driver API : call driver ioctl handler */ |
1043 | if (dev->do_ioctl) | 1058 | if (dev->do_ioctl) |
@@ -1045,27 +1060,154 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned i | |||
1045 | return -EOPNOTSUPP; | 1060 | return -EOPNOTSUPP; |
1046 | } | 1061 | } |
1047 | 1062 | ||
1048 | /* entry point from dev ioctl */ | 1063 | /* If command is `set a parameter', or `get the encoding parameters', |
1049 | int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, | 1064 | * check if the user has the right to do it. |
1050 | void __user *arg) | 1065 | */ |
1066 | static int wext_permission_check(unsigned int cmd) | ||
1051 | { | 1067 | { |
1052 | int ret; | ||
1053 | |||
1054 | /* If command is `set a parameter', or | ||
1055 | * `get the encoding parameters', check if | ||
1056 | * the user has the right to do it */ | ||
1057 | if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT) | 1068 | if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT) |
1058 | && !capable(CAP_NET_ADMIN)) | 1069 | && !capable(CAP_NET_ADMIN)) |
1059 | return -EPERM; | 1070 | return -EPERM; |
1060 | 1071 | ||
1072 | return 0; | ||
1073 | } | ||
1074 | |||
1075 | /* entry point from dev ioctl */ | ||
1076 | static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr, | ||
1077 | unsigned int cmd, struct iw_request_info *info, | ||
1078 | wext_ioctl_func standard, | ||
1079 | wext_ioctl_func private) | ||
1080 | { | ||
1081 | int ret = wext_permission_check(cmd); | ||
1082 | |||
1083 | if (ret) | ||
1084 | return ret; | ||
1085 | |||
1061 | dev_load(net, ifr->ifr_name); | 1086 | dev_load(net, ifr->ifr_name); |
1062 | rtnl_lock(); | 1087 | rtnl_lock(); |
1063 | ret = wireless_process_ioctl(net, ifr, cmd); | 1088 | ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private); |
1064 | rtnl_unlock(); | 1089 | rtnl_unlock(); |
1065 | if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct iwreq))) | 1090 | |
1091 | return ret; | ||
1092 | } | ||
1093 | |||
1094 | int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, | ||
1095 | void __user *arg) | ||
1096 | { | ||
1097 | struct iw_request_info info = { .cmd = cmd, .flags = 0 }; | ||
1098 | int ret; | ||
1099 | |||
1100 | ret = wext_ioctl_dispatch(net, ifr, cmd, &info, | ||
1101 | ioctl_standard_call, | ||
1102 | ioctl_private_call); | ||
1103 | if (ret >= 0 && | ||
1104 | IW_IS_GET(cmd) && | ||
1105 | copy_to_user(arg, ifr, sizeof(struct iwreq))) | ||
1106 | return -EFAULT; | ||
1107 | |||
1108 | return ret; | ||
1109 | } | ||
1110 | |||
1111 | #ifdef CONFIG_COMPAT | ||
1112 | static int compat_standard_call(struct net_device *dev, | ||
1113 | struct iwreq *iwr, | ||
1114 | unsigned int cmd, | ||
1115 | struct iw_request_info *info, | ||
1116 | iw_handler handler) | ||
1117 | { | ||
1118 | const struct iw_ioctl_description *descr; | ||
1119 | struct compat_iw_point *iwp_compat; | ||
1120 | struct iw_point iwp; | ||
1121 | int err; | ||
1122 | |||
1123 | descr = standard_ioctl + (cmd - SIOCIWFIRST); | ||
1124 | |||
1125 | if (descr->header_type != IW_HEADER_TYPE_POINT) | ||
1126 | return ioctl_standard_call(dev, iwr, cmd, info, handler); | ||
1127 | |||
1128 | iwp_compat = (struct compat_iw_point *) &iwr->u.data; | ||
1129 | iwp.pointer = compat_ptr(iwp_compat->pointer); | ||
1130 | iwp.length = iwp_compat->length; | ||
1131 | iwp.flags = iwp_compat->flags; | ||
1132 | |||
1133 | err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info); | ||
1134 | |||
1135 | iwp_compat->pointer = ptr_to_compat(iwp.pointer); | ||
1136 | iwp_compat->length = iwp.length; | ||
1137 | iwp_compat->flags = iwp.flags; | ||
1138 | |||
1139 | return err; | ||
1140 | } | ||
1141 | |||
1142 | static int compat_private_call(struct net_device *dev, struct iwreq *iwr, | ||
1143 | unsigned int cmd, struct iw_request_info *info, | ||
1144 | iw_handler handler) | ||
1145 | { | ||
1146 | const struct iw_priv_args *descr; | ||
1147 | int ret, extra_size; | ||
1148 | |||
1149 | extra_size = get_priv_descr_and_size(dev, cmd, &descr); | ||
1150 | |||
1151 | /* Check if we have a pointer to user space data or not. */ | ||
1152 | if (extra_size == 0) { | ||
1153 | /* No extra arguments. Trivial to handle */ | ||
1154 | ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); | ||
1155 | } else { | ||
1156 | struct compat_iw_point *iwp_compat; | ||
1157 | struct iw_point iwp; | ||
1158 | |||
1159 | iwp_compat = (struct compat_iw_point *) &iwr->u.data; | ||
1160 | iwp.pointer = compat_ptr(iwp_compat->pointer); | ||
1161 | iwp.length = iwp_compat->length; | ||
1162 | iwp.flags = iwp_compat->flags; | ||
1163 | |||
1164 | ret = ioctl_private_iw_point(&iwp, cmd, descr, | ||
1165 | handler, dev, info, extra_size); | ||
1166 | |||
1167 | iwp_compat->pointer = ptr_to_compat(iwp.pointer); | ||
1168 | iwp_compat->length = iwp.length; | ||
1169 | iwp_compat->flags = iwp.flags; | ||
1170 | } | ||
1171 | |||
1172 | /* Call commit handler if needed and defined */ | ||
1173 | if (ret == -EIWCOMMIT) | ||
1174 | ret = call_commit_handler(dev); | ||
1175 | |||
1176 | return ret; | ||
1177 | } | ||
1178 | |||
1179 | int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, | ||
1180 | unsigned long arg) | ||
1181 | { | ||
1182 | void __user *argp = (void __user *)arg; | ||
1183 | struct iw_request_info info; | ||
1184 | struct iwreq iwr; | ||
1185 | char *colon; | ||
1186 | int ret; | ||
1187 | |||
1188 | if (copy_from_user(&iwr, argp, sizeof(struct iwreq))) | ||
1189 | return -EFAULT; | ||
1190 | |||
1191 | iwr.ifr_name[IFNAMSIZ-1] = 0; | ||
1192 | colon = strchr(iwr.ifr_name, ':'); | ||
1193 | if (colon) | ||
1194 | *colon = 0; | ||
1195 | |||
1196 | info.cmd = cmd; | ||
1197 | info.flags = IW_REQUEST_FLAG_COMPAT; | ||
1198 | |||
1199 | ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, &info, | ||
1200 | compat_standard_call, | ||
1201 | compat_private_call); | ||
1202 | |||
1203 | if (ret >= 0 && | ||
1204 | IW_IS_GET(cmd) && | ||
1205 | copy_to_user(argp, &iwr, sizeof(struct iwreq))) | ||
1066 | return -EFAULT; | 1206 | return -EFAULT; |
1207 | |||
1067 | return ret; | 1208 | return ret; |
1068 | } | 1209 | } |
1210 | #endif | ||
1069 | 1211 | ||
1070 | /************************* EVENT PROCESSING *************************/ | 1212 | /************************* EVENT PROCESSING *************************/ |
1071 | /* | 1213 | /* |
@@ -1157,7 +1299,7 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) | |||
1157 | struct sk_buff *skb; | 1299 | struct sk_buff *skb; |
1158 | int err; | 1300 | int err; |
1159 | 1301 | ||
1160 | if (dev_net(dev) != &init_net) | 1302 | if (!net_eq(dev_net(dev), &init_net)) |
1161 | return; | 1303 | return; |
1162 | 1304 | ||
1163 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | 1305 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); |