/** * 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;
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.