|
@@ -748,31 +748,51 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
|
|
|
}
|
|
|
EXPORT_SYMBOL(cfg80211_new_sta);
|
|
|
|
|
|
-struct cfg80211_action_registration {
|
|
|
+struct cfg80211_mgmt_registration {
|
|
|
struct list_head list;
|
|
|
|
|
|
u32 nlpid;
|
|
|
|
|
|
int match_len;
|
|
|
|
|
|
+ __le16 frame_type;
|
|
|
+
|
|
|
u8 match[];
|
|
|
};
|
|
|
|
|
|
-int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid,
|
|
|
- const u8 *match_data, int match_len)
|
|
|
+int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
|
|
|
+ u16 frame_type, const u8 *match_data,
|
|
|
+ int match_len)
|
|
|
{
|
|
|
- struct cfg80211_action_registration *reg, *nreg;
|
|
|
+ struct cfg80211_mgmt_registration *reg, *nreg;
|
|
|
int err = 0;
|
|
|
+ u16 mgmt_type;
|
|
|
+
|
|
|
+ if (!wdev->wiphy->mgmt_stypes)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
|
|
|
+ if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type)))
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL);
|
|
|
if (!nreg)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- spin_lock_bh(&wdev->action_registrations_lock);
|
|
|
+ spin_lock_bh(&wdev->mgmt_registrations_lock);
|
|
|
|
|
|
- list_for_each_entry(reg, &wdev->action_registrations, list) {
|
|
|
+ list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
|
|
|
int mlen = min(match_len, reg->match_len);
|
|
|
|
|
|
+ if (frame_type != le16_to_cpu(reg->frame_type))
|
|
|
+ continue;
|
|
|
+
|
|
|
if (memcmp(reg->match, match_data, mlen) == 0) {
|
|
|
err = -EALREADY;
|
|
|
break;
|
|
@@ -787,62 +807,75 @@ int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid,
|
|
|
memcpy(nreg->match, match_data, match_len);
|
|
|
nreg->match_len = match_len;
|
|
|
nreg->nlpid = snd_pid;
|
|
|
- list_add(&nreg->list, &wdev->action_registrations);
|
|
|
+ nreg->frame_type = cpu_to_le16(frame_type);
|
|
|
+ list_add(&nreg->list, &wdev->mgmt_registrations);
|
|
|
|
|
|
out:
|
|
|
- spin_unlock_bh(&wdev->action_registrations_lock);
|
|
|
+ spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid)
|
|
|
+void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid)
|
|
|
{
|
|
|
- struct cfg80211_action_registration *reg, *tmp;
|
|
|
+ struct cfg80211_mgmt_registration *reg, *tmp;
|
|
|
|
|
|
- spin_lock_bh(&wdev->action_registrations_lock);
|
|
|
+ spin_lock_bh(&wdev->mgmt_registrations_lock);
|
|
|
|
|
|
- list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) {
|
|
|
+ list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
|
|
|
if (reg->nlpid == nlpid) {
|
|
|
list_del(®->list);
|
|
|
kfree(reg);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- spin_unlock_bh(&wdev->action_registrations_lock);
|
|
|
+ spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
|
|
}
|
|
|
|
|
|
-void cfg80211_mlme_purge_actions(struct wireless_dev *wdev)
|
|
|
+void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
|
|
|
{
|
|
|
- struct cfg80211_action_registration *reg, *tmp;
|
|
|
+ struct cfg80211_mgmt_registration *reg, *tmp;
|
|
|
|
|
|
- spin_lock_bh(&wdev->action_registrations_lock);
|
|
|
+ spin_lock_bh(&wdev->mgmt_registrations_lock);
|
|
|
|
|
|
- list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) {
|
|
|
+ list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
|
|
|
list_del(®->list);
|
|
|
kfree(reg);
|
|
|
}
|
|
|
|
|
|
- spin_unlock_bh(&wdev->action_registrations_lock);
|
|
|
+ spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
|
|
}
|
|
|
|
|
|
-int cfg80211_mlme_action(struct cfg80211_registered_device *rdev,
|
|
|
- struct net_device *dev,
|
|
|
- struct ieee80211_channel *chan,
|
|
|
- enum nl80211_channel_type channel_type,
|
|
|
- bool channel_type_valid,
|
|
|
- const u8 *buf, size_t len, u64 *cookie)
|
|
|
+int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
|
|
|
+ struct net_device *dev,
|
|
|
+ struct ieee80211_channel *chan,
|
|
|
+ enum nl80211_channel_type channel_type,
|
|
|
+ bool channel_type_valid,
|
|
|
+ const u8 *buf, size_t len, u64 *cookie)
|
|
|
{
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
const struct ieee80211_mgmt *mgmt;
|
|
|
+ u16 stype;
|
|
|
+
|
|
|
+ if (!wdev->wiphy->mgmt_stypes)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
|
|
- if (rdev->ops->action == NULL)
|
|
|
+ if (!rdev->ops->mgmt_tx)
|
|
|
return -EOPNOTSUPP;
|
|
|
+
|
|
|
if (len < 24 + 1)
|
|
|
return -EINVAL;
|
|
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) buf;
|
|
|
- if (!ieee80211_is_action(mgmt->frame_control))
|
|
|
+
|
|
|
+ if (!ieee80211_is_mgmt(mgmt->frame_control))
|
|
|
return -EINVAL;
|
|
|
- if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
|
|
|
+
|
|
|
+ stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
|
|
|
+ if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4)))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (ieee80211_is_action(mgmt->frame_control) &&
|
|
|
+ mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
|
|
|
/* Verify that we are associated with the destination AP */
|
|
|
wdev_lock(wdev);
|
|
|
|
|
@@ -863,64 +896,75 @@ int cfg80211_mlme_action(struct cfg80211_registered_device *rdev,
|
|
|
return -EINVAL;
|
|
|
|
|
|
/* Transmit the Action frame as requested by user space */
|
|
|
- return rdev->ops->action(&rdev->wiphy, dev, chan, channel_type,
|
|
|
- channel_type_valid, buf, len, cookie);
|
|
|
+ return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, channel_type,
|
|
|
+ channel_type_valid, buf, len, cookie);
|
|
|
}
|
|
|
|
|
|
-bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf,
|
|
|
- size_t len, gfp_t gfp)
|
|
|
+bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf,
|
|
|
+ size_t len, gfp_t gfp)
|
|
|
{
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
- struct cfg80211_action_registration *reg;
|
|
|
- const u8 *action_data;
|
|
|
- int action_data_len;
|
|
|
+ struct cfg80211_mgmt_registration *reg;
|
|
|
+ const struct ieee80211_txrx_stypes *stypes =
|
|
|
+ &wiphy->mgmt_stypes[wdev->iftype];
|
|
|
+ struct ieee80211_mgmt *mgmt = (void *)buf;
|
|
|
+ const u8 *data;
|
|
|
+ int data_len;
|
|
|
bool result = false;
|
|
|
+ __le16 ftype = mgmt->frame_control &
|
|
|
+ cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);
|
|
|
+ u16 stype;
|
|
|
|
|
|
- /* frame length - min size excluding category */
|
|
|
- action_data_len = len - (IEEE80211_MIN_ACTION_SIZE - 1);
|
|
|
+ stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4;
|
|
|
|
|
|
- /* action data starts with category */
|
|
|
- action_data = buf + IEEE80211_MIN_ACTION_SIZE - 1;
|
|
|
+ if (!(stypes->rx & BIT(stype)))
|
|
|
+ return false;
|
|
|
|
|
|
- spin_lock_bh(&wdev->action_registrations_lock);
|
|
|
+ data = buf + ieee80211_hdrlen(mgmt->frame_control);
|
|
|
+ data_len = len - ieee80211_hdrlen(mgmt->frame_control);
|
|
|
+
|
|
|
+ spin_lock_bh(&wdev->mgmt_registrations_lock);
|
|
|
+
|
|
|
+ list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
|
|
|
+ if (reg->frame_type != ftype)
|
|
|
+ continue;
|
|
|
|
|
|
- list_for_each_entry(reg, &wdev->action_registrations, list) {
|
|
|
- if (reg->match_len > action_data_len)
|
|
|
+ if (reg->match_len > data_len)
|
|
|
continue;
|
|
|
|
|
|
- if (memcmp(reg->match, action_data, reg->match_len))
|
|
|
+ if (memcmp(reg->match, data, reg->match_len))
|
|
|
continue;
|
|
|
|
|
|
/* found match! */
|
|
|
|
|
|
/* Indicate the received Action frame to user space */
|
|
|
- if (nl80211_send_action(rdev, dev, reg->nlpid, freq,
|
|
|
- buf, len, gfp))
|
|
|
+ if (nl80211_send_mgmt(rdev, dev, reg->nlpid, freq,
|
|
|
+ buf, len, gfp))
|
|
|
continue;
|
|
|
|
|
|
result = true;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- spin_unlock_bh(&wdev->action_registrations_lock);
|
|
|
+ spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(cfg80211_rx_action);
|
|
|
+EXPORT_SYMBOL(cfg80211_rx_mgmt);
|
|
|
|
|
|
-void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
|
|
|
- const u8 *buf, size_t len, bool ack, gfp_t gfp)
|
|
|
+void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie,
|
|
|
+ const u8 *buf, size_t len, bool ack, gfp_t gfp)
|
|
|
{
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
|
|
/* Indicate TX status of the Action frame to user space */
|
|
|
- nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
|
|
|
+ nl80211_send_mgmt_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
|
|
|
}
|
|
|
-EXPORT_SYMBOL(cfg80211_action_tx_status);
|
|
|
+EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
|
|
|
|
|
|
void cfg80211_cqm_rssi_notify(struct net_device *dev,
|
|
|
enum nl80211_cqm_rssi_threshold_event rssi_event,
|