Skip to content

Commit 45ebac4

Browse files
ilanpeer2jmberg-intel
authored andcommitted
wifi: mac80211: Parse station profile from association response
When processing an association response frame for a Multi-Link connection, extract the per station profile for each additional link, and use it for parsing the link elements. As the Multi-Link element might be fragmented, add support for reassembling a fragmented element. To simplify memory management logic, extend 'struct ieee802_11_elems' to hold a scratch buffer, which is used for the defragmentation. Once an element is reconstructed in the scratch area, point the corresponding element pointer to it. Currently only defragmentation of Multi-Link element and the contained per-STA profile subelement is supported. Signed-off-by: Ilan Peer <ilan.peer@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1 parent fb99c7d commit 45ebac4

File tree

4 files changed

+188
-6
lines changed

4 files changed

+188
-6
lines changed

include/linux/ieee80211.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4666,6 +4666,7 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len)
46664666

46674667
enum ieee80211_mle_subelems {
46684668
IEEE80211_MLE_SUBELEM_PER_STA_PROFILE = 0,
4669+
IEEE80211_MLE_SUBELEM_FRAGMENT = 254,
46694670
};
46704671

46714672
#define IEEE80211_MLE_STA_CONTROL_LINK_ID 0x000f

net/mac80211/ieee80211_i.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1707,8 +1707,27 @@ struct ieee802_11_elems {
17071707
u8 tx_pwr_env_num;
17081708
u8 eht_cap_len;
17091709

1710+
/* mult-link element can be de-fragmented and thus u8 is not sufficient */
1711+
size_t multi_link_len;
1712+
1713+
/*
1714+
* store the per station profile pointer and length in case that the
1715+
* parsing also handled Multi-Link element parsing for a specific link
1716+
* ID.
1717+
*/
1718+
struct ieee80211_mle_per_sta_profile *prof;
1719+
size_t sta_prof_len;
1720+
17101721
/* whether a parse error occurred while retrieving these elements */
17111722
bool parse_error;
1723+
1724+
/*
1725+
* scratch buffer that can be used for various element parsing related
1726+
* tasks, e.g., element de-fragmentation etc.
1727+
*/
1728+
size_t scratch_len;
1729+
u8 *scratch_pos;
1730+
u8 scratch[];
17121731
};
17131732

17141733
static inline struct ieee80211_local *hw_to_local(
@@ -2197,9 +2216,13 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
21972216
* represent a non-transmitting BSS in which case the data
21982217
* for that non-transmitting BSS is returned
21992218
* @link_id: the link ID to parse elements for, if a STA profile
2200-
* is present in the multi-link element, or -1 to ignore
2219+
* is present in the multi-link element, or -1 to ignore;
2220+
* note that the code currently assumes parsing an association
2221+
* (or re-association) response frame if this is given
22012222
* @from_ap: frame is received from an AP (currently used only
22022223
* for EHT capabilities parsing)
2224+
* @scratch_len: if non zero, specifies the requested length of the scratch
2225+
* buffer; otherwise, 'len' is used.
22032226
*/
22042227
struct ieee80211_elems_parse_params {
22052228
const u8 *start;
@@ -2210,6 +2233,7 @@ struct ieee80211_elems_parse_params {
22102233
struct cfg80211_bss *bss;
22112234
int link_id;
22122235
bool from_ap;
2236+
size_t scratch_len;
22132237
};
22142238

22152239
struct ieee802_11_elems *

net/mac80211/mlme.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3923,11 +3923,12 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
39233923
struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
39243924
struct ieee80211_bss_conf *bss_conf = link->conf;
39253925
struct ieee80211_local *local = sdata->local;
3926+
unsigned int link_id = link->link_id;
39263927
struct ieee80211_elems_parse_params parse_params = {
39273928
.start = elem_start,
39283929
.len = elem_len,
39293930
.bss = cbss,
3930-
.link_id = link == &sdata->deflink ? -1 : link->link_id,
3931+
.link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id,
39313932
.from_ap = true,
39323933
};
39333934
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
@@ -3942,8 +3943,18 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
39423943
if (!elems)
39433944
return false;
39443945

3945-
/* FIXME: use from STA profile element after parsing that */
3946-
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
3946+
if (link_id == assoc_data->assoc_link_id) {
3947+
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
3948+
} else if (!elems->prof) {
3949+
ret = false;
3950+
goto out;
3951+
} else {
3952+
const u8 *ptr = elems->prof->variable +
3953+
elems->prof->sta_info_len - 1;
3954+
3955+
/* FIXME: need to also handle the status code */
3956+
capab_info = get_unaligned_le16(ptr);
3957+
}
39473958

39483959
if (!is_s1g && !elems->supp_rates) {
39493960
sdata_info(sdata, "no SuppRates element in AssocResp\n");

net/mac80211/util.c

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,8 +1026,10 @@ ieee80211_parse_extension_element(u32 *crc,
10261026
elems->eht_operation = data;
10271027
break;
10281028
case WLAN_EID_EXT_EHT_MULTI_LINK:
1029-
if (ieee80211_mle_size_ok(data, len))
1029+
if (ieee80211_mle_size_ok(data, len)) {
10301030
elems->multi_link = (void *)data;
1031+
elems->multi_link_len = len;
1032+
}
10311033
break;
10321034
}
10331035
}
@@ -1497,19 +1499,161 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
14971499
return found ? profile_len : 0;
14981500
}
14991501

1502+
static void ieee80211_defragment_element(struct ieee802_11_elems *elems,
1503+
void **elem_ptr, size_t *len,
1504+
size_t total_len, u8 frag_id)
1505+
{
1506+
u8 *data = *elem_ptr, *pos, *start;
1507+
const struct element *elem;
1508+
1509+
/*
1510+
* Since 'data' points to the data of the element, not the element
1511+
* itself, allow 254 in case it was an extended element where the
1512+
* extended ID isn't part of the data we see here and thus not part of
1513+
* 'len' either.
1514+
*/
1515+
if (!data || (*len != 254 && *len != 255))
1516+
return;
1517+
1518+
start = elems->scratch_pos;
1519+
1520+
if (WARN_ON(*len > (elems->scratch + elems->scratch_len -
1521+
elems->scratch_pos)))
1522+
return;
1523+
1524+
memcpy(elems->scratch_pos, data, *len);
1525+
elems->scratch_pos += *len;
1526+
1527+
pos = data + *len;
1528+
total_len -= *len;
1529+
for_each_element(elem, pos, total_len) {
1530+
if (elem->id != frag_id)
1531+
break;
1532+
1533+
if (WARN_ON(elem->datalen >
1534+
(elems->scratch + elems->scratch_len -
1535+
elems->scratch_pos)))
1536+
return;
1537+
1538+
memcpy(elems->scratch_pos, elem->data, elem->datalen);
1539+
elems->scratch_pos += elem->datalen;
1540+
1541+
*len += elem->datalen;
1542+
}
1543+
1544+
*elem_ptr = start;
1545+
}
1546+
1547+
static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems,
1548+
u8 link_id)
1549+
{
1550+
const struct ieee80211_multi_link_elem *ml = elems->multi_link;
1551+
size_t ml_len = elems->multi_link_len;
1552+
const struct element *sub;
1553+
1554+
if (!ml || !ml_len)
1555+
return;
1556+
1557+
if (le16_get_bits(ml->control, IEEE80211_ML_CONTROL_TYPE) !=
1558+
IEEE80211_ML_CONTROL_TYPE_BASIC)
1559+
return;
1560+
1561+
for_each_mle_subelement(sub, (u8 *)ml, ml_len) {
1562+
struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data;
1563+
u16 control;
1564+
1565+
if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
1566+
continue;
1567+
1568+
if (!ieee80211_mle_sta_prof_size_ok(sub->data, sub->datalen))
1569+
return;
1570+
1571+
control = le16_to_cpu(prof->control);
1572+
1573+
if (link_id != u16_get_bits(control,
1574+
IEEE80211_MLE_STA_CONTROL_LINK_ID))
1575+
continue;
1576+
1577+
if (!(control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE))
1578+
return;
1579+
1580+
elems->prof = prof;
1581+
elems->sta_prof_len = sub->datalen;
1582+
1583+
/* the sub element can be fragmented */
1584+
ieee80211_defragment_element(elems, (void **)&elems->prof,
1585+
&elems->sta_prof_len,
1586+
ml_len - (sub->data - (u8 *)ml),
1587+
IEEE80211_MLE_SUBELEM_FRAGMENT);
1588+
return;
1589+
}
1590+
}
1591+
1592+
static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems,
1593+
struct ieee80211_elems_parse_params *params)
1594+
{
1595+
struct ieee80211_mle_per_sta_profile *prof;
1596+
struct ieee80211_elems_parse_params sub = {
1597+
.action = params->action,
1598+
.from_ap = params->from_ap,
1599+
.link_id = -1,
1600+
};
1601+
const struct element *non_inherit = NULL;
1602+
const u8 *end;
1603+
1604+
if (params->link_id == -1)
1605+
return;
1606+
1607+
ieee80211_defragment_element(elems, (void **)&elems->multi_link,
1608+
&elems->multi_link_len,
1609+
elems->total_len - ((u8 *)elems->multi_link -
1610+
elems->ie_start),
1611+
WLAN_EID_FRAGMENT);
1612+
1613+
ieee80211_mle_get_sta_prof(elems, params->link_id);
1614+
prof = elems->prof;
1615+
1616+
if (!prof)
1617+
return;
1618+
1619+
/* check if we have the 4 bytes for the fixed part in assoc response */
1620+
if (elems->sta_prof_len < sizeof(*prof) + prof->sta_info_len - 1 + 4) {
1621+
elems->prof = NULL;
1622+
elems->sta_prof_len = 0;
1623+
return;
1624+
}
1625+
1626+
/*
1627+
* Skip the capability information and the status code that are expected
1628+
* as part of the station profile in association response frames. Note
1629+
* the -1 is because the 'sta_info_len' is accounted to as part of the
1630+
* per-STA profile, but not part of the 'u8 variable[]' portion.
1631+
*/
1632+
sub.start = prof->variable + prof->sta_info_len - 1 + 4;
1633+
end = (const u8 *)prof + elems->sta_prof_len;
1634+
sub.len = end - sub.start;
1635+
1636+
non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
1637+
sub.start, sub.len);
1638+
_ieee802_11_parse_elems_full(&sub, elems, non_inherit);
1639+
}
1640+
15001641
struct ieee802_11_elems *
15011642
ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
15021643
{
15031644
struct ieee802_11_elems *elems;
15041645
const struct element *non_inherit = NULL;
15051646
u8 *nontransmitted_profile;
15061647
int nontransmitted_profile_len = 0;
1648+
size_t scratch_len = params->scratch_len ?: 2 * params->len;
15071649

1508-
elems = kzalloc(sizeof(*elems), GFP_ATOMIC);
1650+
elems = kzalloc(sizeof(*elems) + scratch_len, GFP_ATOMIC);
15091651
if (!elems)
15101652
return NULL;
15111653
elems->ie_start = params->start;
15121654
elems->total_len = params->len;
1655+
elems->scratch_len = scratch_len;
1656+
elems->scratch_pos = elems->scratch;
15131657

15141658
nontransmitted_profile = kmalloc(params->len, GFP_ATOMIC);
15151659
if (nontransmitted_profile) {
@@ -1537,6 +1681,8 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
15371681
_ieee802_11_parse_elems_full(&sub, elems, NULL);
15381682
}
15391683

1684+
ieee80211_mle_parse_link(elems, params);
1685+
15401686
if (elems->tim && !elems->parse_error) {
15411687
const struct ieee80211_tim_ie *tim_ie = elems->tim;
15421688

0 commit comments

Comments
 (0)