Browse Source

P2P: Add mechanism for timing out idle groups

A new configuration parameter, p2p_group_idle, can now be used to set
idle timeout value for P2P groups in seconds (0 = no timeout). If set,
this values is used to remove P2P group (both GO and P2P client)
interfaces after the group has been idle (no clients/GO seen) for the
configuration duration.

The P2P-GROUP-REMOVED event is now indicating the reason for group
removal when known. For example:
P2P-GROUP-REMOVED wlan0 GO reason=REQUESTED
P2P-GROUP-REMOVED wlan1 client reason=IDLE
Jouni Malinen 14 years ago
parent
commit
3071e18109

+ 7 - 0
src/p2p/p2p.h

@@ -1049,6 +1049,13 @@ struct p2p_group_config {
 	 */
 	void (*ie_update)(void *ctx, struct wpabuf *beacon_ies,
 			  struct wpabuf *proberesp_ies);
+
+	/**
+	 * idle_update - Notification of changes in group idle state
+	 * @ctx: Callback context from cb_ctx
+	 * @idle: Whether the group is idle (no associated stations)
+	 */
+	void (*idle_update)(void *ctx, int idle);
 };
 
 /**

+ 5 - 0
src/p2p/p2p_group.c

@@ -72,6 +72,7 @@ struct p2p_group * p2p_group_init(struct p2p_data *p2p,
 	group->group_formation = 1;
 	group->beacon_update = 1;
 	p2p_group_update_ies(group);
+	group->cfg->idle_update(group->cfg->cb_ctx, 1);
 
 	return group;
 }
@@ -338,6 +339,8 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
 	if (group->num_members == group->cfg->max_clients)
 		group->beacon_update = 1;
 	p2p_group_update_ies(group);
+	if (group->num_members == 1)
+		group->cfg->idle_update(group->cfg->cb_ctx, 0);
 
 	return 0;
 }
@@ -396,6 +399,8 @@ void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
 		if (group->num_members == group->cfg->max_clients - 1)
 			group->beacon_update = 1;
 		p2p_group_update_ies(group);
+		if (group->num_members == 0)
+			group->cfg->idle_update(group->cfg->cb_ctx, 1);
 	}
 }
 

+ 1 - 0
wpa_supplicant/config.c

@@ -2403,6 +2403,7 @@ static const struct global_parse_data global_fields[] = {
 	{ STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
 	{ INT_RANGE(persistent_reconnect, 0, 1), 0 },
 	{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
+	{ INT(p2p_group_idle), 0 },
 #endif /* CONFIG_P2P */
 	{ FUNC(country), CFG_CHANGED_COUNTRY },
 	{ INT(bss_max_count), 0 },

+ 12 - 0
wpa_supplicant/config.h

@@ -366,6 +366,18 @@ struct wpa_config {
 	int persistent_reconnect;
 	int p2p_intra_bss;
 
+	/**
+	 * p2p_group_idle - Maximum idle time in seconds for P2P group
+	 *
+	 * This value controls how long a P2P group is maintained after there
+	 * is no other members in the group. As a GO, this means no associated
+	 * stations in the group. As a P2P client, this means no GO seen in
+	 * scan results. The maximum idle time is specified in seconds with 0
+	 * indicating no time limit, i.e., the P2P group remains in active
+	 * state indefinitely until explicitly removed.
+	 */
+	unsigned int p2p_group_idle;
+
 	/**
 	 * bss_max_count - Maximum number of BSS entries to keep in memory
 	 */

+ 2 - 1
wpa_supplicant/config_file.c

@@ -675,7 +675,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
 			config->persistent_reconnect);
 	if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS)
 		fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
-
+	if (config->p2p_group_idle)
+		fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
 #endif /* CONFIG_P2P */
 	if (config->country[0] && config->country[1]) {
 		fprintf(f, "country=%c%c\n",

+ 4 - 0
wpa_supplicant/config_winreg.c

@@ -259,6 +259,8 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
 #ifdef CONFIG_P2P
 	config->p2p_ssid_postfix = wpa_config_read_reg_string(
 		hk, TEXT("p2p_ssid_postfix"));
+	wpa_config_read_reg_dword(hk, TEXT("p2p_group_idle"),
+				  (int *) &config->p2p_group_idle);
 #endif /* CONFIG_P2P */
 
 	wpa_config_read_reg_dword(hk, TEXT("bss_max_count"),
@@ -596,6 +598,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
 #ifdef CONFIG_P2P
 	wpa_config_write_reg_string(hk, "p2p_ssid_postfix",
 				    config->p2p_ssid_postfix);
+	wpa_config_write_reg_dword(hk, TEXT("p2p_group_idle"),
+				   config->p2p_group_idle, 0);
 #endif /* CONFIG_P2P */
 
 	wpa_config_write_reg_dword(hk, TEXT("bss_max_count"),

+ 77 - 2
wpa_supplicant/p2p_supplicant.c

@@ -46,6 +46,8 @@ static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
 			 const u8 *dev_addr, enum p2p_wps_method wps_method);
 static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
 
 
 static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
@@ -175,6 +177,9 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid;
 	char *gtype;
+	const char *reason;
+
+	eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 
 	ssid = wpa_s->current_ssid;
 	if (ssid == NULL) {
@@ -208,8 +213,19 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
 			P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
 			wpa_s->ifname, wpa_s->cross_connect_uplink);
 	}
-	wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s",
-		wpa_s->ifname, gtype);
+	switch (wpa_s->removal_reason) {
+	case P2P_GROUP_REMOVAL_REQUESTED:
+		reason = " reason=REQUESTED";
+		break;
+	case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
+		reason = " reason=IDLE";
+		break;
+	default:
+		reason = "";
+		break;
+	}
+	wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s",
+		wpa_s->ifname, gtype, reason);
 	if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
 		struct wpa_global *global;
 		char *ifname;
@@ -451,6 +467,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
 			MAC2STR(go_dev_addr),
 			persistent ? " [PERSISTENT]" : "");
 		wpas_p2p_cross_connect_setup(wpa_s);
+		wpas_p2p_set_group_idle_timeout(wpa_s);
 	} else {
 		wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
 			"%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
@@ -460,6 +477,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
 			MAC2STR(go_dev_addr),
 			persistent ? " [PERSISTENT]" : "");
 		wpas_p2p_cross_connect_setup(wpa_s);
+		wpas_p2p_set_group_idle_timeout(wpa_s);
 	}
 
 	if (persistent)
@@ -743,6 +761,7 @@ static void p2p_go_configured(void *ctx, void *data)
 				wpa_s->parent, ssid,
 				wpa_s->parent->own_addr);
 		wpas_p2p_cross_connect_setup(wpa_s);
+		wpas_p2p_set_group_idle_timeout(wpa_s);
 		return;
 	}
 
@@ -822,6 +841,8 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
 	C(device_type);
 	C(config_methods);
 #undef C
+
+	d->p2p_group_idle = s->p2p_group_idle;
 }
 
 
@@ -2244,6 +2265,7 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
 	eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
 	wpa_s->p2p_long_listen = 0;
 	eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 	wpas_p2p_remove_pending_group_interface(wpa_s);
 
 	/* TODO: remove group interface from the driver if this wpa_s instance
@@ -2782,6 +2804,7 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
 		while (wpa_s) {
 			prev = wpa_s;
 			wpa_s = wpa_s->next;
+			wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
 			wpas_p2p_group_delete(prev);
 		}
 		return 0;
@@ -2795,6 +2818,7 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
 	if (wpa_s == NULL)
 		return -1;
 
+	wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
 	wpas_p2p_group_delete(wpa_s);
 
 	return 0;
@@ -3001,6 +3025,19 @@ static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
 }
 
 
+static void wpas_p2p_idle_update(void *ctx, int idle)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (!wpa_s->ap_iface)
+		return;
+	wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
+	if (idle)
+		wpas_p2p_set_group_idle_timeout(wpa_s);
+	else
+		eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+}
+
+
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 				       int persistent_group,
 				       int group_formation)
@@ -3021,6 +3058,7 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 		cfg->max_clients = wpa_s->conf->max_num_sta;
 	cfg->cb_ctx = wpa_s;
 	cfg->ie_update = wpas_p2p_ie_update;
+	cfg->idle_update = wpas_p2p_idle_update;
 
 	group = p2p_group_init(wpa_s->global->p2p, cfg);
 	if (group == NULL)
@@ -3375,6 +3413,39 @@ int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
 }
 
 
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (wpa_s->conf->p2p_group_idle == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - "
+			   "disabled");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
+		   "group");
+	wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT;
+	wpas_p2p_group_delete(wpa_s);
+}
+
+
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
+{
+	eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+	if (wpa_s->conf->p2p_group_idle == 0)
+		return;
+
+	if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
+		return;
+
+	wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
+		   wpa_s->conf->p2p_group_idle);
+	eloop_register_timeout(wpa_s->conf->p2p_group_idle, 0,
+			       wpas_p2p_group_idle_timeout, wpa_s, NULL);
+}
+
+
 void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
 			   u16 reason_code, const u8 *ie, size_t ie_len)
 {
@@ -3557,12 +3628,16 @@ void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
 		wpas_p2p_disable_cross_connect(wpa_s);
 	else
 		wpas_p2p_enable_cross_connect(wpa_s);
+	if (!wpa_s->ap_iface)
+		eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 }
 
 
 void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
 {
 	wpas_p2p_disable_cross_connect(wpa_s);
+	if (!wpa_s->ap_iface)
+		wpas_p2p_set_group_idle_timeout(wpa_s);
 }
 
 

+ 6 - 0
wpa_supplicant/wpa_supplicant_i.h

@@ -514,6 +514,12 @@ struct wpa_supplicant {
 	 * Uplink interface name for cross connection
 	 */
 	char cross_connect_uplink[100];
+
+	enum {
+		P2P_GROUP_REMOVAL_UNKNOWN,
+		P2P_GROUP_REMOVAL_REQUESTED,
+		P2P_GROUP_REMOVAL_IDLE_TIMEOUT
+	} removal_reason;
 #endif /* CONFIG_P2P */
 
 	struct wpa_ssid *bgscan_ssid;