|
@@ -57,6 +57,13 @@
|
|
|
|
|
|
#define ERP_INFO_USE_PROTECTION BIT(1)
|
|
|
|
|
|
+/* mgmt header + 1 byte action code */
|
|
|
+#define IEEE80211_MIN_ACTION_SIZE (24 + 1)
|
|
|
+
|
|
|
+#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
|
|
|
+#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
|
|
|
+#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0
|
|
|
+
|
|
|
static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst,
|
|
|
u8 *ssid, size_t ssid_len);
|
|
|
static struct ieee80211_sta_bss *
|
|
@@ -987,6 +994,91 @@ static void ieee80211_auth_challenge(struct net_device *dev,
|
|
|
elems.challenge_len + 2, 1);
|
|
|
}
|
|
|
|
|
|
+static void ieee80211_send_addba_resp(struct net_device *dev, u8 *da, u16 tid,
|
|
|
+ u8 dialog_token, u16 status, u16 policy,
|
|
|
+ u16 buf_size, u16 timeout)
|
|
|
+{
|
|
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
|
|
|
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
|
+ struct sk_buff *skb;
|
|
|
+ struct ieee80211_mgmt *mgmt;
|
|
|
+ u16 capab;
|
|
|
+
|
|
|
+ skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
|
|
|
+ if (!skb) {
|
|
|
+ printk(KERN_DEBUG "%s: failed to allocate buffer "
|
|
|
+ "for addba resp frame\n", dev->name);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
|
|
|
+ memset(mgmt, 0, 24);
|
|
|
+ memcpy(mgmt->da, da, ETH_ALEN);
|
|
|
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
|
|
|
+ if (sdata->type == IEEE80211_IF_TYPE_AP)
|
|
|
+ memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
|
|
|
+ else
|
|
|
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
|
|
|
+ mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
|
|
|
+ IEEE80211_STYPE_ACTION);
|
|
|
+
|
|
|
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp));
|
|
|
+ mgmt->u.action.category = WLAN_CATEGORY_BACK;
|
|
|
+ mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
|
|
|
+ mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
|
|
|
+
|
|
|
+ capab = (u16)(policy << 1); /* bit 1 aggregation policy */
|
|
|
+ capab |= (u16)(tid << 2); /* bit 5:2 TID number */
|
|
|
+ capab |= (u16)(buf_size << 6); /* bit 15:6 max size of aggregation */
|
|
|
+
|
|
|
+ mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
|
|
|
+ mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
|
|
|
+ mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
|
|
|
+
|
|
|
+ ieee80211_sta_tx(dev, skb, 0);
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+static void ieee80211_sta_process_addba_request(struct net_device *dev,
|
|
|
+ struct ieee80211_mgmt *mgmt,
|
|
|
+ size_t len)
|
|
|
+{
|
|
|
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
|
+ struct sta_info *sta;
|
|
|
+ u16 capab, tid, timeout, ba_policy, buf_size, status;
|
|
|
+ u8 dialog_token;
|
|
|
+
|
|
|
+ sta = sta_info_get(local, mgmt->sa);
|
|
|
+ if (!sta)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* extract session parameters from addba request frame */
|
|
|
+ dialog_token = mgmt->u.action.u.addba_req.dialog_token;
|
|
|
+ timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
|
|
|
+
|
|
|
+ capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
|
|
|
+ ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
|
|
|
+ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
|
|
|
+ buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
|
|
|
+
|
|
|
+ /* TODO - currently aggregation is declined (A-MPDU add BA request
|
|
|
+ * acceptance is not obligatory by 802.11n draft), but here is
|
|
|
+ * the entry point for dealing with it */
|
|
|
+#ifdef MAC80211_HT_DEBUG
|
|
|
+ if (net_ratelimit())
|
|
|
+ printk(KERN_DEBUG "Add Block Ack request arrived,"
|
|
|
+ " currently denying it\n");
|
|
|
+#endif /* MAC80211_HT_DEBUG */
|
|
|
+
|
|
|
+ status = WLAN_STATUS_REQUEST_DECLINED;
|
|
|
+
|
|
|
+ ieee80211_send_addba_resp(sta->dev, sta->addr, tid, dialog_token,
|
|
|
+ status, 1, buf_size, timeout);
|
|
|
+ sta_info_put(sta);
|
|
|
+}
|
|
|
|
|
|
static void ieee80211_rx_mgmt_auth(struct net_device *dev,
|
|
|
struct ieee80211_if_sta *ifsta,
|
|
@@ -1870,6 +1962,34 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
|
|
|
ieee80211_sta_tx(dev, skb, 0);
|
|
|
}
|
|
|
|
|
|
+void ieee80211_rx_mgmt_action(struct net_device *dev,
|
|
|
+ struct ieee80211_if_sta *ifsta,
|
|
|
+ struct ieee80211_mgmt *mgmt,
|
|
|
+ size_t len)
|
|
|
+{
|
|
|
+ if (len < IEEE80211_MIN_ACTION_SIZE)
|
|
|
+ return;
|
|
|
+
|
|
|
+ switch (mgmt->u.action.category) {
|
|
|
+ case WLAN_CATEGORY_BACK:
|
|
|
+ switch (mgmt->u.action.u.addba_req.action_code) {
|
|
|
+ case WLAN_ACTION_ADDBA_REQ:
|
|
|
+ if (len < (IEEE80211_MIN_ACTION_SIZE +
|
|
|
+ sizeof(mgmt->u.action.u.addba_req)))
|
|
|
+ break;
|
|
|
+ ieee80211_sta_process_addba_request(dev, mgmt, len);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ if (net_ratelimit())
|
|
|
+ printk(KERN_DEBUG "%s: received unsupported BACK\n",
|
|
|
+ dev->name);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
|
|
|
struct ieee80211_rx_status *rx_status)
|
|
@@ -1899,6 +2019,7 @@ void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
|
|
|
case IEEE80211_STYPE_REASSOC_RESP:
|
|
|
case IEEE80211_STYPE_DEAUTH:
|
|
|
case IEEE80211_STYPE_DISASSOC:
|
|
|
+ case IEEE80211_STYPE_ACTION:
|
|
|
skb_queue_tail(&ifsta->skb_queue, skb);
|
|
|
queue_work(local->hw.workqueue, &ifsta->work);
|
|
|
return;
|
|
@@ -1956,6 +2077,9 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
|
|
|
case IEEE80211_STYPE_DISASSOC:
|
|
|
ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len);
|
|
|
break;
|
|
|
+ case IEEE80211_STYPE_ACTION:
|
|
|
+ ieee80211_rx_mgmt_action(dev, ifsta, mgmt, skb->len);
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
kfree_skb(skb);
|