mac80211: support client probe
authorJohannes Berg <johannes.berg@intel.com>
Fri, 4 Nov 2011 10:18:16 +0000 (11:18 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 9 Nov 2011 21:13:46 +0000 (16:13 -0500)
Support probing clients with null data frames
in AP mode.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/mac80211/cfg.c
net/mac80211/status.c

index 1f1056172ef1f4e04c7f2e260cb6ce96464ad2e7..e072fea69a302b9d1d8bd1bf87e378b0b6dee7e1 100644 (file)
@@ -2507,6 +2507,73 @@ static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
+static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
+                                 const u8 *peer, u64 *cookie)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_qos_hdr *nullfunc;
+       struct sk_buff *skb;
+       int size = sizeof(*nullfunc);
+       __le16 fc;
+       bool qos;
+       struct ieee80211_tx_info *info;
+       struct sta_info *sta;
+
+       rcu_read_lock();
+       sta = sta_info_get(sdata, peer);
+       if (sta)
+               qos = test_sta_flag(sta, WLAN_STA_WME);
+       rcu_read_unlock();
+
+       if (!sta)
+               return -ENOLINK;
+
+       if (qos) {
+               fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
+                                IEEE80211_STYPE_QOS_NULLFUNC |
+                                IEEE80211_FCTL_FROMDS);
+       } else {
+               size -= 2;
+               fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
+                                IEEE80211_STYPE_NULLFUNC |
+                                IEEE80211_FCTL_FROMDS);
+       }
+
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
+       if (!skb)
+               return -ENOMEM;
+
+       skb->dev = dev;
+
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+
+       nullfunc = (void *) skb_put(skb, size);
+       nullfunc->frame_control = fc;
+       nullfunc->duration_id = 0;
+       memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
+       memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
+       memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN);
+       nullfunc->seq_ctrl = 0;
+
+       info = IEEE80211_SKB_CB(skb);
+
+       info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
+                      IEEE80211_TX_INTFL_NL80211_FRAME_TX;
+
+       skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+       skb->priority = 7;
+       if (qos)
+               nullfunc->qos_ctrl = cpu_to_le16(7);
+
+       local_bh_disable();
+       ieee80211_xmit(sdata, skb);
+       local_bh_enable();
+
+       *cookie = (unsigned long) skb;
+       return 0;
+}
+
 struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -2572,4 +2639,5 @@ struct cfg80211_ops mac80211_config_ops = {
        .set_rekey_data = ieee80211_set_rekey_data,
        .tdls_oper = ieee80211_tdls_oper,
        .tdls_mgmt = ieee80211_tdls_mgmt,
+       .probe_client = ieee80211_probe_client,
 };
index e1f69545974aded01045af8a774d7bc2928e2537..94702f103cfcbce328872952457a79ba20ca4933 100644 (file)
@@ -516,27 +516,36 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        }
 
        if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
-               struct ieee80211_work *wk;
                u64 cookie = (unsigned long)skb;
 
-               rcu_read_lock();
-               list_for_each_entry_rcu(wk, &local->work_list, list) {
-                       if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
-                               continue;
-                       if (wk->offchan_tx.frame != skb)
-                               continue;
-                       wk->offchan_tx.status = true;
-                       break;
-               }
-               rcu_read_unlock();
-               if (local->hw_roc_skb_for_status == skb) {
-                       cookie = local->hw_roc_cookie ^ 2;
-                       local->hw_roc_skb_for_status = NULL;
-               }
+               if (ieee80211_is_nullfunc(hdr->frame_control) ||
+                   ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+                       bool acked = info->flags & IEEE80211_TX_STAT_ACK;
+                       cfg80211_probe_status(skb->dev, hdr->addr1,
+                                             cookie, acked, GFP_ATOMIC);
+               } else {
+                       struct ieee80211_work *wk;
+
+                       rcu_read_lock();
+                       list_for_each_entry_rcu(wk, &local->work_list, list) {
+                               if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
+                                       continue;
+                               if (wk->offchan_tx.frame != skb)
+                                       continue;
+                               wk->offchan_tx.status = true;
+                               break;
+                       }
+                       rcu_read_unlock();
+                       if (local->hw_roc_skb_for_status == skb) {
+                               cookie = local->hw_roc_cookie ^ 2;
+                               local->hw_roc_skb_for_status = NULL;
+                       }
 
-               cfg80211_mgmt_tx_status(
-                       skb->dev, cookie, skb->data, skb->len,
-                       !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);
+                       cfg80211_mgmt_tx_status(
+                               skb->dev, cookie, skb->data, skb->len,
+                               !!(info->flags & IEEE80211_TX_STAT_ACK),
+                               GFP_ATOMIC);
+               }
        }
 
        /* this was a transmitted frame, but now we want to reuse it */