diff options
| -rw-r--r-- | include/linux/ieee80211.h | 14 | ||||
| -rw-r--r-- | net/mac80211/rx.c | 69 |
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 | |||
| 530 | struct ieee80211_mgmt { | 532 | struct 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 */ | ||
| 1140 | enum 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 | ||
| 1670 | void 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 | |||
| 1670 | static ieee80211_rx_result debug_noinline | 1721 | static ieee80211_rx_result debug_noinline |
| 1671 | ieee80211_rx_h_action(struct ieee80211_rx_data *rx) | 1722 | ieee80211_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 | } |
