]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
MLD STA: Add SETUP_LINK_RECONFIG control interface command
authorKavita Kavita <quic_kkavita@quicinc.com>
Fri, 9 May 2025 15:50:28 +0000 (21:20 +0530)
committerJouni Malinen <j@w1.fi>
Mon, 2 Jun 2025 14:02:27 +0000 (17:02 +0300)
Add support for SETUP_LINK_RECONFIG control interface command
that allows users to add new setup links and/or remove existing
ones for the current MLO connection in STA mode.

Signed-off-by: Kavita Kavita <quic_kkavita@quicinc.com>
src/drivers/driver.h
src/drivers/driver_nl80211.c
wpa_supplicant/ctrl_iface.c
wpa_supplicant/driver_i.h
wpa_supplicant/wpa_cli.c

index 54b4a9b180ad09b85e782efef2a3b2717ef9a75d..97b0e2e573d1b998e40c5b6ee669f814febf94da 100644 (file)
@@ -2762,6 +2762,22 @@ struct wpa_mlo_signal_info {
        struct wpa_signal_info links[MAX_NUM_MLD_LINKS];
 };
 
+/**
+ * struct wpa_mlo_reconfig_info - Information about user-requested add and/or
+ * remove setup links for the current MLO association.
+ *
+ * @add_links: Bitmask of links to be added
+ * @add_link_bssid: Array of BSSIDs for the links to be added
+ * @add_link_freq: Array of frequencies for the links to be added
+ * @delete_links: Bitmask of links to be removed
+ */
+struct wpa_mlo_reconfig_info {
+       u16 add_links;
+       u8 add_link_bssid[MAX_NUM_MLD_LINKS][ETH_ALEN];
+       int add_link_freq[MAX_NUM_MLD_LINKS];
+       u16 delete_links;
+};
+
 /**
  * struct wpa_channel_info - Information about the current channel
  * @frequency: Center frequency of the primary 20 MHz channel
@@ -4466,6 +4482,15 @@ struct wpa_driver_ops {
        int (*mlo_signal_poll)(void *priv,
                               struct wpa_mlo_signal_info *mlo_signal_info);
 
+       /**
+        * setup_link_reconfig - Used to initiate Link Reconfiguration request
+        * for the current MLO association.
+        * @priv: Private driver interface data
+        * @info: Link reconfiguration request info
+        */
+       int (*setup_link_reconfig)(void *priv,
+                                  struct wpa_mlo_reconfig_info *info);
+
        /**
         * channel_info - Get parameters of the current operating channel
         * @priv: Private driver interface data
index b7c666db470611d308d1b89e7e31d51cae244729..46d04915900caff20f7eb89668c1bb4cc9b944df 100644 (file)
@@ -14713,6 +14713,77 @@ free_all:
        return ret;
 }
 
+
+static int
+nl80211_send_link_reconfig_request(void *priv,
+                                  struct wpa_mlo_reconfig_info *info)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nlattr *add_links, *attr;
+       int ret = -1;
+       u8 link_id;
+
+       if (!drv->sta_mlo_info.valid_links)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Send ML Link Reconfiguration Request");
+
+       if (info->add_links)
+               wpa_printf(MSG_DEBUG, "Add Setup Links Bitmask: 0x%x",
+                          info->add_links);
+
+       if (info->delete_links)
+               wpa_printf(MSG_DEBUG, "Remove Setup Links Bitmask: 0x%x",
+                          info->delete_links);
+
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOC_MLO_RECONF);
+       if (!msg)
+               goto error;
+
+       add_links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+       if (!add_links)
+               goto error;
+
+       for_each_link(info->add_links, link_id) {
+               attr = nla_nest_start(msg, 0);
+               if (!attr)
+                       goto error;
+
+               if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+                   nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+                           info->add_link_bssid[link_id]) ||
+                   nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+                               info->add_link_freq[link_id]))
+                       goto error;
+
+               nla_nest_end(msg, attr);
+       }
+
+       nla_nest_end(msg, add_links);
+
+       if (nla_put_u16(msg, NL80211_ATTR_MLO_RECONF_REM_LINKS,
+                       info->delete_links))
+               goto error;
+
+       ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL, NULL,
+                           NULL);
+       if (ret) {
+               wpa_printf(MSG_INFO,
+                          "nl80211: Failed to send Link Reconfiguration Request err=%d (%s)",
+                          ret, strerror(-ret));
+               return ret;
+       }
+       return 0;
+
+error:
+       nlmsg_free(msg);
+       wpa_printf(MSG_ERROR,
+                  "nl80211: Could not build Link Reconfiguration Request");
+       return ret;
+}
+
 #endif /* CONFIG_IEEE80211BE */
 
 
@@ -14934,6 +15005,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .is_drv_shared = nl80211_is_drv_shared,
        .link_sta_remove = wpa_driver_nl80211_link_sta_remove,
        .can_share_drv = wpa_driver_nl80211_can_share_drv,
+       .setup_link_reconfig = nl80211_send_link_reconfig_request,
 #endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_TESTING_OPTIONS
        .register_frame = testing_nl80211_register_frame,
index 97d9ae1f09b0252e3a52bfa5524ce1e4962a67c1..afad3d18b0667f921fb1d7f86a4c5fc95973ea6e 100644 (file)
@@ -12286,6 +12286,85 @@ static int wpas_ctrl_iface_mlo_signal_poll(struct wpa_supplicant *wpa_s,
 }
 
 
+static int
+wpa_supplicant_ctrl_iface_setup_link_reconfig(struct wpa_supplicant *wpa_s,
+                                             const char *cmd)
+{
+       const char *pos, *add_pos, *dlt_pos;
+       struct wpa_mlo_reconfig_info info;
+       int link_id;
+
+       add_pos = os_strstr(cmd, "add=");
+       dlt_pos = os_strstr(cmd, "delete=");
+
+       if (!add_pos && !dlt_pos) {
+               wpa_printf(MSG_INFO, "No add or delete links info");
+               return -1;
+       }
+
+       if (!wpa_s->current_bss) {
+               wpa_printf(MSG_INFO, "%s: Not connected", __func__);
+               return -1;
+       }
+
+       info.add_links = 0;
+       info.delete_links = 0;
+
+       if (add_pos) {
+               pos = add_pos + 4;
+
+               do {
+                       link_id = atoi(pos);
+                       if (link_id < 0 || link_id >=  MAX_NUM_MLD_LINKS)
+                               return -1;
+
+                       if (wpa_s->current_bss->valid_links & BIT(link_id)) {
+                               info.add_links |= BIT(link_id);
+                               os_memcpy(info.add_link_bssid[link_id],
+                                         wpa_s->current_bss->mld_links[link_id].bssid,
+                                         ETH_ALEN);
+                               info.add_link_freq[link_id] =
+                                       wpa_s->current_bss->mld_links[link_id].freq;
+                       } else {
+                               wpa_printf(MSG_INFO,
+                                          "%s: add link info not present",
+                                          __func__);
+                               return -1;
+                       }
+
+                       pos = os_strchr(pos, ' ');
+                       if (pos)
+                               pos++;
+               } while (pos && pos != dlt_pos);
+       }
+
+       if (dlt_pos) {
+               pos = dlt_pos + 7;
+
+               do {
+                       link_id = atoi(pos);
+                       if (link_id < 0 || link_id >=  MAX_NUM_MLD_LINKS)
+                               return -1;
+
+                       if (wpa_s->valid_links & BIT(link_id))
+                               info.delete_links |= BIT(link_id);
+                       else {
+                               wpa_printf(MSG_INFO,
+                                          "%s: not a valid delete link",
+                                          __func__);
+                               return -1;
+                       }
+
+                       pos = os_strchr(pos, ' ');
+                       if (pos)
+                               pos++;
+               } while (pos && pos != add_pos);
+       }
+
+       return wpa_drv_setup_link_reconfig(wpa_s, &info);
+}
+
+
 static int wpas_ctrl_iface_mlo_status(struct wpa_supplicant *wpa_s,
                                      char *buf, size_t buflen)
 {
@@ -13852,6 +13931,10 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (wpas_ctrl_iface_send_dscp_query(wpa_s, buf + 11))
                        reply_len = -1;
 #endif /* CONFIG_NO_ROBUST_AV */
+       } else if (os_strncmp(buf, "SETUP_LINK_RECONFIG", 19) == 0) {
+               if (wpa_supplicant_ctrl_iface_setup_link_reconfig(wpa_s,
+                                                                 buf + 19) < 0)
+                       reply_len = -1;
        } else if (os_strcmp(buf, "MLO_STATUS") == 0) {
                reply_len = wpas_ctrl_iface_mlo_status(wpa_s, reply,
                                                       reply_size);
index 43847cf4ab2ee2e4d75551dd1793b16431d3ce77..ccebd2cbedb62244c3ffaf32b9bbe241dbe3b24e 100644 (file)
@@ -532,6 +532,16 @@ static inline int wpa_drv_mlo_signal_poll(struct wpa_supplicant *wpa_s,
        return -1;
 }
 
+static inline int
+wpa_drv_setup_link_reconfig(struct wpa_supplicant *wpa_s,
+                           struct wpa_mlo_reconfig_info *info)
+{
+       if (wpa_s->driver->setup_link_reconfig)
+               return wpa_s->driver->setup_link_reconfig(wpa_s->drv_priv,
+                                                         info);
+       return -1;
+}
+
 static inline int wpa_drv_channel_info(struct wpa_supplicant *wpa_s,
                                       struct wpa_channel_info *ci)
 {
index 2ab291712a2fbd225d9becaa4e984f0d7ea9c153..b13bf49924f4e5084912ec43a0654151c2d6f549 100644 (file)
@@ -425,6 +425,13 @@ static int wpa_cli_cmd_mlo_signal_poll(struct wpa_ctrl *ctrl, int argc, char *ar
 }
 
 
+static int wpa_cli_cmd_setup_link_reconfig(struct wpa_ctrl *ctrl, int argc,
+                                          char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "SETUP_LINK_RECONFIG", 1, argc, argv);
+}
+
+
 static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        char cmd[256];
@@ -4092,6 +4099,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
        { "dscp_query", wpa_cli_cmd_dscp_query, NULL,
          cli_cmd_flag_none,
          "wildcard/domain_name=<string> = Send DSCP Query" },
+       { "setup_link_reconfig", wpa_cli_cmd_setup_link_reconfig, NULL,
+         cli_cmd_flag_none,
+         "<<add=/delete=><ID1> [ID2]...> = Add new setup links and/or remove existing ones for the current MLO connection in STA mode" },
        { "mlo_status", wpa_cli_cmd_mlo_status, NULL,
          cli_cmd_flag_none,
          "= get MLO status" },