--- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1934,6 +1934,7 @@ struct wpa_driver_capa { /** Driver supports Extended Key ID */ #define WPA_DRIVER_FLAGS_EXTENDED_KEY_ID 0x8000000000000000ULL u64 flags; +#define WPA_DRIVER_FLAGS_TDLS_INACTIVITY_TIMER 0x0000040000000000ULL /** Driver supports a separate control port RX for EAPOL frames */ #define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX 0x0000000000000001ULL @@ -4402,6 +4403,16 @@ struct wpa_driver_ops { const u8 *bssid); /** + * get_peer_inactive_time - Get peer inactive time + * @priv: Private driver interface data + * @addr: MAC address of the peer + * + * Get the peer inactive time in seconds, for now used only for + * TDLS peers. + */ + int (*get_peer_inactive_time)(void *priv, const u8 *addr); + + /** * update_connect_params - Update the connection parameters * @priv: Private driver interface data * @params: Association parameters --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -11797,6 +11797,7 @@ const struct wpa_driver_ops wpa_driver_n #endif /* CONFIG_DRIVER_NL80211_QCA */ .configure_data_frame_filters = nl80211_configure_data_frame_filters, .get_ext_capab = nl80211_get_ext_capab, + .get_peer_inactive_time = i802_get_inact_sec, .update_connect_params = nl80211_update_connection_params, .send_external_auth_status = nl80211_send_external_auth_status, .set_4addr_mode = nl80211_set_4addr_mode, --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -723,12 +723,12 @@ static void wpa_tdls_peer_clear(struct w static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { + eloop_cancel_timeout(wpa_tdls_peer_inactivity_handler, sm, peer); wpa_tdls_peer_clear(sm, peer); wpa_tdls_peer_remove_from_list(sm, peer); os_free(peer); } - static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer, struct wpa_tdls_lnkid *lnkid) { @@ -1782,7 +1782,6 @@ static int tdls_nonce_set(const u8 *nonc return 0; } - static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { @@ -2143,9 +2142,68 @@ error: return -1; } +void wpa_tdls_peer_inactivity_handler(void *eloop_data, void *user_data) +{ + struct wpa_sm *sm = eloop_data; + struct wpa_tdls_peer *peer = user_data; + int inactive_time = 0; + unsigned long next_time = 0; + u8 teardown = 0; + + if (peer->tpk_in_progress) + return; + + inactive_time = wpa_sm_tdls_get_peer_inactive_time(sm, + peer->addr); + + if (inactive_time == -1) { + wpa_printf(MSG_DEBUG, "TDLS: failed to get station info " + "for " MACSTR, MAC2STR(peer->addr)); + + next_time = sm->tdls_peer_max_inactive_time; + } else if (inactive_time == -ENOENT) { + wpa_printf(MSG_DEBUG, "TDLS: failed to find kernel entry for " + "peer " MACSTR ", teardown the link", + MAC2STR(peer->addr)); + + teardown = 1; + } else if (inactive_time < sm->tdls_peer_max_inactive_time) { + wpa_printf(MSG_DEBUG, "TDLS: peer " MACSTR " has been inactive " + "for %d seconds", MAC2STR(peer->addr), inactive_time); + + next_time = sm->tdls_peer_max_inactive_time - inactive_time; + } else { + wpa_printf(MSG_DEBUG, "TDLS: peer " MACSTR " has been inactive " + "for long time: %d seconds, max inactive time: %d seconds", + MAC2STR(peer->addr), inactive_time, + sm->tdls_peer_max_inactive_time); + + teardown = 1; + } + + if (teardown) { + wpa_printf(MSG_DEBUG, "TDLS: tearing down TDLS link with peer " + MACSTR " (due to inactivity)", MAC2STR(peer->addr)); + + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + return; + } + + if (next_time) { + wpa_printf(MSG_DEBUG, "TDLS: reschedule inactivity timer for " + "peer " MACSTR " (%lu seconds)", MAC2STR(peer->addr), + next_time); + + eloop_register_timeout(next_time, 0, wpa_tdls_peer_inactivity_handler, + sm, peer); + } +} static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { + int ret; + peer->tpk_success = 1; peer->tpk_in_progress = 0; eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); @@ -2175,7 +2233,23 @@ static int wpa_tdls_enable_link(struct w } peer->reconfig_key = 0; - return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); + ret = wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); + + /* Do not trigger supplicant inactivity logic if driver + * already supports it + */ + if (!ret && !sm->tdls_inactivity_teardown && + (sm->tdls_peer_max_inactive_time > 0)) { + wpa_printf(MSG_DEBUG, "TDLS: register inactivity handler for " + "peer "MACSTR " (%d seconds)", MAC2STR(peer->addr), + sm->tdls_peer_max_inactive_time); + + eloop_register_timeout(sm->tdls_peer_max_inactive_time, + 0, wpa_tdls_peer_inactivity_handler, + sm, peer); + } + + return ret; } @@ -2824,9 +2898,12 @@ int wpa_tdls_init(struct wpa_sm *sm) */ if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported, &sm->tdls_external_setup, - &sm->tdls_chan_switch) < 0) { + &sm->tdls_chan_switch, + &sm->tdls_inactivity_teardown, + &sm->tdls_peer_max_inactive_time) < 0) { sm->tdls_supported = 1; sm->tdls_external_setup = 0; + sm->tdls_inactivity_teardown = 1; } wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by " @@ -2835,6 +2912,8 @@ int wpa_tdls_init(struct wpa_sm *sm) sm->tdls_external_setup ? "external" : "internal"); wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching", sm->tdls_chan_switch ? "supports" : "does not support"); + wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS peer inactivity teardown", + sm->tdls_inactivity_teardown ? "supports" : "does not support"); return 0; } --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -58,7 +58,8 @@ struct wpa_sm_ctx { int (*mark_authenticated)(void *ctx, const u8 *target_ap); #ifdef CONFIG_TDLS int (*tdls_get_capa)(void *ctx, int *tdls_supported, - int *tdls_ext_setup, int *tdls_chan_switch); + int *tdls_ext_setup, int *tdls_chan_switch, + int *tdls_inact_teardown, int *tdls_inact_timeout); int (*send_tdls_mgmt)(void *ctx, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, @@ -78,6 +79,7 @@ struct wpa_sm_ctx { void *ctx, const u8 *addr, u8 oper_class, const struct hostapd_freq_params *params); int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr); + int (*tdls_get_peer_inact_time)(void *ctx, const u8 *addr); #endif /* CONFIG_TDLS */ void (*set_rekey_offload)(void *ctx, const u8 *kek, size_t kek_len, const u8 *kck, size_t kck_len, @@ -476,6 +478,7 @@ int wpa_tdls_enable_chan_switch(struct w u8 oper_class, struct hostapd_freq_params *freq_params); int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr); +void wpa_tdls_peer_inactivity_handler(void *eloop_data, void *user_data); #ifdef CONFIG_TDLS_TESTING extern unsigned int tdls_testing; #endif /* CONFIG_TDLS_TESTING */ --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -121,6 +121,12 @@ struct wpa_sm { /* The driver supports TDLS channel switching */ int tdls_chan_switch; + + /* Driver supports TDLS peer inactivity teardown */ + int tdls_inactivity_teardown; + + /* Supplicant TDLS inactivity logic uses this timeout */ + int tdls_peer_max_inactive_time; #endif /* CONFIG_TDLS */ #ifdef CONFIG_IEEE80211R @@ -333,11 +339,15 @@ static inline void wpa_sm_set_rekey_offl static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm, int *tdls_supported, int *tdls_ext_setup, - int *tdls_chan_switch) + int *tdls_chan_switch, + int *tdls_inact_teardown, + int *tdls_inact_timeout) { if (sm->ctx->tdls_get_capa) return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported, - tdls_ext_setup, tdls_chan_switch); + tdls_ext_setup, tdls_chan_switch, + tdls_inact_teardown, + tdls_inact_timeout); return -1; } @@ -406,6 +416,14 @@ wpa_sm_tdls_disable_channel_switch(struc return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr); return -1; } + +static inline int +wpa_sm_tdls_get_peer_inactive_time(struct wpa_sm *sm, const u8 *addr) +{ + if (sm->ctx->tdls_get_peer_inact_time) + return sm->ctx->tdls_get_peer_inact_time(sm->ctx->ctx, addr); + return -1; +} #endif /* CONFIG_TDLS */ static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm, --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -4492,6 +4492,8 @@ struct wpa_config * wpa_config_alloc_emp config->driver_param = os_strdup(driver_param); config->gas_rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME; + config->tdls_peer_max_inactivity = DEFAULT_TDLS_PEER_MAX_INACTIVITY; + return config; } @@ -5240,6 +5242,7 @@ static const struct global_parse_data gl { STR(dpp_mud_url), 0 }, #endif /* CONFIG_DPP */ { INT_RANGE(coloc_intf_reporting, 0, 1), 0 }, + { INT(tdls_peer_max_inactivity), 0 }, #ifdef CONFIG_WNM { INT_RANGE(disable_btm, 0, 1), CFG_CHANGED_DISABLE_BTM }, { INT_RANGE(extended_key_id, 0, 1), 0 }, --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -44,6 +44,7 @@ #define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED #define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75 #define DEFAULT_OCE_SUPPORT OCE_STA +#define DEFAULT_TDLS_PEER_MAX_INACTIVITY 300 #define DEFAULT_EXTENDED_KEY_ID 0 #include "config_ssid.h" @@ -1536,6 +1537,15 @@ struct wpa_config { int coloc_intf_reporting; /** + * tdls_peer_max_inactivity - Timeout to detect TDLS peer inactivity + * + * Time duration in seconds to detect TDLS peer inactivity and clean up + * of inactive TDLS peers. + * Default Value: 300 seconds. + */ + int tdls_peer_max_inactivity; + + /** * p2p_device_random_mac_addr - P2P Device MAC address policy default * * 0 = use permanent MAC address --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -1591,6 +1591,9 @@ static void wpa_config_write_global(FILE if (config->coloc_intf_reporting) fprintf(f, "coloc_intf_reporting=%d\n", config->coloc_intf_reporting); + if (config->tdls_peer_max_inactivity != DEFAULT_TDLS_PEER_MAX_INACTIVITY) + fprintf(f, "tdls_peer_max_inactivity=%d\n", + config->tdls_peer_max_inactivity); if (config->p2p_device_random_mac_addr) fprintf(f, "p2p_device_random_mac_addr=%d\n", config->p2p_device_random_mac_addr); --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -1115,4 +1115,12 @@ static inline int wpa_drv_dpp_listen(str return wpa_s->driver->dpp_listen(wpa_s->drv_priv, enable); } +static inline int wpa_drv_get_inactive_time(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (!wpa_s->driver->get_peer_inactive_time) + return -1; + return wpa_s->driver->get_peer_inactive_time(wpa_s->drv_priv, addr); +} + #endif /* DRIVER_I_H */ --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -1646,6 +1646,18 @@ fast_reauth=1 # In STA mode it defines the EDMG channel for connection (if supported by AP). #edmg_channel=9 +# Inactivity limit for TDLS peers +# +# If a TDLS peer is inactive for tdls_peer_max_inactivity seconds, +# the direct link with that TDLS peer will be terminated. This does +# not break the communication between the peers, they can still +# communicate through the AP, only the direct link will be torn down. +# +# When teardown happens, the STA entries of the TDLS peers will be +# flushed out so that the TDLS link can be re-established if required. +# default: 300 seconds +#tdls_peer_max_inactivity=300 + # Example blocks: # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -728,13 +728,17 @@ static int wpa_supplicant_mark_authentic static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported, int *tdls_ext_setup, - int *tdls_chan_switch) + int *tdls_chan_switch, + int *tdls_inact_teardown, + int *tdls_inact_timeout) { struct wpa_supplicant *wpa_s = ctx; *tdls_supported = 0; *tdls_ext_setup = 0; *tdls_chan_switch = 0; + *tdls_inact_teardown = 0; + *tdls_inact_timeout = 0; if (!wpa_s->drv_capa_known) return -1; @@ -748,6 +752,11 @@ static int wpa_supplicant_tdls_get_capa( if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH) *tdls_chan_switch = 1; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_INACTIVITY_TIMER) + *tdls_inact_teardown = 1; + + *tdls_inact_timeout = wpa_s->conf->tdls_peer_max_inactivity; + return 0; } @@ -834,6 +843,13 @@ static int wpa_supplicant_tdls_disable_c return wpa_drv_tdls_disable_channel_switch(wpa_s, addr); } +static int wpa_supplicant_tdls_get_peer_inact_time(void *ctx, const u8 *addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_get_inactive_time(wpa_s, addr); +} + #endif /* CONFIG_TDLS */ #endif /* CONFIG_NO_WPA */ @@ -1388,6 +1404,7 @@ int wpa_supplicant_init_wpa(struct wpa_s wpa_supplicant_tdls_enable_channel_switch; ctx->tdls_disable_channel_switch = wpa_supplicant_tdls_disable_channel_switch; + ctx->tdls_get_peer_inact_time = wpa_supplicant_tdls_get_peer_inact_time; #endif /* CONFIG_TDLS */ ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload; ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;