aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2011-11-04 06:18:16 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-11-09 16:13:46 -0500
commit06500736c5d26bff93a4f358713689073e66d0f5 (patch)
tree809fb3df5a058e5cedf276e75c25d033405602b3 /net/mac80211
parent7f6cf311a594c1e7ca8120367dd1d4c685aabff1 (diff)
mac80211: support client probe
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>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/cfg.c68
-rw-r--r--net/mac80211/status.c45
2 files changed, 95 insertions, 18 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 1f1056172ef1..e072fea69a30 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2507,6 +2507,73 @@ static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
2507 return 0; 2507 return 0;
2508} 2508}
2509 2509
2510static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
2511 const u8 *peer, u64 *cookie)
2512{
2513 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2514 struct ieee80211_local *local = sdata->local;
2515 struct ieee80211_qos_hdr *nullfunc;
2516 struct sk_buff *skb;
2517 int size = sizeof(*nullfunc);
2518 __le16 fc;
2519 bool qos;
2520 struct ieee80211_tx_info *info;
2521 struct sta_info *sta;
2522
2523 rcu_read_lock();
2524 sta = sta_info_get(sdata, peer);
2525 if (sta)
2526 qos = test_sta_flag(sta, WLAN_STA_WME);
2527 rcu_read_unlock();
2528
2529 if (!sta)
2530 return -ENOLINK;
2531
2532 if (qos) {
2533 fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
2534 IEEE80211_STYPE_QOS_NULLFUNC |
2535 IEEE80211_FCTL_FROMDS);
2536 } else {
2537 size -= 2;
2538 fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
2539 IEEE80211_STYPE_NULLFUNC |
2540 IEEE80211_FCTL_FROMDS);
2541 }
2542
2543 skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
2544 if (!skb)
2545 return -ENOMEM;
2546
2547 skb->dev = dev;
2548
2549 skb_reserve(skb, local->hw.extra_tx_headroom);
2550
2551 nullfunc = (void *) skb_put(skb, size);
2552 nullfunc->frame_control = fc;
2553 nullfunc->duration_id = 0;
2554 memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
2555 memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
2556 memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN);
2557 nullfunc->seq_ctrl = 0;
2558
2559 info = IEEE80211_SKB_CB(skb);
2560
2561 info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
2562 IEEE80211_TX_INTFL_NL80211_FRAME_TX;
2563
2564 skb_set_queue_mapping(skb, IEEE80211_AC_VO);
2565 skb->priority = 7;
2566 if (qos)
2567 nullfunc->qos_ctrl = cpu_to_le16(7);
2568
2569 local_bh_disable();
2570 ieee80211_xmit(sdata, skb);
2571 local_bh_enable();
2572
2573 *cookie = (unsigned long) skb;
2574 return 0;
2575}
2576
2510struct cfg80211_ops mac80211_config_ops = { 2577struct cfg80211_ops mac80211_config_ops = {
2511 .add_virtual_intf = ieee80211_add_iface, 2578 .add_virtual_intf = ieee80211_add_iface,
2512 .del_virtual_intf = ieee80211_del_iface, 2579 .del_virtual_intf = ieee80211_del_iface,
@@ -2572,4 +2639,5 @@ struct cfg80211_ops mac80211_config_ops = {
2572 .set_rekey_data = ieee80211_set_rekey_data, 2639 .set_rekey_data = ieee80211_set_rekey_data,
2573 .tdls_oper = ieee80211_tdls_oper, 2640 .tdls_oper = ieee80211_tdls_oper,
2574 .tdls_mgmt = ieee80211_tdls_mgmt, 2641 .tdls_mgmt = ieee80211_tdls_mgmt,
2642 .probe_client = ieee80211_probe_client,
2575}; 2643};
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index e1f69545974a..94702f103cfc 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -516,27 +516,36 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
516 } 516 }
517 517
518 if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { 518 if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
519 struct ieee80211_work *wk;
520 u64 cookie = (unsigned long)skb; 519 u64 cookie = (unsigned long)skb;
521 520
522 rcu_read_lock(); 521 if (ieee80211_is_nullfunc(hdr->frame_control) ||
523 list_for_each_entry_rcu(wk, &local->work_list, list) { 522 ieee80211_is_qos_nullfunc(hdr->frame_control)) {
524 if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX) 523 bool acked = info->flags & IEEE80211_TX_STAT_ACK;
525 continue; 524 cfg80211_probe_status(skb->dev, hdr->addr1,
526 if (wk->offchan_tx.frame != skb) 525 cookie, acked, GFP_ATOMIC);
527 continue; 526 } else {
528 wk->offchan_tx.status = true; 527 struct ieee80211_work *wk;
529 break; 528
530 } 529 rcu_read_lock();
531 rcu_read_unlock(); 530 list_for_each_entry_rcu(wk, &local->work_list, list) {
532 if (local->hw_roc_skb_for_status == skb) { 531 if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
533 cookie = local->hw_roc_cookie ^ 2; 532 continue;
534 local->hw_roc_skb_for_status = NULL; 533 if (wk->offchan_tx.frame != skb)
535 } 534 continue;
535 wk->offchan_tx.status = true;
536 break;
537 }
538 rcu_read_unlock();
539 if (local->hw_roc_skb_for_status == skb) {
540 cookie = local->hw_roc_cookie ^ 2;
541 local->hw_roc_skb_for_status = NULL;
542 }
536 543
537 cfg80211_mgmt_tx_status( 544 cfg80211_mgmt_tx_status(
538 skb->dev, cookie, skb->data, skb->len, 545 skb->dev, cookie, skb->data, skb->len,
539 !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); 546 !!(info->flags & IEEE80211_TX_STAT_ACK),
547 GFP_ATOMIC);
548 }
540 } 549 }
541 550
542 /* this was a transmitted frame, but now we want to reuse it */ 551 /* this was a transmitted frame, but now we want to reuse it */