/* multicast and non-data only arrives on default queue; avoid checking * for default queue - we don't want to replicate all the logic that's * necessary for checking the PN on fragmented frames, leave that * to mac80211
*/ if (queue == 0 || !ieee80211_is_data(hdr->frame_control) ||
is_multicast_ether_addr(hdr->addr1)) return 0;
if (!(stats->flag & RX_FLAG_DECRYPTED)) return 0;
/* if we are here - this for sure is either CCMP or GCMP */ if (!sta) {
IWL_DEBUG_DROP(mld, "expected hw-decrypted unicast frame for station\n"); return -1;
}
if (likely(!ieee80211_is_beacon(hdr->frame_control))) returnfalse;
/* * if link ID is >= valid ones then that means the RX * was on the AUX link and no correction is needed
*/ if (link_id >= mld->fw->ucode_capa.num_links) returnfalse;
/* for the link conf lookup */
guard(rcu)();
link_conf = rcu_dereference(mld->fw_id_to_bss_conf[link_id]); if (!link_conf) returnfalse;
mld_link = iwl_mld_link_from_mac80211(link_conf); if (!mld_link) returnfalse;
/* * If we know the link by link ID then the frame was * received for the link, so by filtering it means it * was from the AP the link is connected to.
*/
/* skip also in case we don't have it (yet) */ if (!mld_link->average_beacon_energy) returnfalse;
IWL_DEBUG_STATS(mld, "energy override by average %d\n",
mld_link->average_beacon_energy);
rx_status->signal = -mld_link->average_beacon_energy; returntrue;
}
staticvoid iwl_mld_fill_signal(struct iwl_mld *mld, int link_id, struct ieee80211_hdr *hdr, struct ieee80211_rx_status *rx_status, struct iwl_mld_rx_phy_data *phy_data)
{
u32 rate_n_flags = phy_data->rate_n_flags; int energy_a = phy_data->energy_a; int energy_b = phy_data->energy_b; int max_energy;
staticvoid
iwl_mld_decode_he_phy_ru_alloc(struct iwl_mld_rx_phy_data *phy_data, struct ieee80211_radiotap_he *he, struct ieee80211_radiotap_he_mu *he_mu, struct ieee80211_rx_status *rx_status)
{ /* Unfortunately, we have to leave the mac80211 data * incorrect for the case that we receive an HE-MU * transmission and *don't* have the HE phy data (due * to the bits being used for TSF). This shouldn't * happen though as management frames where we need * the TSF/timers are not be transmitted in HE-MU.
*/
u8 ru = le32_get_bits(phy_data->data1, IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK);
u32 rate_n_flags = phy_data->rate_n_flags;
u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
u8 offs = 0;
/* report the AMPDU-EOF bit on single frames */ if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; if (phy_data->data0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF))
rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
}
if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
iwl_mld_decode_he_phy_data(phy_data, he, he_mu, rx_status,
queue);
/* update aggregation data for monitor sake on default queue */ if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) &&
(phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) {
rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; if (phy_data->data0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF))
rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
}
/* actually data is filled in mac80211 */ if (he_type == RATE_MCS_HE_TYPE_SU ||
he_type == RATE_MCS_HE_TYPE_EXT_SU)
he->data1 |=
cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN);
#define CHECK_TYPE(F) \
BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \
(RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS))
switch (phy_data->info_type) { case IWL_RX_PHY_INFO_TYPE_HT: case IWL_RX_PHY_INFO_TYPE_VHT_SU: case IWL_RX_PHY_INFO_TYPE_VHT_MU: case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT: case IWL_RX_PHY_INFO_TYPE_HE_SU: case IWL_RX_PHY_INFO_TYPE_HE_MU: case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT: case IWL_RX_PHY_INFO_TYPE_HE_TB: case IWL_RX_PHY_INFO_TYPE_EHT_MU: case IWL_RX_PHY_INFO_TYPE_EHT_TB: case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT: case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT:
lsig = skb_put(skb, sizeof(*lsig));
lsig->data1 = cpu_to_le16(IEEE80211_RADIOTAP_LSIG_DATA1_LENGTH_KNOWN);
lsig->data2 = le16_encode_bits(le32_get_bits(phy_data->data1,
IWL_RX_PHY_DATA1_LSIG_LEN_MASK),
IEEE80211_RADIOTAP_LSIG_DATA2_LENGTH);
rx_status->flag |= RX_FLAG_RADIOTAP_LSIG; break; default: break;
}
}
/* Put a TLV on the skb and return data pointer * * Also pad the len to 4 and zero out all data part
*/ staticvoid *
iwl_mld_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len)
{ struct ieee80211_radiotap_tlv *tlv;
/* All RU allocating size/index is in TB format */
eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_RU_ALLOC_TB_FMT);
eht->data[8] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PS160,
IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_PS_160);
eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_ALLOC_B0,
IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B0);
eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_ALLOC_B1_B7,
IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1);
iwl_mld_decode_eht_ru(mld, rx_status, eht);
/* We only get here in case of IWL_RX_MPDU_PHY_TSF_OVERLOAD is set * which is on only in case of monitor mode so no need to check monitor * mode
*/
eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRIMARY_80);
eht->data[1] |=
le32_encode_bits(mld->monitor.p80,
IEEE80211_RADIOTAP_EHT_DATA1_PRIMARY_80);
/* report the AMPDU-EOF bit on single frames */ if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; if (phy_data->data0 &
cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF))
rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
}
/* update aggregation data for monitor sake on default queue */ if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) &&
(phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) {
rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; if (phy_data->data0 &
cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF))
rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
}
if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
iwl_mld_decode_eht_phy_data(mld, phy_data, rx_status, eht, usig);
#define CHECK_TYPE(F) \
BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \
(RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS))
if (phy_data->phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
phy_data->info_type =
le32_get_bits(phy_data->data1,
IWL_RX_PHY_DATA1_INFO_TYPE_MASK);
/* set the preamble flag if appropriate */ if (format == RATE_MCS_MOD_TYPE_CCK &&
phy_data->phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE)
rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
/* This may be overridden by iwl_mld_rx_he() to HE_RU */ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { case RATE_MCS_CHAN_WIDTH_20: break; case RATE_MCS_CHAN_WIDTH_40:
rx_status->bw = RATE_INFO_BW_40; break; case RATE_MCS_CHAN_WIDTH_80:
rx_status->bw = RATE_INFO_BW_80; break; case RATE_MCS_CHAN_WIDTH_160:
rx_status->bw = RATE_INFO_BW_160; break; case RATE_MCS_CHAN_WIDTH_320:
rx_status->bw = RATE_INFO_BW_320; break;
}
/* must be before L-SIG data */ if (format == RATE_MCS_MOD_TYPE_HE)
iwl_mld_rx_he(mld, skb, phy_data, queue);
if (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) {
len -= 2;
pad_len = 2;
}
/* For non monitor interface strip the bytes the RADA might not have * removed (it might be disabled, e.g. for mgmt frames). As a monitor * interface cannot exist with other interfaces, this removal is safe * and sufficient, in monitor mode there's no decryption being done.
*/ if (len > mic_crc_len && !ieee80211_hw_check(mld->hw, RX_INCLUDES_FCS))
len -= mic_crc_len;
/* If frame is small enough to fit in skb->head, pull it completely. * If not, only pull ieee80211_hdr (including crypto if present, and * an additional 8 bytes for SNAP/ethertype, see below) so that * splice() or TCP coalesce are more efficient. * * Since, in addition, ieee80211_data_to_8023() always pull in at * least 8 bytes (possibly more for mesh) we can do the same here * to save the cost of doing it later. That still doesn't pull in * the actual IP header since the typical case has a SNAP header. * If the latter changes (there are efforts in the standards group * to do so) we should revisit this and ieee80211_data_to_8023().
*/
headlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8;
/* The firmware may align the packet to DWORD. * The padding is inserted after the IV. * After copying the header + IV skip the padding if * present before copying packet data.
*/
hdrlen += crypt_len;
if (unlikely(headlen < hdrlen)) return -EINVAL;
/* Since data doesn't move data while putting data on skb and that is * the only way we use, data + len is the next place that hdr would * be put
*/
skb_set_mac_header(skb, skb->len);
skb_put_data(skb, hdr, hdrlen);
skb_put_data(skb, (u8 *)hdr + hdrlen + pad_len, headlen - hdrlen);
/* returns true if a packet is a duplicate or invalid tid and * should be dropped. Updates AMSDU PN tracking info
*/
VISIBLE_IF_IWLWIFI_KUNIT bool
iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta, struct ieee80211_hdr *hdr, conststruct iwl_rx_mpdu_desc *mpdu_desc, struct ieee80211_rx_status *rx_status, int queue)
{ struct iwl_mld_sta *mld_sta; struct iwl_mld_rxq_dup_data *dup_data;
u8 tid, sub_frame_idx;
if (WARN_ON(!sta)) returnfalse;
mld_sta = iwl_mld_sta_from_mac80211(sta);
if (WARN_ON_ONCE(!mld_sta->dup_data)) returnfalse;
dup_data = &mld_sta->dup_data[queue];
/* Drop duplicate 802.11 retransmissions * (IEEE 802.11-2020: 10.3.2.14 "Duplicate detection and recovery")
*/ if (ieee80211_is_ctl(hdr->frame_control) ||
ieee80211_is_any_nullfunc(hdr->frame_control) ||
is_multicast_ether_addr(hdr->addr1)) returnfalse;
if (ieee80211_is_data_qos(hdr->frame_control)) { /* frame has qos control */
tid = ieee80211_get_tid(hdr); if (tid >= IWL_MAX_TID_COUNT) returntrue;
} else {
tid = IWL_MAX_TID_COUNT;
}
/* If this wasn't a part of an A-MSDU the sub-frame index will be 0 */
sub_frame_idx = mpdu_desc->amsdu_info &
IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;
if (IWL_FW_CHECK(mld,
sub_frame_idx > 0 &&
!(mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU), "got sub_frame_idx=%d but A-MSDU flag is not set\n",
sub_frame_idx)) returntrue;
/* Allow same PN as the first subframe for following sub frames */ if (dup_data->last_seq[tid] == hdr->seq_ctrl &&
sub_frame_idx > dup_data->last_sub_frame_idx[tid])
rx_status->flag |= RX_FLAG_ALLOW_SAME_PN;
ba_data = rcu_dereference(mld->fw_id_to_ba[baid]); if (!ba_data) {
IWL_DEBUG_HT(mld, "BAID %d not found in map\n", baid); return;
}
if (!ba_data->timeout) return;
/* To minimize cache bouncing between RX queues, avoid frequent updates * to last_rx_timestamp. update it only when the timeout period has * passed. The worst-case scenario is the session expiring after * approximately 2 * timeout, which is negligible (the update is * atomic).
*/
timeout = TU_TO_JIFFIES(ba_data->timeout); if (time_is_before_jiffies(ba_data->last_rx_timestamp + timeout))
ba_data->last_rx_timestamp = now;
}
/* Processes received packets for a station. * Sets *drop to true if the packet should be dropped. * Returns the station if found, or NULL otherwise.
*/ staticstruct ieee80211_sta *
iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr, struct sk_buff *skb, conststruct iwl_rx_mpdu_desc *mpdu_desc, conststruct iwl_rx_packet *pkt, int queue, bool *drop)
{ struct ieee80211_sta *sta = NULL; struct ieee80211_link_sta *link_sta = NULL; struct ieee80211_rx_status *rx_status;
u8 baid;
if (mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_SRC_STA_FOUND)) {
u8 sta_id = le32_get_bits(mpdu_desc->status,
IWL_RX_MPDU_STATUS_STA_ID);
link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); if (!IS_ERR_OR_NULL(link_sta))
sta = link_sta->sta;
} elseif (!is_multicast_ether_addr(hdr->addr2)) { /* Passing NULL is fine since we prevent two stations with the * same address from being added.
*/
sta = ieee80211_find_sta_by_ifaddr(mld->hw, hdr->addr2, NULL);
}
/* we may not have any station yet */ if (!sta) return NULL;
if ((mpdu_status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
IWL_RX_MPDU_STATUS_SEC_NONE) return 0;
/* For non-beacon, we don't really care. But beacons may * be filtered out, and we thus need the firmware's replay * detection, otherwise beacons the firmware previously * filtered could be replayed, or something like that, and * it can filter a lot - though usually only if nothing has * changed.
*/ if (!ieee80211_is_beacon(hdr->frame_control)) return 0;
/* key mismatch - will also report !MIC_OK but we shouldn't count it */ if (!(mpdu_status & IWL_RX_MPDU_STATUS_KEY_VALID)) goto report;
/* good cases */ if (likely(mpdu_status & IWL_RX_MPDU_STATUS_MIC_OK &&
!(mpdu_status & IWL_RX_MPDU_STATUS_REPLAY_ERROR))) {
rx_status->flag |= RX_FLAG_DECRYPTED; return 0;
}
/* both keys will have the same cipher and MIC length, use * whichever one is available
*/
key = rcu_dereference(mld_vif->bigtks[0]); if (!key) {
key = rcu_dereference(mld_vif->bigtks[1]); if (!key) goto report;
}
if (mpdu_len < key->icv_len + IEEE80211_GMAC_PN_LEN + KEY_IDX_LEN) goto report;
/* get the real key ID */
keyidx = frame[mpdu_len - key->icv_len - IEEE80211_GMAC_PN_LEN - KEY_IDX_LEN]; /* and if that's the other key, look it up */ if (keyidx != key->keyidx) { /* shouldn't happen since firmware checked, but be safe * in case the MIC length is wrong too, for example
*/ if (keyidx != 6 && keyidx != 7) return -1;
key = rcu_dereference(mld_vif->bigtks[keyidx - 6]); if (!key) goto report;
}
/* Report status to mac80211 */ if (!(mpdu_status & IWL_RX_MPDU_STATUS_MIC_OK))
ieee80211_key_mic_failure(key); elseif (mpdu_status & IWL_RX_MPDU_STATUS_REPLAY_ERROR)
ieee80211_key_replay(key);
report:
wdev = ieee80211_vif_to_wdev(mld_sta->vif); if (wdev->netdev)
cfg80211_rx_unprot_mlme_mgmt(wdev->netdev, (void *)hdr,
mpdu_len);
rx_status->flag |= RX_FLAG_AMPDU_DETAILS; /* Toggle is switched whenever new aggregation starts. Make * sure ampdu_reference is never 0 so we can later use it to * see if the frame was really part of an A-MPDU or not.
*/ if (toggle_bit != mld->monitor.ampdu_toggle) {
mld->monitor.ampdu_ref++; if (mld->monitor.ampdu_ref == 0)
mld->monitor.ampdu_ref++;
mld->monitor.ampdu_toggle = toggle_bit;
phy_data->first_subframe = true;
}
rx_status->ampdu_reference = mld->monitor.ampdu_ref;
}
if (IWL_FW_CHECK(mld, mpdu_len + mpdu_desc_size > pkt_len, "FW lied about packet len (%d)\n", pkt_len)) return;
/* Don't use dev_alloc_skb(), we'll have enough headroom once * ieee80211_hdr pulled.
*/
skb = alloc_skb(128, GFP_ATOMIC); if (!skb) {
IWL_ERR(mld, "alloc_skb failed\n"); return;
}
hdr = (void *)(pkt->data + mpdu_desc_size);
iwl_mld_fill_phy_data(mld, mpdu_desc, &phy_data);
if (mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) { /* If the device inserted padding it means that (it thought) * the 802.11 header wasn't a multiple of 4 bytes long. In * this case, reserve two bytes at the start of the SKB to * align the payload properly in case we end up copying it.
*/
skb_reserve(skb, 2);
}
rx_status = IEEE80211_SKB_RXCB(skb);
/* this is needed early */
band = u8_get_bits(mpdu_desc->mac_phy_band,
IWL_RX_MPDU_MAC_PHY_BAND_BAND_MASK);
iwl_mld_fill_rx_status_band_freq(rx_status, band,
mpdu_desc->v3.channel);
rcu_read_lock();
sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue, &drop); if (drop) goto drop;
/* update aggregation data for monitor sake on default queue */ if (!queue && (phy_data.phy_info & IWL_RX_MPDU_PHY_AMPDU))
iwl_mld_rx_update_ampdu_ref(mld, &phy_data, rx_status);
/* Keep packets with CRC errors (and with overrun) for monitor mode * (otherwise the firmware discards them) but mark them as bad.
*/ if (!(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_CRC_OK)) ||
!(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_OVERRUN_OK))) {
IWL_DEBUG_RX(mld, "Bad CRC or FIFO: 0x%08X.\n",
le32_to_cpu(mpdu_desc->status));
rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
}
if (likely(!(phy_data.phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) {
rx_status->mactime =
le64_to_cpu(mpdu_desc->v3.tsf_on_air_rise);
/* TSF as indicated by the firmware is at INA time */
rx_status->flag |= RX_FLAG_MACTIME_PLCP_START;
}
/* management stuff on default queue */ if (!queue && unlikely(ieee80211_is_beacon(hdr->frame_control) ||
ieee80211_is_probe_resp(hdr->frame_control))) {
rx_status->boottime_ns = ktime_get_boottime_ns();
if (mld->scan.pass_all_sched_res ==
SCHED_SCAN_PASS_ALL_STATE_ENABLED)
mld->scan.pass_all_sched_res =
SCHED_SCAN_PASS_ALL_STATE_FOUND;
}
if (iwl_mld_build_rx_skb(mld, skb, hdr, mpdu_len, crypto_len, rxb)) goto drop;
/* time sync frame is saved and will be released later when the * notification with the timestamps arrives.
*/ if (iwl_mld_time_sync_frame(mld, skb, hdr->addr2)) goto out;
reorder_res = iwl_mld_reorder(mld, napi, queue, sta, skb, mpdu_desc); switch (reorder_res) { case IWL_MLD_PASS_SKB: break; case IWL_MLD_DROP_SKB: goto drop; case IWL_MLD_BUFFERED_SKB: goto out; default:
WARN_ON(1); goto drop;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.