Interworking: Add ANQP query requests
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.
This commit is contained in:
parent
40eac89023
commit
afc064fe7a
8 changed files with 543 additions and 0 deletions
|
@ -210,6 +210,7 @@ endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef CONFIG_INTERWORKING
|
ifdef CONFIG_INTERWORKING
|
||||||
|
OBJS += interworking.o
|
||||||
CFLAGS += -DCONFIG_INTERWORKING
|
CFLAGS += -DCONFIG_INTERWORKING
|
||||||
NEED_GAS=y
|
NEED_GAS=y
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -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),
|
" SSID '%s'", bss->id, MAC2STR(bss->bssid),
|
||||||
wpa_ssid_txt(bss->ssid, bss->ssid_len));
|
wpa_ssid_txt(bss->ssid, bss->ssid_len));
|
||||||
wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
|
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);
|
os_free(bss);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ struct wpa_scan_res;
|
||||||
#define WPA_BSS_LEVEL_DBM BIT(3)
|
#define WPA_BSS_LEVEL_DBM BIT(3)
|
||||||
#define WPA_BSS_AUTHENTICATED BIT(4)
|
#define WPA_BSS_AUTHENTICATED BIT(4)
|
||||||
#define WPA_BSS_ASSOCIATED BIT(5)
|
#define WPA_BSS_ASSOCIATED BIT(5)
|
||||||
|
#define WPA_BSS_ANQP_FETCH_TRIED BIT(6)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct wpa_bss - BSS table
|
* struct wpa_bss - BSS table
|
||||||
|
@ -65,6 +66,15 @@ struct wpa_bss {
|
||||||
int level;
|
int level;
|
||||||
u64 tsf;
|
u64 tsf;
|
||||||
struct os_time last_update;
|
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 ie_len;
|
||||||
size_t beacon_ie_len;
|
size_t beacon_ie_len;
|
||||||
/* followed by ie_len octets of IEs */
|
/* followed by ie_len octets of IEs */
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "bss.h"
|
#include "bss.h"
|
||||||
#include "scan.h"
|
#include "scan.h"
|
||||||
#include "ctrl_iface.h"
|
#include "ctrl_iface.h"
|
||||||
|
#include "interworking.h"
|
||||||
|
|
||||||
extern struct wpa_driver_ops *wpa_drivers[];
|
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,
|
static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
|
||||||
const char *cmd, char *buf,
|
const char *cmd, char *buf,
|
||||||
size_t buflen)
|
size_t buflen)
|
||||||
|
@ -2013,6 +2049,20 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
|
||||||
pos += ret;
|
pos += ret;
|
||||||
#endif /* CONFIG_P2P */
|
#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;
|
return pos - buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2880,6 +2930,38 @@ static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd)
|
||||||
#endif /* CONFIG_P2P */
|
#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(
|
static int wpa_supplicant_ctrl_iface_sta_autoconnect(
|
||||||
struct wpa_supplicant *wpa_s, char *cmd)
|
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)
|
if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
|
||||||
reply_len = -1;
|
reply_len = -1;
|
||||||
#endif /* CONFIG_P2P */
|
#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)
|
} else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
|
||||||
{
|
{
|
||||||
if (wpa_supplicant_ctrl_iface_ctrl_rsp(
|
if (wpa_supplicant_ctrl_iface_ctrl_rsp(
|
||||||
|
|
353
wpa_supplicant/interworking.c
Normal file
353
wpa_supplicant/interworking.c
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
29
wpa_supplicant/interworking.h
Normal file
29
wpa_supplicant/interworking.h
Normal file
|
@ -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 */
|
|
@ -2219,6 +2219,42 @@ static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
|
||||||
#endif /* CONFIG_P2P */
|
#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,
|
static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc,
|
||||||
char *argv[])
|
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,
|
{ "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, cli_cmd_flag_none,
|
||||||
"[<period> <interval>] = set extended listen timing" },
|
"[<period> <interval>] = set extended listen timing" },
|
||||||
#endif /* CONFIG_P2P */
|
#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,
|
{ "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, cli_cmd_flag_none,
|
||||||
"<0/1> = disable/enable automatic reconnection" },
|
"<0/1> = disable/enable automatic reconnection" },
|
||||||
{ "tdls_discover", wpa_cli_cmd_tdls_discover,
|
{ "tdls_discover", wpa_cli_cmd_tdls_discover,
|
||||||
|
|
|
@ -589,6 +589,10 @@ struct wpa_supplicant {
|
||||||
int best_overall_freq;
|
int best_overall_freq;
|
||||||
|
|
||||||
struct gas_query *gas;
|
struct gas_query *gas;
|
||||||
|
|
||||||
|
#ifdef CONFIG_INTERWORKING
|
||||||
|
int fetch_anqp_in_progress;
|
||||||
|
#endif /* CONFIG_INTERWORKING */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue