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:
Jouni Malinen 2012-08-27 18:13:10 +03:00 committed by Jouni Malinen
parent b52f084cfa
commit b1f122964e
9 changed files with 239 additions and 4 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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 "

View file

@ -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) {

View file

@ -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;
}

View file

@ -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);

View file

@ -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,

View file

@ -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);
} }

View file

@ -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;
}; };