diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 528cc1602..1d1e3d038 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -125,6 +125,9 @@ extern "C" { #define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " +#define INTERWORKING_AP "INTERWORKING-AP " +#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " + /* hostapd control interface - fixed message prefixes */ #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " #define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS " diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index ec1de2ba3..512100432 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -2931,6 +2931,27 @@ static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd) #ifdef CONFIG_INTERWORKING +static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst) +{ + u8 bssid[ETH_ALEN]; + struct wpa_bss *bss; + + if (hwaddr_aton(dst, bssid)) { + wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst); + return -1; + } + + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR, + MAC2STR(bssid)); + return -1; + } + + return interworking_connect(wpa_s, bss); +} + + static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) { u8 dst_addr[ETH_ALEN]; @@ -3262,6 +3283,13 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) { interworking_stop_fetch_anqp(wpa_s); + } else if (os_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) { + if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") != + NULL) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) { + if (ctrl_interworking_connect(wpa_s, buf + 21) < 0) + reply_len = -1; } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) { if (get_anqp(wpa_s, buf + 9) < 0) reply_len = -1; diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 5259c34a2..35a3a2b16 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -17,9 +17,11 @@ #include "common.h" #include "common/ieee802_11_defs.h" #include "common/gas.h" +#include "common/wpa_ctrl.h" #include "drivers/driver.h" #include "wpa_supplicant_i.h" #include "bss.h" +#include "scan.h" #include "gas_query.h" #include "interworking.h" @@ -108,6 +110,46 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, } +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + if (bss == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR, + MAC2STR(bss->bssid)); + /* TODO: create network block and connect */ + return 0; +} + + +static void interworking_select_network(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss, *selected = NULL; + unsigned int count = 0; + + wpa_s->network_select = 0; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bss->anqp_nai_realm == NULL) + continue; + /* TODO: verify that matching credentials are available */ + count++; + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR, + MAC2STR(bss->bssid)); + if (selected == NULL && wpa_s->auto_select) + selected = bss; + } + + if (count == 0) { + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network " + "with matching credentials found"); + } + + if (selected) + interworking_connect(wpa_s, selected); +} + + static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; @@ -137,22 +179,32 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) if (found == 0) { wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed"); wpa_s->fetch_anqp_in_progress = 0; + if (wpa_s->network_select) + interworking_select_network(wpa_s); } } -int interworking_fetch_anqp(struct wpa_supplicant *wpa_s) +static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; - if (wpa_s->fetch_anqp_in_progress) - return 0; - dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED; wpa_s->fetch_anqp_in_progress = 1; interworking_next_anqp_fetch(wpa_s); +} + + +int interworking_fetch_anqp(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) + return 0; + + wpa_s->network_select = 0; + + interworking_start_fetch_anqp(wpa_s); return 0; } @@ -351,3 +403,27 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, pos += slen; } } + + +static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start " + "ANQP fetch"); + interworking_start_fetch_anqp(wpa_s); +} + + +int interworking_select(struct wpa_supplicant *wpa_s, int auto_select) +{ + interworking_stop_fetch_anqp(wpa_s); + wpa_s->network_select = 1; + wpa_s->auto_select = !!auto_select; + wpa_printf(MSG_DEBUG, "Interworking: Start scan for network " + "selection"); + wpa_s->scan_res_handler = interworking_scan_res_handler; + wpa_s->scan_req = 2; + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return 0; +} diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h index 2a9790bb4..247df3069 100644 --- a/wpa_supplicant/interworking.h +++ b/wpa_supplicant/interworking.h @@ -25,5 +25,7 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, const struct wpabuf *resp, u16 status_code); int interworking_fetch_anqp(struct wpa_supplicant *wpa_s); void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s); +int interworking_select(struct wpa_supplicant *wpa_s, int auto_select); +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss); #endif /* INTERWORKING_H */ diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 077d9f14b..2089df29e 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -2234,6 +2234,44 @@ static int wpa_cli_cmd_stop_fetch_anqp(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc == 0) + return wpa_ctrl_command(ctrl, "INTERWORKING_SELECT"); + + res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_SELECT %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 1) { + printf("Invalid INTERWORKING_CONNECT commands: needs one " + "argument (BSSID)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_CONNECT %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[100]; @@ -2655,6 +2693,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { "= fetch ANQP information for all APs" }, { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, cli_cmd_flag_none, "= stop fetch_anqp operation" }, + { "interworking_select", wpa_cli_cmd_interworking_select, + cli_cmd_flag_none, + "[auto] = perform Interworking network selection" }, + { "interworking_connect", wpa_cli_cmd_interworking_connect, + cli_cmd_flag_none, + " = connect using Interworking credentials" }, { "anqp_get", wpa_cli_cmd_anqp_get, cli_cmd_flag_none, " [,]... = request ANQP information" }, #endif /* CONFIG_INTERWORKING */ diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index ff3addf74..b327f4c65 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -591,7 +591,9 @@ struct wpa_supplicant { struct gas_query *gas; #ifdef CONFIG_INTERWORKING - int fetch_anqp_in_progress; + int fetch_anqp_in_progress:1; + int network_select:1; + int auto_select:1; #endif /* CONFIG_INTERWORKING */ };