diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index df8c08b04..a9ed6510f 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -914,6 +914,9 @@ enum { #define HS20_ANQP_DOMAIN_ID_PRESENT 0x04 #define HS20_VERSION 0x10 /* Release 2 */ +/* WNM-Notification WFA vendors specific subtypes */ +#define HS20_WNM_SUB_REM_NEEDED 0 + /* Wi-Fi Direct (P2P) */ #define P2P_OUI_TYPE 9 diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 759cee47d..54f59b12e 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -168,6 +168,8 @@ extern "C" { /* parameters: */ #define GAS_QUERY_DONE "GAS-QUERY-DONE " +#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION " + #define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START " #define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT " diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 8f204a940..42578b61e 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -4376,6 +4376,12 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) ret = -1; +#ifdef CONFIG_HS20 + /* WNM-Notification */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x1a", 2) < 0) + return -1; +#endif /* CONFIG_HS20 */ + nl80211_mgmt_handle_register_eloop(bss); return ret; diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c index 004024482..d419ffb47 100644 --- a/wpa_supplicant/hs20_supplicant.c +++ b/wpa_supplicant/hs20_supplicant.c @@ -235,3 +235,14 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, break; } } + + +void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s, + const char *url, u8 osu_method) +{ + if (url) + wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s", + osu_method, url); + else + wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION); +} diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h index 7f62a1509..b19355bc1 100644 --- a/wpa_supplicant/hs20_supplicant.h +++ b/wpa_supplicant/hs20_supplicant.h @@ -20,4 +20,7 @@ int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss); int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s, + const char *url, u8 osu_method); + #endif /* HS20_SUPPLICANT_H */ diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 6f586670a..f7eb53734 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -365,11 +365,17 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, return; wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB); - wpabuf_put_u8(buf, 4); + wpabuf_put_u8(buf, 6); wpabuf_put_u8(buf, 0x00); wpabuf_put_u8(buf, 0x00); wpabuf_put_u8(buf, 0x00); wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */ + wpabuf_put_u8(buf, 0x00); +#ifdef CONFIG_HS20 + wpabuf_put_u8(buf, 0x40); /* Bit 46 - WNM-Notification */ +#else /* CONFIG_HS20 */ + wpabuf_put_u8(buf, 0x00); +#endif /* CONFIG_HS20 */ wpabuf_put_u8(buf, WLAN_EID_INTERWORKING); wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 : diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 65b278383..9b7bfa704 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -18,6 +18,7 @@ #include "ctrl_iface.h" #include "bss.h" #include "wnm_sta.h" +#include "hs20_supplicant.h" #define MAX_TFS_IE_LEN 1024 #define WNM_MAX_NEIGHBOR_REPORT 10 @@ -751,6 +752,112 @@ int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, } +static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *data, + int len) +{ + const u8 *pos, *end; + u8 ie, ie_len; + + pos = data; + end = data + len; + + while (pos + 1 < end) { + ie = *pos++; + ie_len = *pos++; + wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u", + ie, ie_len); + if (ie_len > end - pos) { + wpa_printf(MSG_DEBUG, "WNM: Not enough room for " + "subelement"); + break; + } + +#ifdef CONFIG_HS20 + if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && + WPA_GET_BE24(pos) == OUI_WFA && + pos[3] == HS20_WNM_SUB_REM_NEEDED) { + /* Subscription Remediation subelement */ + const u8 *ie_end; + u8 url_len; + char *url; + u8 osu_method; + + wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation " + "subelement"); + ie_end = pos + ie_len; + pos += 4; + url_len = *pos++; + if (url_len == 0) { + wpa_printf(MSG_DEBUG, "WNM: No Server URL included"); + url = NULL; + osu_method = 1; + } else { + if (pos + url_len + 1 > ie_end) { + wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", + url_len, + (int) (ie_end - pos)); + break; + } + url = os_malloc(url_len + 1); + if (url == NULL) + break; + os_memcpy(url, pos, url_len); + url[url_len] = '\0'; + osu_method = pos[url_len]; + } + hs20_rx_subscription_remediation(wpa_s, url, + osu_method); + os_free(url); + break; + } +#endif /* CONFIG_HS20 */ + + pos += ie_len; + } +} + + +static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *frm, int len) +{ + const u8 *pos, *end; + u8 dialog_token, type; + + /* Dialog Token [1] | Type [1] | Subelements */ + + if (len < 2 || sa == NULL) + return; + end = frm + len; + pos = frm; + dialog_token = *pos++; + type = *pos++; + + wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request " + "(dialog_token %u type %u sa " MACSTR ")", + dialog_token, type, MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements", + pos, end - pos); + + if (wpa_s->wpa_state != WPA_COMPLETED || + os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not " + "from our AP - ignore it"); + return; + } + + switch (type) { + case 1: + ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos); + break; + default: + wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown " + "WNM-Notification type %u", type); + break; + } +} + + void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -782,6 +889,9 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, case WNM_SLEEP_MODE_RESP: ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos); break; + case WNM_NOTIFICATION_REQ: + ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); + break; default: wpa_printf(MSG_ERROR, "WNM: Unknown request"); break; diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 6278806a5..07a334a54 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -3181,6 +3181,8 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_match(pos, ESS_DISASSOC_IMMINENT)) { wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_match(pos, WPA_EVENT_TERMINATING)) { printf("wpa_supplicant is terminating - stop monitoring\n"); wpa_cli_quit = 1; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index f67daeac6..71b6b63fd 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1208,6 +1208,10 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) #endif /* CONFIG_INTERWORKING */ break; case 5: /* Bits 40-47 */ +#ifdef CONFIG_HS20 + if (wpa_s->conf->hs20) + *pos |= 0x40; /* Bit 46 - WNM-Notification */ +#endif /* CONFIG_HS20 */ break; case 6: /* Bits 48-55 */ break; @@ -1218,7 +1222,7 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf) { u8 *pos = buf; - u8 len = 4, i; + u8 len = 6, i; if (len < wpa_s->extended_capa_len) len = wpa_s->extended_capa_len;