diff options
Diffstat (limited to 'src/nm-device-wifi.c')
| -rw-r--r-- | src/nm-device-wifi.c | 234 |
1 files changed, 197 insertions, 37 deletions
diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index ed964a57..5fbbac7a 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -56,6 +56,7 @@ #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" #include "nm-system.h" +#include "nm-manager-auth.h" #include "nm-settings-connection.h" #include "nm-enum-types.h" #include "wifi-utils.h" @@ -64,11 +65,15 @@ static gboolean impl_device_get_access_points (NMDeviceWifi *device, GPtrArray **aps, GError **err); +static void impl_device_request_scan (NMDeviceWifi *device, + GHashTable *options, + DBusGMethodInvocation *context); + #include "nm-device-wifi-glue.h" /* All of these are in seconds */ -#define SCAN_INTERVAL_MIN 0 +#define SCAN_INTERVAL_MIN 3 #define SCAN_INTERVAL_STEP 20 #define SCAN_INTERVAL_MAX 120 @@ -138,7 +143,7 @@ struct _NMDeviceWifiPrivate { guint32 rate; gboolean enabled; /* rfkilled or not */ - glong scheduled_scan_time; + time_t scheduled_scan_time; guint8 scan_interval; /* seconds */ guint pending_scan_id; guint scanlist_cull_id; @@ -153,7 +158,7 @@ struct _NMDeviceWifiPrivate { NMDeviceWifiCapabilities capabilities; }; -static gboolean request_wireless_scan (gpointer user_data); +static gboolean check_scanning_allowed (NMDeviceWifi *self); static void schedule_scan (NMDeviceWifi *self, gboolean backoff); @@ -193,6 +198,8 @@ static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface, static void schedule_scanlist_cull (NMDeviceWifi *self); +static gboolean request_wireless_scan (gpointer user_data); + /*****************************************************************/ #define NM_WIFI_ERROR (nm_wifi_error_quark ()) @@ -1014,14 +1021,6 @@ real_check_connection_compatible (NMDevice *device, const GByteArray *mac; const GSList *mac_blacklist, *mac_blacklist_iter; - if (is_adhoc_wpa (connection)) { - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, - "WPA Ad-Hoc disabled due to kernel bugs"); - return FALSE; - } - s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); @@ -1040,6 +1039,7 @@ real_check_connection_compatible (NMDevice *device, return FALSE; } + mac = nm_setting_wireless_get_mac_address (s_wireless); if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) { g_set_error (error, @@ -1067,6 +1067,14 @@ real_check_connection_compatible (NMDevice *device, } } + if (is_adhoc_wpa (connection)) { + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, + "WPA Ad-Hoc disabled due to kernel bugs"); + return FALSE; + } + // FIXME: check channel/freq/band against bands the hardware supports // FIXME: check encryption against device capabilities // FIXME: check bitrate against device capabilities @@ -1233,9 +1241,9 @@ real_complete_connection (NMDevice *device, */ if (is_adhoc_wpa (connection)) { g_set_error_literal (error, - NM_SETTING_WIRELESS_ERROR, - NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, - "WPA Ad-Hoc disabled due to kernel bugs"); + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + "WPA Ad-Hoc disabled due to kernel bugs"); return FALSE; } @@ -1257,9 +1265,9 @@ real_complete_connection (NMDevice *device, /* Make sure the setting MAC (if any) matches the device's permanent MAC */ if (memcmp (setting_mac->data, priv->perm_hw_addr, ETH_ALEN)) { g_set_error (error, - NM_SETTING_WIRELESS_ERROR, - NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, - NM_SETTING_WIRELESS_MAC_ADDRESS); + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + NM_SETTING_WIRELESS_MAC_ADDRESS); return FALSE; } } else { @@ -1442,6 +1450,80 @@ impl_device_get_access_points (NMDeviceWifi *self, return TRUE; } +static void +request_scan_cb (NMDevice *device, + DBusGMethodInvocation *context, + GError *error, + gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + + if (error) { + dbus_g_method_return_error (context, error); + g_clear_error (&error); + } else if (!check_scanning_allowed (self)) { + error = g_error_new_literal (NM_WIFI_ERROR, + NM_WIFI_ERROR_SCAN_NOT_ALLOWED, + "Scanning not allowed at this time"); + dbus_g_method_return_error (context, error); + g_error_free (error); + } else { + cancel_pending_scan (self); + request_wireless_scan (self); + dbus_g_method_return (context); + } +} + +static void +impl_device_request_scan (NMDeviceWifi *self, + GHashTable *options, + DBusGMethodInvocation *context) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + time_t last_scan; + GError *error; + + if ( !priv->enabled + || !priv->supplicant.iface + || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED + || nm_device_is_activating (device)) { + error = g_error_new_literal (NM_WIFI_ERROR, + NM_WIFI_ERROR_SCAN_NOT_ALLOWED, + "Scanning not allowed while unavailable or activating"); + goto error; + } + + if (nm_supplicant_interface_get_scanning (priv->supplicant.iface)) { + error = g_error_new_literal (NM_WIFI_ERROR, + NM_WIFI_ERROR_SCAN_NOT_ALLOWED, + "Scanning not allowed while already scanning"); + goto error; + } + + last_scan = nm_supplicant_interface_get_last_scan_time (priv->supplicant.iface); + if ((time (NULL) - last_scan) < 10) { + error = g_error_new_literal (NM_WIFI_ERROR, + NM_WIFI_ERROR_SCAN_NOT_ALLOWED, + "Scanning not allowed immediately following previous scan"); + goto error; + } + + /* Ask the manager to authenticate this request for us */ + g_signal_emit_by_name (device, + NM_DEVICE_AUTH_REQUEST, + context, + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + request_scan_cb, + NULL); + return; + +error: + dbus_g_method_return_error (context, error); + g_error_free (error); +} + static gboolean scanning_allowed (NMDeviceWifi *self) { @@ -1544,20 +1626,102 @@ check_scanning_allowed (NMDeviceWifi *self) } static gboolean +hidden_filter_func (NMConnectionProvider *provider, + NMConnection *connection, + gpointer user_data) +{ + NMSettingWireless *s_wifi; + + s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection); + return s_wifi ? nm_setting_wireless_get_hidden (s_wifi) : FALSE; +} + +static GPtrArray * +build_hidden_probe_list (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + guint max_scan_ssids = nm_supplicant_interface_get_max_scan_ssids (priv->supplicant.iface); + NMConnectionProvider *provider = nm_device_get_connection_provider (NM_DEVICE (self)); + GSList *connections, *iter; + GPtrArray *ssids = NULL; + static GByteArray *nullssid = NULL; + + /* Need at least two: wildcard SSID and one or more hidden SSIDs */ + if (max_scan_ssids < 2) + return NULL; + + /* Static wildcard SSID used for every scan */ + if (G_UNLIKELY (nullssid == NULL)) + nullssid = g_byte_array_new (); + + connections = nm_connection_provider_get_best_connections (provider, + max_scan_ssids - 1, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL, + hidden_filter_func, + NULL); + if (connections && connections->data) { + ssids = g_ptr_array_sized_new (max_scan_ssids - 1); + g_ptr_array_add (ssids, nullssid); /* Add wildcard SSID */ + } + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *connection = iter->data; + NMSettingWireless *s_wifi; + const GByteArray *ssid; + + s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + ssid = nm_setting_wireless_get_ssid (s_wifi); + g_assert (ssid); + g_ptr_array_add (ssids, (gpointer) ssid); + } + g_slist_free (connections); + + return ssids; +} + +static gboolean request_wireless_scan (gpointer user_data) { NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); gboolean backoff = FALSE; + GPtrArray *ssids = NULL; if (check_scanning_allowed (self)) { nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scanning requested", nm_device_get_iface (NM_DEVICE (self))); - if (nm_supplicant_interface_request_scan (priv->supplicant.iface)) { + ssids = build_hidden_probe_list (self); + + if (nm_logging_level_enabled (LOGL_DEBUG)) { + if (ssids) { + guint i; + char *foo; + + for (i = 0; i < ssids->len; i++) { + foo = nm_utils_ssid_to_utf8 (g_ptr_array_index (ssids, i)); + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): (%d) probe scanning SSID '%s'", + nm_device_get_iface (NM_DEVICE (self)), + i, foo ? foo : "<hidden>"); + g_free (foo); + } + } else { + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): no SSIDs to probe scan", + nm_device_get_iface (NM_DEVICE (self))); + } + } + + if (nm_supplicant_interface_request_scan (priv->supplicant.iface, ssids)) { /* success */ backoff = TRUE; } + + if (ssids) { + /* Elements owned by the connections, so we don't free them here */ + g_ptr_array_free (ssids, TRUE); + } } else { nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan requested but not allowed at this time", nm_device_get_iface (NM_DEVICE (self))); @@ -1579,13 +1743,11 @@ static void schedule_scan (NMDeviceWifi *self, gboolean backoff) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - GTimeVal now; - - g_get_current_time (&now); + time_t now = time (NULL); /* Cancel the pending scan if it would happen later than (now + the scan_interval) */ if (priv->pending_scan_id) { - if (now.tv_sec + priv->scan_interval < priv->scheduled_scan_time) + if (now + priv->scan_interval < priv->scheduled_scan_time) cancel_pending_scan (self); } @@ -1600,7 +1762,7 @@ schedule_scan (NMDeviceWifi *self, gboolean backoff) request_wireless_scan, self); - priv->scheduled_scan_time = now.tv_sec + priv->scan_interval; + priv->scheduled_scan_time = now + priv->scan_interval; if (backoff && (priv->scan_interval < (SCAN_INTERVAL_MAX / factor))) { priv->scan_interval += (SCAN_INTERVAL_STEP / factor); /* Ensure the scan interval will never be less than 20s... */ @@ -1643,8 +1805,7 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface, nm_device_get_iface (NM_DEVICE (self)), success ? "successful" : "failed"); - if (check_scanning_allowed (self)) - schedule_scan (self, TRUE); + schedule_scan (self, success); /* Ensure that old APs get removed, which otherwise only * happens when there are new BSSes. @@ -1763,7 +1924,7 @@ static gboolean cull_scan_list (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - GTimeVal now; + time_t now = time (NULL); GSList *outdated_list = NULL; GSList *elt; guint32 removed = 0, total = 0; @@ -1776,7 +1937,6 @@ cull_scan_list (NMDeviceWifi *self) /* Walk the access point list and remove any access points older than * three times the inactive scan interval. */ - g_get_current_time (&now); for (elt = priv->ap_list; elt; elt = g_slist_next (elt), total++) { NMAccessPoint *ap = elt->data; const guint prune_interval_s = SCAN_INTERVAL_MAX * 3; @@ -1796,7 +1956,7 @@ cull_scan_list (NMDeviceWifi *self) && g_object_get_data (G_OBJECT (ap), WPAS_REMOVED_TAG) == NULL) continue; - if (nm_ap_get_last_seen (ap) + prune_interval_s < now.tv_sec) + if (nm_ap_get_last_seen (ap) + prune_interval_s < now) outdated_list = g_slist_append (outdated_list, ap); } @@ -1886,7 +2046,6 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, { NMDeviceState state; NMAccessPoint *ap; - GTimeVal now; g_return_if_fail (self != NULL); g_return_if_fail (object_path != NULL); @@ -1899,10 +2058,8 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, /* Update the AP's last-seen property */ ap = get_ap_by_supplicant_path (self, object_path); - if (ap) { - g_get_current_time (&now); - nm_ap_set_last_seen (ap, now.tv_sec); - } + if (ap) + nm_ap_set_last_seen (ap, (guint32) time (NULL)); /* Remove outdated access points */ schedule_scanlist_cull (self); @@ -1991,8 +2148,11 @@ link_timeout_cb (gpointer user_data) * to reassociate within the timeout period, so the connection must * fail. */ - if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) - nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); + if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) { + nm_device_state_changed (dev, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); + } return FALSE; } @@ -3365,7 +3525,7 @@ get_property (GObject *object, guint prop_id, static void set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) + const GValue *value, GParamSpec *pspec) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (object); @@ -3409,7 +3569,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) parent_class->is_available = real_is_available; parent_class->check_connection_compatible = real_check_connection_compatible; parent_class->complete_connection = real_complete_connection; - parent_class->set_enabled = real_set_enabled; + parent_class->set_enabled = real_set_enabled; parent_class->act_stage1_prepare = real_act_stage1_prepare; parent_class->act_stage2_config = real_act_stage2_config; |