Interworking: Add ANQP query requests
authorJouni Malinen <jouni@qca.qualcomm.com>
Sun, 16 Oct 2011 19:44:28 +0000 (22:44 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 16 Oct 2011 20:55:34 +0000 (23:55 +0300)
Add mechanism for using GAS/ANQP to query Interworking related
information from APs. The received information is stored in the BSS
table and can be viewed with ctrl_iface BSS command.

New ctrl_iface command ANQP_GET can be used to fetch ANQP elements from
a specific AP. Additional commands FETCH_ANQP and STOP_FETCH_ANQP can be
used to initiate and stop an iteration through all APs in the BSS table
that indicate support Interworking to fetch ANQP elements from them.

wpa_supplicant/Makefile
wpa_supplicant/bss.c
wpa_supplicant/bss.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/interworking.c [new file with mode: 0644]
wpa_supplicant/interworking.h [new file with mode: 0644]
wpa_supplicant/wpa_cli.c
wpa_supplicant/wpa_supplicant_i.h

index 1ec7b68..2c46a85 100644 (file)
@@ -210,6 +210,7 @@ endif
 endif
 
 ifdef CONFIG_INTERWORKING
+OBJS += interworking.o
 CFLAGS += -DCONFIG_INTERWORKING
 NEED_GAS=y
 endif
index 21d6322..078e22d 100644 (file)
@@ -50,6 +50,15 @@ static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
                " SSID '%s'", bss->id, MAC2STR(bss->bssid),
                wpa_ssid_txt(bss->ssid, bss->ssid_len));
        wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
+#ifdef CONFIG_INTERWORKING
+       wpabuf_free(bss->anqp_venue_name);
+       wpabuf_free(bss->anqp_network_auth_type);
+       wpabuf_free(bss->anqp_roaming_consortium);
+       wpabuf_free(bss->anqp_ip_addr_type_availability);
+       wpabuf_free(bss->anqp_nai_realm);
+       wpabuf_free(bss->anqp_3gpp);
+       wpabuf_free(bss->anqp_domain_name);
+#endif /* CONFIG_INTERWORKING */
        os_free(bss);
 }
 
index 992b9c0..bb19f49 100644 (file)
@@ -23,6 +23,7 @@ struct wpa_scan_res;
 #define WPA_BSS_LEVEL_DBM              BIT(3)
 #define WPA_BSS_AUTHENTICATED          BIT(4)
 #define WPA_BSS_ASSOCIATED             BIT(5)
+#define WPA_BSS_ANQP_FETCH_TRIED       BIT(6)
 
 /**
  * struct wpa_bss - BSS table
@@ -65,6 +66,15 @@ struct wpa_bss {
        int level;
        u64 tsf;
        struct os_time last_update;
+#ifdef CONFIG_INTERWORKING
+       struct wpabuf *anqp_venue_name;
+       struct wpabuf *anqp_network_auth_type;
+       struct wpabuf *anqp_roaming_consortium;
+       struct wpabuf *anqp_ip_addr_type_availability;
+       struct wpabuf *anqp_nai_realm;
+       struct wpabuf *anqp_3gpp;
+       struct wpabuf *anqp_domain_name;
+#endif /* CONFIG_INTERWORKING */
        size_t ie_len;
        size_t beacon_ie_len;
        /* followed by ie_len octets of IEs */
index 2605a05..ec1de2b 100644 (file)
@@ -38,6 +38,7 @@
 #include "bss.h"
 #include "scan.h"
 #include "ctrl_iface.h"
+#include "interworking.h"
 
 extern struct wpa_driver_ops *wpa_drivers[];
 
@@ -1870,6 +1871,41 @@ static int wpa_supplicant_ctrl_iface_get_capability(
 }
 
 
+#ifdef CONFIG_INTERWORKING
+static char * anqp_add_hex(char *pos, char *end, const char *title,
+                          struct wpabuf *data)
+{
+       char *start = pos;
+       size_t i;
+       int ret;
+       const u8 *d;
+
+       if (data == NULL)
+               return start;
+
+       ret = os_snprintf(pos, end - pos, "%s=", title);
+       if (ret < 0 || ret >= end - pos)
+               return start;
+       pos += ret;
+
+       d = wpabuf_head_u8(data);
+       for (i = 0; i < wpabuf_len(data); i++) {
+               ret = os_snprintf(pos, end - pos, "%02x", *d++);
+               if (ret < 0 || ret >= end - pos)
+                       return start;
+               pos += ret;
+       }
+
+       ret = os_snprintf(pos, end - pos, "\n");
+       if (ret < 0 || ret >= end - pos)
+               return start;
+       pos += ret;
+
+       return pos;
+}
+#endif /* CONFIG_INTERWORKING */
+
+
 static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
                                         const char *cmd, char *buf,
                                         size_t buflen)
@@ -2013,6 +2049,20 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
        pos += ret;
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_INTERWORKING
+       pos = anqp_add_hex(pos, end, "anqp_venue_name", bss->anqp_venue_name);
+       pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
+                          bss->anqp_network_auth_type);
+       pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
+                          bss->anqp_roaming_consortium);
+       pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
+                          bss->anqp_ip_addr_type_availability);
+       pos = anqp_add_hex(pos, end, "anqp_nai_realm", bss->anqp_nai_realm);
+       pos = anqp_add_hex(pos, end, "anqp_3gpp", bss->anqp_3gpp);
+       pos = anqp_add_hex(pos, end, "anqp_domain_name",
+                          bss->anqp_domain_name);
+#endif /* CONFIG_INTERWORKING */
+
        return pos - buf;
 }
 
@@ -2880,6 +2930,38 @@ static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd)
 #endif /* CONFIG_P2P */
 
 
+#ifdef CONFIG_INTERWORKING
+static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
+{
+       u8 dst_addr[ETH_ALEN];
+       int used;
+       char *pos;
+#define MAX_ANQP_INFO_ID 100
+       u16 id[MAX_ANQP_INFO_ID];
+       size_t num_id = 0;
+
+       used = hwaddr_aton2(dst, dst_addr);
+       if (used < 0)
+               return -1;
+       pos = dst + used;
+       while (num_id < MAX_ANQP_INFO_ID) {
+               id[num_id] = atoi(pos);
+               if (id[num_id])
+                       num_id++;
+               pos = os_strchr(pos + 1, ',');
+               if (pos == NULL)
+                       break;
+               pos++;
+       }
+
+       if (num_id == 0)
+               return -1;
+
+       return anqp_send_req(wpa_s, dst_addr, id, num_id);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
 static int wpa_supplicant_ctrl_iface_sta_autoconnect(
        struct wpa_supplicant *wpa_s, char *cmd)
 {
@@ -3174,6 +3256,16 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
                        reply_len = -1;
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+       } else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
+               if (interworking_fetch_anqp(wpa_s) < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
+               interworking_stop_fetch_anqp(wpa_s);
+       } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
+               if (get_anqp(wpa_s, buf + 9) < 0)
+                       reply_len = -1;
+#endif /* CONFIG_INTERWORKING */
        } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
        {
                if (wpa_supplicant_ctrl_iface_ctrl_rsp(
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
new file mode 100644 (file)
index 0000000..5259c34
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "drivers/driver.h"
+#include "wpa_supplicant_i.h"
+#include "bss.h"
+#include "gas_query.h"
+#include "interworking.h"
+
+
+static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
+
+
+static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
+                                     struct wpabuf *extra)
+{
+       struct wpabuf *buf;
+       size_t i;
+       u8 *len_pos;
+
+       buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
+                                        (extra ? wpabuf_len(extra) : 0));
+       if (buf == NULL)
+               return NULL;
+
+       len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
+       for (i = 0; i < num_ids; i++)
+               wpabuf_put_le16(buf, info_ids[i]);
+       gas_anqp_set_element_len(buf, len_pos);
+       if (extra)
+               wpabuf_put_buf(buf, extra);
+
+       gas_anqp_set_len(buf);
+
+       return buf;
+}
+
+
+static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
+                                     u8 dialog_token,
+                                     enum gas_query_result result,
+                                     const struct wpabuf *adv_proto,
+                                     const struct wpabuf *resp,
+                                     u16 status_code)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
+                    status_code);
+       interworking_next_anqp_fetch(wpa_s);
+}
+
+
+static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
+                                     struct wpa_bss *bss)
+{
+       struct wpabuf *buf;
+       int ret = 0;
+       int res;
+       u16 info_ids[] = {
+               ANQP_CAPABILITY_LIST,
+               ANQP_VENUE_NAME,
+               ANQP_NETWORK_AUTH_TYPE,
+               ANQP_ROAMING_CONSORTIUM,
+               ANQP_IP_ADDR_TYPE_AVAILABILITY,
+               ANQP_NAI_REALM,
+               ANQP_3GPP_CELLULAR_NETWORK,
+               ANQP_DOMAIN_NAME
+       };
+       struct wpabuf *extra = NULL;
+
+       wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
+                  MAC2STR(bss->bssid));
+
+       buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
+                            extra);
+       wpabuf_free(extra);
+       if (buf == NULL)
+               return -1;
+
+       res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
+                           interworking_anqp_resp_cb, wpa_s);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+               ret = -1;
+       } else
+               wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+                          "%u", res);
+
+       wpabuf_free(buf);
+       return ret;
+}
+
+
+static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss *bss;
+       int found = 0;
+       const u8 *ie;
+
+       if (!wpa_s->fetch_anqp_in_progress)
+               return;
+
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               if (!(bss->caps & IEEE80211_CAP_ESS))
+                       continue;
+               ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
+               if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
+                       continue; /* AP does not support Interworking */
+
+               if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
+                       found++;
+                       bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
+                       wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
+                               MACSTR, MAC2STR(bss->bssid));
+                       interworking_anqp_send_req(wpa_s, bss);
+                       break;
+               }
+       }
+
+       if (found == 0) {
+               wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
+               wpa_s->fetch_anqp_in_progress = 0;
+       }
+}
+
+
+int interworking_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);
+
+       return 0;
+}
+
+
+void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->fetch_anqp_in_progress)
+               return;
+
+       wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+                 u16 info_ids[], size_t num_ids)
+{
+       struct wpabuf *buf;
+       int ret = 0;
+       int freq;
+       struct wpa_bss *bss;
+       int res;
+
+       freq = wpa_s->assoc_freq;
+       bss = wpa_bss_get_bssid(wpa_s, dst);
+       if (bss)
+               freq = bss->freq;
+       if (freq <= 0)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
+                  MAC2STR(dst), (unsigned int) num_ids);
+
+       buf = anqp_build_req(info_ids, num_ids, NULL);
+       if (buf == NULL)
+               return -1;
+
+       res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+               ret = -1;
+       } else
+               wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+                          "%u", res);
+
+       wpabuf_free(buf);
+       return ret;
+}
+
+
+static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+                                           const u8 *sa, u16 info_id,
+                                           const u8 *data, size_t slen)
+{
+       const u8 *pos = data;
+       struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
+
+       switch (info_id) {
+       case ANQP_CAPABILITY_LIST:
+               wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+                       " ANQP Capability list", MAC2STR(sa));
+               break;
+       case ANQP_VENUE_NAME:
+               wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+                       " Venue Name", MAC2STR(sa));
+               wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
+               if (bss) {
+                       wpabuf_free(bss->anqp_venue_name);
+                       bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen);
+               }
+               break;
+       case ANQP_NETWORK_AUTH_TYPE:
+               wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+                       " Network Authentication Type information",
+                       MAC2STR(sa));
+               wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
+                                 "Type", pos, slen);
+               if (bss) {
+                       wpabuf_free(bss->anqp_network_auth_type);
+                       bss->anqp_network_auth_type =
+                               wpabuf_alloc_copy(pos, slen);
+               }
+               break;
+       case ANQP_ROAMING_CONSORTIUM:
+               wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+                       " Roaming Consortium list", MAC2STR(sa));
+               wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
+                                 pos, slen);
+               if (bss) {
+                       wpabuf_free(bss->anqp_roaming_consortium);
+                       bss->anqp_roaming_consortium =
+                               wpabuf_alloc_copy(pos, slen);
+               }
+               break;
+       case ANQP_IP_ADDR_TYPE_AVAILABILITY:
+               wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+                       " IP Address Type Availability information",
+                       MAC2STR(sa));
+               wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
+                           pos, slen);
+               if (bss) {
+                       wpabuf_free(bss->anqp_ip_addr_type_availability);
+                       bss->anqp_ip_addr_type_availability =
+                               wpabuf_alloc_copy(pos, slen);
+               }
+               break;
+       case ANQP_NAI_REALM:
+               wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+                       " NAI Realm list", MAC2STR(sa));
+               wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
+               if (bss) {
+                       wpabuf_free(bss->anqp_nai_realm);
+                       bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen);
+               }
+               break;
+       case ANQP_3GPP_CELLULAR_NETWORK:
+               wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+                       " 3GPP Cellular Network information", MAC2STR(sa));
+               wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
+                                 pos, slen);
+               if (bss) {
+                       wpabuf_free(bss->anqp_3gpp);
+                       bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
+               }
+               break;
+       case ANQP_DOMAIN_NAME:
+               wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+                       " Domain Name list", MAC2STR(sa));
+               wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
+               if (bss) {
+                       wpabuf_free(bss->anqp_domain_name);
+                       bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
+               }
+               break;
+       case ANQP_VENDOR_SPECIFIC:
+               if (slen < 3)
+                       return;
+
+               switch (WPA_GET_BE24(pos)) {
+               default:
+                       wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
+                                  "vendor-specific ANQP OUI %06x",
+                                  WPA_GET_BE24(pos));
+                       return;
+               }
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
+                          "%u", info_id);
+               break;
+       }
+}
+
+
+void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
+                 enum gas_query_result result,
+                 const struct wpabuf *adv_proto,
+                 const struct wpabuf *resp, u16 status_code)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       const u8 *pos;
+       const u8 *end;
+       u16 info_id;
+       u16 slen;
+
+       if (result != GAS_QUERY_SUCCESS)
+               return;
+
+       pos = wpabuf_head(adv_proto);
+       if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
+           pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
+               wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
+                          "Protocol in response");
+               return;
+       }
+
+       pos = wpabuf_head(resp);
+       end = pos + wpabuf_len(resp);
+
+       while (pos < end) {
+               if (pos + 4 > end) {
+                       wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
+                       break;
+               }
+               info_id = WPA_GET_LE16(pos);
+               pos += 2;
+               slen = WPA_GET_LE16(pos);
+               pos += 2;
+               if (pos + slen > end) {
+                       wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
+                                  "for Info ID %u", info_id);
+                       break;
+               }
+               interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
+                                               slen);
+               pos += slen;
+       }
+}
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
new file mode 100644 (file)
index 0000000..2a9790b
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef INTERWORKING_H
+#define INTERWORKING_H
+
+enum gas_query_result;
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+                 u16 info_ids[], size_t num_ids);
+void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
+                 enum gas_query_result result,
+                 const struct wpabuf *adv_proto,
+                 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);
+
+#endif /* INTERWORKING_H */
index 7a0fdf8..077d9f1 100644 (file)
@@ -2219,6 +2219,42 @@ static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
 #endif /* CONFIG_P2P */
 
 
+#ifdef CONFIG_INTERWORKING
+static int wpa_cli_cmd_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
+                                 char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "FETCH_ANQP");
+}
+
+
+static int wpa_cli_cmd_stop_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
+                                      char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "STOP_FETCH_ANQP");
+}
+
+
+static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[100];
+       int res;
+
+       if (argc != 2) {
+               printf("Invalid ANQP_GET command: needs two arguments "
+                      "(addr and info id list)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "ANQP_GET %s %s",
+                         argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
 static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc,
                                       char *argv[])
 {
@@ -2613,6 +2649,15 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, cli_cmd_flag_none,
          "[<period> <interval>] = set extended listen timing" },
 #endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+       { "fetch_anqp", wpa_cli_cmd_fetch_anqp, cli_cmd_flag_none,
+         "= fetch ANQP information for all APs" },
+       { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, cli_cmd_flag_none,
+         "= stop fetch_anqp operation" },
+       { "anqp_get", wpa_cli_cmd_anqp_get, cli_cmd_flag_none,
+         "<addr> <info id>[,<info id>]... = request ANQP information" },
+#endif /* CONFIG_INTERWORKING */
        { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, cli_cmd_flag_none,
          "<0/1> = disable/enable automatic reconnection" },
        { "tdls_discover", wpa_cli_cmd_tdls_discover,
index d49c88a..ff3addf 100644 (file)
@@ -589,6 +589,10 @@ struct wpa_supplicant {
        int best_overall_freq;
 
        struct gas_query *gas;
+
+#ifdef CONFIG_INTERWORKING
+       int fetch_anqp_in_progress;
+#endif /* CONFIG_INTERWORKING */
 };