diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 3c7b1edee..197190833 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1286,12 +1286,55 @@ static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd, ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout); } + +static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, + size_t len, int session_timeout) +{ + unsigned int swt; + int warning_time, beacon_int; + + if (len < 1) + return; /* Malformed information */ + os_free(sta->hs20_session_info_url); + sta->hs20_session_info_url = os_malloc(len); + if (sta->hs20_session_info_url == NULL) + return; + swt = pos[0]; + os_memcpy(sta->hs20_session_info_url, pos + 1, len - 1); + sta->hs20_session_info_url[len - 1] = '\0'; + wpa_printf(MSG_DEBUG, "HS 2.0: Session Information URL='%s' SWT=%u " + "(session_timeout=%d)", + sta->hs20_session_info_url, swt, session_timeout); + if (session_timeout < 0) { + wpa_printf(MSG_DEBUG, "HS 2.0: No Session-Timeout set - ignore session info URL"); + return; + } + if (swt == 255) + swt = 1; /* Use one minute as the AP selected value */ + + if ((unsigned int) session_timeout < swt * 60) + warning_time = 0; + else + warning_time = session_timeout - swt * 60; + + beacon_int = hapd->iconf->beacon_int; + if (beacon_int < 1) + beacon_int = 100; /* best guess */ + sta->hs20_disassoc_timer = swt * 60 * 1000 / beacon_int * 125 / 128; + if (sta->hs20_disassoc_timer > 65535) + sta->hs20_disassoc_timer = 65535; + + ap_sta_session_warning_timeout(hapd, sta, warning_time); +} + #endif /* CONFIG_HS20 */ static void ieee802_1x_check_hs20(struct hostapd_data *hapd, struct sta_info *sta, - struct radius_msg *msg) + struct radius_msg *msg, + int session_timeout) { #ifdef CONFIG_HS20 u8 *buf, *pos, *end, type, sublen; @@ -1328,6 +1371,10 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd, case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ: ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen); break; + case RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL: + ieee802_1x_hs20_session_info(hapd, sta, pos, sublen, + session_timeout); + break; } } #endif /* CONFIG_HS20 */ @@ -1492,7 +1539,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, ieee802_1x_store_radius_class(hapd, sta, msg); ieee802_1x_update_sta_identity(hapd, sta, msg); ieee802_1x_update_sta_cui(hapd, sta, msg); - ieee802_1x_check_hs20(hapd, sta, msg); + ieee802_1x_check_hs20(hapd, sta, msg, + session_timeout_set ? + (int) session_timeout : -1); if (sm->eap_if->eapKeyAvailable && !sta->remediation && !sta->hs20_deauth_requested && wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index ada110641..f7af0889f 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -30,11 +30,13 @@ #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "gas_serv.h" +#include "wnm_ap.h" #include "sta_info.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx); +static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx); static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx); static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx); #ifdef CONFIG_IEEE80211W @@ -224,6 +226,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) __func__, MAC2STR(sta->addr)); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta); eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); @@ -267,6 +270,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->radius_cui); os_free(sta->remediation_url); wpabuf_free(sta->hs20_deauth_req); + os_free(sta->hs20_session_info_url); #ifdef CONFIG_SAE sae_clear_data(sta->sae); @@ -522,6 +526,32 @@ void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta) } +static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) +{ +#ifdef CONFIG_WNM + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR, + MAC2STR(sta->addr)); + if (sta->hs20_session_info_url == NULL) + return; + + wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url, + sta->hs20_disassoc_timer); +#endif /* CONFIG_WNM */ +} + + +void ap_sta_session_warning_timeout(struct hostapd_data *hapd, + struct sta_info *sta, int warning_time) +{ + eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta); + eloop_register_timeout(warning_time, 0, ap_handle_session_warning_timer, + hapd, sta); +} + + struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index df86cc257..c0bab6f25 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -130,6 +130,8 @@ struct sta_info { u8 remediation_method; char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */ struct wpabuf *hs20_deauth_req; + char *hs20_session_info_url; + int hs20_disassoc_timer; struct os_reltime connected_time; @@ -173,6 +175,8 @@ void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, u32 session_timeout); void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_session_warning_timeout(struct hostapd_data *hapd, + struct sta_info *sta, int warning_time); struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason); diff --git a/src/radius/radius.h b/src/radius/radius.h index 8cb7a0f17..d8bf21eb1 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -172,6 +172,7 @@ enum { RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION = 2, RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3, RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4, + RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5, }; #ifdef _MSC_VER