]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
MLD: Handle link reconfiguration updates from the driver
authorKavita Kavita <quic_kkavita@quicinc.com>
Fri, 9 May 2025 15:50:29 +0000 (21:20 +0530)
committerJouni Malinen <j@w1.fi>
Mon, 2 Jun 2025 14:02:27 +0000 (17:02 +0300)
Processess the NL80211_CMD_ASSOC_MLO_RECONF event from the driver. This
event includes information about added links and the link
reconfiguration response frame from the AP MLD. The event can be
triggered by either wpa_supplicant or driver-initiated link
reconfiguration updates.

Also install group keys (e.g., GTK, IGTK, BIGTK, etc.) received with the
link reconfiguration response frame for the newly added links.

For removed links, the existing NL80211_CMD_LINKS_REMOVED event is used,
requiring no additional changes in wpa_supplicant for both
wpa_supplicant and driver-initiated link reconfiguration updates.

Signed-off-by: Kavita Kavita <quic_kkavita@quicinc.com>
src/drivers/driver.h
src/drivers/driver_common.c
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211.h
src/drivers/driver_nl80211_event.c
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
wpa_supplicant/events.c

index 97b0e2e573d1b998e40c5b6ee669f814febf94da..56d99f2a13ecf576498b8d763442fa204d96900c 100644 (file)
@@ -6128,6 +6128,11 @@ enum wpa_event_type {
         * EVENT_MLD_INTERFACE_FREED - Notification of AP MLD interface removal
         */
        EVENT_MLD_INTERFACE_FREED,
+
+       /**
+        * EVENT_SETUP_LINK_RECONFIG - Notification that new AP links added
+        */
+       EVENT_SETUP_LINK_RECONFIG,
 };
 
 
@@ -7115,6 +7120,17 @@ union wpa_event_data {
                u8 valid_links;
                struct t2lm_mapping t2lmap[MAX_NUM_MLD_LINKS];
        } t2l_map_info;
+
+       /**
+        * struct reconfig_info - Data for EVENT_SETUP_LINK_RECONFIG
+        */
+       struct reconfig_info {
+               u16 added_links;
+               u8 count;
+               const u8 *status_list;
+               const u8 *resp_ie; /* Starting from Group Key Data */
+               size_t resp_ie_len;
+       } reconfig_info;
 };
 
 /**
index 7a9f18c211fa93d1780d53eafe8f43152aea7b95..045c82967f5a155e64ffb81c7d23d7507fada59c 100644 (file)
@@ -101,6 +101,7 @@ const char * event_to_string(enum wpa_event_type event)
        E2S(TID_LINK_MAP);
        E2S(LINK_RECONFIG);
        E2S(MLD_INTERFACE_FREED);
+       E2S(SETUP_LINK_RECONFIG);
        }
 
        return "UNKNOWN";
index 46d04915900caff20f7eb89668c1bb4cc9b944df..68ff649410ac07a0f373dbd3251b70b869f1bb26 100644 (file)
@@ -1130,6 +1130,19 @@ static int nl80211_get_sta_mlo_info(void *priv,
 }
 
 
+int get_sta_mlo_interface_info(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+       if (!msg ||
+           send_and_recv_resp(drv, msg, get_mlo_info, &drv->sta_mlo_info))
+               return -1;
+
+       return 0;
+}
+
+
 static void wpa_driver_nl80211_event_newlink(
        struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
        int ifindex, const char *ifname)
@@ -1734,6 +1747,53 @@ try_again:
 }
 
 
+const u8 * nl80211_get_assoc_bssid(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+       int ret;
+       struct nl80211_get_assoc_freq_arg arg;
+       int count = 0;
+
+try_again:
+       msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+       if (!msg)
+               return NULL;
+       os_memset(&arg, 0, sizeof(arg));
+       arg.drv = drv;
+       ret = send_and_recv_resp(drv, msg, nl80211_get_assoc_freq_handler,
+                                &arg);
+       if (ret == -EAGAIN) {
+               count++;
+               if (count >= 10) {
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: Failed to receive consistent scan result dump for get_assoc_bssid");
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Failed to receive consistent scan result dump for get_assoc_bssid - try again");
+                       goto try_again;
+               }
+       }
+       if (ret == 0) {
+               os_memcpy(drv->bssid, arg.assoc_bssid, ETH_ALEN);
+
+               if (drv->sta_mlo_info.valid_links) {
+                       int i;
+
+                       for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+                               os_memcpy(drv->sta_mlo_info.links[i].bssid,
+                                         arg.bssid[i], ETH_ALEN);
+               }
+
+               return drv->bssid;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)",
+                  ret, strerror(-ret));
+
+       return NULL;
+}
+
+
 static int get_link_noise(struct nl_msg *msg, void *arg)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
index bea87afeb7edc376ce5974769dfcd36e013eb4e8..c570fba1c8c5567993f22bd9edd0bb1d22270b96 100644 (file)
@@ -330,6 +330,7 @@ int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
                         void *arg, int use_existing);
 void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
 unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
+const u8 * nl80211_get_assoc_bssid(struct wpa_driver_nl80211_data *drv);
 int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid);
 enum chan_width convert2width(int width);
 void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
@@ -431,4 +432,6 @@ struct hostapd_multi_hw_info *
 nl80211_get_multi_hw_info(struct i802_bss *bss, unsigned int *num_multi_hws);
 u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv);
 
+int get_sta_mlo_interface_info(struct wpa_driver_nl80211_data *drv);
+
 #endif /* DRIVER_NL80211_H */
index e7b67371a576b6a1ccbbae970c73e77850a48926..9bf9888ae6327bc61aac21f652ba84861580449a 100644 (file)
@@ -1657,6 +1657,69 @@ static void mlme_event_unprot_beacon(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void mlme_event_link_addition(struct wpa_driver_nl80211_data *drv,
+                                    const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       u16 curr_valid_links, added_links;
+       u8 count;
+       const u8 *resp_ie;
+       const u8 *end;
+
+       if (!frame) {
+               wpa_printf(MSG_DEBUG,
+                          "Link Reconfiguration Response frame is NULL - unspecified reason");
+               return;
+       }
+       wpa_hexdump(MSG_DEBUG, "JKM", frame, len);
+       end = frame + len;
+
+       os_memset(&event, 0, sizeof(event));
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+
+       if (len < 24 + 1 + sizeof(mgmt->u.action.u.link_reconf_resp)) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Too short Link Reconfig Response frame");
+               return;
+       }
+
+       count = mgmt->u.action.u.link_reconf_resp.count;
+       event.reconfig_info.count = count;
+       resp_ie = mgmt->u.action.u.link_reconf_resp.variable;
+       if (end - resp_ie < 3 * count) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Truncated Link Reconfig Response frame");
+               return;
+       }
+       event.reconfig_info.status_list = resp_ie;
+       resp_ie += 3 * count;
+       curr_valid_links = drv->sta_mlo_info.valid_links;
+
+       if (get_sta_mlo_interface_info(drv) < 0) {
+               wpa_printf(MSG_INFO, "nl80211: Failed to get STA MLO info");
+               return;
+       }
+
+       if (!nl80211_get_assoc_bssid(drv)) {
+               wpa_printf(MSG_INFO,
+                          "nl80211: Failed to get BSSID info for newly added links");
+               return;
+       }
+
+       added_links = ~curr_valid_links & drv->sta_mlo_info.valid_links;
+
+       event.reconfig_info.resp_ie = resp_ie;
+       event.reconfig_info.resp_ie_len = end - resp_ie;
+       event.reconfig_info.added_links = added_links;
+
+       drv->sta_mlo_info.req_links = drv->sta_mlo_info.valid_links;
+
+       wpa_supplicant_event(drv->ctx, EVENT_SETUP_LINK_RECONFIG, &event);
+}
+
+
 static s8
 nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq)
 {
@@ -4247,6 +4310,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
        case NL80211_CMD_LINKS_REMOVED:
                wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL);
                break;
+       case NL80211_CMD_ASSOC_MLO_RECONF:
+               mlme_event_link_addition(drv, nla_data(frame), nla_len(frame));
+               break;
        default:
                wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
                        "(cmd=%d)", cmd);
index 49412854c396b94107b96937eff4255de1a94c92..9a046317042becd39a1348035890c4b958e6e5c8 100644 (file)
@@ -7282,3 +7282,70 @@ struct wpabuf * wpa_sm_known_sta_identification(struct wpa_sm *sm, const u8 *aa,
 
        return ie;
 }
+
+
+static int mlo_ieee80211w_set_keys_for_new_links(struct wpa_sm *sm,
+                                                struct wpa_eapol_ie_parse *ie,
+                                                u16 added_links_bitmap)
+{
+       int i;
+
+       if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) ||
+           sm->mgmt_group_cipher == WPA_CIPHER_GTK_NOT_USED)
+               return 0;
+
+       for_each_link(added_links_bitmap, i) {
+               if (_mlo_ieee80211w_set_keys(sm, i, ie))
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_supplicant_install_mlo_gtk_keys(struct wpa_sm *sm,
+                                              struct wpa_eapol_ie_parse *ie,
+                                              u16 added_links_bitmap)
+{
+       int i;
+
+       for_each_link(added_links_bitmap, i) {
+               if (!ie->mlo_gtk[i]) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                               "MLO RSN: GTK not found for link ID %u", i);
+                       return -1;
+               }
+
+               if (wpa_supplicant_mlo_gtk(sm, i, ie->mlo_gtk[i],
+                                          ie->mlo_gtk_len[i], 0))
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+int wpa_sm_install_mlo_group_keys(struct wpa_sm *sm, const u8 *key_data,
+                                 size_t key_data_len, u16 added_links_bitmap)
+{
+       struct wpa_eapol_ie_parse ie;
+
+       if (!sm)
+               return -1;
+
+       wpa_hexdump_key(MSG_DEBUG, "RSN: IE KeyData for MLO link reconfig",
+                       key_data, key_data_len);
+
+       if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+               return -1;
+
+       if (wpa_supplicant_install_mlo_gtk_keys(
+                   sm, &ie, added_links_bitmap) < 0)
+               return -1;
+
+       if (mlo_ieee80211w_set_keys_for_new_links(sm, &ie,
+                                                 added_links_bitmap) < 0)
+               return -1;
+
+       return 0;
+}
index c011162f7a93f55a38fcfcd9bc106a944ab44c43..0c360bdeae6cb452d2a7f0d0b91c90c2cb17969d 100644 (file)
@@ -676,5 +676,7 @@ void wpa_sm_set_cur_pmksa(struct wpa_sm *sm,
 const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm);
 struct wpabuf * wpa_sm_known_sta_identification(struct wpa_sm *sm, const u8 *aa,
                                                u64 timestamp);
+int wpa_sm_install_mlo_group_keys(struct wpa_sm *sm, const u8 *key_data,
+                                 size_t key_data_len, u16 added_links_bitmap);
 
 #endif /* WPA_H */
index fe8e45e072764dd359de780e2526124d3ec54000..00b733a1c8ed57ab08a7f781c1c60314632441fc 100644 (file)
@@ -6106,6 +6106,76 @@ static void wpas_tid_link_map(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpas_setup_link_reconfig(struct wpa_supplicant *wpa_s,
+                                    struct reconfig_info *info)
+{
+       const u8 *key_data = info->resp_ie;
+       size_t key_data_len = 0;
+       const u8 *ies, *end;
+       bool success = false;
+       int i;
+
+       if (!info->added_links) {
+               wpa_printf(MSG_INFO, "No links to be added");
+               return;
+       }
+
+       if (wpa_drv_get_mlo_info(wpa_s) < 0) {
+               wpa_printf(MSG_INFO,
+                          "SETUP_LINK_RECONFIG: Failed to set reconfig info to wpa_s");
+               return;
+       }
+
+       if (wpa_sm_set_ml_info(wpa_s)) {
+               wpa_printf(MSG_ERROR,
+                          "SETUP_LINK_RECONFIG: Failed to set reconfig info to wpa_sm");
+               return;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "MLD: Reconfiguration Status List",
+                   info->status_list, info->count * 3);
+       for (i = 0; i < info->count; i++) {
+               if (WPA_GET_LE16(info->status_list + i * 3 + 1) ==
+                   WLAN_STATUS_SUCCESS)
+                       success = true;
+       }
+
+       if (!key_data || info->resp_ie_len == 0)
+               return;
+
+       if (success) {
+               /* Starting with Group Key Data subfield, Key Data Length
+                * field */
+               if (info->resp_ie_len < 1U + key_data[0]) {
+                       wpa_printf(MSG_INFO,
+                                  "MLD: Invalid keys in the link setup response");
+                       return;
+               }
+
+               key_data_len = key_data[0];
+               key_data++;
+               wpa_hexdump_key(MSG_DEBUG,
+                               "MLD: Link reconfig resp - Group Key Data",
+                               key_data, key_data_len);
+
+               if (wpa_sm_install_mlo_group_keys(wpa_s->wpa, key_data,
+                                                 key_data_len,
+                                                 info->added_links)) {
+                       wpa_printf(MSG_ERROR,
+                                  "SETUP_LINK_RECONFIG: Failed to install group keys for added links");
+                       return;
+               }
+       }
+
+       ies = key_data + key_data_len;
+       end = info->resp_ie + info->resp_ie_len;
+       wpa_hexdump(MSG_DEBUG, "MLD: Link reconfig resp - IEs", ies, end - ies);
+
+       wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_LINK_RECONFIG "valid_links=0x%x",
+               wpa_s->valid_links);
+}
+
+
 static void wpas_link_reconfig(struct wpa_supplicant *wpa_s)
 {
        u8 bssid[ETH_ALEN];
@@ -7132,6 +7202,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                if (data)
                        wpas_tid_link_map(wpa_s, &data->t2l_map_info);
                break;
+       case EVENT_SETUP_LINK_RECONFIG:
+               if (data)
+                       wpas_setup_link_reconfig(wpa_s, &data->reconfig_info);
+               break;
        default:
                wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
                break;