Commit 38c6aa29 authored by Johannes Berg's avatar Johannes Berg

wifi: mac80211: fix multi-BSSID element parsing

When parsing a frame containing a multi-BSSID element, we
need to know both the transmitted and non-transmitted BSSID
so we can parse it correctly.

Unfortunately, in quite a number of cases, we got this wrong
and were passing the wrong BSSID or useless information:
 * the mgmt->bssid from a frame is only the transmitted
   BSSID if the frame is a beacon
 * passing just one of the parameters as non-NULL isn't
   useful and ignored

In those case where we need to parse for a specific BSS we
always have a BSS structure pointer, representing the BSS
we need, whether transmitted or not. Thus, pass that pointer
to the parsing function instead of the two BSSIDs.

Also fix two bugs:
 * we need to re-parse all the elements for the other BSS
   when iterating the non-transmitted BSSes in scan
 * we need to parse for the correct BSS when setting up
   the channel data in client code

Fixes: 78ac51f8 ("mac80211: support multi-bssid")
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent ab3a830d
...@@ -502,7 +502,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, ...@@ -502,7 +502,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
u.action.u.addba_req.variable); u.action.u.addba_req.variable);
if (ies_len) { if (ies_len) {
elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable, elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
ies_len, true, mgmt->bssid, NULL); ies_len, true, NULL);
if (!elems || elems->parse_error) if (!elems || elems->parse_error)
goto free; goto free;
} }
......
...@@ -1601,8 +1601,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -1601,8 +1601,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
return; return;
elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable, elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
len - baselen, false, len - baselen, false, NULL);
mgmt->bssid, NULL);
if (elems) { if (elems) {
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, elems); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, elems);
...@@ -1655,7 +1654,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ...@@ -1655,7 +1654,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
elems = ieee802_11_parse_elems( elems = ieee802_11_parse_elems(
mgmt->u.action.u.chan_switch.variable, mgmt->u.action.u.chan_switch.variable,
ies_len, true, mgmt->bssid, NULL); ies_len, true, NULL);
if (elems && !elems->parse_error) if (elems && !elems->parse_error)
ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt,
......
...@@ -2147,10 +2147,9 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, ...@@ -2147,10 +2147,9 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
* @filter: bitmap of element IDs to filter out while calculating * @filter: bitmap of element IDs to filter out while calculating
* the element CRC * the element CRC
* @crc: CRC starting value * @crc: CRC starting value
* @transmitter_bssid: transmitter BSSID to parse the multi-BSSID * @bss: the BSS to parse this as, for multi-BSSID cases this can
* element * represent a non-transmitting BSS in which case the data
* @bss_bssid: BSSID of the BSS we want to obtain elements for * for that non-transmitting BSS is returned
* when parsing the multi-BSSID element
*/ */
struct ieee80211_elems_parse_params { struct ieee80211_elems_parse_params {
const u8 *start; const u8 *start;
...@@ -2158,8 +2157,7 @@ struct ieee80211_elems_parse_params { ...@@ -2158,8 +2157,7 @@ struct ieee80211_elems_parse_params {
bool action; bool action;
u64 filter; u64 filter;
u32 crc; u32 crc;
const u8 *transmitter_bssid; struct cfg80211_bss *bss;
const u8 *bss_bssid;
}; };
struct ieee802_11_elems * struct ieee802_11_elems *
...@@ -2168,8 +2166,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params); ...@@ -2168,8 +2166,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params);
static inline struct ieee802_11_elems * static inline struct ieee802_11_elems *
ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
u64 filter, u32 crc, u64 filter, u32 crc,
const u8 *transmitter_bssid, struct cfg80211_bss *bss)
const u8 *bss_bssid)
{ {
struct ieee80211_elems_parse_params params = { struct ieee80211_elems_parse_params params = {
.start = start, .start = start,
...@@ -2177,8 +2174,7 @@ ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, ...@@ -2177,8 +2174,7 @@ ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
.action = action, .action = action,
.filter = filter, .filter = filter,
.crc = crc, .crc = crc,
.transmitter_bssid = transmitter_bssid, .bss = bss,
.bss_bssid = bss_bssid,
}; };
return ieee802_11_parse_elems_full(&params); return ieee802_11_parse_elems_full(&params);
...@@ -2186,11 +2182,9 @@ ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, ...@@ -2186,11 +2182,9 @@ ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
static inline struct ieee802_11_elems * static inline struct ieee802_11_elems *
ieee802_11_parse_elems(const u8 *start, size_t len, bool action, ieee802_11_parse_elems(const u8 *start, size_t len, bool action,
const u8 *transmitter_bssid, struct cfg80211_bss *bss)
const u8 *bss_bssid)
{ {
return ieee802_11_parse_elems_crc(start, len, action, 0, 0, return ieee802_11_parse_elems_crc(start, len, action, 0, 0, bss);
transmitter_bssid, bss_bssid);
} }
......
...@@ -1256,8 +1256,7 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, ...@@ -1256,8 +1256,7 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
if (baselen > len) if (baselen > len)
return; return;
elems = ieee802_11_parse_elems(pos, len - baselen, false, mgmt->bssid, elems = ieee802_11_parse_elems(pos, len - baselen, false, NULL);
NULL);
if (!elems) if (!elems)
return; return;
...@@ -1326,7 +1325,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, ...@@ -1326,7 +1325,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable, elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
len - baselen, len - baselen,
false, mgmt->bssid, NULL); false, NULL);
if (!elems) if (!elems)
return; return;
...@@ -1468,8 +1467,7 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata, ...@@ -1468,8 +1467,7 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
pos = mgmt->u.action.u.chan_switch.variable; pos = mgmt->u.action.u.chan_switch.variable;
baselen = offsetof(struct ieee80211_mgmt, baselen = offsetof(struct ieee80211_mgmt,
u.action.u.chan_switch.variable); u.action.u.chan_switch.variable);
elems = ieee802_11_parse_elems(pos, len - baselen, true, elems = ieee802_11_parse_elems(pos, len - baselen, true, NULL);
mgmt->bssid, NULL);
if (!elems) if (!elems)
return; return;
......
...@@ -932,7 +932,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, ...@@ -932,7 +932,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt; baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
elems = ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, elems = ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
len - baselen, false, mgmt->bssid, NULL); len - baselen, false, NULL);
if (!elems) if (!elems)
return; return;
......
...@@ -1229,8 +1229,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, ...@@ -1229,8 +1229,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
if (baselen > len) if (baselen > len)
return; return;
} }
elems = ieee802_11_parse_elems(baseaddr, len - baselen, true, elems = ieee802_11_parse_elems(baseaddr, len - baselen, true, NULL);
mgmt->bssid, NULL);
mesh_process_plink_frame(sdata, mgmt, elems, rx_status); mesh_process_plink_frame(sdata, mgmt, elems, rx_status);
kfree(elems); kfree(elems);
} }
...@@ -3536,8 +3536,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ...@@ -3536,8 +3536,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
} }
bss_elems = ieee802_11_parse_elems(bss_ies->data, bss_ies->len, bss_elems = ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
false, mgmt->bssid, false, assoc_data->bss);
assoc_data->bss->bssid);
if (!bss_elems) { if (!bss_elems) {
ret = false; ret = false;
goto out; goto out;
...@@ -3927,7 +3926,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ...@@ -3927,7 +3926,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
return; return;
elems = ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, elems = ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false,
mgmt->bssid, assoc_data->bss->bssid); assoc_data->bss);
if (!elems) if (!elems)
goto notify_driver; goto notify_driver;
...@@ -4252,8 +4251,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ...@@ -4252,8 +4251,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon && if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->bss)) { ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->bss)) {
elems = ieee802_11_parse_elems(variable, len - baselen, false, elems = ieee802_11_parse_elems(variable, len - baselen, false,
bssid, ifmgd->assoc_data->bss);
ifmgd->assoc_data->bss->bssid);
if (!elems) if (!elems)
return; return;
...@@ -4321,7 +4319,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ...@@ -4321,7 +4319,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4); ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
elems = ieee802_11_parse_elems_crc(variable, len - baselen, elems = ieee802_11_parse_elems_crc(variable, len - baselen,
false, care_about_ies, ncrc, false, care_about_ies, ncrc,
mgmt->bssid, bssid); link->u.mgd.bss);
if (!elems) if (!elems)
return; return;
ncrc = elems->crc; ncrc = elems->crc;
...@@ -4566,7 +4564,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ...@@ -4566,7 +4564,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
/* CSA IE cannot be overridden, no need for BSSID */ /* CSA IE cannot be overridden, no need for BSSID */
elems = ieee802_11_parse_elems( elems = ieee802_11_parse_elems(
mgmt->u.action.u.chan_switch.variable, mgmt->u.action.u.chan_switch.variable,
ies_len, true, mgmt->bssid, NULL); ies_len, true, NULL);
if (elems && !elems->parse_error) if (elems && !elems->parse_error)
ieee80211_sta_process_chanswitch(link, ieee80211_sta_process_chanswitch(link,
...@@ -4590,7 +4588,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ...@@ -4590,7 +4588,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
*/ */
elems = ieee802_11_parse_elems( elems = ieee802_11_parse_elems(
mgmt->u.action.u.ext_chan_switch.variable, mgmt->u.action.u.ext_chan_switch.variable,
ies_len, true, mgmt->bssid, NULL); ies_len, true, NULL);
if (elems && !elems->parse_error) { if (elems && !elems->parse_error) {
/* for the handling code pretend it was an IE */ /* for the handling code pretend it was an IE */
...@@ -5445,8 +5443,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ...@@ -5445,8 +5443,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
rcu_read_lock(); rcu_read_lock();
ies = rcu_dereference(cbss->ies); ies = rcu_dereference(cbss->ies);
elems = ieee802_11_parse_elems(ies->data, ies->len, false, elems = ieee802_11_parse_elems(ies->data, ies->len, false, cbss);
NULL, NULL);
if (!elems) { if (!elems) {
rcu_read_unlock(); rcu_read_unlock();
return -ENOMEM; return -ENOMEM;
......
...@@ -209,8 +209,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, ...@@ -209,8 +209,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
if (baselen > len) if (baselen > len)
return NULL; return NULL;
elems = ieee802_11_parse_elems(elements, len - baselen, false, elems = ieee802_11_parse_elems(elements, len - baselen, false, cbss);
mgmt->bssid, cbss->bssid);
if (!elems) if (!elems)
return NULL; return NULL;
...@@ -221,16 +220,21 @@ ieee80211_bss_info_update(struct ieee80211_local *local, ...@@ -221,16 +220,21 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bss = (void *)cbss->priv; bss = (void *)cbss->priv;
ieee80211_update_bss_from_elems(local, bss, elems, rx_status, beacon); ieee80211_update_bss_from_elems(local, bss, elems, rx_status, beacon);
kfree(elems);
list_for_each_entry(non_tx_cbss, &cbss->nontrans_list, nontrans_list) { list_for_each_entry(non_tx_cbss, &cbss->nontrans_list, nontrans_list) {
non_tx_bss = (void *)non_tx_cbss->priv; non_tx_bss = (void *)non_tx_cbss->priv;
elems = ieee802_11_parse_elems(elements, len - baselen, false,
non_tx_cbss);
if (!elems)
continue;
ieee80211_update_bss_from_elems(local, non_tx_bss, elems, ieee80211_update_bss_from_elems(local, non_tx_bss, elems,
rx_status, beacon); rx_status, beacon);
kfree(elems);
} }
kfree(elems);
return bss; return bss;
} }
......
...@@ -1720,7 +1720,7 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata, ...@@ -1720,7 +1720,7 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
} }
elems = ieee802_11_parse_elems(tf->u.chan_switch_resp.variable, elems = ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
skb->len - baselen, false, NULL, NULL); skb->len - baselen, false, NULL);
if (!elems) { if (!elems) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
...@@ -1838,7 +1838,7 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, ...@@ -1838,7 +1838,7 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
} }
elems = ieee802_11_parse_elems(tf->u.chan_switch_req.variable, elems = ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
skb->len - baselen, false, NULL, NULL); skb->len - baselen, false, NULL);
if (!elems) if (!elems)
return -ENOMEM; return -ENOMEM;
......
...@@ -1425,15 +1425,14 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, ...@@ -1425,15 +1425,14 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
struct ieee802_11_elems *elems, struct ieee802_11_elems *elems,
const u8 *transmitter_bssid, struct cfg80211_bss *bss,
const u8 *bss_bssid,
u8 *nontransmitted_profile) u8 *nontransmitted_profile)
{ {
const struct element *elem, *sub; const struct element *elem, *sub;
size_t profile_len = 0; size_t profile_len = 0;
bool found = false; bool found = false;
if (!bss_bssid || !transmitter_bssid) if (!bss || !bss->transmitted_bss)
return profile_len; return profile_len;
for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) { for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) {
...@@ -1475,11 +1474,11 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, ...@@ -1475,11 +1474,11 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
continue; continue;
} }
cfg80211_gen_new_bssid(transmitter_bssid, cfg80211_gen_new_bssid(bss->transmitted_bss->bssid,
elem->data[0], elem->data[0],
index[2], index[2],
new_bssid); new_bssid);
if (ether_addr_equal(new_bssid, bss_bssid)) { if (ether_addr_equal(new_bssid, bss->bssid)) {
found = true; found = true;
elems->bssid_index_len = index[1]; elems->bssid_index_len = index[1];
elems->bssid_index = (void *)&index[2]; elems->bssid_index = (void *)&index[2];
...@@ -1509,9 +1508,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) ...@@ -1509,9 +1508,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
if (nontransmitted_profile) { if (nontransmitted_profile) {
nontransmitted_profile_len = nontransmitted_profile_len =
ieee802_11_find_bssid_profile(params->start, params->len, ieee802_11_find_bssid_profile(params->start, params->len,
elems, elems, params->bss,
params->transmitter_bssid,
params->bss_bssid,
nontransmitted_profile); nontransmitted_profile);
non_inherit = non_inherit =
cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment