diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/nl80211.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 306ae019ea81..431835126e88 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -76,6 +76,12 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
76 | .len = IEEE80211_MAX_DATA_LEN }, | 76 | .len = IEEE80211_MAX_DATA_LEN }, |
77 | [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY, | 77 | [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY, |
78 | .len = IEEE80211_MAX_DATA_LEN }, | 78 | .len = IEEE80211_MAX_DATA_LEN }, |
79 | [NL80211_ATTR_STA_AID] = { .type = NLA_U16 }, | ||
80 | [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED }, | ||
81 | [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, | ||
82 | [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, | ||
83 | .len = NL80211_MAX_SUPP_RATES }, | ||
84 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, | ||
79 | }; | 85 | }; |
80 | 86 | ||
81 | /* message building helper */ | 87 | /* message building helper */ |
@@ -715,6 +721,210 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) | |||
715 | return err; | 721 | return err; |
716 | } | 722 | } |
717 | 723 | ||
724 | static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { | ||
725 | [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG }, | ||
726 | [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG }, | ||
727 | [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG }, | ||
728 | }; | ||
729 | |||
730 | static int parse_station_flags(struct nlattr *nla, u32 *staflags) | ||
731 | { | ||
732 | struct nlattr *flags[NL80211_STA_FLAG_MAX + 1]; | ||
733 | int flag; | ||
734 | |||
735 | *staflags = 0; | ||
736 | |||
737 | if (!nla) | ||
738 | return 0; | ||
739 | |||
740 | if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX, | ||
741 | nla, sta_flags_policy)) | ||
742 | return -EINVAL; | ||
743 | |||
744 | *staflags = STATION_FLAG_CHANGED; | ||
745 | |||
746 | for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) | ||
747 | if (flags[flag]) | ||
748 | *staflags |= (1<<flag); | ||
749 | |||
750 | return 0; | ||
751 | } | ||
752 | |||
753 | static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) | ||
754 | { | ||
755 | return -EOPNOTSUPP; | ||
756 | } | ||
757 | |||
758 | /* | ||
759 | * Get vlan interface making sure it is on the right wiphy. | ||
760 | */ | ||
761 | static int get_vlan(struct nlattr *vlanattr, | ||
762 | struct cfg80211_registered_device *rdev, | ||
763 | struct net_device **vlan) | ||
764 | { | ||
765 | *vlan = NULL; | ||
766 | |||
767 | if (vlanattr) { | ||
768 | *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr)); | ||
769 | if (!*vlan) | ||
770 | return -ENODEV; | ||
771 | if (!(*vlan)->ieee80211_ptr) | ||
772 | return -EINVAL; | ||
773 | if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) | ||
774 | return -EINVAL; | ||
775 | } | ||
776 | return 0; | ||
777 | } | ||
778 | |||
779 | static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | ||
780 | { | ||
781 | struct cfg80211_registered_device *drv; | ||
782 | int err; | ||
783 | struct net_device *dev; | ||
784 | struct station_parameters params; | ||
785 | u8 *mac_addr = NULL; | ||
786 | |||
787 | memset(¶ms, 0, sizeof(params)); | ||
788 | |||
789 | params.listen_interval = -1; | ||
790 | |||
791 | if (info->attrs[NL80211_ATTR_STA_AID]) | ||
792 | return -EINVAL; | ||
793 | |||
794 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
795 | return -EINVAL; | ||
796 | |||
797 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
798 | |||
799 | if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { | ||
800 | params.supported_rates = | ||
801 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | ||
802 | params.supported_rates_len = | ||
803 | nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | ||
804 | } | ||
805 | |||
806 | if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) | ||
807 | params.listen_interval = | ||
808 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | ||
809 | |||
810 | if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS], | ||
811 | ¶ms.station_flags)) | ||
812 | return -EINVAL; | ||
813 | |||
814 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
815 | if (err) | ||
816 | return err; | ||
817 | |||
818 | err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); | ||
819 | if (err) | ||
820 | goto out; | ||
821 | |||
822 | if (!drv->ops->change_station) { | ||
823 | err = -EOPNOTSUPP; | ||
824 | goto out; | ||
825 | } | ||
826 | |||
827 | rtnl_lock(); | ||
828 | err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, ¶ms); | ||
829 | rtnl_unlock(); | ||
830 | |||
831 | out: | ||
832 | if (params.vlan) | ||
833 | dev_put(params.vlan); | ||
834 | cfg80211_put_dev(drv); | ||
835 | dev_put(dev); | ||
836 | return err; | ||
837 | } | ||
838 | |||
839 | static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | ||
840 | { | ||
841 | struct cfg80211_registered_device *drv; | ||
842 | int err; | ||
843 | struct net_device *dev; | ||
844 | struct station_parameters params; | ||
845 | u8 *mac_addr = NULL; | ||
846 | |||
847 | memset(¶ms, 0, sizeof(params)); | ||
848 | |||
849 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
850 | return -EINVAL; | ||
851 | |||
852 | if (!info->attrs[NL80211_ATTR_STA_AID]) | ||
853 | return -EINVAL; | ||
854 | |||
855 | if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) | ||
856 | return -EINVAL; | ||
857 | |||
858 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) | ||
859 | return -EINVAL; | ||
860 | |||
861 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
862 | params.supported_rates = | ||
863 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | ||
864 | params.supported_rates_len = | ||
865 | nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | ||
866 | params.listen_interval = | ||
867 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | ||
868 | params.listen_interval = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); | ||
869 | |||
870 | if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS], | ||
871 | ¶ms.station_flags)) | ||
872 | return -EINVAL; | ||
873 | |||
874 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
875 | if (err) | ||
876 | return err; | ||
877 | |||
878 | err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); | ||
879 | if (err) | ||
880 | goto out; | ||
881 | |||
882 | if (!drv->ops->add_station) { | ||
883 | err = -EOPNOTSUPP; | ||
884 | goto out; | ||
885 | } | ||
886 | |||
887 | rtnl_lock(); | ||
888 | err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms); | ||
889 | rtnl_unlock(); | ||
890 | |||
891 | out: | ||
892 | if (params.vlan) | ||
893 | dev_put(params.vlan); | ||
894 | cfg80211_put_dev(drv); | ||
895 | dev_put(dev); | ||
896 | return err; | ||
897 | } | ||
898 | |||
899 | static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | ||
900 | { | ||
901 | struct cfg80211_registered_device *drv; | ||
902 | int err; | ||
903 | struct net_device *dev; | ||
904 | u8 *mac_addr = NULL; | ||
905 | |||
906 | if (info->attrs[NL80211_ATTR_MAC]) | ||
907 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
908 | |||
909 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
910 | if (err) | ||
911 | return err; | ||
912 | |||
913 | if (!drv->ops->del_station) { | ||
914 | err = -EOPNOTSUPP; | ||
915 | goto out; | ||
916 | } | ||
917 | |||
918 | rtnl_lock(); | ||
919 | err = drv->ops->del_station(&drv->wiphy, dev, mac_addr); | ||
920 | rtnl_unlock(); | ||
921 | |||
922 | out: | ||
923 | cfg80211_put_dev(drv); | ||
924 | dev_put(dev); | ||
925 | return err; | ||
926 | } | ||
927 | |||
718 | static struct genl_ops nl80211_ops[] = { | 928 | static struct genl_ops nl80211_ops[] = { |
719 | { | 929 | { |
720 | .cmd = NL80211_CMD_GET_WIPHY, | 930 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -796,6 +1006,31 @@ static struct genl_ops nl80211_ops[] = { | |||
796 | .flags = GENL_ADMIN_PERM, | 1006 | .flags = GENL_ADMIN_PERM, |
797 | .doit = nl80211_del_beacon, | 1007 | .doit = nl80211_del_beacon, |
798 | }, | 1008 | }, |
1009 | { | ||
1010 | .cmd = NL80211_CMD_GET_STATION, | ||
1011 | .doit = nl80211_get_station, | ||
1012 | /* TODO: implement dumpit */ | ||
1013 | .policy = nl80211_policy, | ||
1014 | .flags = GENL_ADMIN_PERM, | ||
1015 | }, | ||
1016 | { | ||
1017 | .cmd = NL80211_CMD_SET_STATION, | ||
1018 | .doit = nl80211_set_station, | ||
1019 | .policy = nl80211_policy, | ||
1020 | .flags = GENL_ADMIN_PERM, | ||
1021 | }, | ||
1022 | { | ||
1023 | .cmd = NL80211_CMD_NEW_STATION, | ||
1024 | .doit = nl80211_new_station, | ||
1025 | .policy = nl80211_policy, | ||
1026 | .flags = GENL_ADMIN_PERM, | ||
1027 | }, | ||
1028 | { | ||
1029 | .cmd = NL80211_CMD_DEL_STATION, | ||
1030 | .doit = nl80211_del_station, | ||
1031 | .policy = nl80211_policy, | ||
1032 | .flags = GENL_ADMIN_PERM, | ||
1033 | }, | ||
799 | }; | 1034 | }; |
800 | 1035 | ||
801 | /* multicast groups */ | 1036 | /* multicast groups */ |