#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 "
#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];
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;
#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"
}
+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;
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;
}
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;
+}
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 */
}
+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];
"= 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,
+ "<BSSID> = connect using Interworking credentials" },
{ "anqp_get", wpa_cli_cmd_anqp_get, cli_cmd_flag_none,
"<addr> <info id>[,<info id>]... = request ANQP information" },
#endif /* CONFIG_INTERWORKING */
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 */
};