Commit f444eb10 authored by Johannes Berg's avatar Johannes Berg

iwlwifi: fix wakeup status query and packet reporting

The wakeup packet in the status response is padded out
to a multiple of 4 bytes by the firmware for transfer
to the host, take that into account when checking the
length of the command.

Also, the reported wakeup packet includes the FCS but
the userspace API doesn't, so remove that. If it is a
data packet it is reported as an 802.3 packet but I
forgot to take into account and remove the encryption
head/tail, fix all of that as well.
Reviewed-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 38a12b5b
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
* *
*****************************************************************************/ *****************************************************************************/
#include <linux/etherdevice.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include "iwl-modparams.h" #include "iwl-modparams.h"
...@@ -192,6 +193,11 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, ...@@ -192,6 +193,11 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
sizeof(wkc), &wkc); sizeof(wkc), &wkc);
data->error = ret != 0; data->error = ret != 0;
mvm->ptk_ivlen = key->iv_len;
mvm->ptk_icvlen = key->icv_len;
mvm->gtk_ivlen = key->iv_len;
mvm->gtk_icvlen = key->icv_len;
/* don't upload key again */ /* don't upload key again */
goto out_unlock; goto out_unlock;
} }
...@@ -304,9 +310,13 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, ...@@ -304,9 +310,13 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
*/ */
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
key->hw_key_idx = 0; key->hw_key_idx = 0;
mvm->ptk_ivlen = key->iv_len;
mvm->ptk_icvlen = key->icv_len;
} else { } else {
data->gtk_key_idx++; data->gtk_key_idx++;
key->hw_key_idx = data->gtk_key_idx; key->hw_key_idx = data->gtk_key_idx;
mvm->gtk_ivlen = key->iv_len;
mvm->gtk_icvlen = key->icv_len;
} }
ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, true); ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, true);
...@@ -649,6 +659,11 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ...@@ -649,6 +659,11 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
/* We reprogram keys and shouldn't allocate new key indices */ /* We reprogram keys and shouldn't allocate new key indices */
memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
mvm->ptk_ivlen = 0;
mvm->ptk_icvlen = 0;
mvm->ptk_ivlen = 0;
mvm->ptk_icvlen = 0;
/* /*
* The D3 firmware still hardcodes the AP station ID for the * The D3 firmware still hardcodes the AP station ID for the
* BSS we're associated with as 0. As a result, we have to move * BSS we're associated with as 0. As a result, we have to move
...@@ -783,7 +798,6 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, ...@@ -783,7 +798,6 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
struct iwl_wowlan_status *status; struct iwl_wowlan_status *status;
u32 reasons; u32 reasons;
int ret, len; int ret, len;
bool pkt8023 = false;
struct sk_buff *pkt = NULL; struct sk_buff *pkt = NULL;
iwl_trans_read_mem_bytes(mvm->trans, base, iwl_trans_read_mem_bytes(mvm->trans, base,
...@@ -824,7 +838,8 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, ...@@ -824,7 +838,8 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
status = (void *)cmd.resp_pkt->data; status = (void *)cmd.resp_pkt->data;
if (len - sizeof(struct iwl_cmd_header) != if (len - sizeof(struct iwl_cmd_header) !=
sizeof(*status) + le32_to_cpu(status->wake_packet_bufsize)) { sizeof(*status) +
ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) {
IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
goto out; goto out;
} }
...@@ -836,61 +851,96 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, ...@@ -836,61 +851,96 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
goto report; goto report;
} }
if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) { if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET)
wakeup.magic_pkt = true; wakeup.magic_pkt = true;
pkt8023 = true;
}
if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) { if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN)
wakeup.pattern_idx = wakeup.pattern_idx =
le16_to_cpu(status->pattern_number); le16_to_cpu(status->pattern_number);
pkt8023 = true;
}
if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH))
wakeup.disconnect = true; wakeup.disconnect = true;
if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) { if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)
wakeup.gtk_rekey_failure = true; wakeup.gtk_rekey_failure = true;
pkt8023 = true;
}
if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) { if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
wakeup.rfkill_release = true; wakeup.rfkill_release = true;
pkt8023 = true;
}
if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) { if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST)
wakeup.eap_identity_req = true; wakeup.eap_identity_req = true;
pkt8023 = true;
}
if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) { if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE)
wakeup.four_way_handshake = true; wakeup.four_way_handshake = true;
pkt8023 = true;
}
if (status->wake_packet_bufsize) { if (status->wake_packet_bufsize) {
u32 pktsize = le32_to_cpu(status->wake_packet_bufsize); int pktsize = le32_to_cpu(status->wake_packet_bufsize);
u32 pktlen = le32_to_cpu(status->wake_packet_length); int pktlen = le32_to_cpu(status->wake_packet_length);
const u8 *pktdata = status->wake_packet;
struct ieee80211_hdr *hdr = (void *)pktdata;
int truncated = pktlen - pktsize;
/* this would be a firmware bug */
if (WARN_ON_ONCE(truncated < 0))
truncated = 0;
if (ieee80211_is_data(hdr->frame_control)) {
int hdrlen = ieee80211_hdrlen(hdr->frame_control);
int ivlen = 0, icvlen = 4; /* also FCS */
if (pkt8023) {
pkt = alloc_skb(pktsize, GFP_KERNEL); pkt = alloc_skb(pktsize, GFP_KERNEL);
if (!pkt) if (!pkt)
goto report; goto report;
memcpy(skb_put(pkt, pktsize), status->wake_packet,
pktsize); memcpy(skb_put(pkt, hdrlen), pktdata, hdrlen);
pktdata += hdrlen;
pktsize -= hdrlen;
if (ieee80211_has_protected(hdr->frame_control)) {
if (is_multicast_ether_addr(hdr->addr1)) {
ivlen = mvm->gtk_ivlen;
icvlen += mvm->gtk_icvlen;
} else {
ivlen = mvm->ptk_ivlen;
icvlen += mvm->ptk_icvlen;
}
}
/* if truncated, FCS/ICV is (partially) gone */
if (truncated >= icvlen) {
icvlen = 0;
truncated -= icvlen;
} else {
icvlen -= truncated;
truncated = 0;
}
pktsize -= ivlen + icvlen;
pktdata += ivlen;
memcpy(skb_put(pkt, pktsize), pktdata, pktsize);
if (ieee80211_data_to_8023(pkt, vif->addr, vif->type)) if (ieee80211_data_to_8023(pkt, vif->addr, vif->type))
goto report; goto report;
wakeup.packet = pkt->data; wakeup.packet = pkt->data;
wakeup.packet_present_len = pkt->len; wakeup.packet_present_len = pkt->len;
wakeup.packet_len = pkt->len - (pktlen - pktsize); wakeup.packet_len = pkt->len - truncated;
wakeup.packet_80211 = false; wakeup.packet_80211 = false;
} else { } else {
int fcslen = 4;
if (truncated >= 4) {
truncated -= 4;
fcslen = 0;
} else {
fcslen -= truncated;
truncated = 0;
}
pktsize -= fcslen;
wakeup.packet = status->wake_packet; wakeup.packet = status->wake_packet;
wakeup.packet_present_len = pktsize; wakeup.packet_present_len = pktsize;
wakeup.packet_len = pktlen; wakeup.packet_len = pktlen - truncated;
wakeup.packet_80211 = true; wakeup.packet_80211 = true;
} }
} }
......
...@@ -327,6 +327,10 @@ struct iwl_mvm { ...@@ -327,6 +327,10 @@ struct iwl_mvm {
struct led_classdev led; struct led_classdev led;
struct ieee80211_vif *p2p_device_vif; struct ieee80211_vif *p2p_device_vif;
#ifdef CONFIG_PM_SLEEP
int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
#endif
}; };
/* Extract MVM priv from op_mode and _hw */ /* Extract MVM priv from op_mode and _hw */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment