A non-MLD sta could want to send offchannel management frame (e.g. to do a offchannel scan). Because ieee80211_rx_for_interface() fills the link_id information with the link the sta is currently using; hostapd would send back management frame responses through wrong link causing the sta to miss them. To fix that, use link_id that matches the received frame frequency if any or do not fill link_id indication for management frames, relying on hostapd instead to infer the proper link from the received frame frequency. Signed-off-by: Remi Pommarel <repk@xxxxxxxxxxxx> --- net/mac80211/rx.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index caa3e6b3f46e..26be0f378b3f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -5114,6 +5114,37 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw, dev_kfree_skb(skb); } +static int ieee80211_rx_get_link_from_freq(struct ieee80211_rx_data *rx, + struct sk_buff *skb, + struct link_sta_info *link_sta) +{ + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_sta *sta = &link_sta->sta->sta; + struct ieee80211_link_data *link; + struct ieee80211_bss_conf *bss_conf; + struct ieee80211_chanctx_conf *conf; + + if (!status->freq) + return link_sta->link_id; + + for_each_link_data(rx->sdata, link) { + bss_conf = link->conf; + if (!bss_conf) + continue; + conf = rcu_dereference(bss_conf->chanctx_conf); + if (!conf || !conf->def.chan) + continue; + + if (conf->def.chan->center_freq != status->freq) + continue; + + if (ieee80211_rx_is_valid_sta_link_id(sta, link->link_id)) + return link->link_id; + } + + return -1; +} + static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx, struct sk_buff *skb, bool consume) { @@ -5131,7 +5162,15 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx, link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2); if (link_sta) { sta = link_sta->sta; - link_id = link_sta->link_id; + + /* Use freq to get link id information on management frames to + * allow for offchannel scan, roaming, etc. + */ + if (ieee80211_is_mgmt(hdr->frame_control)) + link_id = ieee80211_rx_get_link_from_freq(rx, skb, + link_sta); + else + link_id = link_sta->link_id; } else { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); -- 2.40.0