diff options
author | Naveen Singh <nasingh@google.com> | 2016-04-08 00:29:47 -0700 |
---|---|---|
committer | Patrik Flykt <patrik.flykt@linux.intel.com> | 2016-05-30 10:46:06 +0300 |
commit | 6245582d0dc9a3f47a6880dabf437ee7c351caef (patch) | |
tree | 15020feddadca65ae2fcf9a97a5fe24a7c673e8e | |
parent | d593e995c7c75b9d74e411bf3691b5adb6c56179 (diff) | |
download | connman-6245582d0dc9a3f47a6880dabf437ee7c351caef.tar.gz |
Fast-reconnect and Band-steering support
This patch contains new implementations for following:
1. Band Steering and Load balancing Support: Enterprise AP sometimes
denies association with assoc status code 17. The reason AP sends
this code, is to steer to some other AP (in a different band) or for
doing load balancing (in case this AP is heavily loaded). This will not
work with current connman as connman would take this disconnect
notification as normal disconnect and disable the network. This act
would cause wpa_s search for the next BSSID stop. The next time connect
attempt is issued from connman, the story would repeat again. The idea
of this patch is to not interfere with BSSID selection logic of wpa_s.
wpa_s was sending disconnect reason code on DBUS as a part of
PropertyChanged signal but there was no notification for assoc status
code.
In this commit id of hostapd (c7fb678f3109e62af1ef39be9b12bf8370c35bde)
wpa_s is also sending assoc status code as a part of DBUS
PropertyChanged signal. Idea here is that on a disconnect notification
from wpa_s connman would look into the assoc status code and if it is
set to 17, it would not proceed. This will let wpa_s continue with its
own BSSID selection logic.
2. Optimize DBUS call - In case network path is Not NULL (means a
profile is already plumbed in wpa_s), network_remove should only
be called if the new network is different from what is sitting in
wpa_s. The notion of new network here is SSID, security type and
passphrase. If the new network is same as the one in wpa_s, no need
to do remove, add and select. This would save 3 DBUS calls to wpa_s.
3. Fast Reconnect: On receiving a deauth with reason code 6, 7 or 4
wpa_supplicant will immediately try to connect back. Now with current
implementation of connman, a disconnect notification will cause network
to get disabled and connection procedure would stop. This also impacts
the roaming time in case disconnect is because of any other reason code.
The act of disabling network severly affects wpa_s connection state
machine as it would generate a deauth to current network when half way
the connection was done. The idea here is that we do not disable network
on disconnect and let wpa_s keep trying to find out that network. Only
when connman has another network this network would be removed and new
network would be added.
-rw-r--r-- | gsupplicant/gsupplicant.h | 4 | ||||
-rw-r--r-- | gsupplicant/supplicant.c | 137 | ||||
-rw-r--r-- | plugins/wifi.c | 53 |
3 files changed, 185 insertions, 9 deletions
diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h index a2a76053..26fd2caf 100644 --- a/gsupplicant/gsupplicant.h +++ b/gsupplicant/gsupplicant.h @@ -353,6 +353,10 @@ struct _GSupplicantCallbacks { GSupplicantPeerState state); void (*peer_request) (GSupplicantPeer *peer); void (*debug) (const char *str); + void (*disconnect_reasoncode)(GSupplicantInterface *interface, + int reasoncode); + void (*assoc_status_code)(GSupplicantInterface *interface, + int reasoncode); }; typedef struct _GSupplicantCallbacks GSupplicantCallbacks; diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c index c8fbef66..09a3ac5c 100644 --- a/gsupplicant/supplicant.c +++ b/gsupplicant/supplicant.c @@ -150,6 +150,13 @@ struct _GSupplicantWpsCredentials { char *key; }; +struct added_network_information { + char * ssid; + GSupplicantSecurity security; + char * passphrase; + char * private_passphrase; +}; + struct _GSupplicantInterface { char *path; char *network_path; @@ -181,6 +188,7 @@ struct _GSupplicantInterface { GHashTable *bss_mapping; void *data; const char *pending_peer_path; + struct added_network_information network_info; }; struct g_supplicant_bss { @@ -387,6 +395,70 @@ static GSupplicantState string2state(const char *state) return G_SUPPLICANT_STATE_UNKNOWN; } +static bool compare_network_parameters(GSupplicantInterface *interface, + GSupplicantSSID *ssid) +{ + if (memcmp(interface->network_info.ssid, ssid->ssid, ssid->ssid_len)) + return FALSE; + + if (interface->network_info.security != ssid->security) + return FALSE; + + if (interface->network_info.passphrase && + g_strcmp0(interface->network_info.passphrase, + ssid->passphrase) != 0) { + return FALSE; + } + + if (interface->network_info.private_passphrase && + g_strcmp0(interface->network_info.private_passphrase, + ssid->private_key_passphrase) != 0) { + return FALSE; + } + + return TRUE; +} + +static void remove_network_information(GSupplicantInterface * interface) +{ + g_free(interface->network_info.ssid); + g_free(interface->network_info.passphrase); + g_free(interface->network_info.private_passphrase); + interface->network_info.ssid = NULL; + interface->network_info.passphrase = NULL; + interface->network_info.private_passphrase = NULL; +} + +static int store_network_information(GSupplicantInterface * interface, + GSupplicantSSID *ssid) +{ + interface->network_info.ssid = g_malloc(ssid->ssid_len + 1); + if (interface->network_info.ssid != NULL) { + memcpy(interface->network_info.ssid, ssid->ssid, + ssid->ssid_len); + interface->network_info.ssid[ssid->ssid_len] = '\0'; + } else { + return -ENOMEM; + } + + interface->network_info.security = ssid->security; + + if ((ssid->security == G_SUPPLICANT_SECURITY_WEP || + ssid->security == G_SUPPLICANT_SECURITY_PSK || + ssid->security == G_SUPPLICANT_SECURITY_NONE) && + ssid->passphrase) { + interface->network_info.passphrase = g_strdup(ssid->passphrase); + } + + if (ssid->security == G_SUPPLICANT_SECURITY_IEEE8021X && + ssid->private_key_passphrase) { + interface->network_info.private_passphrase = + g_strdup(ssid->private_key_passphrase); + } + + return 0; +} + static void callback_system_ready(void) { if (system_ready) @@ -576,6 +648,33 @@ static void callback_peer_request(GSupplicantPeer *peer) callbacks_pointer->peer_request(peer); } +static void callback_disconnect_reason_code(GSupplicantInterface *interface, + int reason_code) +{ + if (!callbacks_pointer) + return; + + if (!callbacks_pointer->disconnect_reasoncode) + return; + + if (reason_code != 0) + callbacks_pointer->disconnect_reasoncode(interface, + reason_code); +} + +static void callback_assoc_status_code(GSupplicantInterface *interface, + int status_code) +{ + if (!callbacks_pointer) + return; + + if (!callbacks_pointer->assoc_status_code) + return; + + callbacks_pointer->assoc_status_code(interface, status_code); + +} + static void remove_group(gpointer data) { GSupplicantGroup *group = data; @@ -619,6 +718,7 @@ static void remove_interface(gpointer data) g_free(interface->ifname); g_free(interface->driver); g_free(interface->bridge); + remove_network_information(interface); g_free(interface); } @@ -2135,9 +2235,22 @@ static void interface_property(const char *key, DBusMessageIter *iter, } else if (g_strcmp0(key, "Networks") == 0) { supplicant_dbus_array_foreach(iter, interface_network_added, interface); - } else + } else if (g_strcmp0(key, "DisconnectReason") == 0) { + int reason_code; + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) { + dbus_message_iter_get_basic(iter, &reason_code); + callback_disconnect_reason_code(interface, reason_code); + } + } else if (g_strcmp0(key, "AssocStatusCode") == 0) { + int status_code; + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) { + dbus_message_iter_get_basic(iter, &status_code); + callback_assoc_status_code(interface, status_code); + } + } else { SUPPLICANT_DBG("key %s type %c", key, dbus_message_iter_get_arg_type(iter)); + } } static void scan_network_update(DBusMessageIter *iter, void *user_data) @@ -4111,6 +4224,8 @@ static void interface_add_network_result(const char *error, interface->network_path = g_strdup(path); + store_network_information(interface, data->ssid); + supplicant_dbus_method_call(data->interface->path, SUPPLICANT_INTERFACE ".Interface", "SelectNetwork", interface_select_network_params, @@ -4708,6 +4823,20 @@ int g_supplicant_interface_connect(GSupplicantInterface *interface, g_free(data->path); dbus_free(data); + /* + * If this add network is for the same network for + * which wpa_supplicant already has a profile then do + * not need to add another profile. Only if the + * profile that needs to get added is different from + * what is there in wpa_s delete the current one. A + * network is identified by its SSID, security_type + * and passphrase (private passphrase in case security + * type is 802.11x). + */ + if (compare_network_parameters(interface, ssid)) { + return -EALREADY; + } + intf_data = dbus_malloc0(sizeof(*intf_data)); if (!intf_data) return -ENOMEM; @@ -4753,8 +4882,10 @@ static void network_remove_result(const char *error, result = -ECONNABORTED; } - g_free(data->interface->network_path); - data->interface->network_path = NULL; + g_free(data->interface->network_path); + data->interface->network_path = NULL; + + remove_network_information(data->interface); if (data->network_remove_in_progress == TRUE) { data->network_remove_in_progress = FALSE; diff --git a/plugins/wifi.c b/plugins/wifi.c index bb1cabcf..43c6d4da 100644 --- a/plugins/wifi.c +++ b/plugins/wifi.c @@ -71,6 +71,9 @@ #define P2P_LISTEN_PERIOD 500 #define P2P_LISTEN_INTERVAL 2000 +#define ASSOC_STATUS_NO_CLIENT 17 +#define LOAD_SHAPING_MAX_RETRIES 3 + static struct connman_technology *wifi_technology = NULL; static struct connman_technology *p2p_technology = NULL; @@ -128,6 +131,7 @@ struct wifi_data { unsigned flags; unsigned int watch; int retries; + int load_shaping_retries; struct hidden_params *hidden; bool postpone_hidden; struct wifi_tethering_info *tethering_param; @@ -144,6 +148,8 @@ struct wifi_data { bool p2p_connecting; bool p2p_device; int servicing; + int disconnect_code; + int assoc_code; }; static GList *iface_list = NULL; @@ -2291,6 +2297,19 @@ static bool handle_wps_completion(GSupplicantInterface *interface, return true; } +static bool handle_assoc_status_code(GSupplicantInterface *interface, + struct wifi_data *wifi) +{ + if (wifi->state == G_SUPPLICANT_STATE_ASSOCIATING && + wifi->assoc_code == ASSOC_STATUS_NO_CLIENT && + wifi->load_shaping_retries < LOAD_SHAPING_MAX_RETRIES) { + wifi->load_shaping_retries ++; + return TRUE; + } + wifi->load_shaping_retries = 0; + return FALSE; +} + static bool handle_4way_handshake_failure(GSupplicantInterface *interface, struct connman_network *network, struct wifi_data *wifi) @@ -2382,6 +2401,10 @@ static void interface_state(GSupplicantInterface *interface) break; connman_network_set_connected(network, true); + + wifi->disconnect_code = 0; + wifi->assoc_code = 0; + wifi->load_shaping_retries = 0; break; case G_SUPPLICANT_STATE_DISCONNECTED: @@ -2399,6 +2422,9 @@ static void interface_state(GSupplicantInterface *interface) if (is_idle(wifi)) break; + if (handle_assoc_status_code(interface, wifi)) + break; + /* If previous state was 4way-handshake, then * it's either: psk was incorrect and thus we retry * or if we reach the maximum retries we declare the @@ -2407,12 +2433,6 @@ static void interface_state(GSupplicantInterface *interface) network, wifi)) break; - /* We disable the selected network, if not then - * wpa_supplicant will loop retrying */ - if (g_supplicant_interface_enable_selected_network(interface, - FALSE) != 0) - DBG("Could not disable selected network"); - connman_network_set_connected(network, false); connman_network_set_associating(network, false); wifi->disconnecting = false; @@ -2935,6 +2955,25 @@ static void debug(const char *str) connman_debug("%s", str); } +static void disconnect_reasoncode(GSupplicantInterface *interface, + int reasoncode) +{ + struct wifi_data *wifi = g_supplicant_interface_get_data(interface); + + if (wifi != NULL) { + wifi->disconnect_code = reasoncode; + } +} + +static void assoc_status_code(GSupplicantInterface *interface, int status_code) +{ + struct wifi_data *wifi = g_supplicant_interface_get_data(interface); + + if (wifi != NULL) { + wifi->assoc_code = status_code; + } +} + static const GSupplicantCallbacks callbacks = { .system_ready = system_ready, .system_killed = system_killed, @@ -2953,6 +2992,8 @@ static const GSupplicantCallbacks callbacks = { .peer_changed = peer_changed, .peer_request = peer_request, .debug = debug, + .disconnect_reasoncode = disconnect_reasoncode, + .assoc_status_code = assoc_status_code, }; |