[RFC 25/30] SMD: Add testing options to do ST prepare/execute separately

Ilan Peer ilan.peer at intel.com
Wed Oct 21 06:43:43 PDT 2026


Add testing configuration to allow doing ST prepare and ST
execute separately (triggered from control interface).
In addition add an option to abandon an ongoing ST flow.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/common/wpa_ctrl.h             |  2 +
 wpa_supplicant/ctrl_iface.c       | 27 +++++++++
 wpa_supplicant/smd.c              | 99 +++++++++++++++++++++++++++++++
 wpa_supplicant/wpa_cli.c          | 26 ++++++++
 wpa_supplicant/wpa_supplicant_i.h | 35 ++++++++++-
 5 files changed, 187 insertions(+), 2 deletions(-)

diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 18f0390510b4..627a24407fda 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -34,6 +34,8 @@ extern "C" {
 #define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT "
 /** SMD Transition completed and the STA is connected to the target AP MLD */
 #define WPA_EVENT_SMD_TRANSITION_DONE "CTRL-EVENT-SMD-TRANSITION-DONE "
+/** SMD Transition preparation completed and execution can be requested */
+#define WPA_EVENT_SMD_ST_PREPARE_DONE "CTRL-EVENT-SMD-ST-PREPARE-DONE "
 /** wpa_supplicant is exiting */
 #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
 /** Password change was completed successfully */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 0d995bb60e9f..0411384f876c 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -6213,6 +6213,24 @@ static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+
+static int wpas_ctrl_iface_st_prepare(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 ap_mld_addr[ETH_ALEN];
+
+	if (hwaddr_aton(cmd, ap_mld_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE ST_PREPARE: invalid address '%s'", cmd);
+		return -1;
+	}
+
+	return wpas_smd_st_prepare_req(wpa_s, ap_mld_addr);
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
 static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
 					  char *addr)
 {
@@ -14459,6 +14477,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 #ifdef CONFIG_TESTING_OPTIONS
 	} else if (os_strcmp(buf, "DROP_SA") == 0) {
 		wpa_supplicant_ctrl_iface_drop_sa(wpa_s);
+	} else if (os_strncmp(buf, "ST_PREPARE ", 11) == 0) {
+		if (wpas_ctrl_iface_st_prepare(wpa_s, buf + 11))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "ST_EXECUTE") == 0) {
+		if (wpas_smd_st_execute_req(wpa_s))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "ST_ABANDON ", 11) == 0) {
+		if (wpas_smd_st_abandon_req(wpa_s, atoi(buf + 11) != 0))
+			reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "ROAM ", 5) == 0) {
 		if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
diff --git a/wpa_supplicant/smd.c b/wpa_supplicant/smd.c
index 709e270cd210..2e5dea3fd1c2 100644
--- a/wpa_supplicant/smd.c
+++ b/wpa_supplicant/smd.c
@@ -46,6 +46,10 @@ void wpas_smd_deinit(struct wpa_supplicant *wpa_s)
 {
 	wpa_printf(MSG_DEBUG, "SMD: Deinit SMD state");
 
+#ifdef CONFIG_TESTING_OPTIONS
+	wpa_s->smd_st_manual_execute = false;
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	wpas_smd_abandon(wpa_s, false);
 }
 
@@ -411,6 +415,19 @@ void wpas_smd_st_prep_status(struct wpa_supplicant *wpa_s,
 		   "SMD: ST Prep succeeded, executing transition (ap_mld=" MACSTR ")",
 		   MAC2STR(info->ap_mld_addr));
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->smd_st_manual_execute) {
+		wpa_s->smd_st_manual_execute = false;
+		wpa_s->smd.state = SMD_STATE_PREPARE_DONE;
+		wpa_printf(MSG_DEBUG,
+			   "SMD: TEST: Waiting for ST_EXECUTE command");
+		wpa_msg(wpa_s, MSG_INFO,
+			WPA_EVENT_SMD_ST_PREPARE_DONE "ap_mld=" MACSTR,
+			MAC2STR(info->ap_mld_addr));
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	wpas_smd_st_execute(wpa_s);
 	return;
 
@@ -614,3 +631,85 @@ void wpas_smd_st_abandoned(struct wpa_supplicant *wpa_s,
 
 	/* Assume that this is followed by a disconnection event */
 }
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+int wpas_smd_st_prepare_req(struct wpa_supplicant *wpa_s,
+			    const u8 *ap_mld_addr)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct wpa_bss *bss, *target = NULL;
+
+	if (wpa_s->wpa_state != WPA_COMPLETED || !ssid) {
+		wpa_printf(MSG_DEBUG,
+			   "SMD: Not connected, cannot prepare SMD Transition");
+		return -1;
+	}
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (!ether_addr_equal(bss->mld_addr, ap_mld_addr) ||
+		    bss->ssid_len != ssid->ssid_len ||
+		    os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)
+			continue;
+
+		target = bss;
+		break;
+	}
+
+	if (!target) {
+		wpa_printf(MSG_DEBUG,
+			   "SMD: Target AP MLD " MACSTR " not found from BSS table",
+			   MAC2STR(ap_mld_addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "SMD: TEST: Prepare SMD Transition to AP MLD " MACSTR
+		   " (BSSID " MACSTR ") and wait for ST_EXECUTE",
+		   MAC2STR(ap_mld_addr), MAC2STR(target->bssid));
+
+	/*
+	 * The transition is started through the normal connection path; the
+	 * flag makes the ST execution wait for an explicit ST_EXECUTE command
+	 * once the ST preparation has completed.
+	 */
+	wpa_s->smd_st_manual_execute = true;
+	wpa_s->reassociate = 1;
+	wpa_supplicant_connect(wpa_s, target, ssid);
+
+	return 0;
+}
+
+
+int wpas_smd_st_execute_req(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->smd.state != SMD_STATE_PREPARE_DONE) {
+		wpa_printf(MSG_DEBUG,
+			   "SMD: No prepared SMD Transition to execute (state=%d)",
+			   wpa_s->smd.state);
+		return -1;
+	}
+
+	return wpas_smd_st_execute(wpa_s);
+}
+
+
+int wpas_smd_st_abandon_req(struct wpa_supplicant *wpa_s, bool local_only)
+{
+	if (wpa_s->smd.state == SMD_STATE_NONE) {
+		wpa_printf(MSG_DEBUG,
+			   "SMD: No ongoing SMD Transition to abandon");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "SMD: Abandon the SMD Transition (local_only=%d)",
+		   local_only);
+
+	wpas_smd_abandon(wpa_s, local_only);
+
+	return 0;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index db3e81b1bdfe..c57185966640 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -2121,6 +2121,26 @@ static int wpa_cli_cmd_ml_probe_req(struct wpa_ctrl *ctrl,
 	return wpa_cli_cmd(ctrl, "ML_PROBE_REQ", 2, argc, argv);
 }
 
+static int wpa_cli_cmd_st_prepare(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "ST_PREPARE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_st_execute(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "ST_EXECUTE", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_st_abandon(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "ST_ABANDON", 1, argc, argv);
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -3880,6 +3900,12 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
 	  "= drop SA without deauth/disassoc (test command)" },
 	{ "ml_probe_req", wpa_cli_cmd_ml_probe_req, NULL, cli_cmd_flag_none,
 	  "= send Multi-Link Probe request <bssid=addr> <mld_id=id> [link_id=id] (test command)" },
+	{ "st_prepare", wpa_cli_cmd_st_prepare, NULL, cli_cmd_flag_none,
+	  "<AP MLD addr> = prepare an SMD Transition and wait for st_execute (test command)" },
+	{ "st_execute", wpa_cli_cmd_st_execute, NULL, cli_cmd_flag_none,
+	  "= execute the prepared SMD Transition (test command)" },
+	{ "st_abandon", wpa_cli_cmd_st_abandon, NULL, cli_cmd_flag_none,
+	  "<local only> = abandon the ongoing SMD Transition (test command)" },
 #endif /* CONFIG_TESTING_OPTIONS */
 	{ "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 1effd518cfad..fce1adb9a5a8 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -760,14 +760,17 @@ struct auth_802_1x_data {
  * enum smd_state - SMD state machine states
  * @SMD_STATE_NONE: No SMD operation is in progress
  * @SMD_STATE_PREPARE_REQUESTED: SMD Prepare operation has been requested
+ * @SMD_STATE_PREPARE_DONE: SMD Prepare operation has completed and the
+ *	execution needs to be requested explicitly (testing mode)
  * @SMD_STATE_EXECUTE_REQUESTED: SMD Execute operation has been requested
  * @SMD_STATE_EXECUTION_DONE: SMD Execute operation has completed
  */
 enum smd_state {
 	SMD_STATE_NONE = 0,
 	SMD_STATE_PREPARE_REQUESTED = 1,
-	SMD_STATE_EXECUTE_REQUESTED = 2,
-	SMD_STATE_EXECUTION_DONE = 3,
+	SMD_STATE_PREPARE_DONE = 2,
+	SMD_STATE_EXECUTE_REQUESTED = 3,
+	SMD_STATE_EXECUTION_DONE = 4,
 };
 
 /**
@@ -1546,6 +1549,9 @@ struct wpa_supplicant {
 	unsigned int eapol_2_key_info_set_mask;
 	int test_assoc_comeback_type;
 	struct wpabuf *link_ies[MAX_NUM_MLD_LINKS];
+
+	/* Wait for an explicit ST_EXECUTE command after ST preparation */
+	bool smd_st_manual_execute;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -2359,6 +2365,12 @@ void wpas_smd_st_done(struct wpa_supplicant *wpa_s,
 		      struct st_done_info *info);
 void wpas_smd_st_abandoned(struct wpa_supplicant *wpa_s,
 			   struct st_abandoned_info *info);
+#ifdef CONFIG_TESTING_OPTIONS
+int wpas_smd_st_prepare_req(struct wpa_supplicant *wpa_s,
+			    const u8 *ap_mld_addr);
+int wpas_smd_st_execute_req(struct wpa_supplicant *wpa_s);
+int wpas_smd_st_abandon_req(struct wpa_supplicant *wpa_s, bool local_only);
+#endif /* CONFIG_TESTING_OPTIONS */
 #else /* CONFIG_SME && CONFIG_SMD */
 static inline void wpas_smd_deinit(struct wpa_supplicant *wpa_s)
 {
@@ -2402,6 +2414,25 @@ static inline void wpas_smd_st_abandoned(struct wpa_supplicant *wpa_s,
 					 struct st_abandoned_info *info)
 {
 }
+
+#ifdef CONFIG_TESTING_OPTIONS
+static inline int wpas_smd_st_prepare_req(struct wpa_supplicant *wpa_s,
+					  const u8 *ap_mld_addr)
+{
+	return -1;
+}
+
+static inline int wpas_smd_st_execute_req(struct wpa_supplicant *wpa_s)
+{
+	return -1;
+}
+
+static inline int wpas_smd_st_abandon_req(struct wpa_supplicant *wpa_s,
+					  bool local_only)
+{
+	return -1;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_SME && CONFIG_SMD */
 
 #endif /* WPA_SUPPLICANT_I_H */
-- 
2.55.0




More information about the Hostap mailing list