diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 19a4d8d5c..aa2778b7c 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1648,6 +1648,46 @@ static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) } +static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, + char *addr) +{ + u8 bssid[ETH_ALEN]; + struct wpa_bss *bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (hwaddr_aton(addr, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid)); + + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (!bss) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found " + "from BSS table"); + return -1; + } + + /* + * TODO: Find best network configuration block from configuration to + * allow roaming to other networks + */ + + if (!ssid) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network " + "configuration known for the target AP"); + return -1; + } + + wpa_s->reassociate = 1; + wpa_supplicant_connect(wpa_s, bss, ssid); + + return 0; +} + + char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { @@ -1853,6 +1893,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, wpas_notify_resume(wpa_s->global); } else if (os_strcmp(buf, "DROP_SA") == 0) { wpa_supplicant_ctrl_iface_drop_sa(wpa_s); + } else if (os_strncmp(buf, "ROAM ", 5) == 0) { + if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5)) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index d43a17266..8782ba83b 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -720,9 +720,9 @@ static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s, } -static void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, - struct wpa_bss *selected, - struct wpa_ssid *ssid) +void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_ssid *ssid) { if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 8e17b9692..162a0b828 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -1440,6 +1440,26 @@ static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid ROAM command: needs one argument " + "(target AP's BSSID)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long ROAM command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -1640,6 +1660,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { "= notification of resume/thaw" }, { "drop_sa", wpa_cli_cmd_drop_sa, cli_cmd_flag_none, "= drop SA without deauth/disassoc (test command)" }, + { "roam", wpa_cli_cmd_roam, + cli_cmd_flag_none, + " = roam to the specified BSS" }, { NULL, NULL, cli_cmd_flag_none, NULL } }; diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 49ae1ccdb..ba5345129 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -488,6 +488,9 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, /* events.c */ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s); +void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_ssid *ssid); /* eap_register.c */ int eap_register_methods(void);