diff options
Diffstat (limited to 'plugins/wifi.c')
-rw-r--r-- | plugins/wifi.c | 287 |
1 files changed, 223 insertions, 64 deletions
diff --git a/plugins/wifi.c b/plugins/wifi.c index 34c16dfd..f8c22be3 100644 --- a/plugins/wifi.c +++ b/plugins/wifi.c @@ -30,9 +30,8 @@ #include <string.h> #include <sys/ioctl.h> #include <sys/socket.h> -#include <linux/if_arp.h> -#include <linux/wireless.h> #include <net/ethernet.h> +#include <linux/wireless.h> #ifndef IFF_LOWER_UP #define IFF_LOWER_UP 0x10000 @@ -56,6 +55,7 @@ #include <connman/provision.h> #include <connman/utsname.h> #include <connman/machine.h> +#include <connman/tethering.h> #include <gsupplicant/gsupplicant.h> @@ -64,7 +64,8 @@ #define FAVORITE_MAXIMUM_RETRIES 2 #define BGSCAN_DEFAULT "simple:30:-45:300" -#define AUTOSCAN_DEFAULT "exponential:3:300" +#define AUTOSCAN_EXPONENTIAL "exponential:3:300" +#define AUTOSCAN_SINGLE "single:3" #define P2P_FIND_TIMEOUT 30 #define P2P_CONNECTION_TIMEOUT 100 @@ -83,6 +84,12 @@ enum wifi_ap_capability{ WIFI_AP_NOT_SUPPORTED = 2, }; +enum wifi_scanning_type { + WIFI_SCANNING_UNKNOWN = 0, + WIFI_SCANNING_PASSIVE = 1, + WIFI_SCANNING_ACTIVE = 2, +}; + struct hidden_params { char ssid[32]; unsigned int ssid_len; @@ -143,7 +150,7 @@ struct wifi_data { * autoscan "emulation". */ struct autoscan_params *autoscan; - + enum wifi_scanning_type scanning_type; GSupplicantScanParams *scan_params; unsigned int p2p_find_timeout; unsigned int p2p_connection_timeout; @@ -160,7 +167,7 @@ static GList *iface_list = NULL; static GList *pending_wifi_device = NULL; static GList *p2p_iface_list = NULL; -bool wfd_service_registered = false; +static bool wfd_service_registered = false; static void start_autoscan(struct connman_device *device); static int tech_set_tethering(struct connman_technology *technology, @@ -596,10 +603,8 @@ static int peer_register_service(const unsigned char *specification, params = fill_in_peer_service_params(specification, specification_length, query, query_length, version); - if (!params) { - ret = -ENOMEM; + if (!params) continue; - } if (!found) { ret_f = g_supplicant_interface_p2p_add_service(iface, @@ -684,10 +689,8 @@ static int peer_unregister_service(const unsigned char *specification, params = fill_in_peer_service_params(specification, specification_length, query, query_length, version); - if (!params) { - ret = -ENOMEM; + if (!params) continue; - } ret = g_supplicant_interface_p2p_del_service(iface, params); if (ret != 0 && ret != -EINPROGRESS) @@ -831,13 +834,13 @@ static void reset_autoscan(struct connman_device *device) autoscan = wifi->autoscan; - if (autoscan->timeout == 0 && autoscan->interval == 0) + autoscan->interval = 0; + + if (autoscan->timeout == 0) return; g_source_remove(autoscan->timeout); - autoscan->timeout = 0; - autoscan->interval = 0; connman_device_unref(device); } @@ -893,7 +896,7 @@ static void wifi_remove(struct connman_device *device) remove_pending_wifi_device(wifi); - if (wifi->p2p_find_timeout) { + if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_P2P)) { g_source_remove(wifi->p2p_find_timeout); connman_device_unref(wifi->device); } @@ -1209,7 +1212,7 @@ static int throw_wifi_scan(struct connman_device *device, if (wifi->tethering) return -EBUSY; - if (connman_device_get_scanning(device)) + if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI)) return -EALREADY; connman_device_ref(device); @@ -1283,7 +1286,7 @@ static void scan_callback(int result, GSupplicantInterface *interface, return scan_callback(ret, interface, user_data); } - scanning = connman_device_get_scanning(device); + scanning = connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI); if (scanning) { connman_device_set_scanning(device, @@ -1363,6 +1366,21 @@ static gboolean autoscan_timeout(gpointer data) throw_wifi_scan(wifi->device, scan_callback_hidden); + /* + * In case BackgroundScanning is disabled, interval will reach the + * limit exactly after the very first passive scanning. It allows + * to ensure at most one passive scan is performed in such cases. + */ + if (!connman_setting_get_bool("BackgroundScanning") && + interval == autoscan->limit) { + g_source_remove(autoscan->timeout); + autoscan->timeout = 0; + + connman_device_unref(device); + + return FALSE; + } + set_interval: DBG("interval %d", interval); @@ -1409,19 +1427,25 @@ static struct autoscan_params *parse_autoscan_params(const char *params) int limit; int base; - DBG("Emulating autoscan"); + DBG(""); list_params = g_strsplit(params, ":", 0); if (list_params == 0) return NULL; - if (g_strv_length(list_params) < 3) { + if (!g_strcmp0(list_params[0], "exponential") && + g_strv_length(list_params) == 3) { + base = atoi(list_params[1]); + limit = atoi(list_params[2]); + } else if (!g_strcmp0(list_params[0], "single") && + g_strv_length(list_params) == 2) + base = limit = atoi(list_params[1]); + else { g_strfreev(list_params); return NULL; } - base = atoi(list_params[1]); - limit = atoi(list_params[2]); + DBG("Setup %s autoscanning", list_params[0]); g_strfreev(list_params); @@ -1440,10 +1464,37 @@ static struct autoscan_params *parse_autoscan_params(const char *params) static void setup_autoscan(struct wifi_data *wifi) { - if (!wifi->autoscan) - wifi->autoscan = parse_autoscan_params(AUTOSCAN_DEFAULT); + /* + * If BackgroundScanning is enabled, setup exponential + * autoscanning if it has not been previously done. + */ + if (connman_setting_get_bool("BackgroundScanning")) { + wifi->autoscan = parse_autoscan_params(AUTOSCAN_EXPONENTIAL); + return; + } - start_autoscan(wifi->device); + /* + * On the contrary, if BackgroundScanning is disabled, update autoscan + * parameters based on the type of scanning that is being performed. + */ + if (wifi->autoscan) { + g_free(wifi->autoscan); + wifi->autoscan = NULL; + } + + switch (wifi->scanning_type) { + case WIFI_SCANNING_PASSIVE: + /* Do not setup autoscan. */ + break; + case WIFI_SCANNING_ACTIVE: + /* Setup one single passive scan after active. */ + wifi->autoscan = parse_autoscan_params(AUTOSCAN_SINGLE); + break; + case WIFI_SCANNING_UNKNOWN: + /* Setup autoscan in this case but we should never fall here. */ + wifi->autoscan = parse_autoscan_params(AUTOSCAN_SINGLE); + break; + } } static void finalize_interface_creation(struct wifi_data *wifi) @@ -1457,13 +1508,13 @@ static void finalize_interface_creation(struct wifi_data *wifi) connman_device_set_powered(wifi->device, true); - if (!connman_setting_get_bool("BackgroundScanning")) - return; - if (wifi->p2p_device) return; - setup_autoscan(wifi); + if (!wifi->autoscan) + setup_autoscan(wifi); + + start_autoscan(wifi->device); } static void interface_create_callback(int result, @@ -1535,7 +1586,7 @@ static int wifi_disable(struct connman_device *device) stop_autoscan(device); - if (wifi->p2p_find_timeout) { + if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_P2P)) { g_source_remove(wifi->p2p_find_timeout); wifi->p2p_find_timeout = 0; connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_P2P, false); @@ -1543,7 +1594,7 @@ static int wifi_disable(struct connman_device *device) } /* In case of a user scan, device is still referenced */ - if (connman_device_get_scanning(device)) { + if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI)) { connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_WIFI, false); connman_device_unref(wifi->device); @@ -1691,10 +1742,29 @@ static int get_latest_connections(int max_ssids, return num_ssids; } +static void wifi_update_scanner_type(struct wifi_data *wifi, + enum wifi_scanning_type new_type) +{ + DBG(""); + + if (!wifi || wifi->scanning_type == new_type) + return; + + wifi->scanning_type = new_type; + + setup_autoscan(wifi); +} + static int wifi_scan_simple(struct connman_device *device) { + struct wifi_data *wifi = connman_device_get_data(device); + reset_autoscan(device); + /* Distinguish between devices performing passive and active scanning */ + if (wifi) + wifi_update_scanner_type(wifi, WIFI_SCANNING_PASSIVE); + return throw_wifi_scan(device, scan_callback_hidden); } @@ -1714,7 +1784,7 @@ static gboolean p2p_find_stop(gpointer data) connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_P2P, false); connman_device_unref(device); - reset_autoscan(device); + start_autoscan(device); return FALSE; } @@ -1783,11 +1853,8 @@ static int p2p_find(struct connman_device *device) * Note that the hidden scan is only used when connecting to this specific * hidden AP first time. It is not used when system autoconnects to hidden AP. */ -static int wifi_scan(enum connman_service_type type, - struct connman_device *device, - const char *ssid, unsigned int ssid_len, - const char *identity, const char* passphrase, - const char *security, void *user_data) +static int wifi_scan(struct connman_device *device, + struct connman_device_scan_params *params) { struct wifi_data *wifi = connman_device_get_data(device); GSupplicantScanParams *scan_params = NULL; @@ -1807,14 +1874,15 @@ static int wifi_scan(enum connman_service_type type, if (wifi->tethering) return -EBUSY; - if (type == CONNMAN_SERVICE_TYPE_P2P) + if (params->type == CONNMAN_SERVICE_TYPE_P2P) return p2p_find(device); - DBG("device %p wifi %p hidden ssid %s", device, wifi->interface, ssid); + DBG("device %p wifi %p hidden ssid %s", device, wifi->interface, + params->ssid); - scanning = connman_device_get_scanning(device); + scanning = connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI); - if (!ssid || ssid_len == 0 || ssid_len > 32) { + if (!params->ssid || params->ssid_len == 0 || params->ssid_len > 32) { if (scanning) return -EALREADY; @@ -1843,8 +1911,8 @@ static int wifi_scan(enum connman_service_type type, return -ENOMEM; } - memcpy(scan_ssid->ssid, ssid, ssid_len); - scan_ssid->ssid_len = ssid_len; + memcpy(scan_ssid->ssid, params->ssid, params->ssid_len); + scan_ssid->ssid_len = params->ssid_len; scan_params->ssids = g_slist_prepend(scan_params->ssids, scan_ssid); scan_params->num_ssids = 1; @@ -1860,12 +1928,12 @@ static int wifi_scan(enum connman_service_type type, wifi->hidden = NULL; } - memcpy(hidden->ssid, ssid, ssid_len); - hidden->ssid_len = ssid_len; - hidden->identity = g_strdup(identity); - hidden->passphrase = g_strdup(passphrase); - hidden->security = g_strdup(security); - hidden->user_data = user_data; + memcpy(hidden->ssid, params->ssid, params->ssid_len); + hidden->ssid_len = params->ssid_len; + hidden->identity = g_strdup(params->identity); + hidden->passphrase = g_strdup(params->passphrase); + hidden->security = g_strdup(params->security); + hidden->user_data = params->user_data; wifi->hidden = hidden; if (scanning) { @@ -1879,7 +1947,7 @@ static int wifi_scan(enum connman_service_type type, } else if (wifi->connected) { g_supplicant_free_scan_params(scan_params); return wifi_scan_simple(device); - } else { + } else if (!params->force_full_scan) { ret = get_latest_connections(driver_max_ssids, scan_params); if (ret <= 0) { g_supplicant_free_scan_params(scan_params); @@ -1887,6 +1955,9 @@ static int wifi_scan(enum connman_service_type type, } } + /* Distinguish between devices performing passive and active scanning */ + wifi_update_scanner_type(wifi, WIFI_SCANNING_ACTIVE); + connman_device_ref(device); reset_autoscan(device); @@ -1909,6 +1980,24 @@ static int wifi_scan(enum connman_service_type type, return ret; } +static void wifi_stop_scan(enum connman_service_type type, + struct connman_device *device) +{ + struct wifi_data *wifi = connman_device_get_data(device); + + DBG("device %p wifi %p", device, wifi); + + if (!wifi) + return; + + if (type == CONNMAN_SERVICE_TYPE_P2P) { + if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_P2P)) { + g_source_remove(wifi->p2p_find_timeout); + p2p_find_stop(device); + } + } +} + static void wifi_regdom_callback(int result, const char *alpha2, void *user_data) @@ -1948,6 +2037,7 @@ static struct connman_device_driver wifi_ng_driver = { .enable = wifi_enable, .disable = wifi_disable, .scan = wifi_scan, + .stop_scan = wifi_stop_scan, .set_regdom = wifi_set_regdom, }; @@ -2138,10 +2228,9 @@ static void disconnect_callback(int result, GSupplicantInterface *interface, return; } - if (wifi->network) { + if (wifi->network != wifi->pending_network) connman_network_set_connected(wifi->network, false); - wifi->network = NULL; - } + wifi->network = NULL; wifi->disconnecting = false; wifi->connected = false; @@ -2370,17 +2459,20 @@ static void interface_state(GSupplicantInterface *interface) if (!wifi) return; + device = wifi->device; + if (!device) + return; + if (state == G_SUPPLICANT_STATE_COMPLETED) { if (wifi->tethering_param) { g_free(wifi->tethering_param->ssid); g_free(wifi->tethering_param); wifi->tethering_param = NULL; } - } - device = wifi->device; - if (!device) - return; + if (wifi->tethering) + stop_autoscan(device); + } if (g_supplicant_interface_get_ready(interface) && !wifi->interface_ready) { @@ -2461,8 +2553,11 @@ static void interface_state(GSupplicantInterface *interface) default: break; } - connman_network_set_connected(network, false); - connman_network_set_associating(network, false); + + if (network != wifi->pending_network) { + connman_network_set_connected(network, false); + connman_network_set_associating(network, false); + } wifi->disconnecting = false; start_autoscan(device); @@ -2635,8 +2730,6 @@ static void ap_create_fail(GSupplicantInterface *interface) g_free(wifi->tethering_param); wifi->tethering_param = NULL; } - - return; } static unsigned char calculate_strength(GSupplicantNetwork *supplicant_network) @@ -2714,6 +2807,8 @@ static void network_added(GSupplicantNetwork *supplicant_network) connman_network_set_strength(network, calculate_strength(supplicant_network)); connman_network_set_bool(network, "WiFi.WPS", wps); + connman_network_set_bool(network, "WiFi.WPSAdvertising", + wps_advertizing); if (wps) { /* Is AP advertizing for WPS association? @@ -2780,6 +2875,7 @@ static void network_changed(GSupplicantNetwork *network, const char *property) struct wifi_data *wifi; const char *name, *identifier; struct connman_network *connman_network; + bool update_needed; interface = g_supplicant_network_get_interface(network); wifi = g_supplicant_interface_get_data(interface); @@ -2795,11 +2891,42 @@ static void network_changed(GSupplicantNetwork *network, const char *property) if (!connman_network) return; - if (g_str_equal(property, "Signal")) { - connman_network_set_strength(connman_network, + if (g_str_equal(property, "WPSCapabilities")) { + bool wps; + bool wps_pbc; + bool wps_ready; + bool wps_advertizing; + + wps = g_supplicant_network_get_wps(network); + wps_pbc = g_supplicant_network_is_wps_pbc(network); + wps_ready = g_supplicant_network_is_wps_active(network); + wps_advertizing = + g_supplicant_network_is_wps_advertizing(network); + + connman_network_set_bool(connman_network, "WiFi.WPS", wps); + connman_network_set_bool(connman_network, + "WiFi.WPSAdvertising", wps_advertizing); + + if (wps) { + /* + * Is AP advertizing for WPS association? + * If so, we decide to use WPS by default + */ + if (wps_ready && wps_pbc && wps_advertizing) + connman_network_set_bool(connman_network, + "WiFi.UseWPS", true); + } + + update_needed = true; + } else if (g_str_equal(property, "Signal")) { + connman_network_set_strength(connman_network, calculate_strength(network)); - connman_network_update(connman_network); - } + update_needed = true; + } else + update_needed = false; + + if (update_needed) + connman_network_update(connman_network); } static void network_associated(GSupplicantNetwork *network) @@ -2819,6 +2946,10 @@ static void network_associated(GSupplicantNetwork *network) if (!wifi) return; + /* P2P networks must not be treated as WiFi networks */ + if (wifi->p2p_connecting || wifi->p2p_device) + return; + identifier = g_supplicant_network_get_identifier(network); connman_network = connman_device_get_network(wifi->device, identifier); @@ -2853,6 +2984,32 @@ static void network_associated(GSupplicantNetwork *network) interface_state(interface); } +static void sta_authorized(GSupplicantInterface *interface, + const char *addr) +{ + struct wifi_data *wifi = g_supplicant_interface_get_data(interface); + + DBG("wifi %p station %s authorized", wifi, addr); + + if (!wifi || !wifi->tethering) + return; + + __connman_tethering_client_register(addr); +} + +static void sta_deauthorized(GSupplicantInterface *interface, + const char *addr) +{ + struct wifi_data *wifi = g_supplicant_interface_get_data(interface); + + DBG("wifi %p station %s deauthorized", wifi, addr); + + if (!wifi || !wifi->tethering) + return; + + __connman_tethering_client_unregister(addr); +} + static void apply_peer_services(GSupplicantPeer *peer, struct connman_peer *connman_peer) { @@ -3073,6 +3230,8 @@ static const GSupplicantCallbacks callbacks = { .network_removed = network_removed, .network_changed = network_changed, .network_associated = network_associated, + .sta_authorized = sta_authorized, + .sta_deauthorized = sta_deauthorized, .peer_found = peer_found, .peer_lost = peer_lost, .peer_changed = peer_changed, |