diff options
-rw-r--r-- | include/linux/ieee80211.h | 25 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 4 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 12 | ||||
-rw-r--r-- | net/mac80211/status.c | 55 | ||||
-rw-r--r-- | net/mac80211/tdls.c | 59 |
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 | */ | ||
2430 | static 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 | */ | ||
397 | static 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 | |||
393 | static void ieee80211_report_used_skb(struct ieee80211_local *local, | 433 | static 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; |