/** * DOC: BSS tree/list structure * * At the top level, the BSS list is kept in both a list in each * registered device (@bss_list) as well as an RB-tree for faster * lookup. In the RB-tree, entries can be looked up using their * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID * for other BSSes. * * Due to the possibility of hidden SSIDs, there's a second level * structure, the "hidden_list" and "hidden_beacon_bss" pointer. * The hidden_list connects all BSSes belonging to a single AP * that has a hidden SSID, and connects beacon and probe response * entries. For a probe response entry for a hidden SSID, the * hidden_beacon_bss pointer points to the BSS struct holding the * beacon's information. * * Reference counting is done for all these references except for * the hidden_list, so that a beacon BSS struct that is otherwise * not referenced has one reference for being on the bss_list and * one for each probe response entry that points to it using the * hidden_beacon_bss pointer. When a BSS struct that has such a * pointer is get/put, the refcount update is also propagated to * the referenced struct, this ensure that it cannot get removed * while somebody is using the probe response version. * * Note that the hidden_beacon_bss pointer never changes, due to * the reference counting. Therefore, no locking is needed for * it. * * Also note that the hidden_beacon_bss pointer is only relevant * if the driver uses something other than the IEs, e.g. private * data stored in the BSS struct, since the beacon IEs are * also linked into the probe response struct.
*/
/* * Limit the number of BSS entries stored in mac80211. Each one is * a bit over 4k at most, so this limits to roughly 4-5M of memory. * If somebody wants to really attack this though, they'd likely * use small beacons, and only one type of frame, limiting each of * the entries to a much smaller size (in order to generate more * entries in total, so overhead is bigger.)
*/ staticint bss_entries_limit = 1000;
module_param(bss_entries_limit, int, 0644);
MODULE_PARM_DESC(bss_entries_limit, "limit to number of scan BSS entries (per wiphy, default 1000)");
ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); if (ies && !bss->pub.hidden_beacon_bss)
kfree_rcu(ies, rcu_head);
ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); if (ies)
kfree_rcu(ies, rcu_head);
/* * This happens when the module is removed, it doesn't * really matter any more save for completeness
*/ if (!list_empty(&bss->hidden_list))
list_del(&bss->hidden_list);
if (!list_empty(&bss->hidden_list)) { /* * don't remove the beacon entry if it has * probe responses associated with it
*/ if (!bss->pub.hidden_beacon_bss) returnfalse; /* * if it's a probe response entry break its * link to the other entries in the group
*/
list_del_init(&bss->hidden_list);
}
if (!non_inherit_elem || non_inherit_elem->datalen < 2) returntrue;
/* * non inheritance element format is: * ext ID (56) | IDs list len | list | extension IDs list len | list * Both lists are optional. Both lengths are mandatory. * This means valid length is: * elem_len = 1 (extension ID) + 2 (list len fields) + list lengths
*/
id_len = non_inherit_elem->data[1]; if (non_inherit_elem->datalen < 3 + id_len) returntrue;
if (elem->id == WLAN_EID_EXTENSION) { if (!ext_id_len) returntrue;
loop_len = ext_id_len;
list = &non_inherit_elem->data[3 + id_len];
id = elem->data[0];
} else { if (!id_len) returntrue;
loop_len = id_len;
list = &non_inherit_elem->data[2];
id = elem->id;
}
for (i = 0; i < loop_len; i++) { if (list[i] == id) returnfalse;
}
/* We copy the elements one by one from the parent to the generated * elements. * If they are not inherited (included in subie or in the non * inheritance element), then we copy all occurrences the first time * we see this element type.
*/
for_each_element(parent, ie, ielen) { if (parent->id == WLAN_EID_FRAGMENT) continue;
if (parent->id == WLAN_EID_EXTENSION) { if (parent->datalen < 1) continue;
id = WLAN_EID_EXTENSION;
ext_id = parent->data[0];
match_len = 1;
} else {
id = parent->id;
match_len = 0;
}
/* Find first occurrence in subie */
sub = cfg80211_find_elem_match(id, subie, subie_len,
&ext_id, match_len, 0);
/* Copy from parent if not in subie and inherited */ if (!sub &&
cfg80211_is_element_inherited(parent, non_inherit_elem)) { if (!cfg80211_copy_elem_with_frags(parent,
ie, ielen,
&pos, new_ie,
new_ie_len)) return 0;
continue;
}
/* For ML probe response, match the MLE in the frame body with * MLD id being 'bssid_index'
*/ if (parent->id == WLAN_EID_EXTENSION && parent->datalen > 1 &&
parent->data[0] == WLAN_EID_EXT_EHT_MULTI_LINK &&
bssid_index == ieee80211_mle_get_mld_id(parent->data + 1)) { if (!cfg80211_copy_elem_with_frags(parent,
ie, ielen,
&pos, new_ie,
new_ie_len)) return 0;
/* Continue here to prevent processing the MLE in * sub-element, which AP MLD should not carry
*/ continue;
}
/* Already copied if an earlier element had the same type */ if (cfg80211_find_elem_match(id, ie, (u8 *)parent - ie,
&ext_id, match_len, 0)) continue;
/* Not inheriting, copy all similar elements from subie */ while (sub) { if (!cfg80211_copy_elem_with_frags(sub,
subie, subie_len,
&pos, new_ie,
new_ie_len)) return 0;
/* The above misses elements that are included in subie but not in the * parent, so do a pass over subie and append those. * Skip the non-tx BSSID caps and non-inheritance element.
*/
for_each_element(sub, subie, subie_len) { if (sub->id == WLAN_EID_NON_TX_BSSID_CAP) continue;
if (sub->id == WLAN_EID_FRAGMENT) continue;
if (sub->id == WLAN_EID_EXTENSION) { if (sub->datalen < 1) continue;
id = WLAN_EID_EXTENSION;
ext_id = sub->data[0];
match_len = 1;
if (ext_id == WLAN_EID_EXT_NON_INHERITANCE) continue;
} else {
id = sub->id;
match_len = 0;
}
/* Processed if one was included in the parent */ if (cfg80211_find_elem_match(id, ie, ielen,
&ext_id, match_len, 0)) continue;
if (!cfg80211_copy_elem_with_frags(sub, subie, subie_len,
&pos, new_ie, new_ie_len)) return 0;
}
/* check if nontrans_bss is in the list */
list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) { if (is_bss(bss, nontrans_bss->bssid, ssid_elem->data,
ssid_elem->datalen)) {
rcu_read_unlock(); return 0;
}
}
rcu_read_unlock();
/* * This is a bit weird - it's not on the list, but already on another * one! The only way that could happen is if there's some BSSID/SSID * shared by multiple APs in their multi-BSSID profiles, potentially * with hidden SSID mixed in ... ignore it.
*/ if (!list_empty(&nontrans_bss->nontrans_list)) return -EINVAL;
/* add to the list */
list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list); return 0;
}
list_for_each_entry(bss, &rdev->bss_list, list) { if (atomic_read(&bss->hold)) continue;
if (!list_empty(&bss->hidden_list) &&
!bss->pub.hidden_beacon_bss) continue;
if (oldest && time_before(oldest->ts, bss->ts)) continue;
oldest = bss;
}
if (WARN_ON(!oldest)) returnfalse;
/* * The callers make sure to increase rdev->bss_generation if anything * gets removed (and a new entry added), so there's no need to also do * it here.
*/
ret = __cfg80211_unlink_bss(rdev, oldest);
WARN_ON(!ret); return ret;
}
/* The length is already verified by the caller to contain bss_params */ if (length > sizeof(struct ieee80211_tbtt_info_7_8_9)) { struct ieee80211_tbtt_info_ge_11 *tbtt_info = (void *)pos;
if (length == offsetofend(struct ieee80211_tbtt_info_7_8_9,
psd_20))
entry->psd_20 = tbtt_info->psd_20;
}
/* ignore entries with invalid BSSID */ if (!is_valid_ether_addr(entry->bssid)) return -EINVAL;
/* skip non colocated APs */ if (!cfg80211_parse_bss_param(bss_params, entry)) return -EINVAL;
/* no information about the short ssid. Consider the entry valid * for now. It would later be dropped in case there are explicit * SSIDs that need to be matched
*/ if (!entry->same_ssid && !entry->short_ssid_valid) return 0;
if (entry->same_ssid) {
entry->short_ssid = s_ssid_tmp;
entry->short_ssid_valid = true;
/* * This is safe because we validate datalen in * cfg80211_parse_colocated_ap(), before calling this * function.
*/
memcpy(&entry->ssid, &ssid_elem->data, ssid_elem->datalen);
entry->ssid_len = ssid_elem->datalen;
}
type = u8_get_bits(info->tbtt_info_hdr,
IEEE80211_AP_INFO_TBTT_HDR_TYPE);
for (i = 0; i < count; i++) { switch (iter(iter_data, type, info,
pos, length)) { case RNR_ITER_CONTINUE: break; case RNR_ITER_BREAK: returntrue; case RNR_ITER_ERROR: returnfalse;
}
if (type != IEEE80211_TBTT_INFO_TYPE_TBTT) return RNR_ITER_CONTINUE;
if (!ieee80211_operating_class_to_band(info->op_class, &band)) return RNR_ITER_CONTINUE;
/* TBTT info must include bss param + BSSID + (short SSID or * same_ssid bit to be set). Ignore other options, and move to * the next AP info
*/ if (band != NL80211_BAND_6GHZ ||
!(tbtt_info_len == offsetofend(struct ieee80211_tbtt_info_7_8_9,
bss_params) ||
tbtt_info_len == sizeof(struct ieee80211_tbtt_info_7_8_9) ||
tbtt_info_len >= offsetofend(struct ieee80211_tbtt_info_ge_11,
bss_params))) return RNR_ITER_CONTINUE;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) return RNR_ITER_ERROR;
for (i = 0; i < request->n_ssids; i++) { /* wildcard ssid in the scan request */ if (!request->ssids[i].ssid_len) { if (ap->multi_bss && !ap->transmitted_bssid) continue;
/* In case the scan request specified a specific BSSID * and the BSS is found and operating on 6GHz band then * add this AP to the collocated APs list. * This is relevant for ML probe requests when the lower * band APs have not been discovered.
*/ if (is_broadcast_ether_addr(rdev_req->req.bssid) ||
!ether_addr_equal(rdev_req->req.bssid, res->bssid) ||
res->channel->band != NL80211_BAND_6GHZ) continue;
ret = cfg80211_calc_short_ssid(ies, &ssid_elem,
&s_ssid_tmp); if (ret) continue;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) continue;
*request = *rdev_req;
request->req.n_channels = 0;
request->req.n_6ghz_params = 0; if (rdev_req->req.n_ssids) { /* * Add the ssids from the parent scan request to the new * scan request, so the driver would be able to use them * in its probe requests to discover hidden APs on PSC * channels.
*/
request->req.ssids = (void *)request + offs_ssids;
memcpy(request->req.ssids, rdev_req->req.ssids, sizeof(*request->req.ssids) * request->req.n_ssids);
}
request->req.scan_6ghz_params = (void *)request + offs_6ghz_params;
if (rdev_req->req.ie_len) { void *ie = (void *)request + offs_ies;
/* * PSC channels should not be scanned in case of direct scan with 1 SSID * and at least one of the reported co-located APs with same SSID * indicating that all APs in the same ESS are co-located
*/ if (count &&
request->req.n_ssids == 1 &&
request->req.ssids[0].ssid_len) {
list_for_each_entry(ap, &coloc_ap_list, list) { if (ap->colocated_ess &&
cfg80211_find_ssid_match(ap, &request->req)) {
need_scan_psc = false; break;
}
}
}
/* * add to the scan request the channels that need to be scanned * regardless of the collocated APs (PSC channels or all channels * in case that NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set)
*/ for (i = 0; i < rdev_req->req.n_channels; i++) { if (rdev_req->req.channels[i]->band == NL80211_BAND_6GHZ &&
((need_scan_psc &&
cfg80211_channel_is_psc(rdev_req->req.channels[i])) ||
!(rdev_req->req.flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ))) {
cfg80211_scan_req_add_chan(&request->req,
rdev_req->req.channels[i], false);
}
}
if (!(rdev_req->req.flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ)) goto skip;
/* * If a PSC channel is added to the scan and 'need_scan_psc' is * set to false, then all the APs that the scan logic is * interested with on the channel are collocated and thus there * is no need to perform the initial PSC channel listen.
*/ if (cfg80211_channel_is_psc(chan) && !need_scan_psc)
scan_6ghz_params->psc_no_listen = true;
if (request->req.n_channels) { struct cfg80211_scan_request_int *old = rdev->int_scan_req;
rdev->int_scan_req = request;
/* * If this scan follows a previous scan, save the scan start * info from the first part of the scan
*/ if (!first_part && !WARN_ON(!old))
rdev->int_scan_req->info = old->info;
for (i = idx = 0; i < rdev_req->req.n_channels; i++) { if (rdev_req->req.channels[i]->band != NL80211_BAND_6GHZ)
request->req.channels[idx++] =
rdev_req->req.channels[i];
}
/* * This must be before sending the other events! * Otherwise, wpa_supplicant gets completely confused with * wext events.
*/ if (wdev->netdev)
cfg80211_sme_scan_done(wdev->netdev);
if (!request->info.aborted &&
request->req.flags & NL80211_SCAN_FLAG_FLUSH) { /* flush entries from previous scans */
spin_lock_bh(&rdev->bss_lock);
__cfg80211_bss_expire(rdev, request->req.scan_start);
spin_unlock_bh(&rdev->bss_lock);
}
/* * In case the scan is split, the scan_start_tsf and tsf_bssid should * be of the first part. In such a case old_info.scan_start_tsf should * be non zero.
*/ if (request->scan_6ghz && old_info.scan_start_tsf) {
intreq->info.scan_start_tsf = old_info.scan_start_tsf;
memcpy(intreq->info.tsf_bssid, old_info.tsf_bssid, sizeof(intreq->info.tsf_bssid));
}
/* * Determines if a scheduled scan request can be handled. When a legacy * scheduled scan is running no other scheduled scan is allowed regardless * whether the request is for legacy or multi-support scan. When a multi-support * scheduled scan is running a request for legacy scan is not allowed. In this * case a request for multi-support scan can be handled if resources are * available, ie. struct wiphy::max_sched_scan_reqs limit is not yet reached.
*/ int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev, bool want_multi)
{ struct cfg80211_sched_scan_request *pos; int i = 0;
list_for_each_entry(pos, &rdev->sched_scan_req_list, list) { /* request id zero means legacy in progress */ if (!i && !pos->reqid) return -EINPROGRESS;
i++;
}
if (i) { /* no legacy allowed when multi request(s) are active */ if (!want_multi) return -EINPROGRESS;
/* resource limit reached */ if (i == rdev->wiphy.max_sched_scan_reqs) return -ENOSPC;
} return 0;
}
/* * Note that with "hide_ssid", the function returns a match if * the already-present BSS ("b") is a hidden SSID beacon for * the new BSS ("a").
*/
/* sort missing IE before (left of) present IE */ if (!ie1) return -1; if (!ie2) return 1;
switch (mode) { case BSS_CMP_HIDE_ZLEN: /* * In ZLEN mode we assume the BSS entry we're * looking for has a zero-length SSID. So if * the one we're looking at right now has that, * return 0. Otherwise, return the difference * in length, but since we're looking for the * 0-length it's really equivalent to returning * the length of the one we're looking at. * * No content comparison is needed as we assume * the content length is zero.
*/ return ie2[1]; case BSS_CMP_REGULAR: default: /* sort by length first, then by contents */ if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; return memcmp(ie1 + 2, ie2 + 2, ie1[1]); case BSS_CMP_HIDE_NUL: if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; /* this is equivalent to memcmp(zeroes, ie2 + 2, len) */ for (i = 0; i < ie2[1]; i++) if (ie2[i + 2]) return -1; return 0;
}
}
if (bss_type == IEEE80211_BSS_TYPE_ANY) return ret;
if (band == NL80211_BAND_60GHZ) {
mask = WLAN_CAPABILITY_DMG_TYPE_MASK; switch (bss_type) { case IEEE80211_BSS_TYPE_ESS:
val = WLAN_CAPABILITY_DMG_TYPE_AP; break; case IEEE80211_BSS_TYPE_PBSS:
val = WLAN_CAPABILITY_DMG_TYPE_PBSS; break; case IEEE80211_BSS_TYPE_IBSS:
val = WLAN_CAPABILITY_DMG_TYPE_IBSS; break; default: returnfalse;
}
} else {
mask = WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS; switch (bss_type) { case IEEE80211_BSS_TYPE_ESS:
val = WLAN_CAPABILITY_ESS; break; case IEEE80211_BSS_TYPE_IBSS:
val = WLAN_CAPABILITY_IBSS; break; case IEEE80211_BSS_TYPE_MBSS:
val = 0; break; default: returnfalse;
}
}
ret = ((capability & mask) == val); return ret;
}
/* Returned bss is reference counted and must be cleaned up appropriately. */ struct cfg80211_bss *__cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, const u8 *ssid, size_t ssid_len, enum ieee80211_bss_type bss_type, enum ieee80211_privacy privacy,
u32 use_for)
{ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_internal_bss *bss, *res = NULL; unsignedlong now = jiffies; int bss_privacy;
ies = rcu_access_pointer(new->pub.beacon_ies); if (WARN_ON(!ies)) returnfalse;
ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); if (!ie) { /* nothing to do */ returntrue;
}
ssidlen = ie[1]; for (i = 0; i < ssidlen; i++)
fold |= ie[2 + i];
if (fold) { /* not a hidden SSID */ returntrue;
}
/* This is the bad part ... */
list_for_each_entry(bss, &rdev->bss_list, list) { /* * we're iterating all the entries anyway, so take the * opportunity to validate the list length accounting
*/
n_entries++;
if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid)) continue; if (bss->pub.channel != new->pub.channel) continue; if (rcu_access_pointer(bss->pub.beacon_ies)) continue;
ies = rcu_access_pointer(bss->pub.ies); if (!ies) continue;
ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); if (!ie) continue; if (ssidlen && ie[1] != ssidlen) continue; if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss)) continue; if (WARN_ON_ONCE(!list_empty(&bss->hidden_list)))
list_del(&bss->hidden_list); /* combine them */
list_add(&bss->hidden_list, &new->hidden_list);
bss->pub.hidden_beacon_bss = &new->pub;
new->refcount += bss->refcount;
rcu_assign_pointer(bss->pub.beacon_ies,
new->pub.beacon_ies);
}
if (rcu_access_pointer(new->pub.beacon_ies)) { conststruct cfg80211_bss_ies *old;
if (known->pub.hidden_beacon_bss &&
!list_empty(&known->hidden_list)) { conststruct cfg80211_bss_ies *f;
/* The known BSS struct is one of the probe * response members of a group, but we're * receiving a beacon (beacon_ies in the new * bss is used). This can only mean that the * AP changed its beacon from not having an * SSID to showing it, which is confusing so * drop this information.
*/
f = rcu_access_pointer(new->pub.beacon_ies); if (!new->pub.hidden_beacon_bss)
kfree_rcu((struct cfg80211_bss_ies *)f, rcu_head); returnfalse;
}
/* Override IEs if they were from a beacon before */ if (old == rcu_access_pointer(known->pub.ies))
rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
/* don't update the signal if beacon was heard on * adjacent channel.
*/ if (signal_valid)
known->pub.signal = new->pub.signal;
known->pub.capability = new->pub.capability;
known->parent_tsf = new->parent_tsf;
known->pub.chains = new->pub.chains;
memcpy(known->pub.chain_signal, new->pub.chain_signal,
IEEE80211_MAX_CHAINS);
ether_addr_copy(known->parent_bssid, new->parent_bssid);
known->pub.max_bssid_indicator = new->pub.max_bssid_indicator;
known->pub.bssid_index = new->pub.bssid_index;
known->pub.use_for &= new->pub.use_for;
known->pub.cannot_use_reasons = new->pub.cannot_use_reasons;
known->bss_source = new->bss_source;
returntrue;
}
/* Returned bss is reference counted and must be cleaned up appropriately. */ staticstruct cfg80211_internal_bss *
__cfg80211_bss_update(struct cfg80211_registered_device *rdev, struct cfg80211_internal_bss *tmp, bool signal_valid, unsignedlong ts)
{ struct cfg80211_internal_bss *found = NULL; struct cfg80211_bss_ies *ies;
if (WARN_ON(!tmp->pub.channel)) goto free_ies;
tmp->ts = ts;
if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) goto free_ies;
found = rb_find_bss(rdev, tmp, BSS_CMP_REGULAR);
if (found) { if (!cfg80211_update_known_bss(rdev, found, tmp, signal_valid)) return NULL;
} else { struct cfg80211_internal_bss *new; struct cfg80211_internal_bss *hidden;
/* * create a copy -- the "res" variable that is passed in * is allocated on the stack since it's not needed in the * more common case of an update
*/ new = kzalloc(sizeof(*new) + rdev->wiphy.bss_priv_size,
GFP_ATOMIC); if (!new) goto free_ies;
memcpy(new, tmp, sizeof(*new));
new->refcount = 1;
INIT_LIST_HEAD(&new->hidden_list);
INIT_LIST_HEAD(&new->pub.nontrans_list); /* we'll set this later if it was non-NULL */
new->pub.transmitted_bss = NULL;
if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN); if (!hidden)
hidden = rb_find_bss(rdev, tmp,
BSS_CMP_HIDE_NUL); if (hidden) {
new->pub.hidden_beacon_bss = &hidden->pub;
list_add(&new->hidden_list,
&hidden->hidden_list);
hidden->refcount++;
ies = (void *)rcu_access_pointer(new->pub.beacon_ies);
rcu_assign_pointer(new->pub.beacon_ies,
hidden->pub.beacon_ies); if (ies)
kfree_rcu(ies, rcu_head);
}
} else { /* * Ok so we found a beacon, and don't have an entry. If * it's a beacon with hidden SSID, we might be in for an * expensive search for any probe responses that should * be grouped with this beacon for updates ...
*/ if (!cfg80211_combine_bsses(rdev, new)) {
bss_ref_put(rdev, new); return NULL;
}
}
/* This must be before the call to bss_ref_get */ if (tmp->pub.transmitted_bss) {
new->pub.transmitted_bss = tmp->pub.transmitted_bss;
bss_ref_get(rdev, bss_from_pub(tmp->pub.transmitted_bss));
}
cfg80211_insert_bss(rdev, new);
found = new;
}
rdev->bss_generation++;
bss_ref_get(rdev, found);
return found;
free_ies:
ies = (void *)rcu_access_pointer(tmp->pub.beacon_ies); if (ies)
kfree_rcu(ies, rcu_head);
ies = (void *)rcu_access_pointer(tmp->pub.proberesp_ies); if (ies)
kfree_rcu(ies, rcu_head);
/* * Update RX channel information based on the available frame payload * information. This is mainly for the 2.4 GHz band where frames can be received * from neighboring channels and the Beacon frames use the DSSS Parameter Set * element to indicate the current (transmitting) channel, but this might also * be needed on other bands if RX frequency does not match with the actual * operating channel of a BSS, or if the AP reports a different primary channel.
*/ staticstruct ieee80211_channel *
cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, struct ieee80211_channel *channel)
{
u32 freq; int channel_number; struct ieee80211_channel *alt_channel;
/* * Frame info (beacon/prob res) is the same as received channel, * no need for further processing.
*/ if (freq == ieee80211_channel_to_khz(channel)) return channel;
alt_channel = ieee80211_get_channel_khz(wiphy, freq); if (!alt_channel) { if (channel->band == NL80211_BAND_2GHZ ||
channel->band == NL80211_BAND_6GHZ) { /* * Better not allow unexpected channels when that could * be going beyond the 1-11 range (e.g., discovering * BSS on channel 12 when radio is configured for * channel 11) or beyond the 6 GHz channel range.
*/ return NULL;
}
/* No match for the payload channel number - ignore it */ return channel;
}
/* * Use the channel determined through the payload channel number * instead of the RX channel reported by the driver.
*/ if (alt_channel->flags & IEEE80211_CHAN_DISABLED) return NULL; return alt_channel;
}
switch (u8_get_bits(he_6ghz_oper->control,
IEEE80211_HE_6GHZ_OPER_CTRL_REG_INFO)) { case IEEE80211_6GHZ_CTRL_REG_LPI_AP: case IEEE80211_6GHZ_CTRL_REG_INDOOR_LPI_AP: return IEEE80211_REG_LPI_AP; case IEEE80211_6GHZ_CTRL_REG_SP_AP: case IEEE80211_6GHZ_CTRL_REG_INDOOR_SP_AP: case IEEE80211_6GHZ_CTRL_REG_INDOOR_SP_AP_OLD: return IEEE80211_REG_SP_AP; case IEEE80211_6GHZ_CTRL_REG_VLP_AP: return IEEE80211_REG_VLP_AP; default: return IEEE80211_REG_UNSET_AP;
}
}
/* * If we do not know here whether the IEs are from a Beacon or Probe * Response frame, we need to pick one of the options and only use it * with the driver that does not provide the full Beacon/Probe Response * frame. Use Beacon frame pointer to avoid indicating that this should * override the IEs pointer should we have received an earlier * indication of Probe Response data.
*/
ies = kzalloc(sizeof(*ies) + data->ielen, gfp); if (!ies) return NULL;
ies->len = data->ielen;
ies->tsf = data->tsf;
ies->from_beacon = false;
memcpy(ies->data, data->ie, data->ielen);
switch (data->ftype) { case CFG80211_BSS_FTYPE_BEACON: case CFG80211_BSS_FTYPE_S1G_BEACON:
ies->from_beacon = true;
fallthrough; case CFG80211_BSS_FTYPE_UNKNOWN:
rcu_assign_pointer(tmp.pub.beacon_ies, ies); break; case CFG80211_BSS_FTYPE_PRESP:
rcu_assign_pointer(tmp.pub.proberesp_ies, ies); break;
}
rcu_assign_pointer(tmp.pub.ies, ies);
signal_valid = drv_data->chan == channel;
spin_lock_bh(&rdev->bss_lock);
res = __cfg80211_bss_update(rdev, &tmp, signal_valid, ts); if (!res) goto drop;
if (data->bss_source == BSS_SOURCE_MBSSID) { /* this is a nontransmitting bss, we need to add it to * transmitting bss' list if it is not there
*/ if (cfg80211_add_nontrans_list(data->source_bss, &res->pub)) { if (__cfg80211_unlink_bss(rdev, res)) {
rdev->bss_generation++;
res = NULL;
}
}
if (!res) goto drop;
}
spin_unlock_bh(&rdev->bss_lock);
trace_cfg80211_return_bss(&res->pub); /* __cfg80211_bss_update gives us a referenced result */ return &res->pub;
/* * If it is not the last subelement in current MBSSID IE or there isn't * a next MBSSID IE - profile is complete.
*/ if ((sub_elem->data + sub_elem->datalen < mbssid_end - 1) ||
!next_mbssid) return NULL;
/* For any length error, just return NULL */
if (next_mbssid->datalen < 4) return NULL;
next_sub = (void *)&next_mbssid->data[1];
if (next_mbssid->data + next_mbssid->datalen <
next_sub->data + next_sub->datalen) return NULL;
if (next_sub->id != 0 || next_sub->datalen < 2) return NULL;
/* * Check if the first element in the next sub element is a start * of a new profile
*/ return next_sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP ?
NULL : next_mbssid;
}
size_t cfg80211_merge_profile(const u8 *ie, size_t ielen, conststruct element *mbssid_elem, conststruct element *sub_elem,
u8 *merged_ie, size_t max_copy_len)
{
size_t copied_len = sub_elem->datalen; conststruct element *next_mbssid;
if (!source_bss) return; if (!cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID,
tx_data->ie, tx_data->ielen)) return; if (!wiphy->support_mbssid) return; if (wiphy->support_only_he_mbssid &&
!cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
tx_data->ie, tx_data->ielen)) return;
new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp); if (!new_ie) return;
profile = kmalloc(tx_data->ielen, gfp); if (!profile) goto out;
if (sub->id != 0 || sub->datalen < 4) { /* not a valid BSS profile */ continue;
}
if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP ||
sub->data[1] != 2) { /* The first element within the Nontransmitted * BSSID Profile is not the Nontransmitted * BSSID Capability element.
*/ continue;
}
/* found a Nontransmitted BSSID Profile */
mbssid_index_ie = cfg80211_find_ie
(WLAN_EID_MULTI_BSSID_IDX,
profile, profile_len); if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
mbssid_index_ie[2] == 0 ||
mbssid_index_ie[2] > 46 ||
mbssid_index_ie[2] >= (1 << elem->data[0])) { /* No valid Multiple BSSID-Index element */ continue;
}
if (seen_indices & BIT_ULL(mbssid_index_ie[2])) /* We don't support legacy split of a profile */
net_dbg_ratelimited("Partial info for BSSID index %d\n",
mbssid_index_ie[2]);
/* * We only generate the RNR to permit ML lookups. For that we do not * need an entry for the corresponding transmitting BSS, lets just skip * it even though it would be easy to add.
*/ if (!same_mld) return NULL;
/* We could use tx_data->ies if we change cfg80211_calc_short_ssid */
rcu_read_lock();
ies = rcu_dereference(source_bss->ies);
/* The AP is not providing us with anything to work with. So * make up a somewhat reasonable operating class, but don't * bother with it too much as no one will ever use the * information.
*/
cfg80211_chandef_create(&chandef, source_bss->channel,
NL80211_CHAN_NO_HT);
if (!ieee80211_chandef_to_operating_class(&chandef,
&ap_info.op_class)) goto out_unlock;
}
/* Just set TBTT offset and PSD 20 to invalid/unknown */
tbtt_info.tbtt_offset = 255;
tbtt_info.psd_20 = IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED;
memcpy(tbtt_info.bssid, source_bss->bssid, ETH_ALEN); if (cfg80211_calc_short_ssid(ies, &elem, &short_ssid)) goto out_unlock;
/* Must be present when transmitted by an AP (in a probe response) */ if (!(control & IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT) ||
!(control & IEEE80211_MLC_BASIC_PRES_LINK_ID) ||
!(control & IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP)) return;
/* * The MLD ID of the reporting AP is always zero. It is set if the AP * is part of an MBSSID set and will be non-zero for ML Elements * relating to a nontransmitted BSS (matching the Multi-BSSID Index, * Draft P802.11be_D3.2, 35.3.4.2)
*/
mld_id = ieee80211_mle_get_mld_id(elem->data + 1);
/* Fully defrag the ML element for sta information/profile iteration */
mle = cfg80211_defrag_mle(elem, tx_data->ie, tx_data->ielen, gfp); if (!mle) return;
/* No point in doing anything if there is no per-STA profile */ if (!mle->sta_prof[0]) goto out;
new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp); if (!new_ie) goto out;
/* Find in RNR to look up channel information */
use_for = cfg80211_rnr_info_for_mld_ap(tx_data->ie,
tx_data->ielen,
mld_id, link_id,
&ap_info,
¶m_ch_count,
&non_tx); if (!use_for) continue;
/* * As of 802.11be_D5.0, the specification does not give us any * way of discovering both the MaxBSSID and the Multiple-BSSID * Index. It does seem like the Multiple-BSSID Index element * may be provided, but section 9.4.2.45 explicitly forbids * including a Multiple-BSSID Element (in this case without any * subelements). * Without both pieces of information we cannot calculate the * reference BSSID, so simply ignore the BSS.
*/ if (non_tx) continue;
/* We could sanity check the BSSID is included */
if (!ieee80211_operating_class_to_band(ap_info->op_class,
&band)) continue;
/* Skip if RNR element specifies an unsupported channel */ if (!data.channel) continue;
/* Skip if BSS entry generated from MBSSID or DIRECT source * frame data available already.
*/
bss = cfg80211_get_bss(wiphy, data.channel, data.bssid, ssid,
ssid_len, IEEE80211_BSS_TYPE_ANY,
IEEE80211_PRIVACY_ANY); if (bss) { struct cfg80211_internal_bss *ibss = bss_from_pub(bss);
/* Generate new elements */
memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
data.ie = new_ie;
data.ielen = cfg80211_gen_new_ie(tx_data->ie, tx_data->ielen,
profile, profile_len,
new_ie,
IEEE80211_MAX_DATA_LEN); if (!data.ielen) continue;
/* The generated elements do not contain: * - Basic ML element * - A TBTT entry in the RNR for the transmitting AP * * This information is needed both internally and in userspace * as such, we should append it here.
*/ if (data.ielen + 3 + sizeof(*ml_elem) + ml_common_len >
IEEE80211_MAX_DATA_LEN) continue;
/* Copy the Basic Multi-Link element including the common * information, and then fix up the link ID and BSS param * change count. * Note that the ML element length has been verified and we * also checked that it contains the link ID.
*/
new_ie[data.ielen++] = WLAN_EID_EXTENSION;
new_ie[data.ielen++] = 1 + sizeof(*ml_elem) + ml_common_len;
new_ie[data.ielen++] = WLAN_EID_EXT_EHT_MULTI_LINK;
memcpy(new_ie + data.ielen, ml_elem, sizeof(*ml_elem) + ml_common_len);
/* * Some APs use CSA also for bandwidth changes, i.e., without actually * changing the control channel, so no need to update in such a case.
*/ if (cbss->pub.channel == chan) goto done;
/* use transmitting bss */ if (cbss->pub.transmitted_bss)
cbss = bss_from_pub(cbss->pub.transmitted_bss);
cbss->pub.channel = chan;
list_for_each_entry(bss, &rdev->bss_list, list) { if (!cfg80211_bss_type_match(bss->pub.capability,
bss->pub.channel->band,
wdev->conn_bss_type)) continue;
if (bss == cbss) continue;
if (!cmp_bss(&bss->pub, &cbss->pub, BSS_CMP_REGULAR)) { new = bss; break;
}
}
if (new) { /* to save time, update IEs for transmitting bss only */
cfg80211_update_known_bss(rdev, cbss, new, false);
new->pub.proberesp_ies = NULL;
new->pub.beacon_ies = NULL;
if (rdev->scan_req || rdev->scan_msg) return -EBUSY;
wiphy = &rdev->wiphy;
/* Determine number of channels, needed to allocate creq */ if (wreq && wreq->num_channels) { /* Passed from userspace so should be checked */ if (unlikely(wreq->num_channels > IW_MAX_FREQUENCIES)) return -EINVAL;
n_channels = wreq->num_channels;
} else {
n_channels = ieee80211_get_num_supported_channels(wiphy);
}
/* If we have a wireless request structure and the * wireless request specifies frequencies, then search * for the matching hardware channel.
*/ if (wreq && wreq->num_channels) { int k; int wiphy_freq = wiphy->bands[band]->channels[j].center_freq; for (k = 0; k < wreq->num_channels; k++) { struct iw_freq *freq =
&wreq->channel_list[k]; int wext_freq =
cfg80211_wext_freq(freq);
if (wext_freq == wiphy_freq) goto wext_freq_found;
} goto wext_freq_not_found;
}
/* * If needed, fragment the IEs buffer (at IE boundaries) into short * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
*/
pos = ies->data;
end = pos + ies->len;
while (end - pos > IW_GENERIC_IE_MAX) {
next = pos + 2 + pos[1]; while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
next = next + 2 + next[1];
if (rdev->scan_req || rdev->scan_msg) return -EAGAIN;
res = ieee80211_scan_results(rdev, info, extra, data->length);
data->length = 0; if (res >= 0) {
data->length = res;
res = 0;
}
return res;
} #endif
Messung V0.5 in Prozent
¤ 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.51Bemerkung:
(vorverarbeitet am 2026-04-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.