aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/ieee80211.h14
-rw-r--r--net/mac80211/rx.c69
2 files changed, 83 insertions, 0 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index df98a8a549a2..9fe1948d28d3 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -527,6 +527,8 @@ struct ieee80211_tim_ie {
527 u8 virtual_map[0]; 527 u8 virtual_map[0];
528} __attribute__ ((packed)); 528} __attribute__ ((packed));
529 529
530#define WLAN_SA_QUERY_TR_ID_LEN 16
531
530struct ieee80211_mgmt { 532struct ieee80211_mgmt {
531 __le16 frame_control; 533 __le16 frame_control;
532 __le16 duration; 534 __le16 duration;
@@ -646,6 +648,10 @@ struct ieee80211_mgmt {
646 u8 action_code; 648 u8 action_code;
647 u8 variable[0]; 649 u8 variable[0];
648 } __attribute__((packed)) mesh_action; 650 } __attribute__((packed)) mesh_action;
651 struct {
652 u8 action;
653 u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
654 } __attribute__ ((packed)) sa_query;
649 } u; 655 } u;
650 } __attribute__ ((packed)) action; 656 } __attribute__ ((packed)) action;
651 } u; 657 } u;
@@ -1041,6 +1047,7 @@ enum ieee80211_category {
1041 WLAN_CATEGORY_DLS = 2, 1047 WLAN_CATEGORY_DLS = 2,
1042 WLAN_CATEGORY_BACK = 3, 1048 WLAN_CATEGORY_BACK = 3,
1043 WLAN_CATEGORY_PUBLIC = 4, 1049 WLAN_CATEGORY_PUBLIC = 4,
1050 WLAN_CATEGORY_SA_QUERY = 8,
1044 WLAN_CATEGORY_WMM = 17, 1051 WLAN_CATEGORY_WMM = 17,
1045}; 1052};
1046 1053
@@ -1129,6 +1136,13 @@ enum ieee80211_back_parties {
1129 WLAN_BACK_TIMER = 2, 1136 WLAN_BACK_TIMER = 2,
1130}; 1137};
1131 1138
1139/* SA Query action */
1140enum ieee80211_sa_query_action {
1141 WLAN_ACTION_SA_QUERY_REQUEST = 0,
1142 WLAN_ACTION_SA_QUERY_RESPONSE = 1,
1143};
1144
1145
1132/* A-MSDU 802.11n */ 1146/* A-MSDU 802.11n */
1133#define IEEE80211_QOS_CONTROL_A_MSDU_PRESENT 0x0080 1147#define IEEE80211_QOS_CONTROL_A_MSDU_PRESENT 0x0080
1134 1148
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index abc3aa583ca6..63db89aef3e4 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1667,6 +1667,57 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
1667 return RX_CONTINUE; 1667 return RX_CONTINUE;
1668} 1668}
1669 1669
1670void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
1671 struct ieee80211_mgmt *mgmt,
1672 size_t len)
1673{
1674 struct ieee80211_local *local = sdata->local;
1675 struct sk_buff *skb;
1676 struct ieee80211_mgmt *resp;
1677
1678 if (compare_ether_addr(mgmt->da, sdata->dev->dev_addr) != 0) {
1679 /* Not to own unicast address */
1680 return;
1681 }
1682
1683 if (compare_ether_addr(mgmt->sa, sdata->u.sta.bssid) != 0 ||
1684 compare_ether_addr(mgmt->bssid, sdata->u.sta.bssid) != 0) {
1685 /* Not from the current AP. */
1686 return;
1687 }
1688
1689 if (sdata->u.sta.state == IEEE80211_STA_MLME_ASSOCIATE) {
1690 /* Association in progress; ignore SA Query */
1691 return;
1692 }
1693
1694 if (len < 24 + 1 + sizeof(resp->u.action.u.sa_query)) {
1695 /* Too short SA Query request frame */
1696 return;
1697 }
1698
1699 skb = dev_alloc_skb(sizeof(*resp) + local->hw.extra_tx_headroom);
1700 if (skb == NULL)
1701 return;
1702
1703 skb_reserve(skb, local->hw.extra_tx_headroom);
1704 resp = (struct ieee80211_mgmt *) skb_put(skb, 24);
1705 memset(resp, 0, 24);
1706 memcpy(resp->da, mgmt->sa, ETH_ALEN);
1707 memcpy(resp->sa, sdata->dev->dev_addr, ETH_ALEN);
1708 memcpy(resp->bssid, sdata->u.sta.bssid, ETH_ALEN);
1709 resp->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
1710 IEEE80211_STYPE_ACTION);
1711 skb_put(skb, 1 + sizeof(resp->u.action.u.sa_query));
1712 resp->u.action.category = WLAN_CATEGORY_SA_QUERY;
1713 resp->u.action.u.sa_query.action = WLAN_ACTION_SA_QUERY_RESPONSE;
1714 memcpy(resp->u.action.u.sa_query.trans_id,
1715 mgmt->u.action.u.sa_query.trans_id,
1716 WLAN_SA_QUERY_TR_ID_LEN);
1717
1718 ieee80211_tx_skb(sdata, skb, 1);
1719}
1720
1670static ieee80211_rx_result debug_noinline 1721static ieee80211_rx_result debug_noinline
1671ieee80211_rx_h_action(struct ieee80211_rx_data *rx) 1722ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
1672{ 1723{
@@ -1743,6 +1794,24 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
1743 break; 1794 break;
1744 } 1795 }
1745 break; 1796 break;
1797 case WLAN_CATEGORY_SA_QUERY:
1798 if (len < (IEEE80211_MIN_ACTION_SIZE +
1799 sizeof(mgmt->u.action.u.sa_query)))
1800 return RX_DROP_MONITOR;
1801 switch (mgmt->u.action.u.sa_query.action) {
1802 case WLAN_ACTION_SA_QUERY_REQUEST:
1803 if (sdata->vif.type != NL80211_IFTYPE_STATION)
1804 return RX_DROP_MONITOR;
1805 ieee80211_process_sa_query_req(sdata, mgmt, len);
1806 break;
1807 case WLAN_ACTION_SA_QUERY_RESPONSE:
1808 /*
1809 * SA Query response is currently only used in AP mode
1810 * and it is processed in user space.
1811 */
1812 return RX_CONTINUE;
1813 }
1814 break;
1746 default: 1815 default:
1747 return RX_CONTINUE; 1816 return RX_CONTINUE;
1748 } 1817 }