Commit 2e57b775 authored by Daniel Gabay's avatar Daniel Gabay Committed by Johannes Berg

wifi: iwlwifi: mvm: use correct address 3 in A-MSDU

As described in IEEE sta 802.11-2020, table 9-30 (Address
field contents), A-MSDU address 3 should contain the BSSID
address.

In TX_CMD we copy the MAC header from skb, and skb address 3
holds the destination address, but it may not be identical to
the BSSID.

Using the wrong destination address appears to work with (most)
receivers without MLO, but in MLO some devices are checking for
it carefully, perhaps as a consequence of link to MLD address
translation.

Replace address 3 in the TX_CMD MAC header with the correct
address while retaining the skb address 3 unchanged.
This ensures that skb address 3 will be utilized later for
constructing the A-MSDU subframes.

Note that we fill in the MLD address, but the firmware will do the
necessary translation to link address after encryption.
Signed-off-by: default avatarDaniel Gabay <daniel.gabay@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240204235836.4583a1bf9188.I3f8e7892bdf8f86b4daa28453771a8c9817b2416@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent c98d8836
...@@ -520,13 +520,24 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, ...@@ -520,13 +520,24 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
} }
} }
static void iwl_mvm_copy_hdr(void *cmd, const void *hdr, int hdrlen,
const u8 *addr3_override)
{
struct ieee80211_hdr *out_hdr = cmd;
memcpy(cmd, hdr, hdrlen);
if (addr3_override)
memcpy(out_hdr->addr3, addr3_override, ETH_ALEN);
}
/* /*
* Allocates and sets the Tx cmd the driver data pointers in the skb * Allocates and sets the Tx cmd the driver data pointers in the skb
*/ */
static struct iwl_device_tx_cmd * static struct iwl_device_tx_cmd *
iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_tx_info *info, int hdrlen, struct ieee80211_tx_info *info, int hdrlen,
struct ieee80211_sta *sta, u8 sta_id) struct ieee80211_sta *sta, u8 sta_id,
const u8 *addr3_override)
{ {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct iwl_device_tx_cmd *dev_cmd; struct iwl_device_tx_cmd *dev_cmd;
...@@ -584,7 +595,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, ...@@ -584,7 +595,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
cmd->len = cpu_to_le16((u16)skb->len); cmd->len = cpu_to_le16((u16)skb->len);
/* Copy MAC header from skb into command buffer */ /* Copy MAC header from skb into command buffer */
memcpy(cmd->hdr, hdr, hdrlen); iwl_mvm_copy_hdr(cmd->hdr, hdr, hdrlen, addr3_override);
cmd->flags = cpu_to_le16(flags); cmd->flags = cpu_to_le16(flags);
cmd->rate_n_flags = cpu_to_le32(rate_n_flags); cmd->rate_n_flags = cpu_to_le32(rate_n_flags);
...@@ -599,7 +610,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, ...@@ -599,7 +610,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
cmd->len = cpu_to_le16((u16)skb->len); cmd->len = cpu_to_le16((u16)skb->len);
/* Copy MAC header from skb into command buffer */ /* Copy MAC header from skb into command buffer */
memcpy(cmd->hdr, hdr, hdrlen); iwl_mvm_copy_hdr(cmd->hdr, hdr, hdrlen, addr3_override);
cmd->flags = cpu_to_le32(flags); cmd->flags = cpu_to_le32(flags);
cmd->rate_n_flags = cpu_to_le32(rate_n_flags); cmd->rate_n_flags = cpu_to_le32(rate_n_flags);
...@@ -617,7 +628,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, ...@@ -617,7 +628,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control); iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control);
/* Copy MAC header from skb into command buffer */ /* Copy MAC header from skb into command buffer */
memcpy(tx_cmd->hdr, hdr, hdrlen); iwl_mvm_copy_hdr(tx_cmd->hdr, hdr, hdrlen, addr3_override);
out: out:
return dev_cmd; return dev_cmd;
...@@ -820,7 +831,8 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) ...@@ -820,7 +831,8 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, queue); IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, queue);
dev_cmd = iwl_mvm_set_tx_params(mvm, skb, &info, hdrlen, NULL, sta_id); dev_cmd = iwl_mvm_set_tx_params(mvm, skb, &info, hdrlen, NULL, sta_id,
NULL);
if (!dev_cmd) if (!dev_cmd)
return -1; return -1;
...@@ -1140,7 +1152,8 @@ static int iwl_mvm_tx_pkt_queued(struct iwl_mvm *mvm, ...@@ -1140,7 +1152,8 @@ static int iwl_mvm_tx_pkt_queued(struct iwl_mvm *mvm,
*/ */
static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_tx_info *info, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta) struct ieee80211_sta *sta,
const u8 *addr3_override)
{ {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct iwl_mvm_sta *mvmsta; struct iwl_mvm_sta *mvmsta;
...@@ -1172,7 +1185,8 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, ...@@ -1172,7 +1185,8 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
iwl_mvm_probe_resp_set_noa(mvm, skb); iwl_mvm_probe_resp_set_noa(mvm, skb);
dev_cmd = iwl_mvm_set_tx_params(mvm, skb, info, hdrlen, dev_cmd = iwl_mvm_set_tx_params(mvm, skb, info, hdrlen,
sta, mvmsta->deflink.sta_id); sta, mvmsta->deflink.sta_id,
addr3_override);
if (!dev_cmd) if (!dev_cmd)
goto drop; goto drop;
...@@ -1294,9 +1308,11 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, ...@@ -1294,9 +1308,11 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb,
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct ieee80211_tx_info info; struct ieee80211_tx_info info;
struct sk_buff_head mpdus_skbs; struct sk_buff_head mpdus_skbs;
struct ieee80211_vif *vif;
unsigned int payload_len; unsigned int payload_len;
int ret; int ret;
struct sk_buff *orig_skb = skb; struct sk_buff *orig_skb = skb;
const u8 *addr3;
if (WARN_ON_ONCE(!mvmsta)) if (WARN_ON_ONCE(!mvmsta))
return -1; return -1;
...@@ -1307,26 +1323,59 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, ...@@ -1307,26 +1323,59 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb,
memcpy(&info, skb->cb, sizeof(info)); memcpy(&info, skb->cb, sizeof(info));
if (!skb_is_gso(skb)) if (!skb_is_gso(skb))
return iwl_mvm_tx_mpdu(mvm, skb, &info, sta); return iwl_mvm_tx_mpdu(mvm, skb, &info, sta, NULL);
payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) -
tcp_hdrlen(skb) + skb->data_len; tcp_hdrlen(skb) + skb->data_len;
if (payload_len <= skb_shinfo(skb)->gso_size) if (payload_len <= skb_shinfo(skb)->gso_size)
return iwl_mvm_tx_mpdu(mvm, skb, &info, sta); return iwl_mvm_tx_mpdu(mvm, skb, &info, sta, NULL);
__skb_queue_head_init(&mpdus_skbs); __skb_queue_head_init(&mpdus_skbs);
vif = info.control.vif;
if (!vif)
return -1;
ret = iwl_mvm_tx_tso(mvm, skb, &info, sta, &mpdus_skbs); ret = iwl_mvm_tx_tso(mvm, skb, &info, sta, &mpdus_skbs);
if (ret) if (ret)
return ret; return ret;
WARN_ON(skb_queue_empty(&mpdus_skbs)); WARN_ON(skb_queue_empty(&mpdus_skbs));
/*
* As described in IEEE sta 802.11-2020, table 9-30 (Address
* field contents), A-MSDU address 3 should contain the BSSID
* address.
* Pass address 3 down to iwl_mvm_tx_mpdu() and further to set it
* in the command header. We need to preserve the original
* address 3 in the skb header to correctly create all the
* A-MSDU subframe headers from it.
*/
switch (vif->type) {
case NL80211_IFTYPE_STATION:
addr3 = vif->cfg.ap_addr;
break;
case NL80211_IFTYPE_AP:
addr3 = vif->addr;
break;
default:
addr3 = NULL;
break;
}
while (!skb_queue_empty(&mpdus_skbs)) { while (!skb_queue_empty(&mpdus_skbs)) {
struct ieee80211_hdr *hdr;
bool amsdu;
skb = __skb_dequeue(&mpdus_skbs); skb = __skb_dequeue(&mpdus_skbs);
hdr = (void *)skb->data;
amsdu = ieee80211_is_data_qos(hdr->frame_control) &&
(*ieee80211_get_qos_ctl(hdr) &
IEEE80211_QOS_CTL_A_MSDU_PRESENT);
ret = iwl_mvm_tx_mpdu(mvm, skb, &info, sta); ret = iwl_mvm_tx_mpdu(mvm, skb, &info, sta,
amsdu ? addr3 : NULL);
if (ret) { if (ret) {
/* Free skbs created as part of TSO logic that have not yet been dequeued */ /* Free skbs created as part of TSO logic that have not yet been dequeued */
__skb_queue_purge(&mpdus_skbs); __skb_queue_purge(&mpdus_skbs);
......
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