aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/ieee80211.h25
-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
5 files changed, 142 insertions, 13 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index f65b5446d983..4e2bb9107878 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -19,6 +19,7 @@
19#include <linux/types.h> 19#include <linux/types.h>
20#include <linux/if_ether.h> 20#include <linux/if_ether.h>
21#include <asm/byteorder.h> 21#include <asm/byteorder.h>
22#include <asm/unaligned.h>
22 23
23/* 24/*
24 * DS bit usage 25 * DS bit usage
@@ -2418,6 +2419,30 @@ static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim,
2418 return !!(tim->virtual_map[index] & mask); 2419 return !!(tim->virtual_map[index] & mask);
2419} 2420}
2420 2421
2422/**
2423 * ieee80211_get_tdls_action - get tdls packet action (or -1, if not tdls packet)
2424 * @skb: the skb containing the frame, length will not be checked
2425 * @hdr_size: the size of the ieee80211_hdr that starts at skb->data
2426 *
2427 * This function assumes the frame is a data frame, and that the network header
2428 * is in the correct place.
2429 */
2430static inline int ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
2431{
2432 if (!skb_is_nonlinear(skb) &&
2433 skb->len > (skb_network_offset(skb) + 2)) {
2434 /* Point to where the indication of TDLS should start */
2435 const u8 *tdls_data = skb_network_header(skb) - 2;
2436
2437 if (get_unaligned_be16(tdls_data) == ETH_P_TDLS &&
2438 tdls_data[2] == WLAN_TDLS_SNAP_RFTYPE &&
2439 tdls_data[3] == WLAN_CATEGORY_TDLS)
2440 return tdls_data[4];
2441 }
2442
2443 return -1;
2444}
2445
2421/* convert time units */ 2446/* convert time units */
2422#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024)) 2447#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
2423#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x)) 2448#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
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;