diff options
Diffstat (limited to 'src/devices')
| -rw-r--r-- | src/devices/bluetooth/nm-bluez-manager.c | 6 | ||||
| -rw-r--r-- | src/devices/bluetooth/nm-device-bt.c | 1 | ||||
| -rw-r--r-- | src/devices/nm-acd-manager.c | 18 | ||||
| -rw-r--r-- | src/devices/nm-device-bridge.c | 2 | ||||
| -rw-r--r-- | src/devices/nm-device-private.h | 13 | ||||
| -rw-r--r-- | src/devices/nm-device-wireguard.c | 14 | ||||
| -rw-r--r-- | src/devices/nm-device.c | 158 | ||||
| -rw-r--r-- | src/devices/nm-device.h | 7 | ||||
| -rw-r--r-- | src/devices/ovs/nm-device-ovs-interface.c | 2 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-iwd.c | 77 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-wifi.c | 929 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-broadband.c | 6 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-ofono.c | 6 |
13 files changed, 803 insertions, 436 deletions
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c index 6ff96c32..0bbac4c4 100644 --- a/src/devices/bluetooth/nm-bluez-manager.c +++ b/src/devices/bluetooth/nm-bluez-manager.c @@ -1251,9 +1251,9 @@ _network_server_unregister_bridge (NMBluezManager *self, if (r_req_data) { nm_clear_g_cancellable (&r_req_data->int_cancellable); - nm_utils_invoke_on_idle (_network_server_unregister_bridge_complete_on_idle_cb, - nm_utils_user_data_pack (r_req_data, g_strdup (reason)), - r_req_data->ext_cancellable); + nm_utils_invoke_on_idle (r_req_data->ext_cancellable, + _network_server_unregister_bridge_complete_on_idle_cb, + nm_utils_user_data_pack (r_req_data, g_strdup (reason))); } _nm_device_bridge_notify_unregister_bt_nap (device, reason); diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index ab18897f..a02ce34f 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -1282,6 +1282,7 @@ dispose (GObject *object) if (priv->modem_manager) { g_signal_handlers_disconnect_by_func (priv->modem_manager, G_CALLBACK (mm_name_owner_changed_cb), self); + g_signal_handlers_disconnect_by_func (priv->modem_manager, G_CALLBACK (mm_modem_added_cb), self); nm_modem_manager_name_owner_unref (priv->modem_manager); g_clear_object (&priv->modem_manager); } diff --git a/src/devices/nm-acd-manager.c b/src/devices/nm-acd-manager.c index 735dd25b..1c6c42a0 100644 --- a/src/devices/nm-acd-manager.c +++ b/src/devices/nm-acd-manager.c @@ -81,7 +81,20 @@ _acd_event_to_string (unsigned int event) return NULL; } -#define acd_event_to_string_a(event) NM_UTILS_LOOKUP_STR_A (_acd_event_to_string, event) +#define ACD_EVENT_TO_STRING_BUF_SIZE 50 + +static const char * +_acd_event_to_string_buf (unsigned event, char buffer[static ACD_EVENT_TO_STRING_BUF_SIZE]) +{ + const char *s; + + s = _acd_event_to_string (event); + if (s) + return s; + + g_snprintf (buffer, ACD_EVENT_TO_STRING_BUF_SIZE, "(%u)", event); + return buffer; +} static const char * acd_error_to_string (int error) @@ -172,6 +185,7 @@ acd_event (int fd, while ( !n_acd_pop_event (self->acd, &event) && event) { + char to_string_buffer[ACD_EVENT_TO_STRING_BUF_SIZE]; gs_free char *hwaddr_str = NULL; gboolean check_probing_done = FALSE; @@ -215,7 +229,7 @@ acd_event (int fd, nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex)); break; default: - _LOGD ("unhandled event '%s'", acd_event_to_string_a (event->event)); + _LOGD ("unhandled event '%s'", _acd_event_to_string_buf (event->event, to_string_buffer)); break; } diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index c6c54344..c93c6a54 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -328,6 +328,7 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool nm_auto_unset_gvalue GValue val = G_VALUE_INIT; GParamSpec *pspec; const char *value; + char value_buf[100]; if (slave) nm_assert (NM_IS_SETTING_BRIDGE_PORT (setting)); @@ -350,7 +351,6 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool value = g_value_get_boolean (&val) ? "1" : "0"; break; case G_TYPE_UINT: { - char value_buf[100]; guint uval; uval = g_value_get_uint (&val); diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 8d539026..4e260da2 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -46,6 +46,8 @@ void nm_device_arp_announce (NMDevice *self); NMSettings *nm_device_get_settings (NMDevice *self); +NMManager *nm_device_get_manager (NMDevice *self); + gboolean nm_device_set_ip_ifindex (NMDevice *self, int ifindex); gboolean nm_device_set_ip_iface (NMDevice *self, const char *iface); @@ -203,4 +205,15 @@ gboolean nm_device_match_parent_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hwaddr); +/*****************************************************************************/ + +void nm_device_auth_request (NMDevice *self, + GDBusMethodInvocation *context, + NMConnection *connection, + const char *permission, + gboolean allow_interaction, + GCancellable *cancellable, + NMManagerDeviceAuthRequestFunc callback, + gpointer user_data); + #endif /* NM_DEVICE_PRIVATE_H */ diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c index bb7a5950..9f01a763 100644 --- a/src/devices/nm-device-wireguard.c +++ b/src/devices/nm-device-wireguard.c @@ -1631,8 +1631,20 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, if (prefix < 0) prefix = (addr_family == AF_INET) ? 32 : 128; - if (!ip_config) + if (prefix == 0) { + NMSettingIPConfig *s_ip; + + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); + if (nm_setting_ip_config_get_never_default (s_ip)) + continue; + } + + if (!ip_config) { ip_config = nm_device_ip_config_new (NM_DEVICE (self), addr_family); + nm_ip_config_set_config_flags (ip_config, + NM_IP_CONFIG_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES, + 0); + } nm_utils_ipx_address_clear_host_address (addr_family, &addrbin, NULL, prefix); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index c3483e4d..8a22cba8 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -191,7 +191,6 @@ typedef struct { enum { STATE_CHANGED, AUTOCONNECT_ALLOWED, - AUTH_REQUEST, IP4_CONFIG_CHANGED, IP6_CONFIG_CHANGED, IP6_PREFIX_DELEGATED, @@ -577,6 +576,7 @@ typedef struct _NMDevicePrivate { NMMetered metered; NMSettings *settings; + NMManager *manager; NMNetns *netns; @@ -912,6 +912,12 @@ nm_device_get_settings (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->settings; } +NMManager * +nm_device_get_manager (NMDevice *self) +{ + return NM_DEVICE_GET_PRIVATE (self)->manager; +} + NMNetns * nm_device_get_netns (NMDevice *self) { @@ -6257,6 +6263,27 @@ dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer us } } +void +nm_device_auth_request (NMDevice *self, + GDBusMethodInvocation *context, + NMConnection *connection, + const char *permission, + gboolean allow_interaction, + GCancellable *cancellable, + NMManagerDeviceAuthRequestFunc callback, + gpointer user_data) +{ + nm_manager_device_auth_request (nm_device_get_manager (self), + self, + context, + connection, + permission, + allow_interaction, + cancellable, + callback, + user_data); +} + /*****************************************************************************/ static void @@ -10557,6 +10584,8 @@ act_stage3_ip_config_start (NMDevice *self, } else { NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; const char *ip6_privacy_str = "0"; + NMPlatform *platform; + int ifindex; if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", "1"); @@ -10564,18 +10593,35 @@ act_stage3_ip_config_start (NMDevice *self, } if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { - if ( !priv->master - && !nm_device_sys_iface_state_is_external (self)) { - gboolean ipv6ll_handle_old = priv->ipv6ll_handle; - - /* When activating an IPv6 'ignore' connection we need to revert back - * to kernel IPv6LL, but the kernel won't actually assign an address - * to the interface until disable_ipv6 is bounced. - */ - set_nm_ipv6ll (self, FALSE); - if (ipv6ll_handle_old) - nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", "1"); - restore_ip6_properties (self); + if (!nm_device_sys_iface_state_is_external (self)) { + if (priv->master) { + /* If a device only has an IPv6 link-local address, + * we don't generate an assumed connection. Therefore, + * when a new slave connection (without IP configuration) + * is activated on the device, the link-local address + * remains configured. The IP configuration of an activated + * slave should not depend on the previous state. Flush + * addresses and routes on activation. + */ + ifindex = nm_device_get_ip_ifindex (self); + platform = nm_device_get_platform (self); + + if (ifindex > 0) { + nm_platform_ip_route_flush (platform, AF_INET6, ifindex); + nm_platform_ip_address_flush (platform, AF_INET6, ifindex); + } + } else { + gboolean ipv6ll_handle_old = priv->ipv6ll_handle; + + /* When activating an IPv6 'ignore' connection we need to revert back + * to kernel IPv6LL, but the kernel won't actually assign an address + * to the interface until disable_ipv6 is bounced. + */ + set_nm_ipv6ll (self, FALSE); + if (ipv6ll_handle_old) + nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", "1"); + restore_ip6_properties (self); + } } return NM_ACT_STAGE_RETURN_IP_DONE; } @@ -12234,13 +12280,14 @@ impl_device_reapply (NMDBusObject *obj, } else reapply_data = NULL; - g_signal_emit (self, signals[AUTH_REQUEST], 0, - invocation, - nm_device_get_applied_connection (self), - NM_AUTH_PERMISSION_NETWORK_CONTROL, - TRUE, - reapply_cb, - reapply_data); + nm_device_auth_request (self, + invocation, + nm_device_get_applied_connection (self), + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + NULL, + reapply_cb, + reapply_data); } /*****************************************************************************/ @@ -12277,13 +12324,14 @@ get_applied_connection_cb (NMDevice *self, if (applied_connection != user_data) { /* The applied connection changed due to a race. Reauthenticate. */ - g_signal_emit (self, signals[AUTH_REQUEST], 0, - context, - applied_connection, - NM_AUTH_PERMISSION_NETWORK_CONTROL, - TRUE, - get_applied_connection_cb, - applied_connection /* no need take a ref. We will not dereference this pointer. */); + nm_device_auth_request (self, + context, + applied_connection, + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + NULL, + get_applied_connection_cb, + applied_connection /* no need take a ref. We will not dereference this pointer. */); return; } @@ -12330,13 +12378,14 @@ impl_device_get_applied_connection (NMDBusObject *obj, return; } - g_signal_emit (self, signals[AUTH_REQUEST], 0, - invocation, - applied_connection, - NM_AUTH_PERMISSION_NETWORK_CONTROL, - TRUE, - get_applied_connection_cb, - applied_connection /* no need take a ref. We will not dereference this pointer. */); + nm_device_auth_request (self, + invocation, + applied_connection, + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + NULL, + get_applied_connection_cb, + applied_connection /* no need take a ref. We will not dereference this pointer. */); } /*****************************************************************************/ @@ -12504,13 +12553,14 @@ impl_device_disconnect (NMDBusObject *obj, connection = nm_device_get_applied_connection (self); nm_assert (connection); - g_signal_emit (self, signals[AUTH_REQUEST], 0, - invocation, - connection, - NM_AUTH_PERMISSION_NETWORK_CONTROL, - TRUE, - disconnect_cb, - NULL); + nm_device_auth_request (self, + invocation, + connection, + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + NULL, + disconnect_cb, + NULL); } static void @@ -12556,13 +12606,14 @@ impl_device_delete (NMDBusObject *obj, return; } - g_signal_emit (self, signals[AUTH_REQUEST], 0, - invocation, - NULL, - NM_AUTH_PERMISSION_NETWORK_CONTROL, - TRUE, - delete_cb, - NULL); + nm_device_auth_request (self, + invocation, + NULL, + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + NULL, + delete_cb, + NULL); } static void @@ -17390,8 +17441,8 @@ constructed (GObject *object) g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self); + priv->manager = g_object_ref (NM_MANAGER_GET); priv->settings = g_object_ref (NM_SETTINGS_GET); - g_assert (priv->settings); g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, @@ -17548,6 +17599,7 @@ finalize (GObject *object) /* for testing, NMDeviceTest does not invoke NMDevice::constructed, * and thus @settings might be unset. */ nm_g_object_unref (priv->settings); + nm_g_object_unref (priv->manager); nm_g_object_unref (priv->concheck_mgr); @@ -17944,14 +17996,6 @@ nm_device_class_init (NMDeviceClass *klass) autoconnect_allowed_accumulator, NULL, NULL, G_TYPE_BOOLEAN, 0); - signals[AUTH_REQUEST] = - g_signal_new (NM_DEVICE_AUTH_REQUEST, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - /* context, connection, permission, allow_interaction, callback, user_data */ - G_TYPE_NONE, 6, G_TYPE_DBUS_METHOD_INVOCATION, NM_TYPE_CONNECTION, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER); - signals[IP4_CONFIG_CHANGED] = g_signal_new (NM_DEVICE_IP4_CONFIG_CHANGED, G_OBJECT_CLASS_TYPE (object_class), diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index c18301b2..8d51c707 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -115,7 +115,6 @@ nm_device_state_reason_check (NMDeviceStateReason reason) #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */ /* Internal signals */ -#define NM_DEVICE_AUTH_REQUEST "auth-request" #define NM_DEVICE_IP4_CONFIG_CHANGED "ip4-config-changed" #define NM_DEVICE_IP6_CONFIG_CHANGED "ip6-config-changed" #define NM_DEVICE_IP6_PREFIX_DELEGATED "ip6-prefix-delegated" @@ -455,12 +454,6 @@ typedef struct _NMDeviceClass { } NMDeviceClass; -typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device, - GDBusMethodInvocation *context, - NMAuthSubject *subject, - GError *error, - gpointer user_data); - GType nm_device_get_type (void); struct _NMDedupMultiIndex *nm_device_get_multi_index (NMDevice *self); diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c index 951b5788..10f9fa94 100644 --- a/src/devices/ovs/nm-device-ovs-interface.c +++ b/src/devices/ovs/nm-device-ovs-interface.c @@ -310,7 +310,7 @@ deactivate_async (NMDevice *device, nm_device_get_iface (device))) { _LOGT (LOGD_CORE, "deactivate: link not present, proceeding"); nm_device_update_from_platform_link (NM_DEVICE (self), NULL); - nm_utils_invoke_on_idle (deactivate_cb_on_idle, data, cancellable); + nm_utils_invoke_on_idle (cancellable, deactivate_cb_on_idle, data); return; } diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index 3c1d5b22..4b57fa38 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -507,7 +507,7 @@ deactivate_async (NMDevice *device, user_data = nm_utils_user_data_pack (g_object_ref (self), callback, callback_user_data); if (!priv->dbus_obj) { - nm_utils_invoke_on_idle (disconnect_cb_on_idle, user_data, cancellable); + nm_utils_invoke_on_idle (cancellable, disconnect_cb_on_idle, user_data); return; } @@ -615,13 +615,6 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError return FALSE; } - /* Hidden SSIDs not supported in any mode (client or AP) */ - if (nm_setting_wireless_get_hidden (s_wireless)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "hidden networks not supported by the IWD backend"); - return FALSE; - } - security = nm_wifi_connection_get_iwd_security (connection, &mapped); if (!mapped) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, @@ -630,6 +623,15 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError } mode = nm_setting_wireless_get_mode (s_wireless); + + /* Hidden SSIDs only supported in client mode */ + if ( nm_setting_wireless_get_hidden (s_wireless) + && !NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "non-infrastructure hidden networks not supported by the IWD backend"); + return FALSE; + } + if (NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) { /* 8021x networks can only be used if they've been provisioned on the IWD side and * thus are Known Networks. @@ -757,6 +759,7 @@ complete_connection (NMDevice *device, NMWifiAP *ap; GBytes *ssid; GBytes *setting_ssid = NULL; + gboolean hidden = FALSE; const char *mode; s_wifi = nm_connection_get_setting_wireless (connection); @@ -789,16 +792,14 @@ complete_connection (NMDevice *device, /* Find a compatible AP in the scan list */ ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); if (!ap) { - if (!nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { - g_set_error_literal (error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "No compatible AP in the scan list and hidden SSIDs not supported."); - return FALSE; - } - + /* If we still don't have an AP, then the WiFI settings needs to be + * fully specified by the client. Might not be able to find an AP + * if the network isn't broadcasting the SSID for example. + */ if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) return FALSE; + + hidden = TRUE; } } else { ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); @@ -849,6 +850,9 @@ complete_connection (NMDevice *device, nm_setting_wireless_get_mac_address (s_wifi) ? NULL : nm_device_get_iface (device), TRUE); + if (hidden) + g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL); + return TRUE; } @@ -1061,14 +1065,14 @@ _nm_device_iwd_request_scan (NMDeviceIwd *self, return; } - g_signal_emit_by_name (device, - NM_DEVICE_AUTH_REQUEST, - invocation, - NULL, - NM_AUTH_PERMISSION_WIFI_SCAN, - TRUE, - dbus_request_scan_cb, - options ? g_variant_ref (options) : NULL); + nm_device_auth_request (device, + invocation, + NULL, + NM_AUTH_PERMISSION_WIFI_SCAN, + TRUE, + NULL, + dbus_request_scan_cb, + nm_g_variant_ref (options)); } static gboolean @@ -1786,11 +1790,34 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) goto out_fail; } + if ( !is_connection_known_network (connection) + && nm_setting_wireless_get_hidden (s_wireless)) { + gs_free char *ssid_str = NULL; + + /* Use Station.ConnectHiddenNetwork method instead of Network proxy. */ + ssid_str = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wireless)); + g_dbus_proxy_call (priv->dbus_station_proxy, + "ConnectHiddenNetwork", + g_variant_new ("(s)", ssid_str), + G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, + network_connect_cb, + self); + return NM_ACT_STAGE_RETURN_POSTPONE; + } + + if (!nm_wifi_ap_get_supplicant_path (ap)) { + _LOGW (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) network is provisioned but dbus supplicant path for AP unknown"); + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + goto out_fail; + } + network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), nm_ref_string_get_str (nm_wifi_ap_get_supplicant_path (ap)), NM_IWD_NETWORK_INTERFACE); if (!network_proxy) { - _LOGE (LOGD_DEVICE | LOGD_WIFI, + _LOGW (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) could not get Network interface proxy for %s", nm_ref_string_get_str (nm_wifi_ap_get_supplicant_path (ap))); NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 266a8425..f5b62989 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -48,7 +48,14 @@ _LOG_DECLARE_SELF(NMDeviceWifi); #define SCAN_INTERVAL_SEC_STEP 20 #define SCAN_INTERVAL_SEC_MAX 120 -#define SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN 5 +#define SCAN_EXTRA_DELAY_MSEC 500 + +#define SCAN_RAND_MAC_ADDRESS_EXPIRE_SEC (5*60) + +#define SCAN_REQUEST_SSIDS_MAX_NUM 32u +#define SCAN_REQUEST_SSIDS_MAX_AGE_MSEC (3 * 60 * NM_UTILS_MSEC_PER_SEC) + +#define _LOGT_scan(...) _LOGT (LOGD_WIFI_SCAN, "wifi-scan: " __VA_ARGS__) /*****************************************************************************/ @@ -76,44 +83,58 @@ typedef struct { CList scanning_prohibited_lst_head; + GCancellable *scan_request_cancellable; + + GSource *scan_request_delay_source; + NMWifiAP * current_ap; - guint32 rate; - bool enabled:1; /* rfkilled or not */ - bool requested_scan:1; - bool ssid_found:1; - bool is_scanning:1; - bool hidden_probe_scan_warn:1; - gint64 last_scan_msec; + GHashTable *scan_request_ssids_hash; + CList scan_request_ssids_lst_head; - gint32 scheduled_scan_time; /* seconds */ - guint8 scan_interval_sec; - guint pending_scan_id; - guint ap_dump_id; + NMActRequestGetSecretsCallId *wifi_secrets_id; - NMSupplicantManager *sup_mgr; + NMSupplicantManager *sup_mgr; NMSupplMgrCreateIfaceHandle *sup_create_handle; NMSupplicantInterface *sup_iface; - guint sup_timeout_id; /* supplicant association timeout */ - NM80211Mode mode; + gint64 scan_last_complete_msec; + gint64 scan_periodic_next_msec; - NMActRequestGetSecretsCallId *wifi_secrets_id; + gint64 scan_last_request_started_at_msec; + + guint scan_kickoff_timeout_id; + + guint ap_dump_id; guint periodic_update_id; + guint link_timeout_id; - guint32 failed_iface_count; guint reacquire_iface_id; + guint wps_timeout_id; + guint sup_timeout_id; /* supplicant association timeout */ NMDeviceWifiCapabilities capabilities; + NMSettingWirelessWakeOnWLan wowlan_restore; - gint32 hw_addr_scan_expire; + NMDeviceWifiP2P *p2p_device; + NM80211Mode mode; - guint wps_timeout_id; + guint32 failed_iface_count; + gint32 hw_addr_scan_expire; - NMSettingWirelessWakeOnWLan wowlan_restore; + guint32 rate; + + guint8 scan_periodic_interval_sec; + + bool enabled:1; /* rfkilled or not */ + bool scan_is_scanning:1; + bool scan_periodic_allowed:1; + bool scan_explicit_allowed:1; + bool scan_explicit_requested:1; + bool ssid_found:1; + bool hidden_probe_scan_warn:1; - NMDeviceWifiP2P *p2p_device; } NMDeviceWifiPrivate; struct _NMDeviceWifi @@ -135,12 +156,8 @@ G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) /*****************************************************************************/ -static gboolean check_scanning_prohibited (NMDeviceWifi *self, gboolean periodic); - static void supplicant_iface_state_down (NMDeviceWifi *self); -static void schedule_scan (NMDeviceWifi *self, gboolean backoff); - static void cleanup_association_attempt (NMDeviceWifi * self, gboolean disconnect); @@ -173,15 +190,8 @@ static void supplicant_iface_notify_p2p_available (NMSupplicantInterface *iface, GParamSpec *pspec, NMDeviceWifi *self); -static void _requested_scan_set (NMDeviceWifi *self, gboolean value); - static void periodic_update (NMDeviceWifi *self); -static void request_wireless_scan (NMDeviceWifi *self, - gboolean periodic, - gboolean force_if_scanning, - const GPtrArray *ssids); - static void ap_add_remove (NMDeviceWifi *self, gboolean is_adding, NMWifiAP *ap, @@ -191,6 +201,158 @@ static void _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset); static void recheck_p2p_availability (NMDeviceWifi *self); +static void _scan_kickoff (NMDeviceWifi *self); + +static gboolean _scan_notify_allowed (NMDeviceWifi *self, NMTernary do_kickoff); + +/*****************************************************************************/ + +typedef struct { + GBytes *ssid; + CList lst; + gint64 timestamp_msec; +} ScanRequestSsidData; + +static void +_scan_request_ssids_remove (ScanRequestSsidData *srs_data) +{ + c_list_unlink_stale (&srs_data->lst); + g_bytes_unref (srs_data->ssid); + nm_g_slice_free (srs_data); +} + +static void +_scan_request_ssids_remove_with_hash (NMDeviceWifiPrivate *priv, + ScanRequestSsidData *srs_data) +{ + nm_assert (srs_data); + nm_assert (nm_g_hash_table_lookup (priv->scan_request_ssids_hash, srs_data) == srs_data); + if (!g_hash_table_remove (priv->scan_request_ssids_hash, srs_data)) + nm_assert_not_reached (); + _scan_request_ssids_remove (srs_data); +} + +static void +_scan_request_ssids_remove_all (NMDeviceWifiPrivate *priv, + gint64 cutoff_with_now_msec, + guint cutoff_at_len) +{ + ScanRequestSsidData *srs_data; + + nm_assert ((!priv->scan_request_ssids_hash) == c_list_is_empty (&priv->scan_request_ssids_lst_head)); + if (!priv->scan_request_ssids_hash) + return; + + if (cutoff_at_len == 0) { + nm_clear_pointer (&priv->scan_request_ssids_hash, g_hash_table_destroy); + while ((srs_data = c_list_first_entry (&priv->scan_request_ssids_lst_head, ScanRequestSsidData, lst))) + _scan_request_ssids_remove (srs_data); + return; + } + + if (cutoff_with_now_msec != 0) { + gint64 cutoff_time_msec; + + /* remove all entries that are older than a max-age. */ + nm_assert (cutoff_with_now_msec > 0); + cutoff_time_msec = cutoff_with_now_msec - SCAN_REQUEST_SSIDS_MAX_AGE_MSEC; + while ((srs_data = c_list_last_entry (&priv->scan_request_ssids_lst_head, ScanRequestSsidData, lst))) { + if (srs_data->timestamp_msec > cutoff_time_msec) + break; + _scan_request_ssids_remove_with_hash (priv, srs_data); + } + } + + if (cutoff_at_len != G_MAXUINT) { + guint i; + + /* trim the list to cutoff_at_len elements. */ + i = nm_g_hash_table_size (priv->scan_request_ssids_hash); + for (; i > cutoff_at_len; i--) { + ScanRequestSsidData *d; + + d = c_list_last_entry (&priv->scan_request_ssids_lst_head, ScanRequestSsidData, lst); + _scan_request_ssids_remove_with_hash (priv, d); + } + } + + nm_assert (nm_g_hash_table_size (priv->scan_request_ssids_hash) <= SCAN_REQUEST_SSIDS_MAX_NUM); + nm_assert (nm_g_hash_table_size (priv->scan_request_ssids_hash) == c_list_length (&priv->scan_request_ssids_lst_head)); + if (c_list_is_empty (&priv->scan_request_ssids_lst_head)) + nm_clear_pointer (&priv->scan_request_ssids_hash, g_hash_table_destroy); +} + +static GPtrArray * +_scan_request_ssids_fetch (NMDeviceWifiPrivate *priv, gint64 now_msec) +{ + ScanRequestSsidData *srs_data; + GPtrArray *ssids; + guint len; + + _scan_request_ssids_remove_all (priv, now_msec, G_MAXUINT); + + len = nm_g_hash_table_size (priv->scan_request_ssids_hash); + if (len == 0) + return NULL; + + ssids = g_ptr_array_new_full (len, (GDestroyNotify) g_bytes_unref); + nm_clear_pointer (&priv->scan_request_ssids_hash, g_hash_table_destroy); + while ((srs_data = c_list_first_entry (&priv->scan_request_ssids_lst_head, ScanRequestSsidData, lst))) { + g_ptr_array_add (ssids, g_steal_pointer (&srs_data->ssid)); + _scan_request_ssids_remove (srs_data); + } + return ssids; +} + +static void +_scan_request_ssids_track (NMDeviceWifiPrivate *priv, + const GPtrArray *ssids) +{ + CList old_lst_head; + gint64 now_msec; + guint i; + + if ( !ssids + || ssids->len == 0) + return; + + now_msec = nm_utils_get_monotonic_timestamp_msec (); + + if (!priv->scan_request_ssids_hash) + priv->scan_request_ssids_hash = g_hash_table_new (nm_pgbytes_hash, nm_pgbytes_equal); + + /* Do a little dance. New elements shall keep their order as in @ssids, but all + * new elements should be sorted in the list preexisting elements of the list. + * First move the old elements away, and splice them back afterwards. */ + c_list_init (&old_lst_head); + c_list_splice (&old_lst_head, &priv->scan_request_ssids_lst_head); + + for (i = 0; i < ssids->len; i++) { + GBytes *ssid = ssids->pdata[i]; + ScanRequestSsidData *d; + + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (ScanRequestSsidData, ssid) == 0); + d = g_hash_table_lookup (priv->scan_request_ssids_hash, &ssid); + if (!d) { + d = g_slice_new (ScanRequestSsidData); + *d = (ScanRequestSsidData) { + .lst = C_LIST_INIT (d->lst), + .timestamp_msec = now_msec, + .ssid = g_bytes_ref (ssid), + }; + g_hash_table_add (priv->scan_request_ssids_hash, d); + } else + d->timestamp_msec = now_msec; + c_list_link_tail (&priv->scan_request_ssids_lst_head, &d->lst); + } + + c_list_splice (&priv->scan_request_ssids_lst_head, &old_lst_head); + + /* Trim the excess. After our splice with old_lst_head, the list contains the new + * elements (from @ssids) at the front (in there original order), followed by older elements. */ + _scan_request_ssids_remove_all (priv, now_msec, SCAN_REQUEST_SSIDS_MAX_NUM); +} + /*****************************************************************************/ void @@ -218,16 +380,15 @@ nm_device_wifi_scanning_prohibited_track (NMDeviceWifi *self, if (!temporarily_prohibited) { if (!elem) return; - nm_c_list_elem_free (elem); - return; + } else { + if (elem) + return; + c_list_link_tail (&priv->scanning_prohibited_lst_head, + &nm_c_list_elem_new_stale (tag)->lst); } - if (elem) - return; - - c_list_link_tail (&priv->scanning_prohibited_lst_head, - &nm_c_list_elem_new_stale (tag)->lst); + _scan_notify_allowed (self, NM_TERNARY_DEFAULT); } /*****************************************************************************/ @@ -252,28 +413,36 @@ nm_device_wifi_get_scanning (NMDeviceWifi *self) { g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), FALSE); - return NM_DEVICE_WIFI_GET_PRIVATE (self)->is_scanning; + return NM_DEVICE_WIFI_GET_PRIVATE (self)->scan_is_scanning; } -static void -_notify_scanning (NMDeviceWifi *self) +static gboolean +_scan_is_scanning_eval (NMDeviceWifiPrivate *priv) +{ + return priv->scan_request_cancellable + || priv->scan_request_delay_source + || ( priv->sup_iface + && nm_supplicant_interface_get_scanning (priv->sup_iface)); +} + +static gboolean +_scan_notify_is_scanning (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - gboolean scanning; gboolean last_scan_changed = FALSE; + NMDeviceState state; + gboolean scanning; - scanning = priv->sup_iface - && nm_supplicant_interface_get_scanning (priv->sup_iface); - - if (scanning == priv->is_scanning) - return; + scanning = _scan_is_scanning_eval (priv); + if (scanning == priv->scan_is_scanning) + return FALSE; - priv->is_scanning = scanning; + priv->scan_is_scanning = scanning; if ( !scanning - || priv->last_scan_msec == 0) { + || priv->scan_last_complete_msec == 0) { last_scan_changed = TRUE; - priv->last_scan_msec = nm_utils_get_monotonic_timestamp_msec (); + priv->scan_last_complete_msec = nm_utils_get_monotonic_timestamp_msec (); } _LOGD (LOGD_WIFI, @@ -281,7 +450,16 @@ _notify_scanning (NMDeviceWifi *self) scanning ? "scanning" : "idle", last_scan_changed ? " (notify last-scan)" : ""); - schedule_scan (self, TRUE); + state = nm_device_get_state (NM_DEVICE (self)); + + if (scanning) { + /* while the device is activating/activated, we don't need the pending + * action. The pending action exists to delay startup complete, while + * activating that is already achieved via other means. */ + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state > NM_DEVICE_STATE_ACTIVATED) + nm_device_add_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WIFI_SCAN, FALSE); + } nm_gobject_notify_together (self, PROP_SCANNING, @@ -289,13 +467,73 @@ _notify_scanning (NMDeviceWifi *self) ? PROP_LAST_SCAN : PROP_0); - if (!priv->is_scanning) { - _requested_scan_set (self, FALSE); - if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED) { - /* Run a quick update of current AP when coming out of a scan */ - periodic_update (self); - } + _scan_kickoff (self); + + if (!_scan_is_scanning_eval (priv)) { + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state > NM_DEVICE_STATE_ACTIVATED) + nm_device_emit_recheck_auto_activate (NM_DEVICE (self)); + nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WIFI_SCAN, FALSE); } + + return TRUE; +} + +static gboolean +_scan_notify_allowed (NMDeviceWifi *self, NMTernary do_kickoff) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + gboolean explicit_allowed; + gboolean periodic_allowed; + NMDeviceState state; + gboolean changed = FALSE; + + state = nm_device_get_state (NM_DEVICE (self)); + + explicit_allowed = FALSE; + periodic_allowed = FALSE; + + if (!c_list_is_empty (&priv->scanning_prohibited_lst_head)) { + /* something prohibits scanning. */ + } else if (NM_IN_SET (priv->mode, NM_802_11_MODE_ADHOC, + NM_802_11_MODE_AP)) { + /* Don't scan when a an AP or Ad-Hoc connection is active as it will + * disrupt connected clients or peers. */ + } else if (NM_IN_SET (state, NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_FAILED)) { + /* Can always scan when disconnected */ + explicit_allowed = TRUE; + periodic_allowed = TRUE; + } else if (NM_IN_SET (state, NM_DEVICE_STATE_ACTIVATED)) { + /* Prohibit periodic scans when connected; we ask the supplicant to + * background scan for us, unless the connection is locked to a specific + * BSSID (in which case scanning is effectively disabled). */ + periodic_allowed = FALSE; + + /* Prohibit scans if the supplicant is busy */ + explicit_allowed = !NM_IN_SET (nm_supplicant_interface_get_state (priv->sup_iface), + NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING, + NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED, + NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE, + NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE); + } + + if ( explicit_allowed != priv->scan_explicit_allowed + || periodic_allowed != priv->scan_periodic_allowed) { + priv->scan_periodic_allowed = periodic_allowed; + priv->scan_explicit_allowed = explicit_allowed; + _LOGT_scan ("scan-periodic-allowed=%d, scan-explicit-allowed=%d", + periodic_allowed, + explicit_allowed); + changed = TRUE; + } + + if ( do_kickoff == NM_TERNARY_TRUE + || ( do_kickoff == NM_TERNARY_DEFAULT + && changed)) + _scan_kickoff (self); + + return changed; } static void @@ -303,7 +541,7 @@ supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface, GParamSpec *pspec, NMDeviceWifi *self) { - _notify_scanning (self); + _scan_notify_is_scanning (self); } static gboolean @@ -370,7 +608,7 @@ supplicant_interface_acquire_cb (NMSupplicantManager *supplicant_manager, G_CALLBACK (supplicant_iface_notify_p2p_available), self); - _notify_scanning (self); + _scan_notify_is_scanning (self); if (nm_supplicant_interface_get_state (priv->sup_iface) != NM_SUPPLICANT_INTERFACE_STATE_STARTING) { /* fake an initial state change. */ @@ -399,26 +637,6 @@ supplicant_interface_acquire (NMDeviceWifi *self) } static void -_requested_scan_set (NMDeviceWifi *self, gboolean value) -{ - NMDeviceWifiPrivate *priv; - - value = !!value; - - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - if (priv->requested_scan == value) - return; - - priv->requested_scan = value; - if (value) - nm_device_add_pending_action ((NMDevice *) self, NM_PENDING_ACTION_WIFI_SCAN, TRUE); - else { - nm_device_emit_recheck_auto_activate (NM_DEVICE (self)); - nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WIFI_SCAN, TRUE); - } -} - -static void supplicant_interface_release (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); @@ -426,14 +644,14 @@ supplicant_interface_release (NMDeviceWifi *self) if (nm_clear_pointer (&priv->sup_create_handle, nm_supplicant_manager_create_interface_cancel)) nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); - _requested_scan_set (self, FALSE); + nm_clear_g_source (&priv->scan_kickoff_timeout_id); + nm_clear_g_source_inst (&priv->scan_request_delay_source); + nm_clear_g_cancellable (&priv->scan_request_cancellable); - nm_clear_g_source (&priv->pending_scan_id); + _scan_request_ssids_remove_all (priv, 0, 0); - /* Reset the scan interval to be pretty frequent when disconnected */ - priv->scan_interval_sec = SCAN_INTERVAL_SEC_MIN + SCAN_INTERVAL_SEC_STEP; - _LOGD (LOGD_WIFI, "wifi-scan: reset interval to %u seconds", - (unsigned) priv->scan_interval_sec); + priv->scan_periodic_interval_sec = 0; + priv->scan_periodic_next_msec = 0; nm_clear_g_source (&priv->ap_dump_id); @@ -452,7 +670,7 @@ supplicant_interface_release (NMDeviceWifi *self) nm_device_wifi_p2p_set_mgmt_iface (priv->p2p_device, NULL); } - _notify_scanning (self); + _scan_notify_is_scanning (self); } static void @@ -684,7 +902,7 @@ deactivate_async (NMDevice *device, user_data = nm_utils_user_data_pack (g_object_ref (self), callback, callback_user_data); if (!priv->sup_iface) { - nm_utils_invoke_on_idle (disconnect_cb_on_idle, user_data, cancellable); + nm_utils_invoke_on_idle (cancellable, disconnect_cb_on_idle, user_data); return; } @@ -702,7 +920,6 @@ deactivate (NMDevice *device) NMDeviceWifi *self = NM_DEVICE_WIFI (device); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); int ifindex = nm_device_get_ifindex (device); - NM80211Mode old_mode = priv->mode; nm_clear_g_source (&priv->periodic_update_id); @@ -732,9 +949,7 @@ deactivate (NMDevice *device) _notify (self, PROP_MODE); } - /* Ensure we trigger a scan after deactivating a Hotspot */ - if (old_mode == NM_802_11_MODE_AP) - request_wireless_scan (self, FALSE, FALSE, NULL); + _scan_notify_allowed (self, NM_TERNARY_TRUE); } static void @@ -1057,10 +1272,7 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) static gboolean get_autoconnect_allowed (NMDevice *device) { - NMDeviceWifiPrivate *priv; - - priv = NM_DEVICE_WIFI_GET_PRIVATE (NM_DEVICE_WIFI (device)); - return !priv->requested_scan; + return !NM_DEVICE_WIFI_GET_PRIVATE (NM_DEVICE_WIFI (device))->scan_is_scanning; } static gboolean @@ -1153,8 +1365,12 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) /* expire the temporary MAC address used during scanning */ priv->hw_addr_scan_expire = 0; - if (do_reset) + if (do_reset) { + priv->scan_last_request_started_at_msec = G_MININT64; + priv->scan_periodic_next_msec = 0; + priv->scan_periodic_interval_sec = 0; nm_device_hw_addr_reset (device, "scanning"); + } return; } @@ -1169,13 +1385,16 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) * We don't bother with to update the MAC address exactly when * it expires, instead on the next scan request, we will generate * a new one.*/ - priv->hw_addr_scan_expire = now + (SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN * 60); + priv->hw_addr_scan_expire = now + SCAN_RAND_MAC_ADDRESS_EXPIRE_SEC; generate_mac_address_mask = nm_config_data_get_device_config (NM_CONFIG_GET_DATA, "wifi.scan-generate-mac-address-mask", device, NULL); + priv->scan_last_request_started_at_msec = G_MININT64; + priv->scan_periodic_next_msec = 0; + priv->scan_periodic_interval_sec = 0; hw_addr_scan = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (device), generate_mac_address_mask); nm_device_hw_addr_set (device, hw_addr_scan, "scanning", TRUE); @@ -1238,6 +1457,7 @@ dbus_request_scan_cb (NMDevice *device, gpointer user_data) { NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); gs_unref_ptrarray GPtrArray *ssids = user_data; if (error) { @@ -1245,15 +1465,9 @@ dbus_request_scan_cb (NMDevice *device, return; } - if (check_scanning_prohibited (self, FALSE)) { - g_dbus_method_invocation_return_error_literal (context, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_NOT_ALLOWED, - "Scanning not allowed at this time"); - return; - } - - request_wireless_scan (self, FALSE, FALSE, ssids); + _scan_request_ssids_track (priv, ssids); + priv->scan_explicit_requested = TRUE; + _scan_kickoff (self); g_dbus_method_invocation_return_value (context, NULL); } @@ -1264,7 +1478,6 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self, { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMDevice *device = NM_DEVICE (self); - gint64 last_scan; gs_unref_ptrarray GPtrArray *ssids = NULL; if (options) { @@ -1291,104 +1504,22 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self, if ( !priv->enabled || !priv->sup_iface - || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED - || nm_device_is_activating (device)) { + || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED) { g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, - "Scanning not allowed while unavailable or activating"); + "Scanning not allowed while unavailable"); return; } - if (nm_supplicant_interface_get_scanning (priv->sup_iface)) { - g_dbus_method_invocation_return_error_literal (invocation, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_NOT_ALLOWED, - "Scanning not allowed while already scanning"); - return; - } - - last_scan = nm_supplicant_interface_get_last_scan (priv->sup_iface); - if ( last_scan > 0 - && nm_utils_get_monotonic_timestamp_msec () < last_scan + (10 * NM_UTILS_MSEC_PER_SEC)) { - /* FIXME: we really should not outright reject a scan request in this case. We should - * ensure to start a scan request soon, possibly with rate limiting. And there is no - * need to tell the caller that we aren't going to scan... - * - * Same above, if we are currently scanning... */ - g_dbus_method_invocation_return_error_literal (invocation, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_NOT_ALLOWED, - "Scanning not allowed immediately following previous scan"); - return; - } - - g_signal_emit_by_name (device, - NM_DEVICE_AUTH_REQUEST, - invocation, - NULL, - NM_AUTH_PERMISSION_WIFI_SCAN, - TRUE, - dbus_request_scan_cb, - g_steal_pointer (&ssids)); -} - -static gboolean -check_scanning_prohibited (NMDeviceWifi *self, - gboolean periodic) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - nm_assert (NM_IS_SUPPLICANT_INTERFACE (priv->sup_iface)); - - if (!c_list_is_empty (&priv->scanning_prohibited_lst_head)) - return TRUE; - - /* Don't scan when a an AP or Ad-Hoc connection is active as it will - * disrupt connected clients or peers. - */ - if (NM_IN_SET (priv->mode, NM_802_11_MODE_ADHOC, - NM_802_11_MODE_AP)) - return TRUE; - - switch (nm_device_get_state (NM_DEVICE (self))) { - case NM_DEVICE_STATE_UNKNOWN: - case NM_DEVICE_STATE_UNMANAGED: - case NM_DEVICE_STATE_UNAVAILABLE: - case NM_DEVICE_STATE_PREPARE: - case NM_DEVICE_STATE_CONFIG: - case NM_DEVICE_STATE_NEED_AUTH: - case NM_DEVICE_STATE_IP_CONFIG: - case NM_DEVICE_STATE_IP_CHECK: - case NM_DEVICE_STATE_SECONDARIES: - case NM_DEVICE_STATE_DEACTIVATING: - /* Prohibit scans when unusable or activating */ - return TRUE; - case NM_DEVICE_STATE_DISCONNECTED: - case NM_DEVICE_STATE_FAILED: - /* Can always scan when disconnected */ - return FALSE; - case NM_DEVICE_STATE_ACTIVATED: - /* Prohibit periodic scans when connected; we ask the supplicant to - * background scan for us, unless the connection is locked to a specific - * BSSID. - */ - if (periodic) - return TRUE; - break; - } - - /* Prohibit scans if the supplicant is busy */ - if ( NM_IN_SET (nm_supplicant_interface_get_state (priv->sup_iface), - NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING, - NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED, - NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE, - NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE) - || nm_supplicant_interface_get_scanning (priv->sup_iface)) - return TRUE; - - /* Allow the scan */ - return FALSE; + nm_device_auth_request (device, + invocation, + NULL, + NM_AUTH_PERMISSION_WIFI_SCAN, + TRUE, + NULL, + dbus_request_scan_cb, + g_steal_pointer (&ssids)); } static gboolean @@ -1410,173 +1541,299 @@ hidden_filter_func (NMSettings *settings, } static GPtrArray * -build_hidden_probe_list (NMDeviceWifi *self) +_scan_request_ssids_build_hidden (NMDeviceWifi *self, + gint64 now_msec, + gboolean *out_has_hidden_profiles) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); guint max_scan_ssids = nm_supplicant_interface_get_max_scan_ssids (priv->sup_iface); gs_free NMSettingsConnection **connections = NULL; - guint i, len; - GPtrArray *ssids = NULL; - static GBytes *nullssid = NULL; + gs_unref_ptrarray GPtrArray *ssids = NULL; + gs_unref_hashtable GHashTable *unique_ssids = NULL; + guint connections_len; + guint n_hidden; + guint i; + + NM_SET_OUT (out_has_hidden_profiles, FALSE); + + /* collect all pending explicit SSIDs. */ + ssids = _scan_request_ssids_fetch (priv, now_msec); - /* Need at least two: wildcard SSID and one or more hidden SSIDs */ - if (max_scan_ssids < 2) + if (max_scan_ssids == 0) { + /* no space. @ssids will be ignored. */ return NULL; + } + + if (ssids) { + if (ssids->len < max_scan_ssids) { + /* Add wildcard SSID using a static wildcard SSID used for every scan */ + g_ptr_array_insert (ssids, 0, g_bytes_ref (nm_gbytes_get_empty ())); + } + if (ssids->len >= max_scan_ssids) { + /* there is no more space. Use what we have. */ + g_ptr_array_set_size (ssids, max_scan_ssids); + return g_steal_pointer (&ssids); + } + } connections = nm_settings_get_connections_clone (nm_device_get_settings ((NMDevice *) self), - &len, - hidden_filter_func, NULL, - NULL, NULL); + &connections_len, + hidden_filter_func, + NULL, + NULL, + NULL); if (!connections[0]) - return NULL; + return g_steal_pointer (&ssids); - g_qsort_with_data (connections, len, sizeof (NMSettingsConnection *), nm_settings_connection_cmp_timestamp_p_with_data, NULL); + if (!ssids) { + ssids = g_ptr_array_new_full (max_scan_ssids, (GDestroyNotify) g_bytes_unref); + /* Add wildcard SSID using a static wildcard SSID used for every scan */ + g_ptr_array_insert (ssids, 0, g_bytes_ref (nm_gbytes_get_empty ())); + } - ssids = g_ptr_array_new_full (max_scan_ssids, (GDestroyNotify) g_bytes_unref); + unique_ssids = g_hash_table_new (nm_gbytes_hash, nm_gbytes_equal); + for (i = 1; i < ssids->len; i++) { + if (!g_hash_table_add (unique_ssids, ssids->pdata[i])) + nm_assert_not_reached (); + } - /* Add wildcard SSID using a static wildcard SSID used for every scan */ - if (G_UNLIKELY (nullssid == NULL)) - nullssid = g_bytes_new_static ("", 0); - g_ptr_array_add (ssids, g_bytes_ref (nullssid)); + g_qsort_with_data (connections, + connections_len, + sizeof (NMSettingsConnection *), + nm_settings_connection_cmp_timestamp_p_with_data, + NULL); - for (i = 0; connections[i]; i++) { + n_hidden = 0; + for (i = 0; i < connections_len; i++) { NMSettingWireless *s_wifi; GBytes *ssid; - if (i >= max_scan_ssids - 1) + if (ssids->len >= max_scan_ssids) + break; + + if (n_hidden > 4) { + /* we allow at most 4 hidden profiles to be actively scanned. The + * reason is speed and to not disclose too many SSIDs. */ break; + } - s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (nm_settings_connection_get_connection (connections[i])); + s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (connections[i])); ssid = nm_setting_wireless_get_ssid (s_wifi); + + if (!g_hash_table_add (unique_ssids, ssid)) + continue; + g_ptr_array_add (ssids, g_bytes_ref (ssid)); + n_hidden++; } - return ssids; + NM_SET_OUT (out_has_hidden_profiles, n_hidden > 0); + return g_steal_pointer (&ssids); } -static void -request_wireless_scan (NMDeviceWifi *self, - gboolean periodic, - gboolean force_if_scanning, - const GPtrArray *ssids) +static gboolean +_scan_request_delay_cb (gpointer user_data) { + NMDeviceWifi *self = user_data; NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - gboolean request_started = FALSE; - nm_clear_g_source (&priv->pending_scan_id); + nm_clear_g_source_inst (&priv->scan_request_delay_source); - if (!force_if_scanning && priv->requested_scan) { - /* There's already a scan in progress */ - return; - } + _LOGT_scan ("scan request completed (after extra delay)"); - if (!check_scanning_prohibited (self, periodic)) { - gs_unref_ptrarray GPtrArray *hidden_ssids = NULL; - - _LOGD (LOGD_WIFI, "wifi-scan: scanning requested"); - - if (!ssids) { - hidden_ssids = build_hidden_probe_list (self); - if (hidden_ssids) { - if (priv->hidden_probe_scan_warn) { - priv->hidden_probe_scan_warn = FALSE; - _LOGW (LOGD_WIFI, "wifi-scan: active scanning for networks due to profiles with wifi.hidden=yes. This makes you trackable"); - } - ssids = hidden_ssids; - } else - priv->hidden_probe_scan_warn = TRUE; - } - - if (_LOGD_ENABLED (LOGD_WIFI)) { - if (ssids) { - guint i; + _scan_notify_is_scanning (self); + return G_SOURCE_REMOVE; +} - for (i = 0; i < ssids->len; i++) { - gs_free char *ssid_str = NULL; - GBytes *ssid = ssids->pdata[i]; +static void +_scan_supplicant_request_scan_cb (NMSupplicantInterface *supp_iface, + GCancellable *cancellable, + gpointer user_data) +{ + NMDeviceWifi *self; + NMDeviceWifiPrivate *priv; - ssid_str = g_bytes_get_size (ssid) > 0 - ? _nm_utils_ssid_to_string (ssid) - : NULL; - _LOGD (LOGD_WIFI, "wifi-scan: (%u) probe scanning SSID %s", - i, ssid_str ?: "*any*"); - } - } else - _LOGD (LOGD_WIFI, "wifi-scan: no SSIDs to probe scan"); - } + if (g_cancellable_is_cancelled (cancellable)) + return; - _hw_addr_set_scanning (self, FALSE); + self = user_data; + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - nm_supplicant_interface_request_scan (priv->sup_iface, - ssids ? (GBytes *const*) ssids->pdata : NULL, - ssids ? ssids->len : 0u); - request_started = TRUE; - } else - _LOGD (LOGD_WIFI, "wifi-scan: scanning requested but not allowed at this time"); + _LOGT_scan ("scan request completed (D-Bus request)"); - _requested_scan_set (self, request_started); + /* we just completed a scan request, but possibly the supplicant's state is not yet toggled + * to "scanning". That means, our internal scanning state "priv->scan_is_scanning" would already + * flip to idle, while in a moment the supplicant would toggle the state again. + * + * Artificially keep the scanning state on, for another SCAN_EXTRA_DELAY_MSEC msec. */ + nm_clear_g_source_inst (&priv->scan_request_delay_source); + priv->scan_request_delay_source = nm_g_source_attach (nm_g_timeout_source_new (SCAN_EXTRA_DELAY_MSEC, + G_PRIORITY_DEFAULT, + _scan_request_delay_cb, + self, + NULL), + NULL); - schedule_scan (self, request_started); + g_clear_object (&priv->scan_request_cancellable); + _scan_notify_is_scanning (self); } static gboolean -request_wireless_scan_periodic (gpointer user_data) +_scan_kickoff_timeout_cb (gpointer user_data) { NMDeviceWifi *self = user_data; NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - priv->pending_scan_id = 0; - request_wireless_scan (self, TRUE, FALSE, NULL); + priv->scan_kickoff_timeout_id = 0; + _scan_kickoff (self); return G_SOURCE_REMOVE; } -/* - * schedule_scan - * - * Schedule a wireless scan. - * - */ static void -schedule_scan (NMDeviceWifi *self, gboolean backoff) +_scan_kickoff (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - gint32 now = nm_utils_get_monotonic_timestamp_sec (); - - /* Cancel the pending scan if it would happen later than (now + the scan_interval_sec) */ - if (priv->pending_scan_id) { - if (now + priv->scan_interval_sec < priv->scheduled_scan_time) - nm_clear_g_source (&priv->pending_scan_id); - } - - if (!priv->pending_scan_id) { - guint factor = 2; - guint next_scan = priv->scan_interval_sec; - - if ( nm_device_is_activating (NM_DEVICE (self)) - || (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED)) - factor = 1; - - priv->pending_scan_id = g_timeout_add_seconds (next_scan, - request_wireless_scan_periodic, - self); - - priv->scheduled_scan_time = now + priv->scan_interval_sec; - if (backoff && (priv->scan_interval_sec < (SCAN_INTERVAL_SEC_MAX / factor))) { - priv->scan_interval_sec += (SCAN_INTERVAL_SEC_STEP / factor); - /* Ensure the scan interval will never be less than 20s... */ - priv->scan_interval_sec = MAX(priv->scan_interval_sec, SCAN_INTERVAL_SEC_MIN + SCAN_INTERVAL_SEC_STEP); - /* ... or more than 120s */ - priv->scan_interval_sec = MIN(priv->scan_interval_sec, SCAN_INTERVAL_SEC_MAX); - } else if (!backoff && (priv->scan_interval_sec == 0)) { - /* Invalid combination; would cause continual rescheduling of - * the scan and hog CPU. Reset to something minimally sane. - */ - priv->scan_interval_sec = 5; + gs_unref_ptrarray GPtrArray *ssids = NULL; + gboolean is_explict = FALSE; + NMDeviceState device_state; + gboolean has_hidden_profiles; + gint64 now_msec; + gint64 ratelimit_duration_msec; + + if (!priv->sup_iface) { + _LOGT_scan ("kickoff: don't scan (has no supplicant interface)"); + return; + } + + if (priv->scan_request_cancellable) { + _LOGT_scan ("kickoff: don't scan (has scan_request_cancellable)"); + /* We are currently waiting for a scan request to complete. Wait longer. */ + return; + } + + now_msec = nm_utils_get_monotonic_timestamp_msec (); + + _scan_request_ssids_remove_all (priv, now_msec, G_MAXUINT); + + device_state = nm_device_get_state (NM_DEVICE (self)); + if ( device_state > NM_DEVICE_STATE_DISCONNECTED + && device_state <= NM_DEVICE_STATE_ACTIVATED) { + /* while we are activated, we rate limit more. */ + ratelimit_duration_msec = 8000; + } else + ratelimit_duration_msec = 1500; + + if (priv->scan_last_request_started_at_msec + ratelimit_duration_msec > now_msec) { + _LOGT_scan ("kickoff: don't scan (rate limited for another %d.%03d sec%s)", + (int) ((priv->scan_last_request_started_at_msec + ratelimit_duration_msec - now_msec) / 1000), + (int) ((priv->scan_last_request_started_at_msec + ratelimit_duration_msec - now_msec) % 1000), + !priv->scan_kickoff_timeout_id ? ", schedule timeout" : ""); + if ( !priv->scan_kickoff_timeout_id + && ( priv->scan_explicit_allowed + || priv->scan_periodic_allowed)) { + priv->scan_kickoff_timeout_id = g_timeout_add (priv->scan_last_request_started_at_msec + ratelimit_duration_msec - now_msec, + _scan_kickoff_timeout_cb, + self); + } + return; + } + + if (priv->scan_explicit_requested) { + if (!priv->scan_explicit_allowed) { + _LOGT_scan ("kickoff: don't scan (explicit scan requested but not allowed)"); + return; + } + priv->scan_explicit_requested = FALSE; + is_explict = TRUE; + } else { + + if (!priv->scan_periodic_allowed) { + _LOGT_scan ("kickoff: don't scan (periodic scan currently not allowed)"); + priv->scan_periodic_next_msec = 0; + priv->scan_periodic_interval_sec = 0; + nm_clear_g_source (&priv->scan_kickoff_timeout_id); + return; + } + + nm_assert (priv->scan_explicit_allowed); + + if (now_msec < priv->scan_periodic_next_msec) { + _LOGT_scan ("kickoff: don't scan (periodic scan waiting for another %d.%03d sec%s)", + (int) ((priv->scan_periodic_next_msec - now_msec) / 1000), + (int) ((priv->scan_periodic_next_msec - now_msec) % 1000), + !priv->scan_kickoff_timeout_id ? ", schedule timeout" : ""); + if (!priv->scan_kickoff_timeout_id) { + priv->scan_kickoff_timeout_id = g_timeout_add_seconds ((priv->scan_periodic_next_msec - now_msec + 999) / 1000, + _scan_kickoff_timeout_cb, + self); + } + return; } - _LOGD (LOGD_WIFI, "wifi-scan: scheduled in %d seconds (interval now %d seconds)", - next_scan, priv->scan_interval_sec); + priv->scan_periodic_interval_sec = NM_CLAMP (((int) priv->scan_periodic_interval_sec) * 3 / 2, + SCAN_INTERVAL_SEC_MIN, + SCAN_INTERVAL_SEC_MAX); + priv->scan_periodic_next_msec = now_msec + 1000 * priv->scan_periodic_interval_sec; } + + ssids = _scan_request_ssids_build_hidden (self, now_msec, &has_hidden_profiles); + if (has_hidden_profiles) { + if (priv->hidden_probe_scan_warn) { + priv->hidden_probe_scan_warn = FALSE; + _LOGW (LOGD_WIFI, "wifi-scan: active scanning for networks due to profiles with wifi.hidden=yes. This makes you trackable"); + } + } else if (!is_explict) + priv->hidden_probe_scan_warn = TRUE; + + if (_LOGD_ENABLED (LOGD_WIFI)) { + gs_free char *ssids_str = NULL; + guint ssids_len = 0; + + if (ssids) { + gs_strfreev char **strv = NULL; + guint i; + + strv = g_new (char *, ssids->len + 1u); + for (i = 0; i < ssids->len; i++) + strv[i] = _nm_utils_ssid_to_string (ssids->pdata[i]); + strv[i] = NULL; + + nm_assert (ssids->len > 0); + nm_assert (ssids->len == NM_PTRARRAY_LEN (strv)); + + ssids_str = g_strjoinv (", ", strv); + ssids_len = ssids->len; + } + _LOGD (LOGD_WIFI, "wifi-scan: start %s scan (%u SSIDs to probe scan%s%s%s)", + is_explict ? "explicit" : "periodic", + ssids_len, + NM_PRINT_FMT_QUOTED (ssids_str, " [", ssids_str, "]", "")); + } + + priv->scan_last_request_started_at_msec = now_msec; + + if (is_explict) + _LOGT_scan ("kickoff: explicit scan starting"); + else { + _LOGT_scan ("kickoff: periodic scan starting (next scan is scheduled in %d.%03d sec)", + (int) ((priv->scan_periodic_next_msec - now_msec) / 1000), + (int) ((priv->scan_periodic_next_msec - now_msec) % 1000)); + } + + _hw_addr_set_scanning (self, FALSE); + + priv->scan_request_cancellable = g_cancellable_new (); + nm_supplicant_interface_request_scan (priv->sup_iface, + ssids ? (GBytes *const*) ssids->pdata : NULL, + ssids ? ssids->len : 0u, + priv->scan_request_cancellable, + _scan_supplicant_request_scan_cb, + self); + + /* It's OK to call _scan_notify_is_scanning() again. They mutually call each other, + * but _scan_kickoff() sets "priv->scan_request_cancellable" which will stop + * them from recursing indefinitely. */ + _scan_notify_is_scanning (self); } /**************************************************************************** @@ -1597,16 +1854,15 @@ ap_list_dump (gpointer user_data) gint64 now_msec = nm_utils_get_monotonic_timestamp_msec (); char str_buf[100]; - _LOGD (LOGD_WIFI_SCAN, "APs: [now:%u.%03u, last:%s, next:%u]", + _LOGD (LOGD_WIFI_SCAN, "APs: [now:%u.%03u, last:%s]", (guint) (now_msec / NM_UTILS_MSEC_PER_SEC), (guint) (now_msec % NM_UTILS_MSEC_PER_SEC), - priv->last_scan_msec > 0 + priv->scan_last_complete_msec > 0 ? nm_sprintf_buf (str_buf, "%u.%03u", - (guint) (priv->last_scan_msec / NM_UTILS_MSEC_PER_SEC), - (guint) (priv->last_scan_msec % NM_UTILS_MSEC_PER_SEC)) - : "-1", - priv->scheduled_scan_time); + (guint) (priv->scan_last_complete_msec / NM_UTILS_MSEC_PER_SEC), + (guint) (priv->scan_last_complete_msec % NM_UTILS_MSEC_PER_SEC)) + : "-1"); c_list_for_each_entry (ap, &priv->aps_lst_head, aps_lst) _ap_dump (self, LOGL_DEBUG, ap, "dump", now_msec); } @@ -2138,6 +2394,7 @@ supplicant_iface_state (NMDeviceWifi *self, NMDevice *device = NM_DEVICE (self); NMDeviceState devstate; gboolean scanning; + gboolean scan_changed; _LOGI (LOGD_DEVICE | LOGD_WIFI, "supplicant interface state: %s -> %s%s", @@ -2158,7 +2415,8 @@ supplicant_iface_state (NMDeviceWifi *self, nm_device_queue_recheck_available (NM_DEVICE (device), NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - priv->scan_interval_sec = SCAN_INTERVAL_SEC_MIN; + priv->scan_periodic_interval_sec = 0; + priv->scan_periodic_next_msec = 0; } /* In these states we know the supplicant is actually talking to something */ @@ -2225,17 +2483,19 @@ supplicant_iface_state (NMDeviceWifi *self, } break; case NM_SUPPLICANT_INTERFACE_STATE_INACTIVE: - /* we would clear _requested_scan_set() and trigger a new scan. + /* we would clear _scan_has_pending_action_set() and trigger a new scan. * However, we don't want to cancel the current pending action, so force * a new scan request. */ - request_wireless_scan (self, FALSE, TRUE, NULL); break; default: break; } out: - _notify_scanning (self); + scan_changed = _scan_notify_allowed (self, NM_TERNARY_FALSE); + scan_changed |= _scan_notify_is_scanning (self); + if (scan_changed) + _scan_kickoff (self); if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) nm_device_remove_pending_action (device, NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); @@ -2764,6 +3024,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) ap = ap_fake; } + _scan_notify_allowed (self, NM_TERNARY_DEFAULT); + set_current_ap (self, ap, FALSE); nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))); @@ -3103,8 +3365,8 @@ activation_success_handler (NMDevice *device) update_seen_bssids_cache (self, priv->current_ap); - /* Reset scan interval to something reasonable */ - priv->scan_interval_sec = SCAN_INTERVAL_SEC_MIN + (SCAN_INTERVAL_SEC_STEP * 2); + priv->scan_periodic_interval_sec = 0; + priv->scan_periodic_next_msec = 0; } static void @@ -3164,9 +3426,6 @@ device_state_changed (NMDevice *device, nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), FALSE); break; case NM_DEVICE_STATE_DISCONNECTED: - /* Kick off a scan to get latest results */ - priv->scan_interval_sec = SCAN_INTERVAL_SEC_MIN; - request_wireless_scan (self, FALSE, FALSE, NULL); break; default: break; @@ -3174,6 +3433,8 @@ device_state_changed (NMDevice *device, if (clear_aps) remove_all_aps (self); + + _scan_notify_allowed (self, NM_TERNARY_DEFAULT); } static gboolean @@ -3324,8 +3585,8 @@ get_property (GObject *object, guint prop_id, break; case PROP_LAST_SCAN: g_value_set_int64 (value, - priv->last_scan_msec > 0 - ? nm_utils_monotonic_timestamp_as_boottime (priv->last_scan_msec, NM_UTILS_NSEC_PER_MSEC) + priv->scan_last_complete_msec > 0 + ? nm_utils_monotonic_timestamp_as_boottime (priv->scan_last_complete_msec, NM_UTILS_NSEC_PER_MSEC) : (gint64) -1); break; default: @@ -3361,8 +3622,10 @@ nm_device_wifi_init (NMDeviceWifi *self) c_list_init (&priv->aps_lst_head); c_list_init (&priv->scanning_prohibited_lst_head); + c_list_init (&priv->scan_request_ssids_lst_head); priv->aps_idx_by_supplicant_path = g_hash_table_new (nm_direct_hash, NULL); + priv->scan_last_request_started_at_msec = G_MININT64; priv->hidden_probe_scan_warn = TRUE; priv->mode = NM_802_11_MODE_INFRA; priv->wowlan_restore = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE; diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index f89ec86c..3fdc5ac5 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -1254,9 +1254,9 @@ disconnect (NMModem *modem, /* Already cancelled or no simple-iface? We are done. */ if ( !ctx->self->_priv.simple_iface || g_cancellable_is_cancelled (cancellable)) { - nm_utils_invoke_on_idle (disconnect_context_complete_on_idle, - ctx, - cancellable); + nm_utils_invoke_on_idle (cancellable, + disconnect_context_complete_on_idle, + ctx); return; } diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c index 2fc44881..96b08e7c 100644 --- a/src/devices/wwan/nm-modem-ofono.c +++ b/src/devices/wwan/nm-modem-ofono.c @@ -211,9 +211,9 @@ disconnect (NMModem *modem, if ( state != NM_MODEM_STATE_CONNECTED || g_cancellable_is_cancelled (cancellable)) { - nm_utils_invoke_on_idle (disconnect_context_complete_on_idle, - ctx, - cancellable); + nm_utils_invoke_on_idle (cancellable, + disconnect_context_complete_on_idle, + ctx); return; } |