]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP MLD: Enhance authenticator state machine
authorRameshkumar Sundaram <quic_ramess@quicinc.com>
Thu, 28 Mar 2024 18:16:46 +0000 (23:46 +0530)
committerJouni Malinen <j@w1.fi>
Sat, 20 Apr 2024 15:31:11 +0000 (18:31 +0300)
Add required ML specific members in struct wpa_authenticator and struct
wpa_state_machine to maintain self and partner link information.

Maintain state machine object in all associated link stations and
destroy/remove references from the same whenever link stations are
getting removed.

Increase the wpa_group object reference count for all links in which ML
station is getting associated and release the same whenever link
stations are getting removed.

Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
src/ap/ieee802_11.c
src/ap/sta_info.c
src/ap/wpa_auth.c
src/ap/wpa_auth.h
src/ap/wpa_auth_glue.c
src/ap/wpa_auth_i.h

index c078a05360b026c6dd126a2b0323d97b4b843d54..1a2e44e85f0033f716ce40720fd26e4a70adc5e9 100644 (file)
@@ -4456,6 +4456,8 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
        }
 
        sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+       sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+
        status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
        if (status != WLAN_STATUS_SUCCESS) {
                wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
@@ -4463,7 +4465,6 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
        }
 
        ap_sta_set_mld(sta, true);
-       sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
 
        os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
        for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
@@ -4490,9 +4491,11 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
                        ieee802_11_update_beacons(hapd->iface);
        }
 
-       /* RSN Authenticator should always be the one on the original station */
+       /* Maintain state machine reference on all link STAs, this is needed
+        * during group rekey handling.
+        */
        wpa_auth_sta_deinit(sta->wpa_sm);
-       sta->wpa_sm = NULL;
+       sta->wpa_sm = origin_sta->wpa_sm;
 
        /*
         * Do not initialize the EAPOL state machine.
index b62770735ed4720c6b20ba27e200ae0dd76d715c..87d63e1883cb5330d6eeda42804c83b695abb82d 100644 (file)
@@ -200,6 +200,28 @@ static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 }
 
 
+#ifdef CONFIG_IEEE80211BE
+static void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd,
+                                              struct sta_info *psta)
+{
+       struct sta_info *lsta;
+       struct hostapd_data *lhapd;
+
+       if (!ap_sta_is_mld(hapd, psta))
+               return;
+
+       for_each_mld_link(lhapd, hapd) {
+               if (lhapd == hapd)
+                       continue;
+
+               lsta = ap_get_sta(lhapd, psta->addr);
+               if (lsta)
+                       lsta->wpa_sm = NULL;
+       }
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 {
        int set_beacon = 0;
@@ -317,8 +339,16 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 
 #ifdef CONFIG_IEEE80211BE
        if (!ap_sta_is_mld(hapd, sta) ||
-           hapd->mld_link_id == sta->mld_assoc_link_id)
+           hapd->mld_link_id == sta->mld_assoc_link_id) {
                wpa_auth_sta_deinit(sta->wpa_sm);
+               /* Remove references from partner links. */
+               clear_wpa_sm_for_each_partner_link(hapd, sta);
+       }
+
+       /* Release group references in case non-association link STA is removed
+        * before association link STA */
+       if (hostapd_sta_is_link_sta(hapd, sta))
+               wpa_release_link_auth_ref(sta->wpa_sm, hapd->mld_link_id);
 #else /* CONFIG_IEEE80211BE */
        wpa_auth_sta_deinit(sta->wpa_sm);
 #endif /* CONFIG_IEEE80211BE */
@@ -903,8 +933,10 @@ static void ap_sta_disconnect_common(struct hostapd_data *hapd,
        ieee802_1x_free_station(hapd, sta);
 #ifdef CONFIG_IEEE80211BE
        if (!hapd->conf->mld_ap ||
-           hapd->mld_link_id == sta->mld_assoc_link_id)
+           hapd->mld_link_id == sta->mld_assoc_link_id) {
                wpa_auth_sta_deinit(sta->wpa_sm);
+               clear_wpa_sm_for_each_partner_link(hapd, sta);
+       }
 #else /* CONFIG_IEEE80211BE */
        wpa_auth_sta_deinit(sta->wpa_sm);
 #endif /* CONFIG_IEEE80211BE */
index 0d15c42099dc2ae245eb2ee8386477f363dfa63f..f0098f18620c24217cb756584fe1a5571b73fb01 100644 (file)
@@ -103,6 +103,75 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
 }
 
 
+#ifdef CONFIG_IEEE80211BE
+
+void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
+                              int release_link_id)
+{
+       int link_id;
+
+       if (!sm || release_link_id >= MAX_NUM_MLD_LINKS)
+               return;
+
+       for_each_sm_auth(sm, link_id) {
+               if (link_id == release_link_id) {
+                       wpa_group_put(sm->mld_links[link_id].wpa_auth,
+                                     sm->mld_links[link_id].wpa_auth->group);
+                       sm->mld_links[link_id].wpa_auth = NULL;
+               }
+       }
+}
+
+
+struct wpa_get_link_auth_ctx {
+       const u8 *addr;
+       struct wpa_authenticator *wpa_auth;
+};
+
+static int wpa_get_link_sta_auth(struct wpa_authenticator *wpa_auth, void *data)
+{
+       struct wpa_get_link_auth_ctx *ctx = data;
+
+       if (!ether_addr_equal(wpa_auth->addr, ctx->addr))
+               return 0;
+       ctx->wpa_auth = wpa_auth;
+       return 1;
+}
+
+
+static int wpa_get_primary_auth_cb(struct wpa_authenticator *wpa_auth,
+                                  void *data)
+{
+       struct wpa_get_link_auth_ctx *ctx = data;
+
+       if (!wpa_auth->is_ml ||
+           !ether_addr_equal(wpa_auth->mld_addr, ctx->addr) ||
+           !wpa_auth->primary_auth)
+               return 0;
+
+       ctx->wpa_auth = wpa_auth;
+       return 1;
+}
+
+
+static struct wpa_authenticator *
+wpa_get_primary_auth(struct wpa_authenticator *wpa_auth)
+{
+       struct wpa_get_link_auth_ctx ctx;
+
+       if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
+               return wpa_auth;
+
+       ctx.addr = wpa_auth->mld_addr;
+       ctx.wpa_auth = NULL;
+       wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_auth_cb, &ctx);
+
+       return ctx.wpa_auth;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
 static inline int wpa_auth_mic_failure_report(
        struct wpa_authenticator *wpa_auth, const u8 *addr)
 {
@@ -537,8 +606,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
        wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
        if (!wpa_auth)
                return NULL;
+
        os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
        os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+
+#ifdef CONFIG_IEEE80211BE
+       if (conf->mld_addr) {
+               wpa_auth->is_ml = true;
+               wpa_auth->link_id = conf->link_id;
+               wpa_auth->primary_auth = !conf->first_link_auth;
+               os_memcpy(wpa_auth->mld_addr, conf->mld_addr, ETH_ALEN);
+       }
+#endif /* CONFIG_IEEE80211BE */
+
        wpa_auth->cb = cb;
        wpa_auth->cb_ctx = cb_ctx;
 
@@ -798,6 +878,10 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
 
 static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 {
+#ifdef CONFIG_IEEE80211BE
+       int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
 #ifdef CONFIG_P2P
        if (WPA_GET_BE32(sm->ip_addr)) {
                wpa_printf(MSG_DEBUG,
@@ -821,6 +905,13 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
        os_free(sm->last_rx_eapol_key);
        os_free(sm->wpa_ie);
        os_free(sm->rsnxe);
+#ifdef CONFIG_IEEE80211BE
+       for_each_sm_auth(sm, link_id) {
+               wpa_group_put(sm->mld_links[link_id].wpa_auth,
+                             sm->mld_links[link_id].wpa_auth->group);
+               sm->mld_links[link_id].wpa_auth = NULL;
+       }
+#endif /* CONFIG_IEEE80211BE */
        wpa_group_put(sm->wpa_auth, sm->group);
 #ifdef CONFIG_DPP2
        wpabuf_clear_free(sm->dpp_z);
@@ -838,12 +929,20 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
 
        wpa_auth = sm->wpa_auth;
        if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
+               struct wpa_authenticator *primary_auth = wpa_auth;
+
                wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
                                "strict rekeying - force GTK rekey since STA is leaving");
+
+#ifdef CONFIG_IEEE80211BE
+               if (wpa_auth->is_ml && !wpa_auth->primary_auth)
+                       primary_auth = wpa_get_primary_auth(wpa_auth);
+#endif /* CONFIG_IEEE80211BE */
+
                if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
-                                         wpa_auth, NULL) == -1)
+                                         primary_auth, NULL) == -1)
                        eloop_register_timeout(0, 500000, wpa_rekey_gtk,
-                                              wpa_auth, NULL);
+                                              primary_auth, NULL);
        }
 
        eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
@@ -6835,6 +6934,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
        for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
                struct mld_link_info *link = &info->links[link_id];
                struct mld_link *sm_link = &sm->mld_links[link_id];
+               struct wpa_get_link_auth_ctx ctx;
 
                sm_link->valid = link->valid;
                if (!link->valid)
@@ -6849,10 +6949,30 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
                           MAC2STR(sm_link->own_addr),
                           MAC2STR(sm_link->peer_addr));
 
-               if (link_id != mld_assoc_link_id)
+               ml_rsn_info.links[i++].link_id = link_id;
+
+               if (link_id != mld_assoc_link_id) {
                        sm->n_mld_affiliated_links++;
+                       ctx.addr = link->local_addr;
+                       ctx.wpa_auth = NULL;
+                       wpa_auth_for_each_auth(sm->wpa_auth,
+                                              wpa_get_link_sta_auth, &ctx);
+                       if (ctx.wpa_auth) {
+                               sm_link->wpa_auth = ctx.wpa_auth;
+                               wpa_group_get(sm_link->wpa_auth,
+                                             sm_link->wpa_auth->group);
+                       }
+               } else {
+                       sm_link->wpa_auth = sm->wpa_auth;
+               }
 
-               ml_rsn_info.links[i++].link_id = link_id;
+               if (!sm_link->wpa_auth)
+                       wpa_printf(MSG_ERROR,
+                                  "Unable to find authenticator object for ML STA "
+                                  MACSTR " on link " MACSTR " link id %d",
+                                  MAC2STR(sm->own_mld_addr),
+                                  MAC2STR(sm_link->own_addr),
+                                  link_id);
        }
 
        ml_rsn_info.n_mld_links = i;
index 4247463adb33a78e34df9bacdc1a42d654ecffca..396fc4906f2ddfb6fffcc7f0e91d237fc74fe74e 100644 (file)
@@ -285,6 +285,12 @@ struct wpa_auth_config {
         * Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS
         * and in BSSs that are not part of a Multi-BSSID set. */
        struct wpa_authenticator *tx_bss_auth;
+
+#ifdef CONFIG_IEEE80211BE
+       const u8 *mld_addr;
+       int link_id;
+       struct wpa_authenticator *first_link_auth;
+#endif /* CONFIG_IEEE80211BE */
 };
 
 typedef enum {
@@ -647,4 +653,13 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
                              struct wpa_auth_ml_link_key_info *info,
                              bool mgmt_frame_prot, bool beacon_prot);
 
+void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
+                              int release_link_id);
+
+#define for_each_sm_auth(sm, link_id) \
+       for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++)       \
+               if (sm->mld_links[link_id].valid &&                     \
+                   sm->mld_links[link_id].wpa_auth &&                  \
+                   sm->wpa_auth != sm->mld_links[link_id].wpa_auth)
+
 #endif /* WPA_AUTH_H */
index c786d580ecdc8481bdedd486d2c8e034ea50033d..66d426d51b68071ec1e317757586120ad413aaa6 100644 (file)
@@ -1747,6 +1747,27 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
                !!(hapd->iface->drv_flags2 &
                   WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
 
+#ifdef CONFIG_IEEE80211BE
+       _conf.mld_addr = NULL;
+       _conf.link_id = -1;
+       _conf.first_link_auth = NULL;
+
+       if (hapd->conf->mld_ap) {
+               struct hostapd_data *lhapd;
+
+               _conf.mld_addr = hapd->mld->mld_addr;
+               _conf.link_id = hapd->mld_link_id;
+
+               for_each_mld_link(lhapd, hapd) {
+                       if (lhapd == hapd)
+                               continue;
+
+                       if (lhapd->wpa_auth)
+                               _conf.first_link_auth = lhapd->wpa_auth;
+               }
+       }
+#endif /* CONFIG_IEEE80211BE */
+
        hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
        if (hapd->wpa_auth == NULL) {
                wpa_printf(MSG_ERROR, "WPA initialization failed.");
index aa581c8b1c4afe7bd2b96159fe97aacda0a0ae94..ac45cc413eb6ac1ee501d077d81905f32388c7ed 100644 (file)
@@ -186,6 +186,7 @@ struct wpa_state_machine {
                size_t rsne_len;
                const u8 *rsnxe;
                size_t rsnxe_len;
+               struct wpa_authenticator *wpa_auth;
        } mld_links[MAX_NUM_MLD_LINKS];
 #endif /* CONFIG_IEEE80211BE */
 };
@@ -262,6 +263,13 @@ struct wpa_authenticator {
 #ifdef CONFIG_P2P
        struct bitfield *ip_pool;
 #endif /* CONFIG_P2P */
+
+#ifdef CONFIG_IEEE80211BE
+       bool is_ml;
+       u8 mld_addr[ETH_ALEN];
+       u8 link_id;
+       bool primary_auth;
+#endif /* CONFIG_IEEE80211BE */
 };