From 3071e18109502e7504ab6e3d19a2de102e1f7050 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 25 Oct 2010 18:24:15 +0300 Subject: [PATCH] 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 --- src/p2p/p2p.h | 7 +++ src/p2p/p2p_group.c | 5 ++ wpa_supplicant/config.c | 1 + wpa_supplicant/config.h | 12 +++++ wpa_supplicant/config_file.c | 3 +- wpa_supplicant/config_winreg.c | 4 ++ wpa_supplicant/p2p_supplicant.c | 79 ++++++++++++++++++++++++++++++- wpa_supplicant/wpa_supplicant_i.h | 6 +++ 8 files changed, 114 insertions(+), 3 deletions(-) diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index ffe02a2a8..a4e5d62b9 100644 --- a/src/p2p/p2p.h +++ b/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); }; /** diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 22a1ca73c..7d2588598 100644 --- a/src/p2p/p2p_group.c +++ b/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); } } diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 24bbf0fdf..081f7e065 100644 --- a/wpa_supplicant/config.c +++ b/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 }, diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index ff4cf22de..f288b2c97 100644 --- a/wpa_supplicant/config.h +++ b/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 */ diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index a203e4331..2431e84f3 100644 --- a/wpa_supplicant/config_file.c +++ b/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", diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index b2078af4f..ef95a2945 100644 --- a/wpa_supplicant/config_winreg.c +++ b/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"), diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index ab298f2b0..7a7524e87 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/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); } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 66f544e3a..f5e80bab0 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/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;