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
This commit is contained in:
Jouni Malinen 2010-10-25 18:24:15 +03:00 committed by Jouni Malinen
parent 1f4c7b6b2a
commit 3071e18109
8 changed files with 114 additions and 3 deletions

View file

@ -1049,6 +1049,13 @@ struct p2p_group_config {
*/ */
void (*ie_update)(void *ctx, struct wpabuf *beacon_ies, void (*ie_update)(void *ctx, struct wpabuf *beacon_ies,
struct wpabuf *proberesp_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);
}; };
/** /**

View file

@ -72,6 +72,7 @@ struct p2p_group * p2p_group_init(struct p2p_data *p2p,
group->group_formation = 1; group->group_formation = 1;
group->beacon_update = 1; group->beacon_update = 1;
p2p_group_update_ies(group); p2p_group_update_ies(group);
group->cfg->idle_update(group->cfg->cb_ctx, 1);
return group; 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) if (group->num_members == group->cfg->max_clients)
group->beacon_update = 1; group->beacon_update = 1;
p2p_group_update_ies(group); p2p_group_update_ies(group);
if (group->num_members == 1)
group->cfg->idle_update(group->cfg->cb_ctx, 0);
return 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) if (group->num_members == group->cfg->max_clients - 1)
group->beacon_update = 1; group->beacon_update = 1;
p2p_group_update_ies(group); p2p_group_update_ies(group);
if (group->num_members == 0)
group->cfg->idle_update(group->cfg->cb_ctx, 1);
} }
} }

View file

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

View file

@ -366,6 +366,18 @@ struct wpa_config {
int persistent_reconnect; int persistent_reconnect;
int p2p_intra_bss; 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 * bss_max_count - Maximum number of BSS entries to keep in memory
*/ */

View file

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

View file

@ -259,6 +259,8 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
#ifdef CONFIG_P2P #ifdef CONFIG_P2P
config->p2p_ssid_postfix = wpa_config_read_reg_string( config->p2p_ssid_postfix = wpa_config_read_reg_string(
hk, TEXT("p2p_ssid_postfix")); hk, TEXT("p2p_ssid_postfix"));
wpa_config_read_reg_dword(hk, TEXT("p2p_group_idle"),
(int *) &config->p2p_group_idle);
#endif /* CONFIG_P2P */ #endif /* CONFIG_P2P */
wpa_config_read_reg_dword(hk, TEXT("bss_max_count"), 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 #ifdef CONFIG_P2P
wpa_config_write_reg_string(hk, "p2p_ssid_postfix", wpa_config_write_reg_string(hk, "p2p_ssid_postfix",
config->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 */ #endif /* CONFIG_P2P */
wpa_config_write_reg_dword(hk, TEXT("bss_max_count"), wpa_config_write_reg_dword(hk, TEXT("bss_max_count"),

View file

@ -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); const u8 *dev_addr, enum p2p_wps_method wps_method);
static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s); 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_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, 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; struct wpa_ssid *ssid;
char *gtype; char *gtype;
const char *reason;
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
ssid = wpa_s->current_ssid; ssid = wpa_s->current_ssid;
if (ssid == NULL) { 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", P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
wpa_s->ifname, wpa_s->cross_connect_uplink); wpa_s->ifname, wpa_s->cross_connect_uplink);
} }
wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s", switch (wpa_s->removal_reason) {
wpa_s->ifname, gtype); 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) { if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
struct wpa_global *global; struct wpa_global *global;
char *ifname; char *ifname;
@ -451,6 +467,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
MAC2STR(go_dev_addr), MAC2STR(go_dev_addr),
persistent ? " [PERSISTENT]" : ""); persistent ? " [PERSISTENT]" : "");
wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_cross_connect_setup(wpa_s);
wpas_p2p_set_group_idle_timeout(wpa_s);
} else { } else {
wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
"%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" " "%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), MAC2STR(go_dev_addr),
persistent ? " [PERSISTENT]" : ""); persistent ? " [PERSISTENT]" : "");
wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_cross_connect_setup(wpa_s);
wpas_p2p_set_group_idle_timeout(wpa_s);
} }
if (persistent) if (persistent)
@ -743,6 +761,7 @@ static void p2p_go_configured(void *ctx, void *data)
wpa_s->parent, ssid, wpa_s->parent, ssid,
wpa_s->parent->own_addr); wpa_s->parent->own_addr);
wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_cross_connect_setup(wpa_s);
wpas_p2p_set_group_idle_timeout(wpa_s);
return; return;
} }
@ -822,6 +841,8 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
C(device_type); C(device_type);
C(config_methods); C(config_methods);
#undef C #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); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
wpa_s->p2p_long_listen = 0; wpa_s->p2p_long_listen = 0;
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); 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); wpas_p2p_remove_pending_group_interface(wpa_s);
/* TODO: remove group interface from the driver if this wpa_s instance /* 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) { while (wpa_s) {
prev = wpa_s; prev = wpa_s;
wpa_s = wpa_s->next; wpa_s = wpa_s->next;
wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
wpas_p2p_group_delete(prev); wpas_p2p_group_delete(prev);
} }
return 0; return 0;
@ -2795,6 +2818,7 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
if (wpa_s == NULL) if (wpa_s == NULL)
return -1; return -1;
wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
wpas_p2p_group_delete(wpa_s); wpas_p2p_group_delete(wpa_s);
return 0; 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, struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
int persistent_group, int persistent_group,
int group_formation) 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->max_clients = wpa_s->conf->max_num_sta;
cfg->cb_ctx = wpa_s; cfg->cb_ctx = wpa_s;
cfg->ie_update = wpas_p2p_ie_update; cfg->ie_update = wpas_p2p_ie_update;
cfg->idle_update = wpas_p2p_idle_update;
group = p2p_group_init(wpa_s->global->p2p, cfg); group = p2p_group_init(wpa_s->global->p2p, cfg);
if (group == NULL) 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, void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
u16 reason_code, const u8 *ie, size_t ie_len) 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); wpas_p2p_disable_cross_connect(wpa_s);
else else
wpas_p2p_enable_cross_connect(wpa_s); 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) void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
{ {
wpas_p2p_disable_cross_connect(wpa_s); wpas_p2p_disable_cross_connect(wpa_s);
if (!wpa_s->ap_iface)
wpas_p2p_set_group_idle_timeout(wpa_s);
} }

View file

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