From ae6d15c722ce12620a4ccd7386885d4208dbd5ac Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 17 Mar 2013 16:34:09 +0200 Subject: [PATCH] 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 --- hostapd/config_file.c | 157 ++++++++++++++++++++++++++++++++++++++++++ hostapd/hostapd.conf | 21 ++++++ src/ap/ap_config.c | 17 +++++ src/ap/ap_config.h | 14 ++++ src/ap/gas_serv.c | 115 +++++++++++++++++++++++++++++++ src/ap/gas_serv.h | 4 +- 6 files changed, 327 insertions(+), 1 deletion(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 6b1105b7c..ca95747a5 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -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) \ diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index c07913a8b..c745fe83b 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -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 diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index c48f56743..07fd8c34c 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -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); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 8d0aae5ee..7a40b3e90 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -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 */ diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c index 835d0a8b9..fd1041eac 100644 --- a/src/ap/gas_serv.c +++ b/src/ap/gas_serv.c @@ -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); diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h index ff32dc936..7e392b36a 100644 --- a/src/ap/gas_serv.h +++ b/src/ap/gas_serv.h @@ -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)