]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP: MLO: Handle deauthentication/disassociation of MLD station
authorIlan Peer <ilan.peer@intel.com>
Mon, 22 May 2023 19:33:48 +0000 (22:33 +0300)
committerJouni Malinen <j@w1.fi>
Mon, 12 Jun 2023 10:58:06 +0000 (13:58 +0300)
When a non-AP MLD is deauthenticated/disassociated from an MLD AP, make
sure to clean up its state from all links.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
src/ap/ieee802_11.c

index ad3b1c372da4de1b91d049f2d6283c371a8862c1..6f768e90c051265bad57a2bb2bc0a65b78152d55 100644 (file)
@@ -5582,6 +5582,119 @@ static void hostapd_disassoc_sta(struct hostapd_data *hapd,
 }
 
 
+#ifdef CONFIG_IEEE80211BE
+static struct sta_info *
+hostapd_ml_get_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+                        struct hostapd_data **assoc_hapd)
+{
+       struct hostapd_data *other_hapd = NULL;
+       struct sta_info *tmp_sta;
+
+       *assoc_hapd = hapd;
+
+       /* The station is the one on which the association was performed */
+       if (sta->mld_assoc_link_id == hapd->mld_link_id)
+               return sta;
+
+       other_hapd = hostapd_mld_get_link_bss(hapd, sta->mld_assoc_link_id);
+       if (!other_hapd) {
+               wpa_printf(MSG_DEBUG, "MLD: No link match for link_id=%u",
+                          sta->mld_assoc_link_id);
+               return sta;
+       }
+
+       /*
+        * Iterate over the stations and find the one with the matching link ID
+        * and association ID.
+        */
+       for (tmp_sta = other_hapd->sta_list; tmp_sta; tmp_sta = tmp_sta->next) {
+               if (tmp_sta->mld_assoc_link_id == sta->mld_assoc_link_id &&
+                   tmp_sta->aid == sta->aid) {
+                       *assoc_hapd = other_hapd;
+                       return tmp_sta;
+               }
+       }
+
+       return sta;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
+static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
+                                        struct sta_info *sta,
+                                        const struct ieee80211_mgmt *mgmt,
+                                        bool disassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+       struct hostapd_data *assoc_hapd, *tmp_hapd;
+       struct sta_info *assoc_sta;
+       unsigned int i, link_id;
+
+       if (!hostapd_is_mld_ap(hapd))
+               return false;
+
+       /*
+        * Get the station on which the association was performed, as it holds
+        * the information about all the other links.
+        */
+       assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+
+       for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+               for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
+                       struct sta_info *tmp_sta;
+
+                       if (!assoc_sta->mld_info.links[link_id].valid)
+                               continue;
+
+                       tmp_hapd =
+                               assoc_hapd->iface->interfaces->iface[i]->bss[0];
+
+                       if (!tmp_hapd->conf->mld_ap ||
+                           assoc_hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+                               continue;
+
+                       for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+                            tmp_sta = tmp_sta->next) {
+                               /*
+                                * Remove the station on which the association
+                                * was done only after all other link stations
+                                * are removed. Since there is only a single
+                                * station per struct hostapd_hapd with the
+                                * same association link simply break out from
+                                * the loop.
+                                */
+                               if (tmp_sta == assoc_sta)
+                                       break;
+
+                               if (tmp_sta->mld_assoc_link_id !=
+                                   assoc_sta->mld_assoc_link_id ||
+                                   tmp_sta->aid != assoc_sta->aid)
+                                       continue;
+
+                               if (!disassoc)
+                                       hostapd_deauth_sta(tmp_hapd, tmp_sta,
+                                                          mgmt);
+                               else
+                                       hostapd_disassoc_sta(tmp_hapd, tmp_sta,
+                                                            mgmt);
+                               break;
+                       }
+               }
+       }
+
+       /* Remove the station on which the association was performed. */
+       if (!disassoc)
+               hostapd_deauth_sta(assoc_hapd, assoc_sta, mgmt);
+       else
+               hostapd_disassoc_sta(assoc_hapd, assoc_sta, mgmt);
+
+       return true;
+#else /* CONFIG_IEEE80211BE */
+       return false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
 static void handle_disassoc(struct hostapd_data *hapd,
                            const struct ieee80211_mgmt *mgmt, size_t len)
 {
@@ -5602,6 +5715,9 @@ static void handle_disassoc(struct hostapd_data *hapd,
                return;
        }
 
+       if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, true))
+               return;
+
        hostapd_disassoc_sta(hapd, sta, mgmt);
 }
 
@@ -5629,6 +5745,9 @@ static void handle_deauth(struct hostapd_data *hapd,
                return;
        }
 
+       if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, false))
+               return;
+
        hostapd_deauth_sta(hapd, sta, mgmt);
 }