Add generic GAS request mechanism
The new gas_request and gas_response_get commands can be used to request arbitary GAS queries to be performed. These can be used with ANQP or with other (including vendor specific) advertisement protocols. gas_request <BSSID> <AdvProtoID> [Query] gas_response_get <addr> <dialog token> [offset,length] For example, ANQP query for Capability list in interactive wpa_cli session: > gas_request 02:00:00:00:01:00 00 000102000101 <3>GAS-RESPONSE-INFO addr=02:00:00:00:01:00 dialog_token=0 status_code=0 resp_len=32 > gas_response_get 02:00:00:00:01:00 00 01011c00010102010501070108010c01dddd0c00506f9a110200020304050607 > gas_response_get 02:00:00:00:01:00 00 0,10 01011c00010102010501 > gas_response_get 02:00:00:00:01:00 00 10,10 070108010c01dddd0c00 > gas_response_get 02:00:00:00:01:00 00 20,10 506f9a11020002030405 > gas_response_get 02:00:00:00:01:00 00 30,2 0607 It should be noted that the maximum length of the response buffer is currently 4096 bytes which allows about 2000 bytes of the response data to be fetched with a single gas_response_get command. If the response is longer, it can be fetched in pieces as shown in the example above. Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
b52f084cfa
commit
b1f122964e
9 changed files with 239 additions and 4 deletions
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Generic advertisement service (GAS) (IEEE 802.11u)
|
* Generic advertisement service (GAS) (IEEE 802.11u)
|
||||||
* Copyright (c) 2009, Atheros Communications
|
* Copyright (c) 2009, Atheros Communications
|
||||||
* Copyright (c) 2011, Qualcomm Atheros
|
* Copyright (c) 2011-2012, Qualcomm Atheros
|
||||||
*
|
*
|
||||||
* This software may be distributed under the terms of the BSD license.
|
* This software may be distributed under the terms of the BSD license.
|
||||||
* See README for more details.
|
* See README for more details.
|
||||||
|
@ -31,7 +31,7 @@ gas_build_req(u8 action, u8 dialog_token, size_t size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
|
struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
|
||||||
{
|
{
|
||||||
return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token,
|
return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token,
|
||||||
size);
|
size);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Generic advertisement service (GAS) (IEEE 802.11u)
|
* Generic advertisement service (GAS) (IEEE 802.11u)
|
||||||
* Copyright (c) 2009, Atheros Communications
|
* Copyright (c) 2009, Atheros Communications
|
||||||
* Copyright (c) 2011, Qualcomm Atheros
|
* Copyright (c) 2011-2012, Qualcomm Atheros
|
||||||
*
|
*
|
||||||
* This software may be distributed under the terms of the BSD license.
|
* This software may be distributed under the terms of the BSD license.
|
||||||
* See README for more details.
|
* See README for more details.
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
#ifndef GAS_H
|
#ifndef GAS_H
|
||||||
#define GAS_H
|
#define GAS_H
|
||||||
|
|
||||||
|
struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size);
|
||||||
struct wpabuf * gas_build_comeback_req(u8 dialog_token);
|
struct wpabuf * gas_build_comeback_req(u8 dialog_token);
|
||||||
struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
|
struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
|
||||||
u16 comeback_delay, size_t size);
|
u16 comeback_delay, size_t size);
|
||||||
|
|
|
@ -133,6 +133,8 @@ extern "C" {
|
||||||
#define INTERWORKING_AP "INTERWORKING-AP "
|
#define INTERWORKING_AP "INTERWORKING-AP "
|
||||||
#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
|
#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
|
||||||
|
|
||||||
|
#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO "
|
||||||
|
|
||||||
/* hostapd control interface - fixed message prefixes */
|
/* hostapd control interface - fixed message prefixes */
|
||||||
#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
|
#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
|
||||||
#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
|
#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
|
||||||
|
|
|
@ -3957,6 +3957,122 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
|
||||||
|
|
||||||
return anqp_send_req(wpa_s, dst_addr, id, num_id);
|
return anqp_send_req(wpa_s, dst_addr, id, num_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int gas_request(struct wpa_supplicant *wpa_s, char *cmd)
|
||||||
|
{
|
||||||
|
u8 dst_addr[ETH_ALEN];
|
||||||
|
struct wpabuf *advproto, *query = NULL;
|
||||||
|
int used, ret = -1;
|
||||||
|
char *pos, *end;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
used = hwaddr_aton2(cmd, dst_addr);
|
||||||
|
if (used < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = cmd + used;
|
||||||
|
while (*pos == ' ')
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
/* Advertisement Protocol ID */
|
||||||
|
end = os_strchr(pos, ' ');
|
||||||
|
if (end)
|
||||||
|
len = end - pos;
|
||||||
|
else
|
||||||
|
len = os_strlen(pos);
|
||||||
|
if (len & 0x01)
|
||||||
|
return -1;
|
||||||
|
len /= 2;
|
||||||
|
if (len == 0)
|
||||||
|
return -1;
|
||||||
|
advproto = wpabuf_alloc(len);
|
||||||
|
if (advproto == NULL)
|
||||||
|
return -1;
|
||||||
|
if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (end) {
|
||||||
|
/* Optional Query Request */
|
||||||
|
pos = end + 1;
|
||||||
|
while (*pos == ' ')
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
len = os_strlen(pos);
|
||||||
|
if (len) {
|
||||||
|
if (len & 0x01)
|
||||||
|
goto fail;
|
||||||
|
len /= 2;
|
||||||
|
if (len == 0)
|
||||||
|
goto fail;
|
||||||
|
query = wpabuf_alloc(len);
|
||||||
|
if (query == NULL)
|
||||||
|
goto fail;
|
||||||
|
if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gas_send_request(wpa_s, dst_addr, advproto, query);
|
||||||
|
|
||||||
|
fail:
|
||||||
|
wpabuf_free(advproto);
|
||||||
|
wpabuf_free(query);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
|
||||||
|
size_t buflen)
|
||||||
|
{
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
int dialog_token;
|
||||||
|
int used;
|
||||||
|
char *pos;
|
||||||
|
size_t resp_len, start, requested_len;
|
||||||
|
|
||||||
|
if (!wpa_s->last_gas_resp)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
used = hwaddr_aton2(cmd, addr);
|
||||||
|
if (used < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = cmd + used;
|
||||||
|
while (*pos == ' ')
|
||||||
|
pos++;
|
||||||
|
dialog_token = atoi(pos);
|
||||||
|
|
||||||
|
if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 ||
|
||||||
|
dialog_token != wpa_s->last_gas_dialog_token)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
resp_len = wpabuf_len(wpa_s->last_gas_resp);
|
||||||
|
start = 0;
|
||||||
|
requested_len = resp_len;
|
||||||
|
|
||||||
|
pos = os_strchr(pos, ' ');
|
||||||
|
if (pos) {
|
||||||
|
start = atoi(pos);
|
||||||
|
if (start > resp_len)
|
||||||
|
return os_snprintf(buf, buflen, "FAIL-Invalid range");
|
||||||
|
pos = os_strchr(pos, ',');
|
||||||
|
if (pos == NULL)
|
||||||
|
return -1;
|
||||||
|
pos++;
|
||||||
|
requested_len = atoi(pos);
|
||||||
|
if (start + requested_len > resp_len)
|
||||||
|
return os_snprintf(buf, buflen, "FAIL-Invalid range");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requested_len * 2 + 1 > buflen)
|
||||||
|
return os_snprintf(buf, buflen, "FAIL-Too long response");
|
||||||
|
|
||||||
|
return wpa_snprintf_hex(buf, buflen,
|
||||||
|
wpabuf_head_u8(wpa_s->last_gas_resp) + start,
|
||||||
|
requested_len);
|
||||||
|
}
|
||||||
#endif /* CONFIG_INTERWORKING */
|
#endif /* CONFIG_INTERWORKING */
|
||||||
|
|
||||||
|
|
||||||
|
@ -4432,6 +4548,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
|
||||||
} else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
|
} else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
|
||||||
if (get_anqp(wpa_s, buf + 9) < 0)
|
if (get_anqp(wpa_s, buf + 9) < 0)
|
||||||
reply_len = -1;
|
reply_len = -1;
|
||||||
|
} else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) {
|
||||||
|
if (gas_request(wpa_s, buf + 12) < 0)
|
||||||
|
reply_len = -1;
|
||||||
|
} else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) {
|
||||||
|
reply_len = gas_response_get(wpa_s, buf + 17, reply,
|
||||||
|
reply_size);
|
||||||
#endif /* CONFIG_INTERWORKING */
|
#endif /* CONFIG_INTERWORKING */
|
||||||
#ifdef CONFIG_HS20
|
#ifdef CONFIG_HS20
|
||||||
} else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) {
|
} else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) {
|
||||||
|
|
|
@ -1757,3 +1757,84 @@ int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gas_resp_cb(void *ctx, const u8 *addr, 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;
|
||||||
|
|
||||||
|
wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
|
||||||
|
" dialog_token=%d status_code=%d resp_len=%d",
|
||||||
|
MAC2STR(addr), dialog_token, status_code,
|
||||||
|
resp ? (int) wpabuf_len(resp) : -1);
|
||||||
|
if (!resp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wpabuf_free(wpa_s->last_gas_resp);
|
||||||
|
wpa_s->last_gas_resp = wpabuf_dup(resp);
|
||||||
|
if (wpa_s->last_gas_resp == NULL)
|
||||||
|
return;
|
||||||
|
os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
|
||||||
|
wpa_s->last_gas_dialog_token = dialog_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
|
||||||
|
const struct wpabuf *adv_proto,
|
||||||
|
const struct wpabuf *query)
|
||||||
|
{
|
||||||
|
struct wpabuf *buf;
|
||||||
|
int ret = 0;
|
||||||
|
int freq;
|
||||||
|
struct wpa_bss *bss;
|
||||||
|
int res;
|
||||||
|
size_t len;
|
||||||
|
u8 query_resp_len_limit = 0, pame_bi = 0;
|
||||||
|
|
||||||
|
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, "GAS request to " MACSTR " (freq %d MHz)",
|
||||||
|
MAC2STR(dst), freq);
|
||||||
|
wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
|
||||||
|
wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
|
||||||
|
|
||||||
|
len = 3 + wpabuf_len(adv_proto) + 2;
|
||||||
|
if (query)
|
||||||
|
len += wpabuf_len(query);
|
||||||
|
buf = gas_build_initial_req(0, len);
|
||||||
|
if (buf == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Advertisement Protocol IE */
|
||||||
|
wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
|
||||||
|
wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
|
||||||
|
wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
|
||||||
|
(pame_bi ? 0x80 : 0));
|
||||||
|
wpabuf_put_buf(buf, adv_proto);
|
||||||
|
|
||||||
|
/* GAS Query */
|
||||||
|
if (query) {
|
||||||
|
wpabuf_put_le16(buf, wpabuf_len(query));
|
||||||
|
wpabuf_put_buf(buf, query);
|
||||||
|
} else
|
||||||
|
wpabuf_put_le16(buf, 0);
|
||||||
|
|
||||||
|
res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
|
||||||
|
if (res < 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request");
|
||||||
|
ret = -1;
|
||||||
|
} else
|
||||||
|
wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token "
|
||||||
|
"%u", res);
|
||||||
|
|
||||||
|
wpabuf_free(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Interworking (IEEE 802.11u)
|
* Interworking (IEEE 802.11u)
|
||||||
* Copyright (c) 2011, Qualcomm Atheros
|
* Copyright (c) 2011-2012, Qualcomm Atheros
|
||||||
*
|
*
|
||||||
* This software may be distributed under the terms of the BSD license.
|
* This software may be distributed under the terms of the BSD license.
|
||||||
* See README for more details.
|
* See README for more details.
|
||||||
|
@ -17,6 +17,9 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
|
||||||
enum gas_query_result result,
|
enum gas_query_result result,
|
||||||
const struct wpabuf *adv_proto,
|
const struct wpabuf *adv_proto,
|
||||||
const struct wpabuf *resp, u16 status_code);
|
const struct wpabuf *resp, u16 status_code);
|
||||||
|
int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
|
||||||
|
const struct wpabuf *adv_proto,
|
||||||
|
const struct wpabuf *query);
|
||||||
int interworking_fetch_anqp(struct wpa_supplicant *wpa_s);
|
int interworking_fetch_anqp(struct wpa_supplicant *wpa_s);
|
||||||
void interworking_stop_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_select(struct wpa_supplicant *wpa_s, int auto_select);
|
||||||
|
|
|
@ -2019,6 +2019,20 @@ static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
||||||
{
|
{
|
||||||
return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv);
|
return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_cli_cmd_gas_request(struct wpa_ctrl *ctrl, int argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
return wpa_cli_cmd(ctrl, "GAS_REQUEST", 2, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_cli_cmd_gas_response_get(struct wpa_ctrl *ctrl, int argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
return wpa_cli_cmd(ctrl, "GAS_RESPONSE_GET", 2, argc, argv);
|
||||||
|
}
|
||||||
#endif /* CONFIG_INTERWORKING */
|
#endif /* CONFIG_INTERWORKING */
|
||||||
|
|
||||||
|
|
||||||
|
@ -2493,6 +2507,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
|
||||||
{ "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss,
|
{ "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss,
|
||||||
cli_cmd_flag_none,
|
cli_cmd_flag_none,
|
||||||
"<addr> <info id>[,<info id>]... = request ANQP information" },
|
"<addr> <info id>[,<info id>]... = request ANQP information" },
|
||||||
|
{ "gas_request", wpa_cli_cmd_gas_request, wpa_cli_complete_bss,
|
||||||
|
cli_cmd_flag_none,
|
||||||
|
"<addr> <AdvProtoID> [QueryReq] = GAS request" },
|
||||||
|
{ "gas_response_get", wpa_cli_cmd_gas_response_get,
|
||||||
|
wpa_cli_complete_bss, cli_cmd_flag_none,
|
||||||
|
"<addr> <dialog token> [start,len] = Fetch last GAS response" },
|
||||||
#endif /* CONFIG_INTERWORKING */
|
#endif /* CONFIG_INTERWORKING */
|
||||||
#ifdef CONFIG_HS20
|
#ifdef CONFIG_HS20
|
||||||
{ "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, wpa_cli_complete_bss,
|
{ "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, wpa_cli_complete_bss,
|
||||||
|
|
|
@ -468,6 +468,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
|
||||||
|
|
||||||
ext_password_deinit(wpa_s->ext_pw);
|
ext_password_deinit(wpa_s->ext_pw);
|
||||||
wpa_s->ext_pw = NULL;
|
wpa_s->ext_pw = NULL;
|
||||||
|
|
||||||
|
wpabuf_free(wpa_s->last_gas_resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -585,6 +585,10 @@ struct wpa_supplicant {
|
||||||
int disconnect_reason;
|
int disconnect_reason;
|
||||||
|
|
||||||
struct ext_password_data *ext_pw;
|
struct ext_password_data *ext_pw;
|
||||||
|
|
||||||
|
struct wpabuf *last_gas_resp;
|
||||||
|
u8 last_gas_addr[ETH_ALEN];
|
||||||
|
u8 last_gas_dialog_token;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue