[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