aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorLiad Kaufman <liad.kaufman@intel.com>2014-11-09 11:50:08 -0500
committerJohannes Berg <johannes.berg@intel.com>2014-11-19 12:44:29 -0500
commit1277b4a9f531e84e26f9e0210c1801b0c0bf81ca (patch)
treebabc1273db9300734cecbab2d692bd39db946028 /net/mac80211
parent24d342c514827d52d008736bf02c9f145651ca8e (diff)
mac80211: retransmit TDLS teardown packet through AP if not ACKed
Since the TDLS peer station might not receive the teardown packet (e.g., when in PS), this makes sure the packet is retransmitted - this time through the AP - if the TDLS peer didn't ACK the packet. Signed-off-by: Liad Kaufman <liad.kaufman@intel.com> Signed-off-by: Arik Nemtsov <arik@wizery.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/ieee80211_i.h4
-rw-r--r--net/mac80211/mlme.c12
-rw-r--r--net/mac80211/status.c55
-rw-r--r--net/mac80211/tdls.c59
4 files changed, 117 insertions, 13 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 208953d1d028..bc6f12ff1f61 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -525,8 +525,12 @@ struct ieee80211_if_managed {
525 struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */ 525 struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
526 struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */ 526 struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
527 527
528 /* TDLS support */
528 u8 tdls_peer[ETH_ALEN] __aligned(2); 529 u8 tdls_peer[ETH_ALEN] __aligned(2);
529 struct delayed_work tdls_peer_del_work; 530 struct delayed_work tdls_peer_del_work;
531 struct sk_buff *orig_teardown_skb; /* The original teardown skb */
532 struct sk_buff *teardown_skb; /* A copy to send through the AP */
533 spinlock_t teardown_lock; /* To lock changing teardown_skb */
530 534
531 /* WMM-AC TSPEC support */ 535 /* WMM-AC TSPEC support */
532 struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS]; 536 struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS];
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 243539878991..11a937f3fdeb 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -4003,6 +4003,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
4003 ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC; 4003 ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
4004 else 4004 else
4005 ifmgd->req_smps = IEEE80211_SMPS_OFF; 4005 ifmgd->req_smps = IEEE80211_SMPS_OFF;
4006
4007 /* Setup TDLS data */
4008 spin_lock_init(&ifmgd->teardown_lock);
4009 ifmgd->teardown_skb = NULL;
4010 ifmgd->orig_teardown_skb = NULL;
4006} 4011}
4007 4012
4008/* scan finished notification */ 4013/* scan finished notification */
@@ -4865,6 +4870,13 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
4865 } 4870 }
4866 if (ifmgd->auth_data) 4871 if (ifmgd->auth_data)
4867 ieee80211_destroy_auth_data(sdata, false); 4872 ieee80211_destroy_auth_data(sdata, false);
4873 spin_lock_bh(&ifmgd->teardown_lock);
4874 if (ifmgd->teardown_skb) {
4875 kfree_skb(ifmgd->teardown_skb);
4876 ifmgd->teardown_skb = NULL;
4877 ifmgd->orig_teardown_skb = NULL;
4878 }
4879 spin_unlock_bh(&ifmgd->teardown_lock);
4868 del_timer_sync(&ifmgd->timer); 4880 del_timer_sync(&ifmgd->timer);
4869 sdata_unlock(sdata); 4881 sdata_unlock(sdata);
4870} 4882}
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 9612d89fad56..71de2d3866cc 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -390,6 +390,46 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
390 } 390 }
391} 391}
392 392
393/*
394 * Handles the tx for TDLS teardown frames.
395 * If the frame wasn't ACKed by the peer - it will be re-sent through the AP
396 */
397static void ieee80211_tdls_td_tx_handle(struct ieee80211_local *local,
398 struct ieee80211_sub_if_data *sdata,
399 struct sk_buff *skb, u32 flags)
400{
401 struct sk_buff *teardown_skb;
402 struct sk_buff *orig_teardown_skb;
403 bool is_teardown = false;
404
405 /* Get the teardown data we need and free the lock */
406 spin_lock(&sdata->u.mgd.teardown_lock);
407 teardown_skb = sdata->u.mgd.teardown_skb;
408 orig_teardown_skb = sdata->u.mgd.orig_teardown_skb;
409 if ((skb == orig_teardown_skb) && teardown_skb) {
410 sdata->u.mgd.teardown_skb = NULL;
411 sdata->u.mgd.orig_teardown_skb = NULL;
412 is_teardown = true;
413 }
414 spin_unlock(&sdata->u.mgd.teardown_lock);
415
416 if (is_teardown) {
417 /* This mechanism relies on being able to get ACKs */
418 WARN_ON(!(local->hw.flags &
419 IEEE80211_HW_REPORTS_TX_ACK_STATUS));
420
421 /* Check if peer has ACKed */
422 if (flags & IEEE80211_TX_STAT_ACK) {
423 dev_kfree_skb_any(teardown_skb);
424 } else {
425 tdls_dbg(sdata,
426 "TDLS Resending teardown through AP\n");
427
428 ieee80211_subif_start_xmit(teardown_skb, skb->dev);
429 }
430 }
431}
432
393static void ieee80211_report_used_skb(struct ieee80211_local *local, 433static void ieee80211_report_used_skb(struct ieee80211_local *local,
394 struct sk_buff *skb, bool dropped) 434 struct sk_buff *skb, bool dropped)
395{ 435{
@@ -426,8 +466,19 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
426 if (!sdata) { 466 if (!sdata) {
427 skb->dev = NULL; 467 skb->dev = NULL;
428 } else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) { 468 } else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
429 ieee80211_mgd_conn_tx_status(sdata, hdr->frame_control, 469 unsigned int hdr_size =
430 acked); 470 ieee80211_hdrlen(hdr->frame_control);
471
472 /* Check to see if packet is a TDLS teardown packet */
473 if (ieee80211_is_data(hdr->frame_control) &&
474 (ieee80211_get_tdls_action(skb, hdr_size) ==
475 WLAN_TDLS_TEARDOWN))
476 ieee80211_tdls_td_tx_handle(local, sdata, skb,
477 info->flags);
478 else
479 ieee80211_mgd_conn_tx_status(sdata,
480 hdr->frame_control,
481 acked);
431 } else if (ieee80211_is_nullfunc(hdr->frame_control) || 482 } else if (ieee80211_is_nullfunc(hdr->frame_control) ||
432 ieee80211_is_qos_nullfunc(hdr->frame_control)) { 483 ieee80211_is_qos_nullfunc(hdr->frame_control)) {
433 cfg80211_probe_status(sdata->dev, hdr->addr1, 484 cfg80211_probe_status(sdata->dev, hdr->addr1,
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index b4f368e2cb3b..d4fe091fd98a 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -512,20 +512,22 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
512 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 512 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
513 struct ieee80211_local *local = sdata->local; 513 struct ieee80211_local *local = sdata->local;
514 struct sk_buff *skb = NULL; 514 struct sk_buff *skb = NULL;
515 u32 flags = 0;
515 bool send_direct; 516 bool send_direct;
516 struct sta_info *sta; 517 struct sta_info *sta;
517 int ret; 518 int ret;
518 519
519 skb = dev_alloc_skb(local->hw.extra_tx_headroom + 520 skb = netdev_alloc_skb(dev,
520 max(sizeof(struct ieee80211_mgmt), 521 local->hw.extra_tx_headroom +
521 sizeof(struct ieee80211_tdls_data)) + 522 max(sizeof(struct ieee80211_mgmt),
522 50 + /* supported rates */ 523 sizeof(struct ieee80211_tdls_data)) +
523 7 + /* ext capab */ 524 50 + /* supported rates */
524 26 + /* max(WMM-info, WMM-param) */ 525 7 + /* ext capab */
525 2 + max(sizeof(struct ieee80211_ht_cap), 526 26 + /* max(WMM-info, WMM-param) */
526 sizeof(struct ieee80211_ht_operation)) + 527 2 + max(sizeof(struct ieee80211_ht_cap),
527 extra_ies_len + 528 sizeof(struct ieee80211_ht_operation)) +
528 sizeof(struct ieee80211_tdls_lnkie)); 529 extra_ies_len +
530 sizeof(struct ieee80211_tdls_lnkie));
529 if (!skb) 531 if (!skb)
530 return -ENOMEM; 532 return -ENOMEM;
531 533
@@ -623,9 +625,44 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
623 break; 625 break;
624 } 626 }
625 627
628 /*
629 * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress.
630 * Later, if no ACK is returned from peer, we will re-send the teardown
631 * packet through the AP.
632 */
633 if ((action_code == WLAN_TDLS_TEARDOWN) &&
634 (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
635 struct sta_info *sta = NULL;
636 bool try_resend; /* Should we keep skb for possible resend */
637
638 /* If not sending directly to peer - no point in keeping skb */
639 rcu_read_lock();
640 sta = sta_info_get(sdata, peer);
641 try_resend = sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
642 rcu_read_unlock();
643
644 spin_lock_bh(&sdata->u.mgd.teardown_lock);
645 if (try_resend && !sdata->u.mgd.teardown_skb) {
646 /* Mark it as requiring TX status callback */
647 flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
648 IEEE80211_TX_INTFL_MLME_CONN_TX;
649
650 /*
651 * skb is copied since mac80211 will later set
652 * properties that might not be the same as the AP,
653 * such as encryption, QoS, addresses, etc.
654 *
655 * No problem if skb_copy() fails, so no need to check.
656 */
657 sdata->u.mgd.teardown_skb = skb_copy(skb, GFP_ATOMIC);
658 sdata->u.mgd.orig_teardown_skb = skb;
659 }
660 spin_unlock_bh(&sdata->u.mgd.teardown_lock);
661 }
662
626 /* disable bottom halves when entering the Tx path */ 663 /* disable bottom halves when entering the Tx path */
627 local_bh_disable(); 664 local_bh_disable();
628 ret = ieee80211_subif_start_xmit(skb, dev); 665 __ieee80211_subif_start_xmit(skb, dev, flags);
629 local_bh_enable(); 666 local_bh_enable();
630 667
631 return ret; 668 return ret;