diff options
author | David S. Miller <davem@davemloft.net> | 2010-02-26 02:26:21 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-26 02:26:21 -0500 |
commit | 19bc291c99f018bd4f2c38bbf69144086dca903f (patch) | |
tree | 9d3cf9bc0c5a78e363dc0547da8bcd1e7c394265 /net/wireless | |
parent | 04488734806948624dabc4514f96f14cd75b9a50 (diff) | |
parent | 4a6967b88af02eebeedfbb91bc09160750225bb5 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Conflicts:
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/rt2x00/rt2800pci.c
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.c | 20 | ||||
-rw-r--r-- | net/wireless/core.h | 9 | ||||
-rw-r--r-- | net/wireless/mlme.c | 166 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 440 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 8 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 10 |
6 files changed, 604 insertions, 49 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index 71b6b3a9cf1f..7fdb9409ad2a 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -677,6 +677,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
677 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); | 677 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); |
678 | INIT_LIST_HEAD(&wdev->event_list); | 678 | INIT_LIST_HEAD(&wdev->event_list); |
679 | spin_lock_init(&wdev->event_lock); | 679 | spin_lock_init(&wdev->event_lock); |
680 | INIT_LIST_HEAD(&wdev->action_registrations); | ||
681 | spin_lock_init(&wdev->action_registrations_lock); | ||
682 | |||
680 | mutex_lock(&rdev->devlist_mtx); | 683 | mutex_lock(&rdev->devlist_mtx); |
681 | list_add_rcu(&wdev->list, &rdev->netdev_list); | 684 | list_add_rcu(&wdev->list, &rdev->netdev_list); |
682 | rdev->devlist_generation++; | 685 | rdev->devlist_generation++; |
@@ -695,19 +698,21 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
695 | wdev->wext.default_key = -1; | 698 | wdev->wext.default_key = -1; |
696 | wdev->wext.default_mgmt_key = -1; | 699 | wdev->wext.default_mgmt_key = -1; |
697 | wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; | 700 | wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; |
701 | #endif | ||
702 | |||
698 | if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) | 703 | if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) |
699 | wdev->wext.ps = true; | 704 | wdev->ps = true; |
700 | else | 705 | else |
701 | wdev->wext.ps = false; | 706 | wdev->ps = false; |
702 | wdev->wext.ps_timeout = 100; | 707 | wdev->ps_timeout = 100; |
703 | if (rdev->ops->set_power_mgmt) | 708 | if (rdev->ops->set_power_mgmt) |
704 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, | 709 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, |
705 | wdev->wext.ps, | 710 | wdev->ps, |
706 | wdev->wext.ps_timeout)) { | 711 | wdev->ps_timeout)) { |
707 | /* assume this means it's off */ | 712 | /* assume this means it's off */ |
708 | wdev->wext.ps = false; | 713 | wdev->ps = false; |
709 | } | 714 | } |
710 | #endif | 715 | |
711 | if (!dev->ethtool_ops) | 716 | if (!dev->ethtool_ops) |
712 | dev->ethtool_ops = &cfg80211_ethtool_ops; | 717 | dev->ethtool_ops = &cfg80211_ethtool_ops; |
713 | 718 | ||
@@ -792,6 +797,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
792 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); | 797 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); |
793 | list_del_rcu(&wdev->list); | 798 | list_del_rcu(&wdev->list); |
794 | rdev->devlist_generation++; | 799 | rdev->devlist_generation++; |
800 | cfg80211_mlme_purge_actions(wdev); | ||
795 | #ifdef CONFIG_CFG80211_WEXT | 801 | #ifdef CONFIG_CFG80211_WEXT |
796 | kfree(wdev->wext.keys); | 802 | kfree(wdev->wext.keys); |
797 | #endif | 803 | #endif |
diff --git a/net/wireless/core.h b/net/wireless/core.h index c326a667022a..d52da913145a 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -329,6 +329,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
329 | const u8 *resp_ie, size_t resp_ie_len, | 329 | const u8 *resp_ie, size_t resp_ie_len, |
330 | u16 status, bool wextev, | 330 | u16 status, bool wextev, |
331 | struct cfg80211_bss *bss); | 331 | struct cfg80211_bss *bss); |
332 | int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, | ||
333 | const u8 *match_data, int match_len); | ||
334 | void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid); | ||
335 | void cfg80211_mlme_purge_actions(struct wireless_dev *wdev); | ||
336 | int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, | ||
337 | struct net_device *dev, | ||
338 | struct ieee80211_channel *chan, | ||
339 | enum nl80211_channel_type channel_type, | ||
340 | const u8 *buf, size_t len, u64 *cookie); | ||
332 | 341 | ||
333 | /* SME */ | 342 | /* SME */ |
334 | int __cfg80211_connect(struct cfg80211_registered_device *rdev, | 343 | int __cfg80211_connect(struct cfg80211_registered_device *rdev, |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 94d151f6f73e..62bc8855e123 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -728,3 +728,169 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, | |||
728 | nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); | 728 | nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); |
729 | } | 729 | } |
730 | EXPORT_SYMBOL(cfg80211_new_sta); | 730 | EXPORT_SYMBOL(cfg80211_new_sta); |
731 | |||
732 | struct cfg80211_action_registration { | ||
733 | struct list_head list; | ||
734 | |||
735 | u32 nlpid; | ||
736 | |||
737 | int match_len; | ||
738 | |||
739 | u8 match[]; | ||
740 | }; | ||
741 | |||
742 | int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, | ||
743 | const u8 *match_data, int match_len) | ||
744 | { | ||
745 | struct cfg80211_action_registration *reg, *nreg; | ||
746 | int err = 0; | ||
747 | |||
748 | nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); | ||
749 | if (!nreg) | ||
750 | return -ENOMEM; | ||
751 | |||
752 | spin_lock_bh(&wdev->action_registrations_lock); | ||
753 | |||
754 | list_for_each_entry(reg, &wdev->action_registrations, list) { | ||
755 | int mlen = min(match_len, reg->match_len); | ||
756 | |||
757 | if (memcmp(reg->match, match_data, mlen) == 0) { | ||
758 | err = -EALREADY; | ||
759 | break; | ||
760 | } | ||
761 | } | ||
762 | |||
763 | if (err) { | ||
764 | kfree(nreg); | ||
765 | goto out; | ||
766 | } | ||
767 | |||
768 | memcpy(nreg->match, match_data, match_len); | ||
769 | nreg->match_len = match_len; | ||
770 | nreg->nlpid = snd_pid; | ||
771 | list_add(&nreg->list, &wdev->action_registrations); | ||
772 | |||
773 | out: | ||
774 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
775 | return err; | ||
776 | } | ||
777 | |||
778 | void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid) | ||
779 | { | ||
780 | struct cfg80211_action_registration *reg, *tmp; | ||
781 | |||
782 | spin_lock_bh(&wdev->action_registrations_lock); | ||
783 | |||
784 | list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) { | ||
785 | if (reg->nlpid == nlpid) { | ||
786 | list_del(®->list); | ||
787 | kfree(reg); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
792 | } | ||
793 | |||
794 | void cfg80211_mlme_purge_actions(struct wireless_dev *wdev) | ||
795 | { | ||
796 | struct cfg80211_action_registration *reg, *tmp; | ||
797 | |||
798 | spin_lock_bh(&wdev->action_registrations_lock); | ||
799 | |||
800 | list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) { | ||
801 | list_del(®->list); | ||
802 | kfree(reg); | ||
803 | } | ||
804 | |||
805 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
806 | } | ||
807 | |||
808 | int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, | ||
809 | struct net_device *dev, | ||
810 | struct ieee80211_channel *chan, | ||
811 | enum nl80211_channel_type channel_type, | ||
812 | const u8 *buf, size_t len, u64 *cookie) | ||
813 | { | ||
814 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
815 | const struct ieee80211_mgmt *mgmt; | ||
816 | |||
817 | if (rdev->ops->action == NULL) | ||
818 | return -EOPNOTSUPP; | ||
819 | if (len < 24 + 1) | ||
820 | return -EINVAL; | ||
821 | |||
822 | mgmt = (const struct ieee80211_mgmt *) buf; | ||
823 | if (!ieee80211_is_action(mgmt->frame_control)) | ||
824 | return -EINVAL; | ||
825 | if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { | ||
826 | /* Verify that we are associated with the destination AP */ | ||
827 | if (!wdev->current_bss || | ||
828 | memcmp(wdev->current_bss->pub.bssid, mgmt->bssid, | ||
829 | ETH_ALEN) != 0 || | ||
830 | memcmp(wdev->current_bss->pub.bssid, mgmt->da, | ||
831 | ETH_ALEN) != 0) | ||
832 | return -ENOTCONN; | ||
833 | } | ||
834 | |||
835 | if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0) | ||
836 | return -EINVAL; | ||
837 | |||
838 | /* Transmit the Action frame as requested by user space */ | ||
839 | return rdev->ops->action(&rdev->wiphy, dev, chan, channel_type, | ||
840 | buf, len, cookie); | ||
841 | } | ||
842 | |||
843 | bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf, | ||
844 | size_t len, gfp_t gfp) | ||
845 | { | ||
846 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
847 | struct wiphy *wiphy = wdev->wiphy; | ||
848 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
849 | struct cfg80211_action_registration *reg; | ||
850 | const u8 *action_data; | ||
851 | int action_data_len; | ||
852 | bool result = false; | ||
853 | |||
854 | /* frame length - min size excluding category */ | ||
855 | action_data_len = len - (IEEE80211_MIN_ACTION_SIZE - 1); | ||
856 | |||
857 | /* action data starts with category */ | ||
858 | action_data = buf + IEEE80211_MIN_ACTION_SIZE - 1; | ||
859 | |||
860 | spin_lock_bh(&wdev->action_registrations_lock); | ||
861 | |||
862 | list_for_each_entry(reg, &wdev->action_registrations, list) { | ||
863 | if (reg->match_len > action_data_len) | ||
864 | continue; | ||
865 | |||
866 | if (memcmp(reg->match, action_data, reg->match_len)) | ||
867 | continue; | ||
868 | |||
869 | /* found match! */ | ||
870 | |||
871 | /* Indicate the received Action frame to user space */ | ||
872 | if (nl80211_send_action(rdev, dev, reg->nlpid, freq, | ||
873 | buf, len, gfp)) | ||
874 | continue; | ||
875 | |||
876 | result = true; | ||
877 | break; | ||
878 | } | ||
879 | |||
880 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
881 | |||
882 | return result; | ||
883 | } | ||
884 | EXPORT_SYMBOL(cfg80211_rx_action); | ||
885 | |||
886 | void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, | ||
887 | const u8 *buf, size_t len, bool ack, gfp_t gfp) | ||
888 | { | ||
889 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
890 | struct wiphy *wiphy = wdev->wiphy; | ||
891 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
892 | |||
893 | /* Indicate TX status of the Action frame to user space */ | ||
894 | nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp); | ||
895 | } | ||
896 | EXPORT_SYMBOL(cfg80211_action_tx_status); | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a001ea32cb7d..e447db04cf76 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * This is the new netlink-based wireless configuration interface. | 2 | * This is the new netlink-based wireless configuration interface. |
3 | * | 3 | * |
4 | * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <linux/if.h> | 7 | #include <linux/if.h> |
@@ -145,6 +145,10 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
145 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, | 145 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, |
146 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, | 146 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, |
147 | [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, | 147 | [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, |
148 | [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, | ||
149 | .len = IEEE80211_MAX_DATA_LEN }, | ||
150 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, | ||
151 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, | ||
148 | }; | 152 | }; |
149 | 153 | ||
150 | /* policy for the attributes */ | 154 | /* policy for the attributes */ |
@@ -576,6 +580,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
576 | CMD(flush_pmksa, FLUSH_PMKSA); | 580 | CMD(flush_pmksa, FLUSH_PMKSA); |
577 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | 581 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); |
578 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); | 582 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); |
583 | CMD(action, ACTION); | ||
579 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | 584 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
580 | i++; | 585 | i++; |
581 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 586 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
@@ -2009,6 +2014,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2009 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) | 2014 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) |
2010 | return -EINVAL; | 2015 | return -EINVAL; |
2011 | 2016 | ||
2017 | if (!info->attrs[NL80211_ATTR_STA_AID]) | ||
2018 | return -EINVAL; | ||
2019 | |||
2012 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | 2020 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); |
2013 | params.supported_rates = | 2021 | params.supported_rates = |
2014 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 2022 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
@@ -2017,11 +2025,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2017 | params.listen_interval = | 2025 | params.listen_interval = |
2018 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 2026 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); |
2019 | 2027 | ||
2020 | if (info->attrs[NL80211_ATTR_STA_AID]) { | 2028 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); |
2021 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); | 2029 | if (!params.aid || params.aid > IEEE80211_MAX_AID) |
2022 | if (!params.aid || params.aid > IEEE80211_MAX_AID) | 2030 | return -EINVAL; |
2023 | return -EINVAL; | ||
2024 | } | ||
2025 | 2031 | ||
2026 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 2032 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
2027 | params.ht_capa = | 2033 | params.ht_capa = |
@@ -2036,6 +2042,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2036 | if (err) | 2042 | if (err) |
2037 | goto out_rtnl; | 2043 | goto out_rtnl; |
2038 | 2044 | ||
2045 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | ||
2046 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { | ||
2047 | err = -EINVAL; | ||
2048 | goto out; | ||
2049 | } | ||
2050 | |||
2039 | err = get_vlan(info, rdev, ¶ms.vlan); | 2051 | err = get_vlan(info, rdev, ¶ms.vlan); |
2040 | if (err) | 2052 | if (err) |
2041 | goto out; | 2053 | goto out; |
@@ -2043,35 +2055,6 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2043 | /* validate settings */ | 2055 | /* validate settings */ |
2044 | err = 0; | 2056 | err = 0; |
2045 | 2057 | ||
2046 | switch (dev->ieee80211_ptr->iftype) { | ||
2047 | case NL80211_IFTYPE_AP: | ||
2048 | case NL80211_IFTYPE_AP_VLAN: | ||
2049 | /* all ok but must have AID */ | ||
2050 | if (!params.aid) | ||
2051 | err = -EINVAL; | ||
2052 | break; | ||
2053 | case NL80211_IFTYPE_MESH_POINT: | ||
2054 | /* disallow things mesh doesn't support */ | ||
2055 | if (params.vlan) | ||
2056 | err = -EINVAL; | ||
2057 | if (params.aid) | ||
2058 | err = -EINVAL; | ||
2059 | if (params.ht_capa) | ||
2060 | err = -EINVAL; | ||
2061 | if (params.listen_interval >= 0) | ||
2062 | err = -EINVAL; | ||
2063 | if (params.supported_rates) | ||
2064 | err = -EINVAL; | ||
2065 | if (params.sta_flags_mask) | ||
2066 | err = -EINVAL; | ||
2067 | break; | ||
2068 | default: | ||
2069 | err = -EINVAL; | ||
2070 | } | ||
2071 | |||
2072 | if (err) | ||
2073 | goto out; | ||
2074 | |||
2075 | if (!rdev->ops->add_station) { | 2058 | if (!rdev->ops->add_station) { |
2076 | err = -EOPNOTSUPP; | 2059 | err = -EOPNOTSUPP; |
2077 | goto out; | 2060 | goto out; |
@@ -2112,8 +2095,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | |||
2112 | goto out_rtnl; | 2095 | goto out_rtnl; |
2113 | 2096 | ||
2114 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 2097 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
2115 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && | 2098 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { |
2116 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { | ||
2117 | err = -EINVAL; | 2099 | err = -EINVAL; |
2118 | goto out; | 2100 | goto out; |
2119 | } | 2101 | } |
@@ -4545,6 +4527,257 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, | |||
4545 | return err; | 4527 | return err; |
4546 | } | 4528 | } |
4547 | 4529 | ||
4530 | static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info) | ||
4531 | { | ||
4532 | struct cfg80211_registered_device *rdev; | ||
4533 | struct net_device *dev; | ||
4534 | int err; | ||
4535 | |||
4536 | if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) | ||
4537 | return -EINVAL; | ||
4538 | |||
4539 | if (nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]) < 1) | ||
4540 | return -EINVAL; | ||
4541 | |||
4542 | rtnl_lock(); | ||
4543 | |||
4544 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4545 | if (err) | ||
4546 | goto unlock_rtnl; | ||
4547 | |||
4548 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4549 | err = -EOPNOTSUPP; | ||
4550 | goto out; | ||
4551 | } | ||
4552 | |||
4553 | /* not much point in registering if we can't reply */ | ||
4554 | if (!rdev->ops->action) { | ||
4555 | err = -EOPNOTSUPP; | ||
4556 | goto out; | ||
4557 | } | ||
4558 | |||
4559 | err = cfg80211_mlme_register_action(dev->ieee80211_ptr, info->snd_pid, | ||
4560 | nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), | ||
4561 | nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); | ||
4562 | out: | ||
4563 | cfg80211_unlock_rdev(rdev); | ||
4564 | dev_put(dev); | ||
4565 | unlock_rtnl: | ||
4566 | rtnl_unlock(); | ||
4567 | return err; | ||
4568 | } | ||
4569 | |||
4570 | static int nl80211_action(struct sk_buff *skb, struct genl_info *info) | ||
4571 | { | ||
4572 | struct cfg80211_registered_device *rdev; | ||
4573 | struct net_device *dev; | ||
4574 | struct ieee80211_channel *chan; | ||
4575 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
4576 | u32 freq; | ||
4577 | int err; | ||
4578 | void *hdr; | ||
4579 | u64 cookie; | ||
4580 | struct sk_buff *msg; | ||
4581 | |||
4582 | if (!info->attrs[NL80211_ATTR_FRAME] || | ||
4583 | !info->attrs[NL80211_ATTR_WIPHY_FREQ]) | ||
4584 | return -EINVAL; | ||
4585 | |||
4586 | rtnl_lock(); | ||
4587 | |||
4588 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4589 | if (err) | ||
4590 | goto unlock_rtnl; | ||
4591 | |||
4592 | if (!rdev->ops->action) { | ||
4593 | err = -EOPNOTSUPP; | ||
4594 | goto out; | ||
4595 | } | ||
4596 | |||
4597 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4598 | err = -EOPNOTSUPP; | ||
4599 | goto out; | ||
4600 | } | ||
4601 | |||
4602 | if (!netif_running(dev)) { | ||
4603 | err = -ENETDOWN; | ||
4604 | goto out; | ||
4605 | } | ||
4606 | |||
4607 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
4608 | channel_type = nla_get_u32( | ||
4609 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
4610 | if (channel_type != NL80211_CHAN_NO_HT && | ||
4611 | channel_type != NL80211_CHAN_HT20 && | ||
4612 | channel_type != NL80211_CHAN_HT40PLUS && | ||
4613 | channel_type != NL80211_CHAN_HT40MINUS) | ||
4614 | err = -EINVAL; | ||
4615 | goto out; | ||
4616 | } | ||
4617 | |||
4618 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
4619 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
4620 | if (chan == NULL) { | ||
4621 | err = -EINVAL; | ||
4622 | goto out; | ||
4623 | } | ||
4624 | |||
4625 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4626 | if (!msg) { | ||
4627 | err = -ENOMEM; | ||
4628 | goto out; | ||
4629 | } | ||
4630 | |||
4631 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4632 | NL80211_CMD_ACTION); | ||
4633 | |||
4634 | if (IS_ERR(hdr)) { | ||
4635 | err = PTR_ERR(hdr); | ||
4636 | goto free_msg; | ||
4637 | } | ||
4638 | err = cfg80211_mlme_action(rdev, dev, chan, channel_type, | ||
4639 | nla_data(info->attrs[NL80211_ATTR_FRAME]), | ||
4640 | nla_len(info->attrs[NL80211_ATTR_FRAME]), | ||
4641 | &cookie); | ||
4642 | if (err) | ||
4643 | goto free_msg; | ||
4644 | |||
4645 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
4646 | |||
4647 | genlmsg_end(msg, hdr); | ||
4648 | err = genlmsg_reply(msg, info); | ||
4649 | goto out; | ||
4650 | |||
4651 | nla_put_failure: | ||
4652 | err = -ENOBUFS; | ||
4653 | free_msg: | ||
4654 | nlmsg_free(msg); | ||
4655 | out: | ||
4656 | cfg80211_unlock_rdev(rdev); | ||
4657 | dev_put(dev); | ||
4658 | unlock_rtnl: | ||
4659 | rtnl_unlock(); | ||
4660 | return err; | ||
4661 | } | ||
4662 | |||
4663 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) | ||
4664 | { | ||
4665 | struct cfg80211_registered_device *rdev; | ||
4666 | struct wireless_dev *wdev; | ||
4667 | struct net_device *dev; | ||
4668 | u8 ps_state; | ||
4669 | bool state; | ||
4670 | int err; | ||
4671 | |||
4672 | if (!info->attrs[NL80211_ATTR_PS_STATE]) { | ||
4673 | err = -EINVAL; | ||
4674 | goto out; | ||
4675 | } | ||
4676 | |||
4677 | ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); | ||
4678 | |||
4679 | if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) { | ||
4680 | err = -EINVAL; | ||
4681 | goto out; | ||
4682 | } | ||
4683 | |||
4684 | rtnl_lock(); | ||
4685 | |||
4686 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4687 | if (err) | ||
4688 | goto unlock_rdev; | ||
4689 | |||
4690 | wdev = dev->ieee80211_ptr; | ||
4691 | |||
4692 | if (!rdev->ops->set_power_mgmt) { | ||
4693 | err = -EOPNOTSUPP; | ||
4694 | goto unlock_rdev; | ||
4695 | } | ||
4696 | |||
4697 | state = (ps_state == NL80211_PS_ENABLED) ? true : false; | ||
4698 | |||
4699 | if (state == wdev->ps) | ||
4700 | goto unlock_rdev; | ||
4701 | |||
4702 | wdev->ps = state; | ||
4703 | |||
4704 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps, | ||
4705 | wdev->ps_timeout)) | ||
4706 | /* assume this means it's off */ | ||
4707 | wdev->ps = false; | ||
4708 | |||
4709 | unlock_rdev: | ||
4710 | cfg80211_unlock_rdev(rdev); | ||
4711 | dev_put(dev); | ||
4712 | rtnl_unlock(); | ||
4713 | |||
4714 | out: | ||
4715 | return err; | ||
4716 | } | ||
4717 | |||
4718 | static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) | ||
4719 | { | ||
4720 | struct cfg80211_registered_device *rdev; | ||
4721 | enum nl80211_ps_state ps_state; | ||
4722 | struct wireless_dev *wdev; | ||
4723 | struct net_device *dev; | ||
4724 | struct sk_buff *msg; | ||
4725 | void *hdr; | ||
4726 | int err; | ||
4727 | |||
4728 | rtnl_lock(); | ||
4729 | |||
4730 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4731 | if (err) | ||
4732 | goto unlock_rtnl; | ||
4733 | |||
4734 | wdev = dev->ieee80211_ptr; | ||
4735 | |||
4736 | if (!rdev->ops->set_power_mgmt) { | ||
4737 | err = -EOPNOTSUPP; | ||
4738 | goto out; | ||
4739 | } | ||
4740 | |||
4741 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4742 | if (!msg) { | ||
4743 | err = -ENOMEM; | ||
4744 | goto out; | ||
4745 | } | ||
4746 | |||
4747 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4748 | NL80211_CMD_GET_POWER_SAVE); | ||
4749 | if (!hdr) { | ||
4750 | err = -ENOMEM; | ||
4751 | goto free_msg; | ||
4752 | } | ||
4753 | |||
4754 | if (wdev->ps) | ||
4755 | ps_state = NL80211_PS_ENABLED; | ||
4756 | else | ||
4757 | ps_state = NL80211_PS_DISABLED; | ||
4758 | |||
4759 | NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); | ||
4760 | |||
4761 | genlmsg_end(msg, hdr); | ||
4762 | err = genlmsg_reply(msg, info); | ||
4763 | goto out; | ||
4764 | |||
4765 | nla_put_failure: | ||
4766 | err = -ENOBUFS; | ||
4767 | |||
4768 | free_msg: | ||
4769 | nlmsg_free(msg); | ||
4770 | |||
4771 | out: | ||
4772 | cfg80211_unlock_rdev(rdev); | ||
4773 | dev_put(dev); | ||
4774 | |||
4775 | unlock_rtnl: | ||
4776 | rtnl_unlock(); | ||
4777 | |||
4778 | return err; | ||
4779 | } | ||
4780 | |||
4548 | static struct genl_ops nl80211_ops[] = { | 4781 | static struct genl_ops nl80211_ops[] = { |
4549 | { | 4782 | { |
4550 | .cmd = NL80211_CMD_GET_WIPHY, | 4783 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -4825,6 +5058,30 @@ static struct genl_ops nl80211_ops[] = { | |||
4825 | .policy = nl80211_policy, | 5058 | .policy = nl80211_policy, |
4826 | .flags = GENL_ADMIN_PERM, | 5059 | .flags = GENL_ADMIN_PERM, |
4827 | }, | 5060 | }, |
5061 | { | ||
5062 | .cmd = NL80211_CMD_REGISTER_ACTION, | ||
5063 | .doit = nl80211_register_action, | ||
5064 | .policy = nl80211_policy, | ||
5065 | .flags = GENL_ADMIN_PERM, | ||
5066 | }, | ||
5067 | { | ||
5068 | .cmd = NL80211_CMD_ACTION, | ||
5069 | .doit = nl80211_action, | ||
5070 | .policy = nl80211_policy, | ||
5071 | .flags = GENL_ADMIN_PERM, | ||
5072 | }, | ||
5073 | { | ||
5074 | .cmd = NL80211_CMD_SET_POWER_SAVE, | ||
5075 | .doit = nl80211_set_power_save, | ||
5076 | .policy = nl80211_policy, | ||
5077 | .flags = GENL_ADMIN_PERM, | ||
5078 | }, | ||
5079 | { | ||
5080 | .cmd = NL80211_CMD_GET_POWER_SAVE, | ||
5081 | .doit = nl80211_get_power_save, | ||
5082 | .policy = nl80211_policy, | ||
5083 | /* can be retrieved by unprivileged users */ | ||
5084 | }, | ||
4828 | }; | 5085 | }; |
4829 | 5086 | ||
4830 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5087 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
@@ -5497,6 +5754,110 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | |||
5497 | nl80211_mlme_mcgrp.id, gfp); | 5754 | nl80211_mlme_mcgrp.id, gfp); |
5498 | } | 5755 | } |
5499 | 5756 | ||
5757 | int nl80211_send_action(struct cfg80211_registered_device *rdev, | ||
5758 | struct net_device *netdev, u32 nlpid, | ||
5759 | int freq, const u8 *buf, size_t len, gfp_t gfp) | ||
5760 | { | ||
5761 | struct sk_buff *msg; | ||
5762 | void *hdr; | ||
5763 | int err; | ||
5764 | |||
5765 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5766 | if (!msg) | ||
5767 | return -ENOMEM; | ||
5768 | |||
5769 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION); | ||
5770 | if (!hdr) { | ||
5771 | nlmsg_free(msg); | ||
5772 | return -ENOMEM; | ||
5773 | } | ||
5774 | |||
5775 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5776 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5777 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); | ||
5778 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
5779 | |||
5780 | err = genlmsg_end(msg, hdr); | ||
5781 | if (err < 0) { | ||
5782 | nlmsg_free(msg); | ||
5783 | return err; | ||
5784 | } | ||
5785 | |||
5786 | err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); | ||
5787 | if (err < 0) | ||
5788 | return err; | ||
5789 | return 0; | ||
5790 | |||
5791 | nla_put_failure: | ||
5792 | genlmsg_cancel(msg, hdr); | ||
5793 | nlmsg_free(msg); | ||
5794 | return -ENOBUFS; | ||
5795 | } | ||
5796 | |||
5797 | void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | ||
5798 | struct net_device *netdev, u64 cookie, | ||
5799 | const u8 *buf, size_t len, bool ack, | ||
5800 | gfp_t gfp) | ||
5801 | { | ||
5802 | struct sk_buff *msg; | ||
5803 | void *hdr; | ||
5804 | |||
5805 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5806 | if (!msg) | ||
5807 | return; | ||
5808 | |||
5809 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION_TX_STATUS); | ||
5810 | if (!hdr) { | ||
5811 | nlmsg_free(msg); | ||
5812 | return; | ||
5813 | } | ||
5814 | |||
5815 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5816 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5817 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
5818 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
5819 | if (ack) | ||
5820 | NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); | ||
5821 | |||
5822 | if (genlmsg_end(msg, hdr) < 0) { | ||
5823 | nlmsg_free(msg); | ||
5824 | return; | ||
5825 | } | ||
5826 | |||
5827 | genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); | ||
5828 | return; | ||
5829 | |||
5830 | nla_put_failure: | ||
5831 | genlmsg_cancel(msg, hdr); | ||
5832 | nlmsg_free(msg); | ||
5833 | } | ||
5834 | |||
5835 | static int nl80211_netlink_notify(struct notifier_block * nb, | ||
5836 | unsigned long state, | ||
5837 | void *_notify) | ||
5838 | { | ||
5839 | struct netlink_notify *notify = _notify; | ||
5840 | struct cfg80211_registered_device *rdev; | ||
5841 | struct wireless_dev *wdev; | ||
5842 | |||
5843 | if (state != NETLINK_URELEASE) | ||
5844 | return NOTIFY_DONE; | ||
5845 | |||
5846 | rcu_read_lock(); | ||
5847 | |||
5848 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) | ||
5849 | list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) | ||
5850 | cfg80211_mlme_unregister_actions(wdev, notify->pid); | ||
5851 | |||
5852 | rcu_read_unlock(); | ||
5853 | |||
5854 | return NOTIFY_DONE; | ||
5855 | } | ||
5856 | |||
5857 | static struct notifier_block nl80211_netlink_notifier = { | ||
5858 | .notifier_call = nl80211_netlink_notify, | ||
5859 | }; | ||
5860 | |||
5500 | /* initialisation/exit functions */ | 5861 | /* initialisation/exit functions */ |
5501 | 5862 | ||
5502 | int nl80211_init(void) | 5863 | int nl80211_init(void) |
@@ -5530,6 +5891,10 @@ int nl80211_init(void) | |||
5530 | goto err_out; | 5891 | goto err_out; |
5531 | #endif | 5892 | #endif |
5532 | 5893 | ||
5894 | err = netlink_register_notifier(&nl80211_netlink_notifier); | ||
5895 | if (err) | ||
5896 | goto err_out; | ||
5897 | |||
5533 | return 0; | 5898 | return 0; |
5534 | err_out: | 5899 | err_out: |
5535 | genl_unregister_family(&nl80211_fam); | 5900 | genl_unregister_family(&nl80211_fam); |
@@ -5538,5 +5903,6 @@ int nl80211_init(void) | |||
5538 | 5903 | ||
5539 | void nl80211_exit(void) | 5904 | void nl80211_exit(void) |
5540 | { | 5905 | { |
5906 | netlink_unregister_notifier(&nl80211_netlink_notifier); | ||
5541 | genl_unregister_family(&nl80211_fam); | 5907 | genl_unregister_family(&nl80211_fam); |
5542 | } | 5908 | } |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 14855b8fb430..4ca511102c6c 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -74,4 +74,12 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | |||
74 | struct net_device *dev, const u8 *mac_addr, | 74 | struct net_device *dev, const u8 *mac_addr, |
75 | struct station_info *sinfo, gfp_t gfp); | 75 | struct station_info *sinfo, gfp_t gfp); |
76 | 76 | ||
77 | int nl80211_send_action(struct cfg80211_registered_device *rdev, | ||
78 | struct net_device *netdev, u32 nlpid, int freq, | ||
79 | const u8 *buf, size_t len, gfp_t gfp); | ||
80 | void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | ||
81 | struct net_device *netdev, u64 cookie, | ||
82 | const u8 *buf, size_t len, bool ack, | ||
83 | gfp_t gfp); | ||
84 | |||
77 | #endif /* __NET_WIRELESS_NL80211_H */ | 85 | #endif /* __NET_WIRELESS_NL80211_H */ |
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index b17eeae448d5..9ab51838849e 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c | |||
@@ -1099,8 +1099,8 @@ int cfg80211_wext_siwpower(struct net_device *dev, | |||
1099 | { | 1099 | { |
1100 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 1100 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
1101 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | 1101 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); |
1102 | bool ps = wdev->wext.ps; | 1102 | bool ps = wdev->ps; |
1103 | int timeout = wdev->wext.ps_timeout; | 1103 | int timeout = wdev->ps_timeout; |
1104 | int err; | 1104 | int err; |
1105 | 1105 | ||
1106 | if (wdev->iftype != NL80211_IFTYPE_STATION) | 1106 | if (wdev->iftype != NL80211_IFTYPE_STATION) |
@@ -1133,8 +1133,8 @@ int cfg80211_wext_siwpower(struct net_device *dev, | |||
1133 | if (err) | 1133 | if (err) |
1134 | return err; | 1134 | return err; |
1135 | 1135 | ||
1136 | wdev->wext.ps = ps; | 1136 | wdev->ps = ps; |
1137 | wdev->wext.ps_timeout = timeout; | 1137 | wdev->ps_timeout = timeout; |
1138 | 1138 | ||
1139 | return 0; | 1139 | return 0; |
1140 | 1140 | ||
@@ -1147,7 +1147,7 @@ int cfg80211_wext_giwpower(struct net_device *dev, | |||
1147 | { | 1147 | { |
1148 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 1148 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
1149 | 1149 | ||
1150 | wrq->disabled = !wdev->wext.ps; | 1150 | wrq->disabled = !wdev->ps; |
1151 | 1151 | ||
1152 | return 0; | 1152 | return 0; |
1153 | } | 1153 | } |