diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index eab34c5..7fdcef7 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1292,6 +1292,8 @@ struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE	0x0000010000000000ULL
 /** Driver supports P2P Listen offload */
 #define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD     0x0000020000000000ULL
+/** Driver takes care cleaning up of inactive TDLS peers */
+#define WPA_DRIVER_FLAGS_TDLS_INACTIVITY_TIMER 0x0000040000000000ULL
	u64 flags;

 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -3629,6 +3631,15 @@ struct wpa_driver_ops {
	 */
	int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len);

+	/**
+	 * 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);
 };


diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 8b58837..178da86 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -9600,4 +9600,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 #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,
 };
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 6adc3f6..9e7198b 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -362,6 +362,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,

	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM))
		capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_TDLS_INACTIVITY_TIMER))
+		capa->flags |= WPA_DRIVER_FLAGS_TDLS_INACTIVITY_TIMER;
 }


diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 2206941..eaaa138 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -4562,6 +4562,10 @@ enum nl80211_ext_feature_index {
	NL80211_EXT_FEATURE_SCAN_START_TIME,
	NL80211_EXT_FEATURE_BSS_PARENT_TSF,
	NL80211_EXT_FEATURE_SET_SCAN_DWELL,
+	NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+	NL80211_EXT_FEATURE_BEACON_RATE_HT,
+	NL80211_EXT_FEATURE_BEACON_RATE_VHT,
+	NL80211_EXT_FEATURE_TDLS_INACTIVITY_TIMER,

	/* add new features before the definition below */
	NUM_NL80211_EXT_FEATURES,
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index e424168..e3e2b89 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -706,12 +706,12 @@ static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)

 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)
 {
@@ -1750,7 +1750,6 @@ static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
				       peer->supp_oper_classes_len);
 }

-
 static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
				   const u8 *buf, size_t len)
 {
@@ -2102,9 +2101,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);
@@ -2134,7 +2192,23 @@ static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
	}
	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;
 }


@@ -2783,9 +2857,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 "
@@ -2794,6 +2871,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;
 }
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 0b7477f..605f7d2 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -52,7 +52,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,
@@ -72,6 +73,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,
@@ -418,6 +420,7 @@ int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
				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 */
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index f653ba6..576df1e 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -109,6 +109,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
@@ -270,11 +276,15 @@ static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm)
 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;
 }

@@ -343,6 +353,14 @@ wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr)
		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,
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 7d163b1..0c2b3fd 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -3825,6 +3825,8 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
	if (driver_param)
		config->driver_param = os_strdup(driver_param);

+	config->tdls_peer_max_inactivity = DEFAULT_TDLS_PEER_MAX_INACTIVITY;
+
	return config;
 }

@@ -4531,6 +4533,7 @@ static const struct global_parse_data global_fields[] = {
	{ INT(gas_address3), 0 },
	{ INT_RANGE(ftm_responder, 0, 1), 0 },
	{ INT_RANGE(ftm_initiator, 0, 1), 0 },
+	{ INT(tdls_peer_max_inactivity), 0 },
 };

 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 48e64be..2d26b5e 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -41,6 +41,7 @@
 #define DEFAULT_P2P_GO_CTWINDOW 0
 #define DEFAULT_WPA_RSC_RELAXATION 1
 #define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED
+#define DEFAULT_TDLS_PEER_MAX_INACTIVITY 300

 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -1328,6 +1329,15 @@ struct wpa_config {
	 * wpa_supplicant.
	 */
	int ftm_initiator;
+
+	/**
+	 * 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;
 };


diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 7ae1654..8fdb95f 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1344,6 +1344,11 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
		fprintf(f, "ftm_responder=%d\n", config->ftm_responder);
	if (config->ftm_initiator)
		fprintf(f, "ftm_initiator=%d\n", config->ftm_initiator);
+
+	if (config->tdls_peer_max_inactivity != DEFAULT_TDLS_PEER_MAX_INACTIVITY)
+		fprintf(f, "tdls_peer_max_inactivity=%d\n",
+			config->tdls_peer_max_inactivity);
+
 }

 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 220b7ba..d2c980c 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -976,4 +976,12 @@ static inline int wpa_drv_set_default_scan_ies(struct wpa_supplicant *wpa_s,
	return wpa_s->driver->set_default_scan_ies(wpa_s->drv_priv, ies, len);
 }

+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 */
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index b3138e3..37c411e 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1301,6 +1301,18 @@ fast_reauth=1
 # Transitioning between states).
 #fst_llt=100

+# 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
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index f84c8b9..b898c9f 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -610,13 +610,17 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)

 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;
@@ -630,6 +634,11 @@ static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
	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;
 }

@@ -716,6 +725,13 @@ static int wpa_supplicant_tdls_disable_channel_switch(void *ctx, const u8 *addr)
	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 */
@@ -1081,6 +1097,7 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *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;
