diff --git a/hostapd/config_file.c b/hostapd/config_file.c index fceb33f25..549bba9c4 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2573,6 +2573,34 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->hs20 = atoi(pos); } else if (os_strcmp(buf, "disable_dgaf") == 0) { bss->disable_dgaf = atoi(pos); + } else if (os_strcmp(buf, "hs20_operating_class") == 0) { + u8 *oper_class; + size_t oper_class_len; + oper_class_len = os_strlen(pos); + if (oper_class_len < 2 || (oper_class_len & 0x01)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_operating_class '%s'", + line, pos); + errors++; + return errors; + } + oper_class_len /= 2; + oper_class = os_malloc(oper_class_len); + if (oper_class == NULL) { + errors++; + return errors; + } + if (hexstr2bin(pos, oper_class, oper_class_len)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_operating_class '%s'", + line, pos); + os_free(oper_class); + errors++; + return errors; + } + os_free(bss->hs20_operating_class); + bss->hs20_operating_class = oper_class; + bss->hs20_operating_class_len = oper_class_len; #endif /* CONFIG_HS20 */ } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration " diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index f02c3cd8a..04474ba93 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1400,6 +1400,15 @@ own_ip_addr=127.0.0.1 # forging such frames to other stations in the BSS. #disable_dgaf=1 +# Operating Class Indication +# List of operating classes the BSSes in this ESS use. The Global operating +# classes in Table E-4 of IEEE Std 802.11-2012 Annex E define the values that +# can be used in this. +# format: hexdump of operating class octets +# for example, operating classes 81 (2.4 GHz channels 1-13) and 115 (5 GHz +# channels 36-48): +#hs20_operating_class=5173 + ##### Multiple BSSID support ################################################## # # Above configuration is using the default interface (wlan#, or multi-SSID VLAN diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index c804ec586..8f6a6e844 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -505,6 +505,10 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) #ifdef CONFIG_RADIUS_TEST os_free(conf->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ + +#ifdef CONFIG_HS20 + os_free(conf->hs20_operating_class); +#endif /* CONFIG_HS20 */ } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 80f65c54d..221ebe2df 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -410,6 +410,8 @@ struct hostapd_bss_config { #ifdef CONFIG_HS20 int hs20; int disable_dgaf; + u8 *hs20_operating_class; + u8 hs20_operating_class_len; #endif /* CONFIG_HS20 */ u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c index dfbed39ac..c8bfa111f 100644 --- a/src/ap/gas_serv.c +++ b/src/ap/gas_serv.c @@ -139,6 +139,8 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd, wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); wpabuf_put_u8(buf, 0); /* Reserved */ wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); + if (hapd->conf->hs20_operating_class) + wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); gas_anqp_set_element_len(buf, len); } @@ -251,6 +253,22 @@ static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) } +static void anqp_add_operating_class(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_operating_class) { + 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_OPERATING_CLASS); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_operating_class, + hapd->conf->hs20_operating_class_len); + gas_anqp_set_element_len(buf, len); + } +} + + static struct wpabuf * gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, unsigned int request, @@ -279,6 +297,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, if (request & ANQP_REQ_HS_CAPABILITY_LIST) anqp_add_hs_capab_list(hapd, buf); + if (request & ANQP_REQ_OPERATING_CLASS) + anqp_add_operating_class(hapd, buf); return buf; } @@ -390,6 +410,11 @@ static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List", 1, 0, 0, qi); break; + case HS20_STYPE_OPERATING_CLASS: + set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", + hapd->conf->hs20_operating_class != NULL, + 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 dc4bf05bd..369daad48 100644 --- a/src/ap/gas_serv.h +++ b/src/ap/gas_serv.h @@ -25,6 +25,8 @@ (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST)) #define ANQP_REQ_HS_CAPABILITY_LIST \ (0x10000 << HS20_STYPE_CAPABILITY_LIST) +#define ANQP_REQ_OPERATING_CLASS \ + (0x10000 << HS20_STYPE_OPERATING_CLASS) /* To account for latencies between hostapd and external ANQP processor */ #define GAS_SERV_COMEBACK_DELAY_FUDGE 10