HS 2.0R2 AP: Add OSU Providers list ANQP element

hostapd can now be configured to advertise OSU Providers with the
new osu_* confgiuration parameters.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2013-03-17 16:34:09 +02:00 committed by Jouni Malinen
parent f7bd7a01a8
commit ae6d15c722
6 changed files with 327 additions and 1 deletions

View file

@ -1630,6 +1630,142 @@ static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos)
return 0;
}
static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss,
char *pos, int line)
{
size_t slen;
char *str;
str = wpa_config_parse_string(pos, &slen);
if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
return -1;
}
os_memcpy(bss->osu_ssid, str, slen);
bss->osu_ssid_len = slen;
os_free(str);
return 0;
}
static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss,
char *pos, int line)
{
struct hs20_osu_provider *p;
p = os_realloc_array(bss->hs20_osu_providers,
bss->hs20_osu_providers_count + 1, sizeof(*p));
if (p == NULL)
return -1;
bss->hs20_osu_providers = p;
bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count];
bss->hs20_osu_providers_count++;
os_memset(bss->last_osu, 0, sizeof(*p));
bss->last_osu->server_uri = os_strdup(pos);
return 0;
}
static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss,
char *pos, int line)
{
if (bss->last_osu == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
if (parse_lang_string(&bss->last_osu->friendly_name,
&bss->last_osu->friendly_name_count, pos)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'",
line, pos);
return -1;
}
return 0;
}
static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
char *pos, int line)
{
if (bss->last_osu == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
os_free(bss->last_osu->osu_nai);
bss->last_osu->osu_nai = os_strdup(pos);
if (bss->last_osu->osu_nai == NULL)
return -1;
return 0;
}
static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
int line)
{
if (bss->last_osu == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line);
return -1;
}
return 0;
}
static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos,
int line)
{
char **n;
struct hs20_osu_provider *p = bss->last_osu;
if (p == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *));
if (n == NULL)
return -1;
p->icons = n;
p->icons[p->icons_count] = os_strdup(pos);
if (p->icons[p->icons_count] == NULL)
return -1;
p->icons_count++;
return 0;
}
static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss,
char *pos, int line)
{
if (bss->last_osu == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
if (parse_lang_string(&bss->last_osu->service_desc,
&bss->last_osu->service_desc_count, pos)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'",
line, pos);
return -1;
}
return 0;
}
#endif /* CONFIG_HS20 */
@ -2918,6 +3054,27 @@ static int hostapd_config_fill(struct hostapd_config *conf,
errors++;
return errors;
}
} else if (os_strcmp(buf, "osu_ssid") == 0) {
if (hs20_parse_osu_ssid(bss, pos, line) < 0)
errors++;
} else if (os_strcmp(buf, "osu_server_uri") == 0) {
if (hs20_parse_osu_server_uri(bss, pos, line) < 0)
errors++;
} else if (os_strcmp(buf, "osu_friendly_name") == 0) {
if (hs20_parse_osu_friendly_name(bss, pos, line) < 0)
errors++;
} else if (os_strcmp(buf, "osu_nai") == 0) {
if (hs20_parse_osu_nai(bss, pos, line) < 0)
errors++;
} else if (os_strcmp(buf, "osu_method_list") == 0) {
if (hs20_parse_osu_method_list(bss, pos, line) < 0)
errors++;
} else if (os_strcmp(buf, "osu_icon") == 0) {
if (hs20_parse_osu_icon(bss, pos, line) < 0)
errors++;
} else if (os_strcmp(buf, "osu_service_desc") == 0) {
if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
errors++;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_TESTING_OPTIONS
#define PARSE_TEST_PROBABILITY(_val) \

View file

@ -1645,6 +1645,27 @@ own_ip_addr=127.0.0.1
#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
# OSU SSID (see ssid2 for format description)
# This is the SSID used for all OSU connections to all the listed OSU Providers.
#osu_ssid="example"
# OSU Providers
# One or more sets of following parameter. Each OSU provider is started by the
# mandatory osu_server_uri item. The other parameters add information for the
# last added OSU provider.
#
#osu_server_uri=https://example.com/osu/
#osu_friendly_name=eng:Example operator
#osu_friendly_name=fin:Esimerkkipalveluntarjoaja
#osu_nai=anonymous@example.com
#osu_method_list=1 0
#osu_icon=icon32
#osu_icon=icon64
#osu_service_desc=eng:Example services
#osu_service_desc=fin:Esimerkkipalveluja
#
#osu_server_uri=...
##### TESTING OPTIONS #########################################################
#
# The options in this section are only available when the build configuration

View file

@ -529,6 +529,23 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->hs20_connection_capability);
os_free(conf->hs20_operating_class);
os_free(conf->hs20_icons);
if (conf->hs20_osu_providers) {
size_t i;
for (i = 0; i < conf->hs20_osu_providers_count; i++) {
struct hs20_osu_provider *p;
size_t j;
p = &conf->hs20_osu_providers[i];
os_free(p->friendly_name);
os_free(p->server_uri);
os_free(p->method_list);
for (j = 0; j < p->icons_count; j++)
os_free(p->icons[j]);
os_free(p->icons);
os_free(p->osu_nai);
os_free(p->service_desc);
}
os_free(conf->hs20_osu_providers);
}
#endif /* CONFIG_HS20 */
wpabuf_free(conf->vendor_elements);

View file

@ -474,6 +474,20 @@ struct hostapd_bss_config {
char file[256];
} *hs20_icons;
size_t hs20_icons_count;
u8 osu_ssid[HOSTAPD_MAX_SSID_LEN];
size_t osu_ssid_len;
struct hs20_osu_provider {
unsigned int friendly_name_count;
struct hostapd_lang_string *friendly_name;
char *server_uri;
int *method_list;
char **icons;
size_t icons_count;
char *osu_nai;
unsigned int service_desc_count;
struct hostapd_lang_string *service_desc;
} *hs20_osu_providers, *last_osu;
size_t hs20_osu_providers_count;
unsigned int hs20_deauth_req_timeout;
#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);
if (hapd->conf->hs20_operating_class)
wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
if (hapd->conf->hs20_osu_providers_count)
wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
if (hapd->conf->hs20_icons_count)
wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
gas_anqp_set_element_len(buf, len);
@ -517,6 +519,113 @@ static void anqp_add_operating_class(struct hostapd_data *hapd,
}
static void anqp_add_osu_provider(struct wpabuf *buf,
struct hostapd_bss_config *bss,
struct hs20_osu_provider *p)
{
u8 *len, *len2, *count;
unsigned int i;
len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
/* OSU Friendly Name Duples */
len2 = wpabuf_put(buf, 2);
for (i = 0; i < p->friendly_name_count; i++) {
struct hostapd_lang_string *s = &p->friendly_name[i];
wpabuf_put_u8(buf, 3 + s->name_len);
wpabuf_put_data(buf, s->lang, 3);
wpabuf_put_data(buf, s->name, s->name_len);
}
WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
/* OSU Server URI */
if (p->server_uri) {
wpabuf_put_u8(buf, os_strlen(p->server_uri));
wpabuf_put_str(buf, p->server_uri);
} else
wpabuf_put_u8(buf, 0);
/* OSU Method List */
count = wpabuf_put(buf, 1);
for (i = 0; p->method_list[i] >= 0; i++)
wpabuf_put_u8(buf, p->method_list[i]);
*count = i;
/* Icons Available */
len2 = wpabuf_put(buf, 2);
for (i = 0; i < p->icons_count; i++) {
size_t j;
struct hs20_icon *icon = NULL;
for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
0)
icon = &bss->hs20_icons[j];
}
if (!icon)
continue; /* icon info not found */
wpabuf_put_le16(buf, icon->width);
wpabuf_put_le16(buf, icon->height);
wpabuf_put_data(buf, icon->language, 3);
wpabuf_put_u8(buf, os_strlen(icon->type));
wpabuf_put_str(buf, icon->type);
wpabuf_put_u8(buf, os_strlen(icon->name));
wpabuf_put_str(buf, icon->name);
}
WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
/* OSU_NAI */
if (p->osu_nai) {
wpabuf_put_u8(buf, os_strlen(p->osu_nai));
wpabuf_put_str(buf, p->osu_nai);
} else
wpabuf_put_u8(buf, 0);
/* OSU Service Description Duples */
len2 = wpabuf_put(buf, 2);
for (i = 0; i < p->service_desc_count; i++) {
struct hostapd_lang_string *s = &p->service_desc[i];
wpabuf_put_u8(buf, 3 + s->name_len);
wpabuf_put_data(buf, s->lang, 3);
wpabuf_put_data(buf, s->name, s->name_len);
}
WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
}
static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
struct wpabuf *buf)
{
if (hapd->conf->hs20_osu_providers_count) {
size_t i;
u8 *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_OSU_PROVIDERS_LIST);
wpabuf_put_u8(buf, 0); /* Reserved */
/* OSU SSID */
wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
wpabuf_put_data(buf, hapd->conf->osu_ssid,
hapd->conf->osu_ssid_len);
/* Number of OSU Providers */
wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
anqp_add_osu_provider(
buf, hapd->conf,
&hapd->conf->hs20_osu_providers[i]);
}
gas_anqp_set_element_len(buf, len);
}
}
static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
struct wpabuf *buf,
const u8 *name, size_t name_len)
@ -625,6 +734,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
anqp_add_connection_capability(hapd, buf);
if (request & ANQP_REQ_OPERATING_CLASS)
anqp_add_operating_class(hapd, buf);
if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
anqp_add_osu_providers_list(hapd, buf);
if (request & ANQP_REQ_ICON_REQUEST)
anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
#endif /* CONFIG_HS20 */
@ -770,6 +881,10 @@ static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
hapd->conf->hs20_operating_class != NULL,
0, 0, qi);
break;
case HS20_STYPE_OSU_PROVIDERS_LIST:
set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
hapd->conf->hs20_osu_providers_count, 0, 0, qi);
break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
subtype);

View file

@ -1,6 +1,6 @@
/*
* Generic advertisement service (GAS) server
* Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@ -37,6 +37,8 @@
(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
#define ANQP_REQ_OPERATING_CLASS \
(0x10000 << HS20_STYPE_OPERATING_CLASS)
#define ANQP_REQ_OSU_PROVIDERS_LIST \
(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
#define ANQP_REQ_ICON_REQUEST \
(0x10000 << HS20_STYPE_ICON_REQUEST)