diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index b3a9d55f7..1d66aa76d 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -227,6 +227,12 @@ CFLAGS += -DCONFIG_P2P_STRICT endif endif +ifdef CONFIG_HS20 +OBJS += hs20_supplicant.o +CFLAGS += -DCONFIG_HS20 +CONFIG_INTERWORKING=y +endif + ifdef CONFIG_INTERWORKING OBJS += interworking.o CFLAGS += -DCONFIG_INTERWORKING diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 792316d1c..a37311635 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -53,6 +53,12 @@ static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) wpabuf_free(bss->anqp_3gpp); wpabuf_free(bss->anqp_domain_name); #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + wpabuf_free(bss->hs20_operator_friendly_name); + wpabuf_free(bss->hs20_wan_metrics); + wpabuf_free(bss->hs20_connection_capability); + wpabuf_free(bss->hs20_operating_class); +#endif /* CONFIG_HS20 */ os_free(bss); } diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index 37ca72c94..65e962b6b 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -69,6 +69,12 @@ struct wpa_bss { struct wpabuf *anqp_3gpp; struct wpabuf *anqp_domain_name; #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + struct wpabuf *hs20_operator_friendly_name; + struct wpabuf *hs20_wan_metrics; + struct wpabuf *hs20_connection_capability; + struct wpabuf *hs20_operating_class; +#endif /* CONFIG_HS20 */ size_t ie_len; size_t beacon_ie_len; /* followed by ie_len octets of IEs */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 904c142a3..25d8cc919 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -2523,6 +2523,14 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos = anqp_add_hex(pos, end, "anqp_3gpp", bss->anqp_3gpp); pos = anqp_add_hex(pos, end, "anqp_domain_name", bss->anqp_domain_name); +#ifdef CONFIG_HS20 + pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name", + bss->hs20_operator_friendly_name); + pos = anqp_add_hex(pos, end, "hs20_wan_metrics", + bss->hs20_wan_metrics); + pos = anqp_add_hex(pos, end, "hs20_connection_capability", + bss->hs20_connection_capability); +#endif /* CONFIG_HS20 */ } #endif /* CONFIG_INTERWORKING */ diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 7f66576fa..2d6bc8184 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -481,3 +481,6 @@ CONFIG_PEERKEY=y # external networks (GAS/ANQP to learn more about the networks and network # selection based on available credentials). #CONFIG_INTERWORKING=y + +# Hotspot 2.0 +#CONFIG_HS20=y diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c new file mode 100644 index 000000000..5800d55ea --- /dev/null +++ b/wpa_supplicant/hs20_supplicant.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2009, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "config.h" +#include "bss.h" +#include "gas_query.h" +#include "interworking.h" +#include "hs20_supplicant.h" + + +struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, + size_t payload_len) +{ + struct wpabuf *buf; + u8 *len_pos; + + buf = gas_anqp_build_initial_req(0, 100 + payload_len); + if (buf == NULL) + return NULL; + + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) { + wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); + wpabuf_put_u8(buf, 0); /* Reserved */ + if (payload) + wpabuf_put_data(buf, payload, payload_len); + } else { + u8 i; + wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + for (i = 0; i < 32; i++) { + if (stypes & BIT(i)) + wpabuf_put_u8(buf, i); + } + } + gas_anqp_set_element_len(buf, len_pos); + + gas_anqp_set_len(buf); + + return buf; +} + + +int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, + const u8 *payload, size_t payload_len) +{ + struct wpabuf *buf; + int ret = 0; + int freq; + struct wpa_bss *bss; + int res; + + freq = wpa_s->assoc_freq; + bss = wpa_bss_get_bssid(wpa_s, dst); + if (bss) + freq = bss->freq; + if (freq <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for " + "subtypes 0x%x", MAC2STR(dst), stypes); + + buf = hs20_build_anqp_req(stypes, payload, payload_len); + if (buf == NULL) + return -1; + + res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); + if (res < 0) { + wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + ret = -1; + } else + wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " + "%u", res); + + wpabuf_free(buf); + return ret; +} + + +void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *data, size_t slen) +{ + const u8 *pos = data; + u8 subtype; + struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa); + + if (slen < 2) + return; + + subtype = *pos++; + slen--; + + pos++; /* Reserved */ + slen--; + + switch (subtype) { + case HS20_STYPE_CAPABILITY_LIST: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " HS Capability List", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen); + break; + case HS20_STYPE_OPERATOR_FRIENDLY_NAME: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " Operator Friendly Name", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen); + if (bss) { + wpabuf_free(bss->hs20_operator_friendly_name); + bss->hs20_operator_friendly_name = + wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_WAN_METRICS: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " WAN Metrics", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "WAN Metrics", pos, slen); + if (bss) { + wpabuf_free(bss->hs20_wan_metrics); + bss->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_CONNECTION_CAPABILITY: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " Connection Capability", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen); + if (bss) { + wpabuf_free(bss->hs20_connection_capability); + bss->hs20_connection_capability = + wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_OPERATING_CLASS: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " Operating Class", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen); + if (bss) { + wpabuf_free(bss->hs20_operating_class); + bss->hs20_operating_class = + wpabuf_alloc_copy(pos, slen); + } + break; + default: + wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype); + break; + } +} diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h new file mode 100644 index 000000000..075e52cf8 --- /dev/null +++ b/wpa_supplicant/hs20_supplicant.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HS20_SUPPLICANT_H +#define HS20_SUPPLICANT_H + +int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, + const u8 *payload, size_t payload_len); +struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, + size_t payload_len); +void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *data, size_t slen); + +#endif /* HS20_SUPPLICANT_H */