if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END) { /* data is pointer at tlv all other info was pulled off */
tlvs_len = skb_mac_header(skb) - skb->data;
}
if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END)
it_present_val |= BIT(IEEE80211_RADIOTAP_TLV);
put_unaligned_le32(it_present_val, it_present);
/* This references through an offset into it_optional[] rather *thanviait_presentotherwiselaterusesofposwillcause *thecompilertothinkwehavewalkedpasttheendofthe *structmember.
*/
pos = (void *)&rthdr->it_optional[it_present + 1 - rthdr->it_optional];
/* the order of the following fields is important */
/* room for the radiotap header based on driver features */
rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, *origskb);
needed_headroom = rt_hdrlen - rtap_space;
if (use_origskb) { /* only need to expand headroom if necessary */
skb = *origskb;
*origskb = NULL;
if (WARN_ON_ONCE(status->flag & RX_FLAG_RADIOTAP_TLV_AT_END &&
!skb_mac_header_was_set(origskb))) { /* with this skb no way to know where frame payload starts */
dev_kfree_skb(origskb); return NULL;
}
if (status->flag & RX_FLAG_RADIOTAP_HE)
rtap_space += sizeof(struct ieee80211_radiotap_he);
if (status->flag & RX_FLAG_RADIOTAP_HE_MU)
rtap_space += sizeof(struct ieee80211_radiotap_he_mu);
if (status->flag & RX_FLAG_RADIOTAP_LSIG)
rtap_space += sizeof(struct ieee80211_radiotap_lsig);
if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END)
rtap_space += skb_mac_header(origskb) - &origskb->data[rtap_space];
/* does the frame have a qos control field? */ if (ieee80211_is_data_qos(hdr->frame_control)) {
u8 *qc = ieee80211_get_qos_ctl(hdr); /* frame has qos control */
tid = *qc & IEEE80211_QOS_CTL_TID_MASK; if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
status->rx_flags |= IEEE80211_RX_AMSDU;
rx->seqno_idx = seqno_idx;
rx->security_idx = security_idx; /* Set skb->priority to 1d tag if highest order bit of TID is not set.
* For now, set skb->priority to 0 for other cases. */
rx->skb->priority = (tid > 7) ? 0 : tid;
}
if (!is_multicast_ether_addr(hdr->addr1)) return0;
return ieee80211_is_robust_mgmt_frame(skb);
}
/* Get the BIP key index from MMIE; return -1 if this is not a BIP frame */ staticint ieee80211_get_mmie_keyidx(struct sk_buff *skb)
{ struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data; struct ieee80211_mmie *mmie; struct ieee80211_mmie_16 *mmie16;
if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da)) return -1;
if (!ieee80211_is_robust_mgmt_frame(skb) &&
!ieee80211_is_beacon(hdr->frame_control)) return -1; /* not a robust management frame */
if (!ieee80211_rx_reorder_ready(tid_agg_rx, index)) {
__skb_queue_purge(skb_list); goto no_frame;
}
/* release frames from the reorder ring buffer */
tid_agg_rx->stored_mpdu_num--; while ((skb = __skb_dequeue(skb_list))) {
status = IEEE80211_SKB_RXCB(skb);
status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
__skb_queue_tail(frames, skb);
}
no_frame: if (tid_agg_rx->reorder_buf_filtered)
tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index);
tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
}
staticvoid ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, struct sk_buff_head *frames)
{ int index, i, j;
lockdep_assert_held(&tid_agg_rx->reorder_lock);
/* release the buffer until next missing frame */
index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; if (!ieee80211_rx_reorder_ready(tid_agg_rx, index) &&
tid_agg_rx->stored_mpdu_num) { /* *Nobuffersreadytobereleased,butcheckwhetherany *framesinthereorderbufferhavetimedout.
*/ int skipped = 1; for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
j = (j + 1) % tid_agg_rx->buf_size) { if (!ieee80211_rx_reorder_ready(tid_agg_rx, j)) {
skipped++; continue;
} if (skipped &&
!time_after(jiffies, tid_agg_rx->reorder_time[j] +
HT_RX_REORDER_BUF_TIMEOUT)) goto set_release_timer;
/* don't leave incomplete A-MSDUs around */ for (i = (index + 1) % tid_agg_rx->buf_size; i != j;
i = (i + 1) % tid_agg_rx->buf_size)
__skb_queue_purge(&tid_agg_rx->reorder_buf[i]);
ht_dbg_ratelimited(sdata, "release an RX reorder frame due to timeout on earlier frames\n");
ieee80211_release_reorder_frame(sdata, tid_agg_rx, j,
frames);
/* *IfthecurrentMPDU'sSNissmallerthantheSSN,itshouldn't *bereordered.
*/ if (unlikely(!tid_agg_rx->started)) { if (ieee80211_sn_less(mpdu_seq_num, head_seq_num)) {
ret = false; goto out;
}
tid_agg_rx->started = true;
}
/* frame with out of date sequence number */ if (ieee80211_sn_less(mpdu_seq_num, head_seq_num)) {
dev_kfree_skb(skb); goto out;
}
/* *Ifframethesequencenumberexceedsourbufferingwindow *sizereleasesomepreviousframestomakeroomforthisone.
*/ if (!ieee80211_sn_less(mpdu_seq_num, head_seq_num + buf_size)) {
head_seq_num = ieee80211_sn_inc(
ieee80211_sn_sub(mpdu_seq_num, buf_size)); /* release stored frames up to new head to stack */
ieee80211_release_reorder_frames(sdata, tid_agg_rx,
head_seq_num, frames);
}
/* Now the new frame is always in the range of the reordering buffer */
index = mpdu_seq_num % tid_agg_rx->buf_size;
/* check if we already stored this frame */ if (ieee80211_rx_reorder_ready(tid_agg_rx, index)) {
dev_kfree_skb(skb); goto out;
}
/* *IfthecurrentMPDUisintherightorderandnothingelse *isstoredwecanprocessitdirectly,noneedtobufferit. *Ifitisfirstbutthere'ssomethingstored,wemaybeable *toreleaseframesafterthisone.
*/ if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
tid_agg_rx->stored_mpdu_num == 0) { if (!(status->flag & RX_FLAG_AMSDU_MORE))
tid_agg_rx->head_seq_num =
ieee80211_sn_inc(tid_agg_rx->head_seq_num);
ret = false; goto out;
}
/* put the frame in the reordering buffer */
__skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb); if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
tid_agg_rx->reorder_time[index] = jiffies;
tid_agg_rx->stored_mpdu_num++;
ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
}
/* Drop disallowed frame classes based on STA auth/assoc state; *IEEE802.11,Chap5.5. * *mac80211filtersonlybasedonassociationstate,i.e.itdrops *Class3framesfromnotassociatedstations.hostapdsends *deauth/disassocframeswhenneeded.Inaddition,hostapdis *responsibleforfilteringonbothauthandassocstates.
*/
if (ieee80211_vif_is_mesh(&rx->sdata->vif)) return ieee80211_rx_mesh_check(rx);
atomic_inc(&ps->num_sta_ps);
set_sta_flag(sta, WLAN_STA_PS_STA); if (!ieee80211_hw_check(&local->hw, AP_LINK_PS))
drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta);
ps_dbg(sdata, "STA %pM aid %d enters power save mode\n",
sta->sta.addr, sta->sta.aid);
ieee80211_clear_fast_xmit(sta);
for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { struct ieee80211_txq *txq = sta->sta.txq[tid]; struct txq_info *txqi = to_txq_info(txq);
spin_lock(&local->active_txq_lock[txq->ac]); if (!list_empty(&txqi->schedule_order))
list_del_init(&txqi->schedule_order);
spin_unlock(&local->active_txq_lock[txq->ac]);
if (txq_has_queue(txq))
set_bit(tid, &sta->txq_buffered_tids); else
clear_bit(tid, &sta->txq_buffered_tids);
}
}
staticvoid sta_ps_end(struct sta_info *sta)
{
ps_dbg(sta->sdata, "STA %pM aid %d exits power save mode\n",
sta->sta.addr, sta->sta.aid);
/* Don't let the same PS state be set twice */
in_ps = test_sta_flag(sta, WLAN_STA_PS_STA); if ((start && in_ps) || (!start && !in_ps)) return -EINVAL;
if (start)
sta_ps_start(sta); else
sta_ps_end(sta);
if (status->chains) {
link_sta->rx_stats.chains = status->chains; for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) { int signal = status->chain_signal[i];
staticstruct ieee80211_key *
ieee80211_rx_get_bigtk(struct ieee80211_rx_data *rx, int idx)
{ struct ieee80211_key *key = NULL; int idx2;
/* Make sure key gets set if either BIGTK key index is set so that *ieee80211_drop_unencrypted_mgmt()canproperlydropbothunprotected *BeaconframesandBeaconframesthatclaimtouseanotherBIGTKkey *index(i.e.,akeythatwedonothave).
*/
if (ieee80211_is_beacon(fc)) {
key = ieee80211_rx_get_bigtk(rx, -1);
} elseif (ieee80211_is_mgmt(fc) &&
is_multicast_ether_addr(hdr->addr1)) {
key = rcu_dereference(rx->link->default_mgmt_key);
} else { if (rx->link_sta) { for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
key = rcu_dereference(rx->link_sta->gtk[i]); if (key) break;
}
} if (!key) { for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
key = rcu_dereference(rx->link->gtk[i]); if (key) break;
}
}
} if (key)
rx->key = key; return RX_CONTINUE;
} else { /* *Thedevicedoesn'tgiveustheIVsowewon'tbe *abletolookupthekey.That'sokthough,we *don'tneedtodecrypttheframe,wejustwon't *beabletokeepstatisticsaccurate. *Exceptforkeythresholdnotifications,should *wesomehowallowthedrivertotelluswhichkey *thehardwareusedifthisflagisset?
*/ if ((status->flag & RX_FLAG_DECRYPTED) &&
(status->flag & RX_FLAG_IV_STRIPPED)) return RX_CONTINUE;
keyidx = ieee80211_get_keyid(rx->skb);
if (unlikely(keyidx < 0)) return RX_DROP_U_NO_KEY_ID;
/* check per-station GTK first, if multicast packet */ if (is_multicast_ether_addr(hdr->addr1) && rx->link_sta)
rx->key = rcu_dereference(rx->link_sta->gtk[keyidx]);
/* if not found, try default key */ if (!rx->key) { if (is_multicast_ether_addr(hdr->addr1))
rx->key = rcu_dereference(rx->link->gtk[keyidx]); if (!rx->key)
rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
switch (rx->key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104:
result = ieee80211_crypto_wep_decrypt(rx); break; case WLAN_CIPHER_SUITE_TKIP:
result = ieee80211_crypto_tkip_decrypt(rx); break; case WLAN_CIPHER_SUITE_CCMP:
result = ieee80211_crypto_ccmp_decrypt(
rx, IEEE80211_CCMP_MIC_LEN); break; case WLAN_CIPHER_SUITE_CCMP_256:
result = ieee80211_crypto_ccmp_decrypt(
rx, IEEE80211_CCMP_256_MIC_LEN); break; case WLAN_CIPHER_SUITE_AES_CMAC:
result = ieee80211_crypto_aes_cmac_decrypt(rx); break; case WLAN_CIPHER_SUITE_BIP_CMAC_256:
result = ieee80211_crypto_aes_cmac_256_decrypt(rx); break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256:
result = ieee80211_crypto_aes_gmac_decrypt(rx); break; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256:
result = ieee80211_crypto_gcmp_decrypt(rx); break; default:
result = RX_DROP_U_BAD_CIPHER;
}
/* the hdr variable is invalid after the decrypt handlers */
/* either the frame has been decrypted or will be dropped */
status->flag |= RX_FLAG_DECRYPTED;
if (unlikely(ieee80211_is_beacon(fc) && RX_RES_IS_UNUSABLE(result) &&
rx->sdata->dev))
cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
skb->data, skb->len);
return result;
}
void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache)
{ int i;
for (i = 0; i < ARRAY_SIZE(cache->entries); i++)
skb_queue_head_init(&cache->entries[i].skb_list);
}
void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache)
{ int i;
for (i = 0; i < ARRAY_SIZE(cache->entries); i++)
__skb_queue_purge(&cache->entries[i].skb_list);
}
if (frag == 0) { /* This is the first fragment of a new frame. */
entry = ieee80211_reassemble_add(cache, frag, seq,
rx->seqno_idx, &(rx->skb)); if (requires_sequential_pn(rx, fc)) { int queue = rx->security_idx;
/* Store CCMP/GCMP PN so that we can verify that the *nextfragmenthasasequentialPNvalue.
*/
entry->check_sequential_pn = true;
entry->is_protected = true;
entry->key_color = rx->key->color;
memcpy(entry->last_pn,
rx->key->u.ccmp.rx_pn[queue],
IEEE80211_CCMP_PN_LEN);
BUILD_BUG_ON(offsetof(struct ieee80211_key,
u.ccmp.rx_pn) !=
offsetof(struct ieee80211_key,
u.gcmp.rx_pn));
BUILD_BUG_ON(sizeof(rx->key->u.ccmp.rx_pn[queue]) != sizeof(rx->key->u.gcmp.rx_pn[queue]));
BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN !=
IEEE80211_GCMP_PN_LEN);
} elseif (rx->key &&
(ieee80211_has_protected(fc) ||
(status->flag & RX_FLAG_DECRYPTED))) {
entry->is_protected = true;
entry->key_color = rx->key->color;
} return RX_QUEUED;
}
/* This is a fragment for a frame that should already be pending in *fragmentcache.Addthisfragmenttotheendofthependingentry.
*/
entry = ieee80211_reassemble_find(cache, frag, seq,
rx->seqno_idx, hdr); if (!entry) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); return RX_DROP;
}
/* "The receiver shall discard MSDUs and MMPDUs whose constituent *MPDUPNvaluesarenotincrementinginstepsof1." *seeIEEEP802.11-REVmc/D5.0,12.5.3.4.4,itemd(forCCMP) *andIEEEP802.11-REVmc/D5.0,12.5.5.4.4,itemd(forGCMP)
*/ if (entry->check_sequential_pn) { int i;
u8 pn[IEEE80211_CCMP_PN_LEN], *rpn;
if (!requires_sequential_pn(rx, fc)) return RX_DROP_U_NONSEQ_PN;
/* Prevent mixed key and fragment cache attacks */ if (entry->key_color != rx->key->color) return RX_DROP_U_BAD_KEY_COLOR;
memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN); for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) {
pn[i]++; if (pn[i]) break;
}
rpn = rx->ccm_gcm.pn; if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN)) return RX_DROP_U_REPLAY;
memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN);
} elseif (entry->is_protected &&
(!rx->key ||
(!ieee80211_has_protected(fc) &&
!(status->flag & RX_FLAG_DECRYPTED)) ||
rx->key->color != entry->key_color)) { /* Drop this as a mixed key or fragment cache attack, even *ifforTKIPMichaelMICshouldprotectus,andWEPisa *lostcauseanyway.
*/ return RX_DROP_U_EXPECT_DEFRAG_PROT;
} elseif (entry->is_protected && rx->key &&
entry->key_color != rx->key->color &&
(status->flag & RX_FLAG_DECRYPTED)) { return RX_DROP_U_BAD_KEY_COLOR;
}
/* *Passthroughunencryptedframesifthehardwarehas *decryptedthemalready.
*/ if (status->flag & RX_FLAG_DECRYPTED) return0;
/* Drop unencrypted frames if key is set. */ if (unlikely(!ieee80211_has_protected(fc) &&
!ieee80211_is_any_nullfunc(fc) &&
ieee80211_is_data(fc) && rx->key)) return -EACCES;
/* *Passthroughunencryptedframesifthehardwarehas *decryptedthemalready.
*/ if (status->flag & RX_FLAG_DECRYPTED) return RX_CONTINUE;
/* drop unicast protected dual (that wasn't protected) */ if (ieee80211_is_action(fc) &&
mgmt->u.action.category == WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION) return RX_DROP_U_UNPROT_DUAL;
if (rx->sta && test_sta_flag(rx->sta, WLAN_STA_MFP)) { if (unlikely(!ieee80211_has_protected(fc) &&
ieee80211_is_unicast_robust_mgmt_frame(rx->skb))) { if (ieee80211_is_deauth(fc) ||
ieee80211_is_disassoc(fc)) { /* *Permitunprotecteddeauth/disassocframes *during4-way-HS(keyisinstalledafterHS).
*/ if (!rx->key) return RX_CONTINUE;
cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
rx->skb->data,
rx->skb->len);
} return RX_DROP_U_UNPROT_UCAST_MGMT;
} /* BIP does not use Protected field, so need to check MMIE */ if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) &&
ieee80211_get_mmie_keyidx(rx->skb) < 0)) { if (ieee80211_is_deauth(fc) ||
ieee80211_is_disassoc(fc))
cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
rx->skb->data,
rx->skb->len); return RX_DROP_U_UNPROT_MCAST_MGMT;
} if (unlikely(ieee80211_is_beacon(fc) && rx->key &&
ieee80211_get_mmie_keyidx(rx->skb) < 0)) {
cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
rx->skb->data,
rx->skb->len); return RX_DROP_U_UNPROT_BEACON;
} /* *WhenusingMFP,Actionframesarenotallowedpriorto *havingconfiguredkeys.
*/ if (unlikely(ieee80211_is_action(fc) && !rx->key &&
ieee80211_is_robust_mgmt_frame(rx->skb))) return RX_DROP_U_UNPROT_ACTION;
/* drop unicast public action frames when using MPF */ if (is_unicast_ether_addr(mgmt->da) &&
ieee80211_is_protected_dual_of_public_action(rx->skb)) return RX_DROP_U_UNPROT_UNICAST_PUB_ACTION;
}
if (rx->sta) { /* The seqno index has the same property as needed *fortherx_msdufield,i.e.itisIEEE80211_NUM_TIDS *fornon-QoS-dataframes.Hereweknowit'sadata *frame,socountMSDUs.
*/
u64_stats_update_begin(&rx->link_sta->rx_stats.syncp);
rx->link_sta->rx_stats.msdu[rx->seqno_idx]++;
u64_stats_update_end(&rx->link_sta->rx_stats.syncp);
}
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS if (skb) { /* 'align' will only take the values 0 or 2 here since all *framesarerequiredtobealignedto2-byteboundaries *whenbeingpassedtomac80211;thecodehereworksjust *aswellifthatisn'ttrue,butmac80211assumesitcan *accessfieldsas2-bytealigned(e.g.forether_addr_equal)
*/ int align;
entry = mesh_fast_tx_get(sdata, &key); if (!entry) returnfalse;
sta = rcu_dereference(entry->mpath->next_hop); if (!sta) returnfalse;
if (skb_linearize(skb)) returnfalse;
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); if (tid_tx) { if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) returnfalse;
if (!(status->rx_flags & IEEE80211_RX_AMSDU)) return RX_CONTINUE;
if (unlikely(!ieee80211_is_data(fc))) return RX_CONTINUE;
if (unlikely(!ieee80211_is_data_present(fc))) return RX_DROP;
if (unlikely(ieee80211_has_a4(hdr->frame_control))) { switch (rx->sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: if (!rx->sdata->u.vlan.sta) return RX_DROP_U_BAD_4ADDR; break; case NL80211_IFTYPE_STATION: if (!rx->sdata->u.mgd.use_4addr) return RX_DROP_U_BAD_4ADDR; break; case NL80211_IFTYPE_MESH_POINT: break; default: return RX_DROP_U_BAD_4ADDR;
}
}
if (is_multicast_ether_addr(hdr->addr1) || !rx->sta) return RX_DROP_U_BAD_AMSDU;
if (rx->key) { /* *WeshouldnotreceiveA-MSDUsonpre-HTconnections, *andHTconnectionscannotuseoldciphers.Thusdrop *them,asinthosecaseswecouldn'tevenhaveSPP *A-MSDUsorsuch.
*/ switch (rx->key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: return RX_DROP_U_BAD_AMSDU_CIPHER; default: break;
}
}
/* reset session timer */ if (tid_agg_rx->timeout)
mod_timer(&tid_agg_rx->session_timer,
TU_TO_EXP_TIME(tid_agg_rx->timeout));
spin_lock(&tid_agg_rx->reorder_lock); /* release stored frames up to start of BAR */
ieee80211_release_reorder_frames(rx->sdata, tid_agg_rx,
start_seq_num, frames);
spin_unlock(&tid_agg_rx->reorder_lock);
if (!ether_addr_equal(mgmt->da, sdata->vif.addr)) { /* Not to own unicast address */ return;
}
if (!ether_addr_equal(mgmt->sa, sdata->vif.cfg.ap_addr) ||
!ether_addr_equal(mgmt->bssid, sdata->vif.cfg.ap_addr)) { /* Not from the current AP or not associated yet. */ return;
}
if (len < 24 + 1 + sizeof(resp->u.action.u.sa_query)) { /* Too short SA Query request frame */ return;
}
skb = dev_alloc_skb(sizeof(*resp) + local->hw.extra_tx_headroom); if (skb == NULL) return;
if (!ieee80211_is_mgmt(mgmt->frame_control)) return RX_DROP;
/* drop too small action frames */ if (ieee80211_is_action(mgmt->frame_control) &&
rx->skb->len < IEEE80211_MIN_ACTION_SIZE) return RX_DROP_U_RUNT_ACTION;
if (rx->sdata->vif.type == NL80211_IFTYPE_AP &&
ieee80211_is_beacon(mgmt->frame_control) &&
!(rx->flags & IEEE80211_RX_BEACON_REPORTED)) { int sig = 0;
/* sw bss color collision detection */
ieee80211_rx_check_bss_color_collision(rx);
if (ieee80211_hw_check(&rx->local->hw, SIGNAL_DBM) &&
!(status->flag & RX_FLAG_NO_SIGNAL_VAL))
sig = status->signal;
switch (mgmt->u.action.category) { case WLAN_CATEGORY_HT: /* reject HT action frames from stations not supporting HT */ if (!rx->link_sta->pub->ht_cap.ht_supported) goto invalid;
goto queue;
}
} break; case WLAN_CATEGORY_SELF_PROTECTED: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.self_prot.action_code))) break;
switch (mgmt->u.action.u.self_prot.action_code) { case WLAN_SP_MESH_PEERING_OPEN: case WLAN_SP_MESH_PEERING_CLOSE: case WLAN_SP_MESH_PEERING_CONFIRM: if (!ieee80211_vif_is_mesh(&sdata->vif)) goto invalid; if (sdata->u.mesh.user_mpm) /* userspace handles this frame */ break; goto queue; case WLAN_SP_MGK_INFORM: case WLAN_SP_MGK_ACK: if (!ieee80211_vif_is_mesh(&sdata->vif)) goto invalid; break;
} break; case WLAN_CATEGORY_MESH_ACTION: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.mesh_action.action_code))) break;
if (!ieee80211_vif_is_mesh(&sdata->vif)) break; if (mesh_action_is_path_sel(mgmt) &&
!mesh_path_sel_is_hwmp(sdata)) break; goto queue; case WLAN_CATEGORY_S1G: if (len < offsetofend(typeof(*mgmt),
u.action.u.s1g.action_code)) break;
switch (mgmt->u.action.u.s1g.action_code) { case WLAN_S1G_TWT_SETUP: case WLAN_S1G_TWT_TEARDOWN: if (ieee80211_process_rx_twt_action(rx)) goto queue; break; default: break;
} break; case WLAN_CATEGORY_PROTECTED_EHT: if (len < offsetofend(typeof(*mgmt),
u.action.u.ttlm_req.action_code)) break;
switch (mgmt->u.action.u.ttlm_req.action_code) { case WLAN_PROTECTED_EHT_ACTION_TTLM_REQ: if (sdata->vif.type != NL80211_IFTYPE_STATION) break;
if (len < offsetofend(typeof(*mgmt),
u.action.u.ttlm_req)) goto invalid; goto queue; case WLAN_PROTECTED_EHT_ACTION_TTLM_RES: if (sdata->vif.type != NL80211_IFTYPE_STATION) break;
if (len < offsetofend(typeof(*mgmt),
u.action.u.ttlm_res)) goto invalid; goto queue; case WLAN_PROTECTED_EHT_ACTION_TTLM_TEARDOWN: if (sdata->vif.type != NL80211_IFTYPE_STATION) break;
if (len < offsetofend(typeof(*mgmt),
u.action.u.ttlm_tear_down)) goto invalid; goto queue; case WLAN_PROTECTED_EHT_ACTION_LINK_RECONFIG_RESP: if (sdata->vif.type != NL80211_IFTYPE_STATION) break;
/* The reconfiguration response action frame must *leastone'StatusDuple'entry(3octets)
*/ if (len <
offsetofend(typeof(*mgmt),
u.action.u.ml_reconf_resp) + 3) goto invalid; goto queue; case WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_RESP: if (sdata->vif.type != NL80211_IFTYPE_STATION) break;
if (len < offsetofend(typeof(*mgmt),
u.action.u.epcs) +
IEEE80211_EPCS_ENA_RESP_BODY_LEN) goto invalid; goto queue; case WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_TEARDOWN: if (sdata->vif.type != NL80211_IFTYPE_STATION) break;
switch (stype) { case cpu_to_le16(IEEE80211_STYPE_AUTH): case cpu_to_le16(IEEE80211_STYPE_BEACON): case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): /* process for all: mesh, mlme, ibss */ break; case cpu_to_le16(IEEE80211_STYPE_DEAUTH): if (is_multicast_ether_addr(mgmt->da) &&
!is_broadcast_ether_addr(mgmt->da)) return RX_DROP;
/* process only for station/IBSS */ if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_ADHOC) return RX_DROP; break; case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): case cpu_to_le16(IEEE80211_STYPE_DISASSOC): if (is_multicast_ether_addr(mgmt->da) &&
!is_broadcast_ether_addr(mgmt->da)) return RX_DROP;
/* process only for station */ if (sdata->vif.type != NL80211_IFTYPE_STATION) return RX_DROP; break; case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): /* process only for ibss and mesh */ if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
sdata->vif.type != NL80211_IFTYPE_MESH_POINT) return RX_DROP; break; default: return RX_DROP;
}
#define CALL_RXH(rxh) \ do { \
res = rxh(rx); \ if (res != RX_CONTINUE) \ goto rxh_next; \
} while (0)
/* Lock here to avoid hitting all of the data used in the RX *path(e.g.keydata,stationdata,...)concurrentlywhen *aframeisreleasedfromthereorderbufferduetotimeout *fromthetimer,potentiallyconcurrentlywithRXfromthe *driver.
*/
spin_lock_bh(&rx->local->rx_path_lock);
CALL_RXH(ieee80211_rx_h_check_more_data);
CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll);
CALL_RXH(ieee80211_rx_h_sta_process);
CALL_RXH(ieee80211_rx_h_decrypt);
CALL_RXH(ieee80211_rx_h_defragment);
CALL_RXH(ieee80211_rx_h_michael_mic_verify); /* must be after MMIC verify so header is counted in MPDU mic */
CALL_RXH(ieee80211_rx_h_amsdu);
CALL_RXH(ieee80211_rx_h_data);
/* special treatment -- needs the queue */
res = ieee80211_rx_h_ctrl(rx, frames); if (res != RX_CONTINUE) goto rxh_next;
void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
u16 ssn, u64 filtered,
u16 received_mpdus)
{ struct ieee80211_local *local; struct sta_info *sta; struct tid_ampdu_rx *tid_agg_rx; struct sk_buff_head frames; struct ieee80211_rx_data rx = { /* This is OK -- must be QoS data frame */
.security_idx = tid,
.seqno_idx = tid,
}; int i, diff;
if (WARN_ON(!pubsta || tid >= IEEE80211_NUM_TIDS)) return;
__skb_queue_head_init(&frames);
sta = container_of(pubsta, struct sta_info, sta);
local = sta->sdata->local;
WARN_ONCE(local->hw.max_rx_aggregation_subframes > 64, "RX BA marker can't support max_rx_aggregation_subframes %u > 64\n",
local->hw.max_rx_aggregation_subframes);
if (!ieee80211_rx_data_set_sta(&rx, sta, -1)) return;
rcu_read_lock();
tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); if (!tid_agg_rx) goto out;
spin_lock_bh(&tid_agg_rx->reorder_lock);
if (received_mpdus >= IEEE80211_SN_MODULO >> 1) { int release;
/* release all frames in the reorder buffer */
release = (tid_agg_rx->head_seq_num + tid_agg_rx->buf_size) %
IEEE80211_SN_MODULO;
ieee80211_release_reorder_frames(sta->sdata, tid_agg_rx,
release, &frames); /* update ssn to match received ssn */
tid_agg_rx->head_seq_num = ssn;
} else {
ieee80211_release_reorder_frames(sta->sdata, tid_agg_rx, ssn,
&frames);
}
/* handle the case that received ssn is behind the mac ssn.
* it can be tid_agg_rx->buf_size behind and still be valid */
diff = (tid_agg_rx->head_seq_num - ssn) & IEEE80211_SN_MASK; if (diff >= tid_agg_rx->buf_size) {
tid_agg_rx->reorder_buf_filtered = 0; goto release;
}
filtered = filtered >> diff;
ssn += diff;
/* update bitmap */ for (i = 0; i < tid_agg_rx->buf_size; i++) { int index = (ssn + i) % tid_agg_rx->buf_size;
switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: if (!bssid && !sdata->u.mgd.use_4addr) returnfalse; if (ieee80211_is_first_frag(hdr->seq_ctrl) &&
ieee80211_is_robust_mgmt_frame(skb) && !rx->sta) returnfalse; if (multicast) returntrue; return ieee80211_is_our_addr(sdata, hdr->addr1, &rx->link_id); case NL80211_IFTYPE_ADHOC: if (!bssid) returnfalse; if (ether_addr_equal(sdata->vif.addr, hdr->addr2) ||
ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2) ||
!is_valid_ether_addr(hdr->addr2)) returnfalse; if (ieee80211_is_beacon(hdr->frame_control)) returntrue; if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) returnfalse; if (!multicast &&
!ether_addr_equal(sdata->vif.addr, hdr->addr1)) returnfalse; if (!rx->sta) { int rate_idx; if (status->encoding != RX_ENC_LEGACY)
rate_idx = 0; /* TODO: HT/VHT rates */ else
rate_idx = status->rate_idx;
ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2,
BIT(rate_idx));
} returntrue; case NL80211_IFTYPE_OCB: if (!bssid) returnfalse; if (!ieee80211_is_data_present(hdr->frame_control)) returnfalse; if (!is_broadcast_ether_addr(bssid)) returnfalse; if (!multicast &&
!ether_addr_equal(sdata->dev->dev_addr, hdr->addr1)) returnfalse; /* reject invalid/our STA address */ if (!is_valid_ether_addr(hdr->addr2) ||
ether_addr_equal(sdata->dev->dev_addr, hdr->addr2)) returnfalse; if (!rx->sta) { int rate_idx; if (status->encoding != RX_ENC_LEGACY)
rate_idx = 0; /* TODO: HT rates */ else
rate_idx = status->rate_idx;
ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2,
BIT(rate_idx));
} returntrue; case NL80211_IFTYPE_MESH_POINT: if (ether_addr_equal(sdata->vif.addr, hdr->addr2)) returnfalse; if (multicast) returntrue; return ether_addr_equal(sdata->vif.addr, hdr->addr1); case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP: if (!bssid) return ieee80211_is_our_addr(sdata, hdr->addr1,
&rx->link_id);
if (!is_broadcast_ether_addr(bssid) &&
!ieee80211_is_our_addr(sdata, bssid, NULL)) { /* *Acceptpublicactionframesevenwhenthe *BSSIDdoesn'tmatch,thisisusedforP2P *andlocationupdates.Notethatmac80211 *itselfneverlooksattheseframes.
*/ if (!multicast &&
!ieee80211_is_our_addr(sdata, hdr->addr1,
&rx->link_id)) returnfalse; if (ieee80211_is_public_action(hdr, skb->len)) returntrue; return ieee80211_is_beacon(hdr->frame_control);
}
if (!ieee80211_has_tods(hdr->frame_control)) { /* ignore data frames to TDLS-peers */ if (ieee80211_is_data(hdr->frame_control)) returnfalse; /* ignore action frames to TDLS-peers */ if (ieee80211_is_action(hdr->frame_control) &&
!is_broadcast_ether_addr(bssid) &&
!ether_addr_equal(bssid, hdr->addr1)) returnfalse;
}
returntrue; case NL80211_IFTYPE_P2P_DEVICE: return ieee80211_is_public_action(hdr, skb->len) ||
ieee80211_is_probe_req(hdr->frame_control) ||
ieee80211_is_probe_resp(hdr->frame_control) ||
ieee80211_is_beacon(hdr->frame_control) ||
(ieee80211_is_auth(hdr->frame_control) &&
ether_addr_equal(sdata->vif.addr, hdr->addr1)); case NL80211_IFTYPE_NAN: /* Currently no frames on NAN interface are allowed */ returnfalse; default: break;
}
/* software powersave is a huge mess, avoid all of it */ if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) goto clear; if (ieee80211_hw_check(&local->hw, SUPPORTS_PS) &&
!ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS)) goto clear; break; case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP: /* parallel-rx requires this, at least with calls to *ieee80211_sta_ps_transition()
*/ if (!ieee80211_hw_check(&local->hw, AP_LINK_PS)) goto clear;
fastrx.da_offs = offsetof(struct ieee80211_hdr, addr3);
fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr2);
fastrx.expected_ds_bits = cpu_to_le16(IEEE80211_FCTL_TODS);
if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED)) goto clear;
rcu_read_lock();
key = rcu_dereference(sta->ptk[sta->ptk_idx]); if (!key)
key = rcu_dereference(sdata->default_unicast_key); if (key) { switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_TKIP: /* we don't want to deal with MMIC in fast-rx */ goto clear_rcu; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256: case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: break; default: /* We also don't want to deal with *WEPorcipherscheme.
*/ goto clear_rcu;
}
stats = &link_sta->rx_stats; if (fast_rx->uses_rss)
stats = this_cpu_ptr(link_sta->pcpu_rx_stats);
/* statistics part of ieee80211_rx_h_sta_process() */ if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
stats->last_signal = status->signal; if (!fast_rx->uses_rss)
ewma_signal_add(&link_sta->rx_stats_avg.signal,
-status->signal);
}
if (status->chains) { int i;
stats->chains = status->chains; for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) { int signal = status->chain_signal[i];
if (!(status->chains & BIT(i))) continue;
stats->chain_signal_last[i] = signal; if (!fast_rx->uses_rss)
ewma_signal_add(&link_sta->rx_stats_avg.chain_signal[i],
-signal);
}
} /* end of statistics */
/* The seqno index has the same property as needed *fortherx_msdufield,i.e.itisIEEE80211_NUM_TIDS *fornon-QoS-dataframes.Hereweknowit'sadata *frame,socountMSDUs.
*/
u64_stats_update_begin(&stats->syncp);
stats->msdu[rx->seqno_idx]++;
stats->bytes += orig_len;
u64_stats_update_end(&stats->syncp);
/* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write *toacommondatastructure;driverscanimplementthatperqueue *butwedon'thavethatinformationinmac80211
*/ if (!(status->flag & RX_FLAG_DUP_VALIDATED)) returnfalse;
/* If using encryption, we also need to have: *-PN_VALIDATED:similar,buttheimplementationistricky *-DECRYPTED:necessaryforPN_VALIDATED
*/ if (fast_rx->key &&
(status->flag & FAST_RX_CRYPT_FLAGS) != FAST_RX_CRYPT_FLAGS) returnfalse;
if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) returnfalse;
if (unlikely(ieee80211_is_frag(hdr))) returnfalse;
/* Since our interface address cannot be multicast, this *implicitlyalsorejectsmulticastframeswithoutthe *explicitcheck. * *Weshouldn'tgetany*data*framesnotaddressedtous *(APmodewillacceptmulticast*management*frames),but *puntingherewillmakeitgothroughthefullchecksin *ieee80211_accept_frame().
*/ if (!ether_addr_equal(fast_rx->vif_addr, hdr->addr1)) returnfalse;
if ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FROMDS |
IEEE80211_FCTL_TODS)) !=
fast_rx->expected_ds_bits) returnfalse;
/* assign the key to drop unencrypted frames (later) *andstriptheIV/MICifnecessary
*/ if (fast_rx->key && !(status->flag & RX_FLAG_IV_STRIPPED)) { /* GCMP header length is the same */
snap_offs += IEEE80211_CCMP_HDR_LEN;
}
if (!ieee80211_vif_is_mesh(&rx->sdata->vif) &&
!(status->rx_flags & IEEE80211_RX_AMSDU)) { if (!pskb_may_pull(skb, snap_offs + sizeof(*payload))) returnfalse;
payload = (void *)(skb->data + snap_offs);
if (!ether_addr_equal(payload->snap, fast_rx->rfc1042_hdr)) returnfalse;
/* Don't handle these here since they require special code. *AcceptAARPandIPXeventhoughtheyshouldcomewitha *bridge-tunnelheader-butifwegetthemthiswaythen *there'slittlepointindiscardingthem.
*/ if (unlikely(payload->proto == cpu_to_be16(ETH_P_TDLS) ||
payload->proto == fast_rx->control_port_protocol)) returnfalse;
}
/* after this point, don't punt to the slowpath! */
if (rx->key && !ieee80211_has_protected(hdr->frame_control)) goto drop;
if (status->rx_flags & IEEE80211_RX_AMSDU) { if (__ieee80211_rx_h_amsdu(rx, snap_offs - hdrlen) !=
RX_QUEUED) goto drop;
returntrue;
}
/* do the header conversion - first grab the addresses */
ether_addr_copy(addrs.da, skb->data + fast_rx->da_offs);
ether_addr_copy(addrs.sa, skb->data + fast_rx->sa_offs); if (ieee80211_vif_is_mesh(&rx->sdata->vif)) {
skb_pull(skb, snap_offs - 2);
put_unaligned_be16(skb->len - 2, skb->data);
} else {
skb_postpull_rcsum(skb, skb->data + snap_offs, sizeof(rfc1042_header) + 2);
/* remove the SNAP but leave the ethertype */
skb_pull(skb, snap_offs + sizeof(rfc1042_header));
} /* push the addresses in front */
memcpy(skb_push(skb, sizeof(addrs)), &addrs, sizeof(addrs));
res = ieee80211_rx_mesh_data(rx->sdata, rx->sta, rx->skb); switch (res) { case RX_QUEUED: returntrue; case RX_CONTINUE: break; default: goto drop;
}
ieee80211_rx_8023(rx, fast_rx, orig_len);
returntrue;
drop:
dev_kfree_skb(skb);
if (fast_rx->uses_rss)
stats = this_cpu_ptr(rx->link_sta->pcpu_rx_stats); else
stats = &rx->link_sta->rx_stats;
/* See if we can do fast-rx; if we have to copy we already lost, *sopuntinthatcase.Weshouldneverhavetodeliveradata *frametomultipleinterfacesanyway. * *Weskiptheieee80211_accept_frame()callanddothenecessary *checkinginsideieee80211_invoke_fast_rx().
*/ if (consume && rx->sta) { struct ieee80211_fast_rx *fast_rx;
fast_rx = rcu_dereference(rx->sta->fast_rx); if (fast_rx && ieee80211_invoke_fast_rx(rx, fast_rx)) returntrue;
}
if (!ieee80211_accept_frame(rx)) returnfalse;
if (!consume) { struct skb_shared_hwtstamps *shwt;
rx->skb = skb_copy(skb, GFP_ATOMIC); if (!rx->skb) { if (net_ratelimit())
wiphy_debug(local->hw.wiphy, "failed to copy skb for %s\n",
sdata->name); returntrue;
}
/* skb_copy() does not copy the hw timestamps, so copy it *explicitly
*/
shwt = skb_hwtstamps(rx->skb);
shwt->hwtstamp = skb_hwtstamps(skb)->hwtstamp;
/* Update the hdr pointer to the new skb for translation below */
hdr = (struct ieee80211_hdr *)rx->skb->data;
}
if (unlikely(rx->sta && rx->sta->sta.mlo) &&
is_unicast_ether_addr(hdr->addr1) &&
!ieee80211_is_probe_resp(hdr->frame_control) &&
!ieee80211_is_beacon(hdr->frame_control)) { /* translate to MLD addresses */ if (ether_addr_equal(link->conf->addr, hdr->addr1))
ether_addr_copy(hdr->addr1, rx->sdata->vif.addr); if (ether_addr_equal(link_sta->addr, hdr->addr2))
ether_addr_copy(hdr->addr2, rx->sta->addr); /* translate A3 only if it's the BSSID */ if (!ieee80211_has_tods(hdr->frame_control) &&
!ieee80211_has_fromds(hdr->frame_control)) { if (ether_addr_equal(link_sta->addr, hdr->addr3))
ether_addr_copy(hdr->addr3, rx->sta->addr); elseif (ether_addr_equal(link->conf->addr, hdr->addr3))
ether_addr_copy(hdr->addr3, rx->sdata->vif.addr);
} /* not needed for A4 since it can only carry the SA */
}
if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
if (ieee80211_is_mgmt(fc)) { /* drop frame if too short for header */ if (skb->len < ieee80211_hdrlen(fc))
err = -ENOBUFS; else
err = skb_linearize(skb);
} else {
err = !pskb_may_pull(skb, ieee80211_hdrlen(fc));
}
switch (status->encoding) { case RX_ENC_HT: /* *rate_idxisMCSindex,whichcanbe[0-76] *asdocumentedon: * *https://wireless.wiki.kernel.org/en/developers/Documentation/ieee80211/802.11n * *Anythingelsewouldbesomesortofdriveror *hardwareerror.Thedrivershouldcatchhardware *errors.
*/ if (WARN(status->rate_idx > 76, "Rate marked as an HT rate but passed " "status->rate_idx is not " "an MCS index [0-76]: %d (0x%02x)\n",
status->rate_idx,
status->rate_idx)) goto drop; break; case RX_ENC_VHT: if (WARN_ONCE(status->rate_idx > 11 ||
!status->nss ||
status->nss > 8, "Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n",
status->rate_idx, status->nss)) goto drop; break; case RX_ENC_HE: if (WARN_ONCE(status->rate_idx > 11 ||
!status->nss ||
status->nss > 8, "Rate marked as an HE rate but data is invalid: MCS: %d, NSS: %d\n",
status->rate_idx, status->nss)) goto drop; break; case RX_ENC_EHT: if (WARN_ONCE(status->rate_idx > 15 ||
!status->nss ||
status->nss > 8 ||
status->eht.gi > NL80211_RATE_INFO_EHT_GI_3_2, "Rate marked as an EHT rate but data is invalid: MCS:%d, NSS:%d, GI:%d\n",
status->rate_idx, status->nss, status->eht.gi)) goto drop; break; default:
WARN_ON_ONCE(1);
fallthrough; case RX_ENC_LEGACY: if (WARN_ON(status->rate_idx >= sband->n_bitrates)) goto drop;
rate = &sband->bitrates[status->rate_idx];
}
}
if (WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED)) goto drop;
/* This is a version of the rx handler that can be called from hard irq
* context. Post the skb on the queue and schedule the tasklet */ void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)
{ struct ieee80211_local *local = hw_to_local(hw);
¤ 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.0.229Bemerkung:
(vorverarbeitet am 2026-09-27)
¤
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.