summaryrefslogtreecommitdiff
path: root/plugins/wifi.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/wifi.c')
-rw-r--r--plugins/wifi.c287
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,