HS 2.0R2 AP: Add Icon Request and Icon binary File ANQP elements

hostapd can now be configured to provide access for icon files
(hs20_icon config file parameter) for OSU. The hs20_icon data contains
additional meta data about the icon that is not yet used, but it will be
needed for the OSU Providers list ANQP element.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2013-03-17 16:28:59 +02:00 committed by Jouni Malinen
parent 97596f8ed4
commit f7bd7a01a8
6 changed files with 174 additions and 5 deletions

View file

@ -1576,6 +1576,60 @@ static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss,
} }
return 0; return 0;
} }
static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos)
{
struct hs20_icon *icon;
char *end;
icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1,
sizeof(struct hs20_icon));
if (icon == NULL)
return -1;
bss->hs20_icons = icon;
icon = &bss->hs20_icons[bss->hs20_icons_count];
os_memset(icon, 0, sizeof(*icon));
icon->width = atoi(pos);
pos = os_strchr(pos, ':');
if (pos == NULL)
return -1;
pos++;
icon->height = atoi(pos);
pos = os_strchr(pos, ':');
if (pos == NULL)
return -1;
pos++;
end = os_strchr(pos, ':');
if (end == NULL || end - pos > 3)
return -1;
os_memcpy(icon->language, pos, end - pos);
pos = end + 1;
end = os_strchr(pos, ':');
if (end == NULL || end - pos > 255)
return -1;
os_memcpy(icon->type, pos, end - pos);
pos = end + 1;
end = os_strchr(pos, ':');
if (end == NULL || end - pos > 255)
return -1;
os_memcpy(icon->name, pos, end - pos);
pos = end + 1;
if (os_strlen(pos) > 255)
return -1;
os_memcpy(icon->file, pos, os_strlen(pos));
bss->hs20_icons_count++;
return 0;
}
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
@ -2857,6 +2911,13 @@ static int hostapd_config_fill(struct hostapd_config *conf,
os_free(bss->hs20_operating_class); os_free(bss->hs20_operating_class);
bss->hs20_operating_class = oper_class; bss->hs20_operating_class = oper_class;
bss->hs20_operating_class_len = oper_class_len; bss->hs20_operating_class_len = oper_class_len;
} else if (os_strcmp(buf, "hs20_icon") == 0) {
if (hs20_parse_icon(bss, pos) < 0) {
wpa_printf(MSG_ERROR, "Line %d: Invalid "
"hs20_icon '%s'", line, pos);
errors++;
return errors;
}
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
#ifdef CONFIG_TESTING_OPTIONS #ifdef CONFIG_TESTING_OPTIONS
#define PARSE_TEST_PROBABILITY(_val) \ #define PARSE_TEST_PROBABILITY(_val) \

View file

@ -1640,6 +1640,11 @@ own_ip_addr=127.0.0.1
# channels 36-48): # channels 36-48):
#hs20_operating_class=5173 #hs20_operating_class=5173
# OSU icons
# <Icon Width>:<Icon Height>:<Language code>:<Icon Type>:<Name>:<file path>
#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
##### TESTING OPTIONS ######################################################### ##### TESTING OPTIONS #########################################################
# #
# The options in this section are only available when the build configuration # The options in this section are only available when the build configuration

View file

@ -528,6 +528,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->hs20_wan_metrics); os_free(conf->hs20_wan_metrics);
os_free(conf->hs20_connection_capability); os_free(conf->hs20_connection_capability);
os_free(conf->hs20_operating_class); os_free(conf->hs20_operating_class);
os_free(conf->hs20_icons);
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
wpabuf_free(conf->vendor_elements); wpabuf_free(conf->vendor_elements);

View file

@ -465,6 +465,15 @@ struct hostapd_bss_config {
size_t hs20_connection_capability_len; size_t hs20_connection_capability_len;
u8 *hs20_operating_class; u8 *hs20_operating_class;
u8 hs20_operating_class_len; u8 hs20_operating_class_len;
struct hs20_icon {
u16 width;
u16 height;
char language[3];
char type[256];
char name[256];
char file[256];
} *hs20_icons;
size_t hs20_icons_count;
unsigned int hs20_deauth_req_timeout; unsigned int hs20_deauth_req_timeout;
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */

View file

@ -159,6 +159,8 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
if (hapd->conf->hs20_operating_class) if (hapd->conf->hs20_operating_class)
wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
if (hapd->conf->hs20_icons_count)
wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
gas_anqp_set_element_len(buf, len); gas_anqp_set_element_len(buf, len);
} }
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
@ -514,6 +516,62 @@ static void anqp_add_operating_class(struct hostapd_data *hapd,
} }
} }
static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
struct wpabuf *buf,
const u8 *name, size_t name_len)
{
struct hs20_icon *icon;
size_t i;
u8 *len;
wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
name, name_len);
for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
icon = &hapd->conf->hs20_icons[i];
if (name_len == os_strlen(icon->name) &&
os_memcmp(name, icon->name, name_len) == 0)
break;
}
if (i < hapd->conf->hs20_icons_count)
icon = &hapd->conf->hs20_icons[i];
else
icon = NULL;
len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
wpabuf_put_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
wpabuf_put_u8(buf, 0); /* Reserved */
if (icon) {
char *data;
size_t data_len;
data = os_readfile(icon->file, &data_len);
if (data == NULL || data_len > 65535) {
wpabuf_put_u8(buf, 2); /* Download Status:
* Unspecified file error */
wpabuf_put_u8(buf, 0);
wpabuf_put_le16(buf, 0);
} else {
wpabuf_put_u8(buf, 0); /* Download Status: Success */
wpabuf_put_u8(buf, os_strlen(icon->type));
wpabuf_put_str(buf, icon->type);
wpabuf_put_le16(buf, data_len);
wpabuf_put_data(buf, data, data_len);
}
os_free(data);
} else {
wpabuf_put_u8(buf, 1); /* Download Status: File not found */
wpabuf_put_u8(buf, 0);
wpabuf_put_le16(buf, 0);
}
gas_anqp_set_element_len(buf, len);
}
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
@ -521,11 +579,19 @@ static struct wpabuf *
gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
unsigned int request, unsigned int request,
struct gas_dialog_info *di, struct gas_dialog_info *di,
const u8 *home_realm, size_t home_realm_len) const u8 *home_realm, size_t home_realm_len,
const u8 *icon_name, size_t icon_name_len)
{ {
struct wpabuf *buf; struct wpabuf *buf;
size_t len;
buf = wpabuf_alloc(1400); len = 1400;
if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
len += 1000;
if (request & ANQP_REQ_ICON_REQUEST)
len += 65536;
buf = wpabuf_alloc(len);
if (buf == NULL) if (buf == NULL)
return NULL; return NULL;
@ -559,6 +625,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
anqp_add_connection_capability(hapd, buf); anqp_add_connection_capability(hapd, buf);
if (request & ANQP_REQ_OPERATING_CLASS) if (request & ANQP_REQ_OPERATING_CLASS)
anqp_add_operating_class(hapd, buf); anqp_add_operating_class(hapd, buf);
if (request & ANQP_REQ_ICON_REQUEST)
anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
return buf; return buf;
@ -581,6 +649,8 @@ struct anqp_query_info {
unsigned int remote_request; unsigned int remote_request;
const u8 *home_realm_query; const u8 *home_realm_query;
size_t home_realm_query_len; size_t home_realm_query_len;
const u8 *icon_name;
size_t icon_name_len;
u16 remote_delay; u16 remote_delay;
}; };
@ -725,6 +795,23 @@ static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
} }
static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
const u8 *pos, const u8 *end,
struct anqp_query_info *qi)
{
qi->request |= ANQP_REQ_ICON_REQUEST;
qi->icon_name = pos;
qi->icon_name_len = end - pos;
if (hapd->conf->hs20_icons_count) {
wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
"(local)");
} else {
wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
"available");
}
}
static void rx_anqp_vendor_specific(struct hostapd_data *hapd, static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
const u8 *pos, const u8 *end, const u8 *pos, const u8 *end,
struct anqp_query_info *qi) struct anqp_query_info *qi)
@ -769,6 +856,9 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
case HS20_STYPE_NAI_HOME_REALM_QUERY: case HS20_STYPE_NAI_HOME_REALM_QUERY:
rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
break; break;
case HS20_STYPE_ICON_REQUEST:
rx_anqp_hs_icon_request(hapd, pos, end, qi);
break;
default: default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
"%u", subtype); "%u", subtype);
@ -787,7 +877,8 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL, buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
qi->home_realm_query, qi->home_realm_query,
qi->home_realm_query_len); qi->home_realm_query_len,
qi->icon_name, qi->icon_name_len);
wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
buf); buf);
if (!buf) if (!buf)
@ -954,7 +1045,7 @@ void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
if (dialog->sd_resp == NULL) { if (dialog->sd_resp == NULL) {
buf = gas_serv_build_gas_resp_payload(hapd, buf = gas_serv_build_gas_resp_payload(hapd,
dialog->all_requested, dialog->all_requested,
dialog, NULL, 0); dialog, NULL, 0, NULL, 0);
wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
buf); buf);
if (!buf) if (!buf)
@ -1087,7 +1178,7 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
buf = gas_serv_build_gas_resp_payload(hapd, buf = gas_serv_build_gas_resp_payload(hapd,
dialog->all_requested, dialog->all_requested,
dialog, NULL, 0); dialog, NULL, 0, NULL, 0);
wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
buf); buf);
if (!buf) if (!buf)

View file

@ -37,6 +37,8 @@
(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY) (0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
#define ANQP_REQ_OPERATING_CLASS \ #define ANQP_REQ_OPERATING_CLASS \
(0x10000 << HS20_STYPE_OPERATING_CLASS) (0x10000 << HS20_STYPE_OPERATING_CLASS)
#define ANQP_REQ_ICON_REQUEST \
(0x10000 << HS20_STYPE_ICON_REQUEST)
/* To account for latencies between hostapd and external ANQP processor */ /* To account for latencies between hostapd and external ANQP processor */
#define GAS_SERV_COMEBACK_DELAY_FUDGE 10 #define GAS_SERV_COMEBACK_DELAY_FUDGE 10