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:
parent
97596f8ed4
commit
f7bd7a01a8
6 changed files with 174 additions and 5 deletions
|
@ -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) \
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue