This enables service to autoconnect to hidden wifi service. Fixes BMC#11860 --- plugins/wifi.c | 270 +++++++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 200 insertions(+), 70 deletions(-)
diff --git a/plugins/wifi.c b/plugins/wifi.c index 2f1f7a6..1dfbc44 100644 --- a/plugins/wifi.c +++ b/plugins/wifi.c @@ -61,6 +61,14 @@ #define BGSCAN_DEFAULT "simple:30:-45:300" #define AUTOSCAN_DEFAULT "exponential:3:300" +/* + * Max number of SSIDs that can be scanned, from wps_s 0.8 or later. + * In wpa_s 0.7x the limit is 4. + * The value is only used if wpa_supplicant does not return any max limit + * for number of scannable SSIDs. + */ +#define WPAS_MAX_SCAN_SSIDS 16 + static struct connman_technology *wifi_technology = NULL; struct hidden_params { @@ -252,6 +260,146 @@ static void wifi_remove(struct connman_device *device) g_free(wifi); } +static int add_scan_param(gchar *hex_ssid, int freq, + GSupplicantScanParams *scan_data, + int driver_max_scan_ssids) +{ + unsigned int i; + struct scan_ssid *scan_ssid; + + if (driver_max_scan_ssids > scan_data->num_ssids && hex_ssid != NULL) { + gchar *ssid; + unsigned int j = 0, hex; + size_t hex_ssid_len = strlen(hex_ssid); + + ssid = g_try_malloc0(hex_ssid_len / 2); + if (ssid == NULL) + return -ENOMEM; + + for (i = 0; i < hex_ssid_len; i += 2) { + sscanf(hex_ssid + i, "%02x", &hex); + ssid[j++] = hex; + } + + scan_ssid = g_try_new(struct scan_ssid, 1); + if (scan_ssid == NULL) { + g_free(ssid); + return -ENOMEM; + } + + memcpy(scan_ssid->ssid, ssid, j); + scan_ssid->ssid_len = j; + scan_data->ssids = g_slist_prepend(scan_data->ssids, + scan_ssid); + + scan_data->num_ssids++; + + g_free(ssid); + } else + return -EINVAL; + + scan_data->ssids = g_slist_reverse(scan_data->ssids); + + if (scan_data->freqs == NULL) { + scan_data->freqs = g_try_malloc0(sizeof(uint16_t) * + scan_data->num_ssids); + if (scan_data->freqs == NULL) { + g_slist_free_full(scan_data->ssids, g_free); + return -ENOMEM; + } + } else { + scan_data->freqs = g_try_realloc(scan_data->freqs, + sizeof(uint16_t) * scan_data->num_ssids); + if (scan_data->freqs == NULL) { + g_slist_free_full(scan_data->ssids, g_free); + return -ENOMEM; + } + scan_data->freqs[scan_data->num_ssids - 1] = 0; + } + + /* Don't add duplicate entries */ + for (i = 0; i < scan_data->num_ssids; i++) { + if (scan_data->freqs[i] == 0) { + scan_data->freqs[i] = freq; + break; + } else if (scan_data->freqs[i] == freq) + break; + } + + return 0; +} + +static int get_hidden_connections(int max_ssids, + GSupplicantScanParams *scan_data) +{ + GKeyFile *keyfile; + gchar **services; + gchar *str; + char *ssid; + int i, freq; + gboolean hidden; + int num_ssids = 0, add_param_failed = 0; + + services = connman_storage_get_services(); + for (i = 0; services && services[i]; i++) { + if (strncmp(services[i], "wifi_", 5) != 0) + continue; + + keyfile = connman_storage_load_service(services[i]); + + hidden = g_key_file_get_boolean(keyfile, + services[i], "Hidden", NULL); + if (hidden == FALSE) { + g_key_file_free(keyfile); + continue; + } + + str = g_key_file_get_string(keyfile, + services[i], "Favorite", NULL); + if (str == NULL || g_strcmp0(str, "true")) { + g_free(str); + g_key_file_free(keyfile); + continue; + } + g_free(str); + + str = g_key_file_get_string(keyfile, + services[i], "AutoConnect", NULL); + if (str == NULL || g_strcmp0(str, "true")) { + g_free(str); + g_key_file_free(keyfile); + continue; + } + g_free(str); + + ssid = g_key_file_get_string(keyfile, + services[i], "SSID", NULL); + + freq = g_key_file_get_integer(keyfile, services[i], + "Frequency", NULL); + + if (add_scan_param(ssid, freq, scan_data, max_ssids) < 0) { + str = g_key_file_get_string(keyfile, + services[i], "Name", NULL); + DBG("Cannot scan %s (%s)", ssid, str); + g_free(str); + add_param_failed++; + } + + num_ssids++; + + g_key_file_free(keyfile); + } + + if (add_param_failed > 0) + connman_warn("Unable to scan %d out of %d SSIDs (max is %d)", + add_param_failed, num_ssids, max_ssids); + + g_strfreev(services); + + return num_ssids > max_ssids ? max_ssids : num_ssids; +} + static int throw_wifi_scan(struct connman_device *device, GSupplicantInterfaceCallback callback) { @@ -275,13 +423,60 @@ static int throw_wifi_scan(struct connman_device *device, return ret; } +static void hidden_scan_callback(int result, + GSupplicantInterface *interface, void *user_data) +{ + struct connman_device *device = user_data; + + DBG("result %d", result); + + connman_device_set_scanning(device, FALSE); + connman_device_unref(device); +} + static void autoscan_scan_callback(int result, GSupplicantInterface *interface, void *user_data) { struct connman_device *device = user_data; + struct wifi_data *wifi = connman_device_get_data(device); + int driver_max_ssids; - DBG(""); + DBG("result %d", result); + + /* + * Scan hidden networks so that we can autoconnect to them. + * If the supplicant does not give the max limit (this means that + * relevant patch is missing from wpa_s code), then use + * the default value. + */ + driver_max_ssids = g_supplicant_interface_get_max_scan_ssids( + wifi->interface); + if (driver_max_ssids == 0) { + driver_max_ssids = WPAS_MAX_SCAN_SSIDS; + DBG("max ssids not found, using default %d", driver_max_ssids); + } else + DBG("max ssids %d", driver_max_ssids); + + if (driver_max_ssids > 0) { + GSupplicantScanParams *scan_params; + int ret; + + scan_params = g_try_malloc0(sizeof(GSupplicantScanParams)); + if (scan_params == NULL) + goto out; + + if (get_hidden_connections(driver_max_ssids, + scan_params) > 0) { + ret = g_supplicant_interface_scan(wifi->interface, + scan_params, + hidden_scan_callback, + device); + if (ret == 0) + return; + } + } +out: connman_device_set_scanning(device, FALSE); connman_device_unref(device); } @@ -490,75 +685,6 @@ static void scan_callback(int result, GSupplicantInterface *interface, start_autoscan(device); } -static int add_scan_param(gchar *hex_ssid, int freq, - GSupplicantScanParams *scan_data, - int driver_max_scan_ssids) -{ - unsigned int i; - struct scan_ssid *scan_ssid; - - if (driver_max_scan_ssids > scan_data->num_ssids && hex_ssid != NULL) { - gchar *ssid; - unsigned int j = 0, hex; - size_t hex_ssid_len = strlen(hex_ssid); - - ssid = g_try_malloc0(hex_ssid_len / 2); - if (ssid == NULL) - return -ENOMEM; - - for (i = 0; i < hex_ssid_len; i += 2) { - sscanf(hex_ssid + i, "%02x", &hex); - ssid[j++] = hex; - } - - scan_ssid = g_try_new(struct scan_ssid, 1); - if (scan_ssid == NULL) { - g_free(ssid); - return -ENOMEM; - } - - memcpy(scan_ssid->ssid, ssid, j); - scan_ssid->ssid_len = j; - scan_data->ssids = g_slist_prepend(scan_data->ssids, - scan_ssid); - - scan_data->num_ssids++; - - g_free(ssid); - } else - return -EINVAL; - - scan_data->ssids = g_slist_reverse(scan_data->ssids); - - if (scan_data->freqs == NULL) { - scan_data->freqs = g_try_malloc0(sizeof(uint16_t) * - scan_data->num_ssids); - if (scan_data->freqs == NULL) { - g_slist_free_full(scan_data->ssids, g_free); - return -ENOMEM; - } - } else { - scan_data->freqs = g_try_realloc(scan_data->freqs, - sizeof(uint16_t) * scan_data->num_ssids); - if (scan_data->freqs == NULL) { - g_slist_free_full(scan_data->ssids, g_free); - return -ENOMEM; - } - scan_data->freqs[scan_data->num_ssids - 1] = 0; - } - - /* Don't add duplicate entries */ - for (i = 0; i < scan_data->num_ssids; i++) { - if (scan_data->freqs[i] == 0) { - scan_data->freqs[i] = freq; - break; - } else if (scan_data->freqs[i] == freq) - break; - } - - return 0; -} - struct last_connected { GTimeVal modified; gchar *ssid; @@ -738,6 +864,10 @@ static int wifi_scan_fast(struct connman_device *device) return ret; } +/* + * This func is only used when connecting to this specific AP first time. + * It is not used when system autoconnects to hidden AP. + */ static int wifi_scan_hidden(struct connman_device *device, const char *ssid, unsigned int ssid_len, const char *identity, const char* passphrase) -- 1.7.5.4 _______________________________________________ connman mailing list connman@connman.net http://lists.connman.net/listinfo/connman