diff options
Diffstat (limited to 'src/devices/wifi')
| -rw-r--r-- | src/devices/wifi/meson.build | 14 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-iwd.c | 1139 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-iwd.h | 2 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-olpc-mesh.c | 11 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-wifi-p2p.c | 1316 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-wifi-p2p.h | 50 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-wifi.c | 346 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-wifi.h | 1 | ||||
| -rw-r--r-- | src/devices/wifi/nm-iwd-manager.c | 134 | ||||
| -rw-r--r-- | src/devices/wifi/nm-wifi-ap.c | 6 | ||||
| -rw-r--r-- | src/devices/wifi/nm-wifi-factory.c | 69 | ||||
| -rw-r--r-- | src/devices/wifi/nm-wifi-p2p-peer.c | 796 | ||||
| -rw-r--r-- | src/devices/wifi/nm-wifi-p2p-peer.h | 114 | ||||
| -rw-r--r-- | src/devices/wifi/nm-wifi-utils.c | 7 | ||||
| -rw-r--r-- | src/devices/wifi/tests/meson.build | 5 | ||||
| -rw-r--r-- | src/devices/wifi/tests/test-general.c | 3 |
16 files changed, 3478 insertions, 535 deletions
diff --git a/src/devices/wifi/meson.build b/src/devices/wifi/meson.build index 2745040b..4dfbe4c8 100644 --- a/src/devices/wifi/meson.build +++ b/src/devices/wifi/meson.build @@ -1,13 +1,15 @@ common_sources = files( 'nm-wifi-ap.c', - 'nm-wifi-utils.c' + 'nm-wifi-p2p-peer.c', + 'nm-wifi-utils.c', ) sources = common_sources + files( - 'nm-wifi-factory.c', - 'nm-wifi-common.c', + 'nm-device-olpc-mesh.c', + 'nm-device-wifi-p2p.c', 'nm-device-wifi.c', - 'nm-device-olpc-mesh.c' + 'nm-wifi-common.c', + 'nm-wifi-factory.c', ) if enable_iwd @@ -18,7 +20,7 @@ if enable_iwd endif deps = [ - nm_dep + nm_dep, ] libnm_device_plugin_wifi = shared_module( @@ -28,7 +30,7 @@ libnm_device_plugin_wifi = shared_module( link_args: ldflags_linker_script_devices, link_depends: linker_script_devices, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_device_plugin_wifi diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index 1d1be742..dcc161d2 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -22,8 +22,6 @@ #include "nm-device-iwd.h" -#include <string.h> - #include "nm-common-macros.h" #include "devices/nm-device.h" #include "devices/nm-device-private.h" @@ -41,6 +39,7 @@ #include "nm-config.h" #include "nm-iwd-manager.h" #include "nm-dbus-manager.h" +#include "nm-dbus-compat.h" #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceIwd); @@ -69,6 +68,8 @@ typedef struct { GDBusObject * dbus_obj; GDBusProxy * dbus_device_proxy; GDBusProxy * dbus_station_proxy; + GDBusProxy * dbus_ap_proxy; + GDBusProxy * dbus_adhoc_proxy; CList aps_lst_head; NMWifiAP * current_ap; GCancellable * cancellable; @@ -80,6 +81,7 @@ typedef struct { bool can_connect:1; bool scanning:1; bool scan_requested:1; + bool act_mode_switch:1; gint64 last_scan; } NMDeviceIwdPrivate; @@ -104,7 +106,7 @@ G_DEFINE_TYPE (NMDeviceIwd, nm_device_iwd, NM_TYPE_DEVICE) /*****************************************************************************/ static void schedule_periodic_scan (NMDeviceIwd *self, - NMDeviceState current_state); + gboolean initial_scan); /*****************************************************************************/ @@ -229,7 +231,11 @@ vardict_from_network_type (const char *type) } static void -insert_ap_from_network (GHashTable *aps, const char *path, int16_t signal, uint32_t ap_id) +insert_ap_from_network (NMDeviceIwd *self, + GHashTable *aps, + const char *path, + int16_t signal, + uint32_t ap_id) { gs_unref_object GDBusProxy *network_proxy = NULL; gs_unref_variant GVariant *name_value = NULL, *type_value = NULL; @@ -240,6 +246,11 @@ insert_ap_from_network (GHashTable *aps, const char *path, int16_t signal, uint3 uint8_t bssid[6]; NMWifiAP *ap; + if (g_hash_table_lookup (aps, path)) { + _LOGD (LOGD_WIFI, "Duplicate network at %s", path); + return; + } + network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), path, NM_IWD_NETWORK_INTERFACE); @@ -304,7 +315,7 @@ static void get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) { NMDeviceIwd *self = user_data; - NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDeviceIwdPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *variant = NULL; GVariantIter *networks; @@ -314,33 +325,42 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) gboolean changed = FALSE; GHashTableIter ap_iter; gs_unref_hashtable GHashTable *new_aps = NULL; - /* Depending on whether we're using the Station interface or the Device - * interface for compatibility with IWD <= 0.7, the return signature of - * GetOrderedNetworks will be different. - */ - gboolean compat = priv->dbus_station_proxy == priv->dbus_device_proxy; - const char *return_sig = compat ? "(a(osns))" : "(a(on))"; + gboolean compat; + const char *return_sig; static uint32_t ap_id = 0; - variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, - G_VARIANT_TYPE (return_sig), - &error); + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); if (!variant) { _LOGE (LOGD_WIFI, "Station.GetOrderedNetworks failed: %s", error->message); return; } + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + /* Depending on whether we're using the Station interface or the Device + * interface for compatibility with IWD <= 0.7, the return signature of + * GetOrderedNetworks will be different. + */ + compat = priv->dbus_station_proxy == priv->dbus_device_proxy; + return_sig = compat ? "(a(osns))" : "(a(on))"; + + if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (return_sig))) { + _LOGE (LOGD_WIFI, "Station.GetOrderedNetworks returned type %s instead of %s", + g_variant_get_type_string (variant), return_sig); + return; + } + new_aps = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); g_variant_get (variant, return_sig, &networks); if (compat) { while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type)) - insert_ap_from_network (new_aps, path, signal, ap_id++); + insert_ap_from_network (self, new_aps, path, signal, ap_id++); } else { while (g_variant_iter_next (networks, "(&on)", &path, &signal)) - insert_ap_from_network (new_aps, path, signal, ap_id++); + insert_ap_from_network (self, new_aps, path, signal, ap_id++); } g_variant_iter_free (networks); @@ -393,7 +413,7 @@ update_aps (NMDeviceIwd *self) priv->cancellable = g_cancellable_new (); g_dbus_proxy_call (priv->dbus_station_proxy, "GetOrderedNetworks", - g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, + NULL, G_DBUS_CALL_FLAGS_NONE, 2000, priv->cancellable, get_ordered_networks_cb, self); } @@ -403,8 +423,8 @@ send_disconnect (NMDeviceIwd *self) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); + g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } static void @@ -431,55 +451,104 @@ cleanup_association_attempt (NMDeviceIwd *self, gboolean disconnect) } static void +reset_mode (NMDeviceIwd *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + g_dbus_proxy_call (priv->dbus_device_proxy, + DBUS_INTERFACE_PROPERTIES ".Set", + g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE, + "Mode", + g_variant_new_string ("station")), + G_DBUS_CALL_FLAGS_NONE, 2000, + cancellable, + callback, + user_data); +} + +static void deactivate (NMDevice *device) { - cleanup_association_attempt (NM_DEVICE_IWD (device), TRUE); + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if (!priv->dbus_obj) + return; + + cleanup_association_attempt (self, TRUE); + priv->act_mode_switch = FALSE; + + if (!priv->dbus_station_proxy) + reset_mode (self, NULL, NULL, NULL); } -static gboolean -deactivate_async_finish (NMDevice *device, GAsyncResult *res, GError **error) +static void +disconnect_cb (GObject *source, GAsyncResult *res, gpointer user_data) { - NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device)); + gs_unref_object NMDeviceIwd *self = NULL; + NMDeviceDeactivateCallback callback; + gpointer callback_user_data; gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; - variant = g_dbus_proxy_call_finish (priv->dbus_station_proxy, res, error); - return variant != NULL; -} + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); -typedef struct { - NMDeviceIwd *self; - GAsyncReadyCallback callback; - gpointer user_data; -} DeactivateContext; + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + callback (NM_DEVICE (self), error, callback_user_data); +} static void -disconnect_cb (GObject *source, GAsyncResult *res, gpointer user_data) +disconnect_cb_on_idle (gpointer user_data, + GCancellable *cancellable) { - DeactivateContext *ctx = user_data; + gs_unref_object NMDeviceIwd *self = NULL; + NMDeviceDeactivateCallback callback; + gpointer callback_user_data; + gs_free_error GError *cancelled_error = NULL; - ctx->callback (G_OBJECT (ctx->self), res, ctx->user_data); + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); - g_object_unref (ctx->self); - g_slice_free (DeactivateContext, ctx); + g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); + callback (NM_DEVICE (self), cancelled_error, callback_user_data); } static void deactivate_async (NMDevice *device, GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) + NMDeviceDeactivateCallback callback, + gpointer callback_user_data) { NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - DeactivateContext *ctx; + gpointer user_data; - ctx = g_slice_new0 (DeactivateContext); - ctx->self = g_object_ref (self); - ctx->callback = callback; - ctx->user_data = user_data; + nm_assert (G_IS_CANCELLABLE (cancellable)); + nm_assert (callback); - g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, cancellable, disconnect_cb, ctx); + 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); + return; + } + + cleanup_association_attempt (self, FALSE); + priv->act_mode_switch = FALSE; + + if (priv->dbus_station_proxy) { + g_dbus_proxy_call (priv->dbus_station_proxy, + "Disconnect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + disconnect_cb, + user_data); + } else + reset_mode (self, cancellable, disconnect_cb, user_data); } static gboolean @@ -510,13 +579,37 @@ is_connection_known_network (NMConnection *connection) } static gboolean +is_ap_known_network (NMWifiAP *ap) +{ + GDBusProxy *network_proxy; + gs_unref_variant GVariant *known_network = NULL; + + network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), + nm_wifi_ap_get_supplicant_path (ap), + NM_IWD_NETWORK_INTERFACE); + if (!network_proxy) + return FALSE; + + known_network = g_dbus_proxy_get_cached_property (network_proxy, "KnownNetwork"); + g_object_unref (network_proxy); + + return known_network + && g_variant_is_of_type (known_network, G_VARIANT_TYPE_OBJECT_PATH); +} + +static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error) { + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMSettingWireless *s_wireless; const char *mac; const char * const *mac_blacklist; int i; const char *perm_hw_addr; + const char *mode; + NMIwdNetworkSecurity security; + gboolean mapped; if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->check_connection_compatible (device, connection, error)) return FALSE; @@ -549,23 +642,65 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError return FALSE; } - if (!NM_IN_STRSET (nm_setting_wireless_get_mode (s_wireless), - NULL, - NM_SETTING_WIRELESS_MODE_INFRA)) { + /* 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, - "IWD only support infrastructure type profiles"); + "hidden networks not supported by the IWD backend"); return FALSE; } - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) { + security = nm_wifi_connection_get_iwd_security (connection, &mapped); + if (!mapped) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "connection authentication type not supported by IWD backend"); + return FALSE; + } + + mode = nm_setting_wireless_get_mode (s_wireless); + 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. + */ + if (security == NM_IWD_NETWORK_SECURITY_8021X) { + if (!is_connection_known_network (connection)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "802.1x connections must have IWD provisioning files"); + return FALSE; + } + } else if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_NONE, NM_IWD_NETWORK_SECURITY_PSK)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IWD backend only supports Open, PSK and 802.1x network " + "authentication in Infrastructure mode"); + return FALSE; + } + } else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_AP)) { + if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "device does not support Access Point mode"); + return FALSE; + } + + if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_PSK)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IWD backend only supports PSK authentication in AP mode"); + return FALSE; + } + } else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { + if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "device does not support Ad-Hoc mode"); + return FALSE; + } + + if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_NONE, NM_IWD_NETWORK_SECURITY_PSK)) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "802.1x profile is not a known network"); + "IWD backend only supports Open and PSK authentication in Ad-Hoc mode"); return FALSE; } + } else { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "%s type profiles not supported by IWD backend"); + return FALSE; } return TRUE; @@ -582,42 +717,15 @@ check_connection_available (NMDevice *device, NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMSettingWireless *s_wifi; const char *mode; + NMWifiAP *ap = NULL; s_wifi = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wifi, FALSE); - /* Only Infrastrusture mode at this time */ - mode = nm_setting_wireless_get_mode (s_wifi); - if (!NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "iwd only supports infrastructure mode connections"); - return FALSE; - } - - /* Hidden SSIDs not supported yet */ - if (nm_setting_wireless_get_hidden (s_wifi)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "hidden networks not supported by iwd"); - return FALSE; - } - - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "network is not known to iwd"); - return FALSE; - } - } - /* a connection that is available for a certain @specific_object, MUST * also be available in general (without @specific_object). */ if (specific_object) { - NMWifiAP *ap; - ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); if (!ap) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, @@ -629,18 +737,36 @@ check_connection_available (NMDevice *device, "requested access point is not compatible with profile"); return FALSE; } - return TRUE; } + /* AP and Ad-Hoc connections can be activated independent of the scan list */ + mode = nm_setting_wireless_get_mode (s_wifi); + if (NM_IN_STRSET (mode, NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC)) + return TRUE; + if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP)) return TRUE; - if (!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection)) { + if (!ap) + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); + + if (!ap) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "no compatible access point found"); return FALSE; } + /* 8021x networks can only be used if they've been provisioned on the IWD side and + * thus are Known Networks. + */ + if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { + if (!is_ap_known_network (ap)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "802.1x network is not an IWD Known Network (missing provisioning file?)"); + return FALSE; + } + } + return TRUE; } @@ -666,15 +792,11 @@ complete_connection (NMDevice *device, mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL; - if (mode && !nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) { - g_set_error_literal (error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "Only Infrastructure mode is supported."); - return FALSE; - } - - if (!specific_object) { + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) { + if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) + return FALSE; + ap = NULL; + } else if (!specific_object) { /* If not given a specific object, we need at minimum an SSID */ if (!s_wifi) { g_set_error_literal (error, @@ -696,11 +818,16 @@ 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) { - 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 (!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 (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) + return FALSE; } } else { ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); @@ -720,7 +847,10 @@ complete_connection (NMDevice *device, nm_connection_add_setting (connection, NM_SETTING (s_wifi)); } - ssid = nm_wifi_ap_get_ssid (ap); + ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!ssid && ap) + ssid = nm_wifi_ap_get_ssid (ap); + if (!ssid) { g_set_error_literal (error, NM_DEVICE_ERROR, @@ -729,11 +859,13 @@ complete_connection (NMDevice *device, return FALSE; } - if (!nm_wifi_ap_complete_connection (ap, - connection, - nm_wifi_utils_is_manf_default_ssid (ssid), - error)) - return FALSE; + if (ap) { + if (!nm_wifi_ap_complete_connection (ap, + connection, + nm_wifi_utils_is_manf_default_ssid (ssid), + error)) + return FALSE; + } ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid); nm_utils_complete_generic (nm_device_get_platform (device), @@ -745,19 +877,6 @@ complete_connection (NMDevice *device, NULL, TRUE); - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "This 8021x network has not been provisioned on this machine"); - return FALSE; - } - } - perm_hw_addr = nm_device_get_permanent_hw_address (device); if (perm_hw_addr) { setting_mac = nm_setting_wireless_get_mac_address (s_wifi); @@ -820,8 +939,21 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) { NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - - return priv->enabled && priv->dbus_station_proxy; + NMDeviceState state = nm_device_get_state (device); + + /* Available if either the device is UP and in station mode + * or in AP/Ad-Hoc modes while activating or activated. Device + * may be temporarily DOWN while activating or deactivating and + * we don't want it to be marked unavailable because of this. + * + * For reference: + * We call nm_device_queue_recheck_available whenever + * priv->enabled changes or priv->dbus_station_proxy changes. + */ + return priv->dbus_obj + && priv->enabled + && ( priv->dbus_station_proxy + || (state >= NM_DEVICE_STATE_CONFIG && state <= NM_DEVICE_STATE_DEACTIVATING)); } static gboolean @@ -829,8 +961,7 @@ get_autoconnect_allowed (NMDevice *device) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device)); - return is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE) - && priv->can_connect; + return priv->can_connect; } static gboolean @@ -856,7 +987,9 @@ can_auto_connect (NMDevice *device, s_wifi = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wifi, FALSE); - /* Only Infrastrusture mode */ + /* Don't auto-activate AP or Ad-Hoc connections. + * Note the wpa_supplicant backend has the opposite policy. + */ mode = nm_setting_wireless_get_mode (s_wifi); if (mode && g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0) return FALSE; @@ -870,14 +1003,6 @@ can_auto_connect (NMDevice *device, return FALSE; } - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) - return FALSE; - } - ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); if (ap) { /* All good; connection is usable */ @@ -908,11 +1033,11 @@ scan_cb (GObject *source, GAsyncResult *res, gpointer user_data) { NMDeviceIwd *self = user_data; NMDeviceIwdPrivate *priv; + gs_unref_variant GVariant *variant = NULL; gs_free_error GError *error = NULL; - if ( !_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, - G_VARIANT_TYPE ("()"), &error) - && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant && nm_utils_error_is_cancelled (error, FALSE)) return; priv = NM_DEVICE_IWD_GET_PRIVATE (self); @@ -925,11 +1050,8 @@ scan_cb (GObject *source, GAsyncResult *res, gpointer user_data) * scheduled when priv->scanning goes back to false. On error, * schedule a retry now. */ - if (error && !priv->scanning) { - NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); - - schedule_periodic_scan (self, state); - } + if (error && !priv->scanning) + schedule_periodic_scan (self, FALSE); } static void @@ -958,9 +1080,7 @@ dbus_request_scan_cb (NMDevice *device, priv = NM_DEVICE_IWD_GET_PRIVATE (self); - if ( !priv->can_scan - || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED - || nm_device_is_activating (device)) { + if (!priv->can_scan) { g_dbus_method_invocation_return_error_literal (context, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, @@ -982,8 +1102,7 @@ dbus_request_scan_cb (NMDevice *device, if (!priv->scanning && !priv->scan_requested) { g_dbus_proxy_call (priv->dbus_station_proxy, "Scan", - g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, + NULL, G_DBUS_CALL_FLAGS_NONE, -1, priv->cancellable, scan_cb, self); priv->scan_requested = TRUE; } @@ -999,9 +1118,7 @@ _nm_device_iwd_request_scan (NMDeviceIwd *self, NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMDevice *device = NM_DEVICE (self); - if ( !priv->can_scan - || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED - || nm_device_is_activating (device)) { + if (!priv->can_scan) { g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, @@ -1013,7 +1130,7 @@ _nm_device_iwd_request_scan (NMDeviceIwd *self, NM_DEVICE_AUTH_REQUEST, invocation, NULL, - NM_AUTH_PERMISSION_NETWORK_CONTROL, + NM_AUTH_PERMISSION_WIFI_SCAN, TRUE, dbus_request_scan_cb, options ? g_variant_ref (options) : NULL); @@ -1041,8 +1158,6 @@ scanning_prohibited (NMDeviceIwd *self, gboolean periodic) return TRUE; case NM_DEVICE_STATE_DISCONNECTED: case NM_DEVICE_STATE_FAILED: - /* Can always scan when disconnected */ - return FALSE; case NM_DEVICE_STATE_ACTIVATED: break; } @@ -1199,7 +1314,7 @@ wifi_secrets_cb (NMActRequest *req, priv->wifi_secrets_id = NULL; - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + if (nm_utils_error_is_cancelled (error, FALSE)) { g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "NM secrets request cancelled"); @@ -1263,7 +1378,7 @@ wifi_secrets_get_one (NMDeviceIwd *self, TRUE, setting_name, flags, - setting_key, + NM_MAKE_STRV (setting_key), wifi_secrets_cb, nm_utils_user_data_pack (self, invocation)); } @@ -1274,6 +1389,7 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) NMDeviceIwd *self = user_data; NMDevice *device = NM_DEVICE (self); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + gs_unref_variant GVariant *variant = NULL; gs_free_error GError *error = NULL; NMConnection *connection; NMSettingWireless *s_wifi; @@ -1282,9 +1398,8 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED; GVariant *value; - if (!_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, - G_VARIANT_TYPE ("()"), - &error)) { + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant) { gs_free char *dbus_error = NULL; /* Connection failed; radio problems or if the network wasn't @@ -1295,7 +1410,7 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) "Activation: (wifi) Network.Connect failed: %s", error->message); - if (nm_utils_error_is_cancelled (error, TRUE)) + if (nm_utils_error_is_cancelled (error, FALSE)) return; if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH)) @@ -1359,12 +1474,262 @@ failed: } static void +act_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDevice *device = NM_DEVICE (self); + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant && nm_utils_error_is_cancelled (error, FALSE)) + return; + + /* Change state to FAILED unless already done by state_changed + * which may have been triggered by the station interface + * appearing on DBus. + */ + if (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG) + nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); +} + +static void +act_start_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + NMSettingWireless *s_wireless; + GBytes *ssid; + gs_free char *ssid_utf8 = NULL; + + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Network.Connect failed: %s", + error->message); + + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG)) + return; + + goto error; + } + + nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG); + + s_wireless = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + if (!s_wireless) + goto error; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) + goto error; + + ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid); + + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Stage 2 of 5 (Device Configure) successful. Started '%s'.", + ssid_utf8); + + nm_device_activate_schedule_stage3_ip_config_start (device); + return; + +error: + reset_mode (self, priv->cancellable, act_failed_cb, self); +} + +/* Check if we're activating an AP/AdHoc connection and if the target + * DBus interface has appeared already. If so proceed to call Start or + * StartOpen on that interface. + */ +static void +act_check_interface (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + GDBusProxy *proxy = NULL; + GBytes *ssid; + gs_free char *ssid_utf8 = NULL; + const char *mode; + + if (!priv->act_mode_switch) + return; + + s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + + mode = nm_setting_wireless_get_mode (s_wireless); + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) + proxy = priv->dbus_ap_proxy; + else if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) + proxy = priv->dbus_adhoc_proxy; + + if (!proxy) + return; + + priv->act_mode_switch = FALSE; + + if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG)) + return; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) + goto failed; + + ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid); + + s_wireless_sec = (NMSettingWirelessSecurity *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS_SECURITY); + + if (!s_wireless_sec) { + g_dbus_proxy_call (proxy, "StartOpen", + g_variant_new ("(s)", ssid_utf8), + G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, act_start_cb, self); + } else { + const char *psk = nm_setting_wireless_security_get_psk (s_wireless_sec); + + if (!psk) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) No PSK for '%s'.", + ssid_utf8); + goto failed; + } + + g_dbus_proxy_call (proxy, "Start", + g_variant_new ("(ss)", ssid_utf8, psk), + G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, act_start_cb, self); + } + + _LOGD (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Called Start('%s').", + ssid_utf8); + return; + +failed: + reset_mode (self, priv->cancellable, act_failed_cb, self); +} + +static void +act_set_mode_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Setting Device.Mode failed: %s", + error->message); + + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + if ( !NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG) + || !priv->act_mode_switch) + return; + + priv->act_mode_switch = FALSE; + nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return; + } + + _LOGD (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) IWD Device.Mode set successfully"); + + act_check_interface (self); +} + +static void +act_set_mode (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + const char *iwd_mode; + const char *mode; + NMSettingWireless *s_wireless; + + s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + mode = nm_setting_wireless_get_mode (s_wireless); + + /* We need to first set interface mode (Device.Mode) to ap or ad-hoc. + * We can't directly queue a call to the Start/StartOpen method on + * the DBus interface that's going to be created after the property + * set call returns. + */ + iwd_mode = nm_streq (mode, NM_SETTING_WIRELESS_MODE_AP) ? "ap" : "ad-hoc"; + + if (!priv->cancellable) + priv->cancellable = g_cancellable_new (); + + g_dbus_proxy_call (priv->dbus_device_proxy, + DBUS_INTERFACE_PROPERTIES ".Set", + g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE, + "Mode", + g_variant_new ("s", iwd_mode)), + G_DBUS_CALL_FLAGS_NONE, 2000, + priv->cancellable, act_set_mode_cb, self); + priv->act_mode_switch = TRUE; +} + +static void +act_psk_cb (NMActRequest *req, + NMActRequestGetSecretsCallId *call_id, + NMSettingsConnection *s_connection, + GError *error, + gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv; + NMDevice *device; + + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + device = NM_DEVICE (self); + + g_return_if_fail (priv->wifi_secrets_id == call_id); + priv->wifi_secrets_id = NULL; + + g_return_if_fail (req == nm_device_get_act_request (device)); + g_return_if_fail (nm_act_request_get_settings_connection (req) == s_connection); + + if (nm_device_get_state (device) != NM_DEVICE_STATE_NEED_AUTH) + goto secrets_error; + + if (error) { + _LOGW (LOGD_WIFI, "%s", error->message); + goto secrets_error; + } + + _LOGD (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) missing PSK request completed"); + + /* Change state back to what it was before NEED_AUTH */ + nm_device_state_changed (device, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); + act_set_mode (self); + return; + +secrets_error: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS); + cleanup_association_attempt (self, FALSE); +} + +static void set_powered (NMDeviceIwd *self, gboolean powered) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); g_dbus_proxy_call (priv->dbus_device_proxy, - "org.freedesktop.DBus.Properties.Set", + DBUS_INTERFACE_PROPERTIES ".Set", g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE, "Powered", g_variant_new ("b", powered)), @@ -1384,6 +1749,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMActRequest *req; NMConnection *connection; NMSettingWireless *s_wireless; + const char *mode; const char *ap_path; ret = NM_DEVICE_CLASS (nm_device_iwd_parent_class)->act_stage1_prepare (device, out_failure_reason); @@ -1399,20 +1765,51 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) s_wireless = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE); + /* AP mode never uses a specific object or existing scanned AP */ + mode = nm_setting_wireless_get_mode (s_wireless); + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) + goto add_new; + ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req)); ap = ap_path ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL; - if (!ap) { - ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); - if (!ap) { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; - } + if (ap) { + set_current_ap (self, ap, TRUE); + return NM_ACT_STAGE_RETURN_SUCCESS; + } + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); + if (ap) { nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))); + set_current_ap (self, ap, TRUE); + return NM_ACT_STAGE_RETURN_SUCCESS; + } + + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) { + /* Hidden networks not supported at this time */ + return NM_ACT_STAGE_RETURN_FAILURE; } +add_new: + /* If the user is trying to connect to an AP that NM doesn't yet know about + * (hidden network or something) or starting a Hotspot, create an fake AP + * from the security settings in the connection. This "fake" AP gets used + * until the real one is found in the scan list (Ad-Hoc or Hidden), or until + * the device is deactivated (Ad-Hoc or Hotspot). + */ + ap = nm_wifi_ap_new_fake_from_connection (connection); + g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + if (nm_wifi_ap_is_hotspot (ap)) + nm_wifi_ap_set_address (ap, nm_device_get_hw_address (device)); + + g_object_freeze_notify (G_OBJECT (self)); + ap_add_remove (self, TRUE, ap, FALSE); + g_object_thaw_notify (G_OBJECT (self)); 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))); + g_object_unref (ap); return NM_ACT_STAGE_RETURN_SUCCESS; } @@ -1423,60 +1820,85 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; NMActRequest *req; - NMWifiAP *ap; NMConnection *connection; - GDBusProxy *network_proxy; + NMSettingWireless *s_wireless; + const char *mode; req = nm_device_get_act_request (device); g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); - ap = priv->current_ap; - if (!ap) { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - goto out; - } - connection = nm_act_request_get_applied_connection (req); g_assert (connection); - /* 802.1x networks that are not IWD Known Networks will definitely - * fail, for other combinations we will let the Connect call fail - * or ask us for any missing secrets through the Agent. - */ - if ( !is_connection_known_network (connection) - && nm_connection_get_setting_802_1x (connection)) { - _LOGI (LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) access point '%s' has 802.1x security, but is not configured.", - nm_connection_get_id (connection)); + s_wireless = nm_connection_get_setting_wireless (connection); + g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE); - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); - ret = NM_ACT_STAGE_RETURN_FAILURE; - goto out; - } + mode = nm_setting_wireless_get_mode (s_wireless); + if (NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) { + GDBusProxy *network_proxy; + NMWifiAP *ap = priv->current_ap; - network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), - nm_wifi_ap_get_supplicant_path (ap), - NM_IWD_NETWORK_INTERFACE); - if (!network_proxy) { - _LOGE (LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) could not get Network interface proxy for %s", - nm_wifi_ap_get_supplicant_path (ap)); - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - goto out; - } + if (!ap) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + goto out; + } - if (!priv->cancellable) - priv->cancellable = g_cancellable_new (); + /* 802.1x networks that are not IWD Known Networks will definitely + * fail, for other combinations we will let the Connect call fail + * or ask us for any missing secrets through the Agent. + */ + if ( nm_connection_get_setting_802_1x (connection) + && !is_ap_known_network (ap)) { + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) access point '%s' has 802.1x security but is not configured in IWD.", + nm_connection_get_id (connection)); + + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); + goto out; + } - /* Call Network.Connect. No timeout because IWD already handles - * timeouts. - */ - g_dbus_proxy_call (network_proxy, "Connect", - g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, G_MAXINT, - priv->cancellable, network_connect_cb, self); + network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), + nm_wifi_ap_get_supplicant_path (ap), + NM_IWD_NETWORK_INTERFACE); + if (!network_proxy) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) could not get Network interface proxy for %s", + nm_wifi_ap_get_supplicant_path (ap)); + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + goto out; + } - g_object_unref (network_proxy); + if (!priv->cancellable) + priv->cancellable = g_cancellable_new (); + + /* Call Network.Connect. No timeout because IWD already handles + * timeouts. + */ + g_dbus_proxy_call (network_proxy, "Connect", + NULL, G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, network_connect_cb, self); + + g_object_unref (network_proxy); + } else if (NM_IN_STRSET (mode, NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC)) { + NMSettingWirelessSecurity *s_wireless_sec; + + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); + if (s_wireless_sec && !nm_setting_wireless_security_get_psk (s_wireless_sec)) { + /* PSK is missing from the settings, have to request it */ + + wifi_secrets_cancel (self); + + priv->wifi_secrets_id = nm_act_request_get_secrets (req, + TRUE, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION, + NM_MAKE_STRV (NM_SETTING_WIRELESS_SECURITY_PSK), + act_psk_cb, + self); + nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + } else + act_set_mode (self); + } /* We'll get stage3 started when the supplicant connects */ ret = NM_ACT_STAGE_RETURN_POSTPONE; @@ -1507,8 +1929,8 @@ periodic_scan_timeout_cb (gpointer user_data) if (priv->scanning || priv->scan_requested) return FALSE; - g_dbus_proxy_call (priv->dbus_station_proxy, "Scan", g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, + g_dbus_proxy_call (priv->dbus_station_proxy, "Scan", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, priv->cancellable, scan_cb, self); priv->scan_requested = TRUE; @@ -1516,18 +1938,37 @@ periodic_scan_timeout_cb (gpointer user_data) } static void -schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state) +schedule_periodic_scan (NMDeviceIwd *self, gboolean initial_scan) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + GVariant *value; + gboolean disconnected; guint interval; - if (current_state <= NM_DEVICE_STATE_UNAVAILABLE) + if (!priv->can_scan || priv->scan_requested) return; - if (current_state == NM_DEVICE_STATE_DISCONNECTED) - interval = 10; + value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "State"); + disconnected = nm_streq0 (get_variant_state (value), "disconnected"); + g_variant_unref (value); + + /* Start scan immediately after a disconnect, mode change or + * device UP, otherwise wait a period dependent on the current + * state. + * + * (initial_scan && disconnected) override priv->scanning below + * because of an IWD quirk where a device will often be in the + * autoconnect state and scanning at the time of our initial_scan, + * but our logic will the send it a Disconnect() causeing IWD to + * exit autoconnect and interrupt the ongoing scan, meaning that + * we still want a new scan ASAP. + */ + if (initial_scan && disconnected) + interval = 0; + else if (!priv->periodic_scan_id && !priv->scanning) + interval = disconnected ? 10 : 20; else - interval = 20; + return; nm_clear_g_source (&priv->periodic_scan_id); priv->periodic_scan_id = g_timeout_add_seconds (interval, @@ -1536,6 +1977,19 @@ schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state) } static void +set_can_scan (NMDeviceIwd *self, gboolean can_scan) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if (priv->can_scan == can_scan) + return; + + priv->can_scan = can_scan; + + schedule_periodic_scan (self, TRUE); +} + +static void device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, @@ -1544,14 +1998,6 @@ device_state_changed (NMDevice *device, NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) { - remove_all_aps (self); - nm_clear_g_source (&priv->periodic_scan_id); - } else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE) { - update_aps (self); - schedule_periodic_scan (self, new_state); - } - switch (new_state) { case NM_DEVICE_STATE_UNMANAGED: break; @@ -1669,10 +2115,13 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_MODE: - if (priv->current_ap) - g_value_set_uint (value, NM_802_11_MODE_INFRA); - else + if (!priv->current_ap) g_value_set_uint (value, NM_802_11_MODE_UNKNOWN); + else if (nm_wifi_ap_is_hotspot (priv->current_ap)) + g_value_set_uint (value, NM_802_11_MODE_AP); + else + g_value_set_uint (value, nm_wifi_ap_get_mode (priv->current_ap)); + break; case PROP_BITRATE: g_value_set_uint (value, 65000); @@ -1702,24 +2151,6 @@ get_property (GObject *object, guint prop_id, } } -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMDeviceIwd *device = NM_DEVICE_IWD (object); - NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (device); - - switch (prop_id) { - case PROP_CAPABILITIES: - /* construct-only */ - priv->capabilities = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - /*****************************************************************************/ static void @@ -1729,7 +2160,7 @@ state_changed (NMDeviceIwd *self, const char *new_state) NMDevice *device = NM_DEVICE (self); NMDeviceState dev_state = nm_device_get_state (device); gboolean iwd_connection = FALSE; - gboolean can_connect; + gboolean can_connect = priv->can_connect; _LOGI (LOGD_DEVICE | LOGD_WIFI, "new IWD device state is %s", new_state); @@ -1738,7 +2169,9 @@ state_changed (NMDeviceIwd *self, const char *new_state) iwd_connection = TRUE; /* Don't allow scanning while connecting, disconnecting or roaming */ - priv->can_scan = NM_IN_STRSET (new_state, "connected", "disconnected"); + set_can_scan (self, NM_IN_STRSET (new_state, "connected", "disconnected")); + + priv->can_connect = FALSE; if (NM_IN_STRSET (new_state, "connecting", "connected", "roaming")) { /* If we were connecting, do nothing, the confirmation of @@ -1766,7 +2199,7 @@ state_changed (NMDeviceIwd *self, const char *new_state) * callback will have more information on the specific failure * reason. */ - if (dev_state == NM_DEVICE_STATE_CONFIG) + if (NM_IN_SET (dev_state, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH)) return; if (iwd_connection) @@ -1781,10 +2214,10 @@ state_changed (NMDeviceIwd *self, const char *new_state) /* Don't allow new connection until iwd exits disconnecting and no * Connect callback is pending. */ - can_connect = NM_IN_STRSET (new_state, "disconnected"); - if (can_connect != priv->can_connect) { - priv->can_connect = can_connect; - nm_device_emit_recheck_auto_activate (device); + if (NM_IN_STRSET (new_state, "disconnected")) { + priv->can_connect = TRUE; + if (!can_connect) + nm_device_emit_recheck_auto_activate (device); } } @@ -1792,7 +2225,6 @@ static void scanning_changed (NMDeviceIwd *self, gboolean new_scanning) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); if (new_scanning == priv->scanning) return; @@ -1805,7 +2237,7 @@ scanning_changed (NMDeviceIwd *self, gboolean new_scanning) update_aps (self); if (!priv->scan_requested) - schedule_periodic_scan (self, state); + schedule_periodic_scan (self, FALSE); } } @@ -1814,40 +2246,81 @@ station_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidate_properties, gpointer user_data) { NMDeviceIwd *self = user_data; - GVariantIter *iter; - const char *key; - GVariant *value; + const char *new_str; + gboolean new_bool; - g_variant_get (changed_properties, "a{sv}", &iter); - while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { - if (!strcmp (key, "State")) - state_changed (self, get_variant_state (value)); + if (g_variant_lookup (changed_properties, "State", "&s", &new_str)) + state_changed (self, new_str); - if (!strcmp (key, "Scanning")) - scanning_changed (self, get_variant_boolean (value, "Scanning")); + if (g_variant_lookup (changed_properties, "Scanning", "b", &new_bool)) + scanning_changed (self, new_bool); +} - g_variant_unref (value); - } +static void +ap_adhoc_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, + GStrv invalidate_properties, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + gboolean new_bool; - g_variant_iter_free (iter); + if (g_variant_lookup (changed_properties, "Started", "b", &new_bool)) + _LOGI (LOGD_DEVICE | LOGD_WIFI, "IWD AP/AdHoc state is now %s", new_bool ? "Started" : "Stopped"); } static void powered_changed (NMDeviceIwd *self, gboolean new_powered) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + GDBusInterface *interface; + GVariant *value; nm_device_queue_recheck_available (NM_DEVICE (self), NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - if (new_powered) { - GDBusInterface *interface; - GVariant *value; + interface = new_powered ? g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_AP_INTERFACE) : NULL; - if (priv->dbus_station_proxy) - return; + if (priv->dbus_ap_proxy) { + g_signal_handlers_disconnect_by_func (priv->dbus_ap_proxy, + ap_adhoc_properties_changed, self); + g_clear_object (&priv->dbus_ap_proxy); + } + + if (interface) { + priv->dbus_ap_proxy = G_DBUS_PROXY (interface); + g_signal_connect (priv->dbus_ap_proxy, "g-properties-changed", + G_CALLBACK (ap_adhoc_properties_changed), self); + + if (priv->act_mode_switch) + act_check_interface (self); + else + reset_mode (self, NULL, NULL, NULL); + } + + interface = new_powered ? g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_ADHOC_INTERFACE) : NULL; + + if (priv->dbus_adhoc_proxy) { + g_signal_handlers_disconnect_by_func (priv->dbus_adhoc_proxy, + ap_adhoc_properties_changed, self); + g_clear_object (&priv->dbus_adhoc_proxy); + } + + if (interface) { + priv->dbus_adhoc_proxy = G_DBUS_PROXY (interface); + g_signal_connect (priv->dbus_adhoc_proxy, "g-properties-changed", + G_CALLBACK (ap_adhoc_properties_changed), self); + if (priv->act_mode_switch) + act_check_interface (self); + else + reset_mode (self, NULL, NULL, NULL); + } + + /* We expect one of the three interfaces to always be present when + * device is Powered so if AP and AdHoc are not present we should + * be in station mode. + */ + if (new_powered && !priv->dbus_ap_proxy && !priv->dbus_adhoc_proxy) { interface = g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_STATION_INTERFACE); if (!interface) { /* No Station interface on the device object. Check if the @@ -1858,18 +2331,30 @@ powered_changed (NMDeviceIwd *self, gboolean new_powered) * priv->dbus_station_proxy both point at the Device interface. */ value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "State"); - if (!value) { + if (value) { + g_variant_unref (value); + interface = g_object_ref (G_DBUS_INTERFACE (priv->dbus_device_proxy)); + } else { _LOGE (LOGD_WIFI, "Interface %s not found on obj %s", NM_IWD_STATION_INTERFACE, g_dbus_object_get_object_path (priv->dbus_obj)); - return; + interface = NULL; } - g_variant_unref (value); - interface = g_object_ref (priv->dbus_device_proxy); } + } else + interface = NULL; + + if (priv->dbus_station_proxy) { + g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy, + station_properties_changed, self); + g_clear_object (&priv->dbus_station_proxy); + } + if (interface) { priv->dbus_station_proxy = G_DBUS_PROXY (interface); + g_signal_connect (priv->dbus_station_proxy, "g-properties-changed", + G_CALLBACK (station_properties_changed), self); value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "Scanning"); priv->scanning = get_variant_boolean (value, "Scanning"); @@ -1879,27 +2364,15 @@ powered_changed (NMDeviceIwd *self, gboolean new_powered) state_changed (self, get_variant_state (value)); g_variant_unref (value); - g_signal_connect (priv->dbus_station_proxy, "g-properties-changed", - G_CALLBACK (station_properties_changed), self); - - /* Call Disconnect to make sure IWD's autoconnect is disabled. - * Autoconnect is the default state after device is brought UP. - */ - if (priv->enabled) - send_disconnect (self); + update_aps (self); } else { - if (!priv->dbus_station_proxy) - return; - - g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy, - station_properties_changed, self); - g_clear_object (&priv->dbus_station_proxy); - - priv->can_scan = FALSE; + set_can_scan (self, FALSE); + nm_clear_g_source (&priv->periodic_scan_id); priv->scanning = FALSE; priv->scan_requested = FALSE; priv->can_connect = FALSE; cleanup_association_attempt (self, FALSE); + remove_all_aps (self); } } @@ -1908,19 +2381,10 @@ device_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidate_properties, gpointer user_data) { NMDeviceIwd *self = user_data; - GVariantIter *iter; - const char *key; - GVariant *value; - - g_variant_get (changed_properties, "a{sv}", &iter); - while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { - if (!strcmp (key, "Powered")) - powered_changed (self, get_variant_boolean (value, "Powered")); + gboolean new_bool; - g_variant_unref (value); - } - - g_variant_iter_free (iter); + if (g_variant_lookup (changed_properties, "Powered", "b", &new_bool)) + powered_changed (self, new_bool); } void @@ -1928,24 +2392,24 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); GDBusInterface *interface; - GVariant *value; + gs_unref_variant GVariant *value = NULL; + gs_unref_object GDBusProxy *adapter_proxy = NULL; + GVariantIter *iter; + const char *mode; gboolean powered; + NMDeviceWifiCapabilities capabilities; - if (!nm_g_object_ref_set ((GObject **) &priv->dbus_obj, (GObject *) object)) + if (!nm_g_object_ref_set (&priv->dbus_obj, object)) return; - if (priv->enabled && priv->dbus_station_proxy) { - nm_device_queue_recheck_available (NM_DEVICE (self), - NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, - NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - } - if (priv->dbus_device_proxy) { g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy, device_properties_changed, self); g_clear_object (&priv->dbus_device_proxy); powered_changed (self, FALSE); + + priv->act_mode_switch = FALSE; } if (!object) @@ -1965,14 +2429,63 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object) g_signal_connect (priv->dbus_device_proxy, "g-properties-changed", G_CALLBACK (device_properties_changed), self); + /* Parse list of interface modes supported by adapter (wiphy) */ + + value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Adapter"); + if (!value || !g_variant_is_of_type (value, G_VARIANT_TYPE_OBJECT_PATH)) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Adapter property not cached or not an object path"); + goto error; + } + + adapter_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), + g_variant_get_string (value, NULL), + NM_IWD_WIPHY_INTERFACE); + if (!adapter_proxy) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Can't get DBus proxy for IWD Adapter for IWD Device"); + goto error; + } + + g_variant_unref (value); + value = g_dbus_proxy_get_cached_property (adapter_proxy, "SupportedModes"); + if (!value || !g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "SupportedModes property not cached or not a string array"); + goto error; + } + + capabilities = NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_RSN; + + g_variant_get (value, "as", &iter); + while (g_variant_iter_next (iter, "&s", &mode)) { + if (nm_streq (mode, "ap")) + capabilities |= NM_WIFI_DEVICE_CAP_AP; + else if (nm_streq (mode, "ad-hoc")) + capabilities |= NM_WIFI_DEVICE_CAP_ADHOC; + } + g_variant_iter_free (iter); + + if (priv->capabilities != capabilities) { + priv->capabilities = capabilities; + _notify (self, PROP_CAPABILITIES); + } + + g_variant_unref (value); value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Powered"); powered = get_variant_boolean (value, "Powered"); - g_variant_unref (value); if (powered != priv->enabled) set_powered (self, priv->enabled); else if (powered) powered_changed (self, TRUE); + + return; + +error: + g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy, + device_properties_changed, self); + g_clear_object (&priv->dbus_device_proxy); } gboolean @@ -2041,15 +2554,14 @@ nm_device_iwd_init (NMDeviceIwd *self) } NMDevice * -nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities) +nm_device_iwd_new (const char *iface) { return g_object_new (NM_TYPE_DEVICE_IWD, NM_DEVICE_IFACE, iface, - NM_DEVICE_TYPE_DESC, "802.11 WiFi", + NM_DEVICE_TYPE_DESC, "802.11 Wi-Fi", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, - NM_DEVICE_IWD_CAPABILITIES, (guint) capabilities, NULL); } @@ -2067,6 +2579,8 @@ dispose (GObject *object) g_clear_object (&priv->dbus_device_proxy); g_clear_object (&priv->dbus_station_proxy); + g_clear_object (&priv->dbus_ap_proxy); + g_clear_object (&priv->dbus_adhoc_proxy); g_clear_object (&priv->dbus_obj); remove_all_aps (self); @@ -2084,7 +2598,6 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass) NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); object_class->get_property = get_property; - object_class->set_property = set_property; object_class->dispose = dispose; dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&nm_interface_info_device_wireless); @@ -2108,7 +2621,6 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass) device_class->get_configured_mtu = get_configured_mtu; device_class->deactivate = deactivate; device_class->deactivate_async = deactivate_async; - device_class->deactivate_async_finish = deactivate_async_finish; device_class->can_reapply_change = can_reapply_change; device_class->state_changed = device_state_changed; @@ -2144,8 +2656,7 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass) obj_properties[PROP_CAPABILITIES] = g_param_spec_uint (NM_DEVICE_IWD_CAPABILITIES, "", "", 0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_SCANNING] = diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h index 825123b1..4a2bd31e 100644 --- a/src/devices/wifi/nm-device-iwd.h +++ b/src/devices/wifi/nm-device-iwd.h @@ -47,7 +47,7 @@ typedef struct _NMDeviceIwdClass NMDeviceIwdClass; GType nm_device_iwd_get_type (void); -NMDevice *nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities); +NMDevice *nm_device_iwd_new (const char *iface); void nm_device_iwd_set_dbus_object (NMDeviceIwd *device, GDBusObject *object); diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index 4c42e7d3..1172a613 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -29,13 +29,11 @@ #include "nm-device-olpc-mesh.h" #include <netinet/in.h> -#include <string.h> #include <sys/stat.h> #include <sys/wait.h> #include <signal.h> #include <unistd.h> #include <sys/ioctl.h> -#include <errno.h> #include "devices/nm-device.h" #include "nm-device-wifi.h" @@ -166,7 +164,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) nm_device_get_iface (priv->companion)); } - /* wait with continuing configuration untill the companion device is done scanning */ + /* wait with continuing configuration until the companion device is done scanning */ g_object_get (priv->companion, NM_DEVICE_WIFI_SCANNING, &scanning, NULL); if (scanning) { priv->stage1_waiting = TRUE; @@ -193,16 +191,13 @@ static NMActStageReturn act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); - NMConnection *connection; NMSettingOlpcMesh *s_mesh; guint32 channel; GBytes *ssid; const char *anycast_addr; - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + s_mesh = nm_device_get_applied_setting (device, NM_TYPE_SETTING_OLPC_MESH); - s_mesh = nm_connection_get_setting_olpc_mesh (connection); g_return_val_if_fail (s_mesh, NM_ACT_STAGE_RETURN_FAILURE); channel = nm_setting_olpc_mesh_get_channel (s_mesh); @@ -333,7 +328,7 @@ check_companion (NMDeviceOlpcMesh *self, NMDevice *other) g_assert (priv->companion == NULL); priv->companion = g_object_ref (other); - _LOGI (LOGD_OLPC, "found companion WiFi device %s", + _LOGI (LOGD_OLPC, "found companion Wi-Fi device %s", nm_device_get_iface (other)); g_signal_connect (G_OBJECT (other), NM_DEVICE_STATE_CHANGED, diff --git a/src/devices/wifi/nm-device-wifi-p2p.c b/src/devices/wifi/nm-device-wifi-p2p.c new file mode 100644 index 00000000..8381ebc7 --- /dev/null +++ b/src/devices/wifi/nm-device-wifi-p2p.c @@ -0,0 +1,1316 @@ +/* NetworkManager -- Wi-Fi P2P Device + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-device-wifi-p2p.h" + +#include <sys/socket.h> + +#include "supplicant/nm-supplicant-manager.h" +#include "supplicant/nm-supplicant-interface.h" + +#include "nm-manager.h" +#include "nm-utils.h" +#include "nm-wifi-p2p-peer.h" +#include "NetworkManagerUtils.h" +#include "devices/nm-device-private.h" +#include "settings/nm-settings.h" +#include "nm-setting-wifi-p2p.h" +#include "nm-act-request.h" +#include "nm-ip4-config.h" +#include "platform/nm-platform.h" +#include "nm-manager.h" +#include "nm-core-internal.h" +#include "platform/nmp-object.h" + +#include "devices/nm-device-logging.h" +_LOG_DECLARE_SELF(NMDeviceWifiP2P); + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifiP2P, + PROP_PEERS, +); + +typedef struct { + NMSupplicantManager *sup_mgr; + + /* NOTE: In theory management and group ifaces could be identical. However, + * in practice, this cannot happen currently as NMDeviceWifiP2P is only + * created for existing non-P2P interfaces. + * (i.e. a single standalone P2P interface is not supported at this point) + */ + NMSupplicantInterface *mgmt_iface; + NMSupplicantInterface *group_iface; + + CList peers_lst_head; + + guint sup_timeout_id; + guint peer_dump_id; + guint peer_missing_id; + + bool is_waiting_for_supplicant:1; +} NMDeviceWifiP2PPrivate; + +struct _NMDeviceWifiP2P { + NMDevice parent; + NMDeviceWifiP2PPrivate _priv; +}; + +struct _NMDeviceWifiP2PClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceWifiP2P, nm_device_wifi_p2p, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIFI_P2P_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceWifiP2P, NM_IS_DEVICE_WIFI_P2P, NMDevice) + +/*****************************************************************************/ + +static const NMDBusInterfaceInfoExtended interface_info_device_wifi_p2p; +static const GDBusSignalInfo nm_signal_info_wifi_p2p_peer_added; +static const GDBusSignalInfo nm_signal_info_wifi_p2p_peer_removed; + +static void supplicant_group_interface_release (NMDeviceWifiP2P *self); +static void supplicant_interfaces_release (NMDeviceWifiP2P *self, gboolean set_is_waiting); + +/*****************************************************************************/ + +static void +_peer_dump (NMDeviceWifiP2P *self, + NMLogLevel log_level, + const NMWifiP2PPeer *peer, + const char *prefix, + gint32 now_s) +{ + char buf[1024]; + + _NMLOG (log_level, LOGD_WIFI_SCAN, "wifi-peer: %-7s %s", + prefix, + nm_wifi_p2p_peer_to_string (peer, buf, sizeof (buf), now_s)); +} + +static gboolean +peer_list_dump (gpointer user_data) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + priv->peer_dump_id = 0; + + if (_LOGD_ENABLED (LOGD_WIFI_SCAN)) { + NMWifiP2PPeer *peer; + gint32 now_s = nm_utils_get_monotonic_timestamp_s (); + + _LOGD (LOGD_WIFI_SCAN, "P2P Peers: [now:%u]", now_s); + c_list_for_each_entry (peer, &priv->peers_lst_head, peers_lst) + _peer_dump (self, LOGL_DEBUG, peer, "dump", now_s); + } + return G_SOURCE_REMOVE; +} + +static void +schedule_peer_list_dump (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if ( !priv->peer_dump_id + && _LOGD_ENABLED (LOGD_WIFI_SCAN)) + priv->peer_dump_id = g_timeout_add_seconds (1, peer_list_dump, self); +} + +/*****************************************************************************/ + +static void +_set_is_waiting_for_supplicant (NMDeviceWifiP2P *self, gboolean is_waiting) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if (priv->is_waiting_for_supplicant == (!!is_waiting)) + return; + + priv->is_waiting_for_supplicant = is_waiting; + + if (is_waiting) + nm_device_add_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); + else + nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); +} + +/*****************************************************************************/ + +static gboolean +check_connection_peer_joined (NMDeviceWifiP2P *device) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (device); + NMConnection *conn = nm_device_get_applied_connection (NM_DEVICE (device)); + NMWifiP2PPeer *peer; + const char* group; + const char * const * groups; + + if (!conn || !priv->group_iface) + return FALSE; + + /* Comparing the object path found on the group_iface with the peers + * found on the mgmt_iface is legal. */ + group = nm_supplicant_interface_get_p2p_group_path (priv->group_iface); + if (!group) + return FALSE; + + /* NOTE: We currently only support connections to a specific peer */ + peer = nm_wifi_p2p_peers_find_first_compatible (&priv->peers_lst_head, conn); + if (!peer) + return FALSE; + + groups = nm_wifi_p2p_peer_get_groups (peer); + if ( !groups + || !g_strv_contains (groups, group)) + return FALSE; + + return TRUE; +} + +static gboolean +disconnect_on_connection_peer_missing_cb (gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + _LOGW (LOGD_WIFI, "Peer requested in connection is missing for too long, failing connection."); + + priv->peer_missing_id = 0; + + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_PEER_NOT_FOUND); + return FALSE; +} + +static void +update_disconnect_on_connection_peer_missing (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMDeviceState state; + + state = nm_device_get_state (NM_DEVICE (self)); + if ( state < NM_DEVICE_STATE_IP_CONFIG + || state > NM_DEVICE_STATE_ACTIVATED) { + nm_clear_g_source (&priv->peer_missing_id); + return; + } + + if (check_connection_peer_joined (self)) { + if (nm_clear_g_source (&priv->peer_missing_id)) + _LOGD (LOGD_WIFI, "Peer requested in connection is joined, removing timeout"); + return; + } + + if (priv->peer_missing_id == 0) { + _LOGD (LOGD_WIFI, "Peer requested in connection is missing, adding timeout"); + priv->peer_missing_id = g_timeout_add_seconds (5, disconnect_on_connection_peer_missing_cb, self); + } +} + +static gboolean +is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMSupplicantInterfaceState supplicant_state; + + if (!priv->mgmt_iface) + return FALSE; + + supplicant_state = nm_supplicant_interface_get_state (priv->mgmt_iface); + if ( supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_READY + || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) + return FALSE; + + return TRUE; +} + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS (nm_device_wifi_p2p_parent_class)->check_connection_compatible (device, connection, error)) + return FALSE; + + /* TODO: Allow limitting the interface using the HW-address? */ + + /* We don't need to check anything else here. The P2P device will only + * exists if we are able to establish a P2P connection, and there should + * be no further restrictions necessary. + */ + + return TRUE; +} + +static gboolean +complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + NMConnection *const*existing_connections, + GError **error) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + gs_free char *setting_name = NULL; + NMSettingWifiP2P *s_wifi_p2p; + NMWifiP2PPeer *peer; + const char *setting_peer; + + s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P)); + + if (!specific_object) { + /* If not given a specific object, we need at minimum a peer address */ + if (!s_wifi_p2p) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "A '%s' setting is required if no Peer path was given", + NM_SETTING_WIFI_P2P_SETTING_NAME); + return FALSE; + } + + setting_peer = nm_setting_wifi_p2p_get_peer (s_wifi_p2p); + if (!setting_peer) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "A '%s' setting with a valid Peer is required if no Peer path was given", + NM_SETTING_WIFI_P2P_SETTING_NAME); + return FALSE; + } + + } else { + peer = nm_wifi_p2p_peer_lookup_for_device (NM_DEVICE (self), specific_object); + if (!peer) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND, + "The P2P peer %s is unknown", + specific_object); + return FALSE; + } + + setting_peer = nm_wifi_p2p_peer_get_address (peer); + g_return_val_if_fail (setting_peer, FALSE); + } + + /* Add a Wi-Fi P2P setting if one doesn't exist yet */ + if (!s_wifi_p2p) { + s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_setting_wifi_p2p_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_wifi_p2p)); + } + + g_object_set (G_OBJECT (s_wifi_p2p), NM_SETTING_WIFI_P2P_PEER, setting_peer, NULL); + + setting_name = g_strdup_printf ("Wi-Fi P2P Peer %s", setting_peer); + nm_utils_complete_generic (nm_device_get_platform (device), + connection, + NM_SETTING_WIFI_P2P_SETTING_NAME, + existing_connections, + setting_name, + setting_name, + NULL, + TRUE); + + return TRUE; +} + +/* + * supplicant_find_timeout_cb + * + * Called when the supplicant has been unable to find the peer we want to connect to. + */ +static gboolean +supplicant_find_timeout_cb (gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + priv->sup_timeout_id = 0; + + nm_supplicant_interface_p2p_cancel_connect (priv->mgmt_iface); + + if (nm_device_is_activating (device)) { + _LOGW (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi-p2p) could not find peer, failing activation"); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_PEER_NOT_FOUND); + } + + return G_SOURCE_REMOVE; +} + +static NMActStageReturn +act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMActStageReturn ret; + NMActRequest *req; + NMConnection *connection; + NMSettingWifiP2P *s_wifi_p2p; + NMWifiP2PPeer *peer; + + nm_clear_g_source (&priv->sup_timeout_id); + + ret = NM_DEVICE_CLASS (nm_device_wifi_p2p_parent_class)->act_stage1_prepare (device, out_failure_reason); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) + return ret; + + if (!priv->mgmt_iface) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); + + connection = nm_act_request_get_applied_connection (req); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + + s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P)); + g_return_val_if_fail (s_wifi_p2p, NM_ACT_STAGE_RETURN_FAILURE); + + peer = nm_wifi_p2p_peers_find_first_compatible (&priv->peers_lst_head, connection); + if (!peer) { + /* Set up a timeout on the find attempt and run a find for the same period of time */ + priv->sup_timeout_id = g_timeout_add_seconds (10, + supplicant_find_timeout_cb, + self); + + nm_supplicant_interface_p2p_start_find (priv->mgmt_iface, 10); + + return NM_ACT_STAGE_RETURN_POSTPONE; + } + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +cleanup_p2p_connect_attempt (NMDeviceWifiP2P *self, gboolean disconnect) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + nm_clear_g_source (&priv->sup_timeout_id); + nm_clear_g_source (&priv->peer_missing_id); + + if (priv->mgmt_iface) + nm_supplicant_interface_p2p_cancel_connect (priv->mgmt_iface); + + if (disconnect && priv->group_iface) + nm_supplicant_interface_p2p_disconnect (priv->group_iface); +} + +/* + * supplicant_connection_timeout_cb + * + * Called when the supplicant has been unable to connect to a peer + * within a specified period of time. + */ +static gboolean +supplicant_connection_timeout_cb (gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + priv->sup_timeout_id = 0; + + nm_supplicant_interface_p2p_cancel_connect (priv->mgmt_iface); + + if (nm_device_is_activating (device)) { + _LOGW (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi-p2p) connecting took too long, failing activation"); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); + } + + return G_SOURCE_REMOVE; +} + +static NMActStageReturn +act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMConnection *connection; + NMSettingWifiP2P *s_wifi_p2p; + NMWifiP2PPeer *peer; + GBytes *wfd_ies; + + nm_clear_g_source (&priv->sup_timeout_id); + + if (!priv->mgmt_iface) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + connection = nm_device_get_applied_connection (device); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + + nm_assert (NM_IS_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P))); + + /* The prepare stage ensures that the peer has been found */ + peer = nm_wifi_p2p_peers_find_first_compatible (&priv->peers_lst_head, connection); + if (!peer) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PEER_NOT_FOUND); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + /* Set the WFD IEs before trying to establish the connection. */ + s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P)); + wfd_ies = nm_setting_wifi_p2p_get_wfd_ies (s_wifi_p2p); + nm_supplicant_manager_set_wfd_ies (priv->sup_mgr, wfd_ies); + + /* TODO: Grab secrets if we don't have them yet! */ + + /* TODO: Fix "pbc" being hardcoded here! */ + nm_supplicant_interface_p2p_connect (priv->mgmt_iface, + nm_wifi_p2p_peer_get_supplicant_path (peer), + "pbc", NULL); + + /* Set up a timeout on the connect attempt */ + priv->sup_timeout_id = g_timeout_add_seconds (45, + supplicant_connection_timeout_cb, + self); + + /* We'll get stage3 started when the P2P group has been started */ + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +/*****************************************************************************/ + +static void +emit_signal_p2p_peer_add_remove (NMDeviceWifiP2P *device, + NMWifiP2PPeer *peer, + gboolean is_added /* or else is_removed */) +{ + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (device), + &interface_info_device_wifi_p2p, + is_added + ? &nm_signal_info_wifi_p2p_peer_added + : &nm_signal_info_wifi_p2p_peer_removed, + "(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (peer))); +} + +static void +peer_add_remove (NMDeviceWifiP2P *self, + gboolean is_adding, /* or else removing */ + NMWifiP2PPeer *peer, + gboolean recheck_available_connections) +{ + NMDevice *device = NM_DEVICE (self); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if (is_adding) { + g_object_ref (peer); + peer->wifi_device = device; + c_list_link_tail (&priv->peers_lst_head, &peer->peers_lst); + nm_dbus_object_export (NM_DBUS_OBJECT (peer)); + _peer_dump (self, LOGL_DEBUG, peer, "added", 0); + + emit_signal_p2p_peer_add_remove (self, peer, TRUE); + } else { + peer->wifi_device = NULL; + c_list_unlink (&peer->peers_lst); + _peer_dump (self, LOGL_DEBUG, peer, "removed", 0); + } + + _notify (self, PROP_PEERS); + + if (!is_adding) { + emit_signal_p2p_peer_add_remove (self, peer, FALSE); + nm_dbus_object_clear_and_unexport (&peer); + } + + if (is_adding) { + /* If we are in prepare state, then we are currently runnign a find + * to search for the requested peer. */ + if (nm_device_get_state (device) == NM_DEVICE_STATE_PREPARE) { + NMConnection *connection; + + connection = nm_device_get_applied_connection (device); + g_assert (connection); + + peer = nm_wifi_p2p_peers_find_first_compatible (&priv->peers_lst_head, connection); + if (peer) { + /* A peer for the connection was found, cancel the timeout and go to configure state. */ + nm_clear_g_source (&priv->sup_timeout_id); + nm_device_activate_schedule_stage2_device_config (device); + } + } + + /* TODO: We may want to re-check auto-activation here, otherwise it will never work. */ + } + + update_disconnect_on_connection_peer_missing (self); +} + +static void +remove_all_peers (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMWifiP2PPeer *peer; + + if (c_list_is_empty (&priv->peers_lst_head)) + return; + + while ((peer = c_list_first_entry (&priv->peers_lst_head, NMWifiP2PPeer, peers_lst))) + peer_add_remove (self, FALSE, peer, FALSE); + + nm_device_recheck_available_connections (NM_DEVICE (self)); +} + +/*****************************************************************************/ + + +static NMActStageReturn +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) +{ + gboolean indicate_addressing_running; + NMConnection *connection; + const char *method; + + connection = nm_device_get_applied_connection (device); + + method = nm_utils_get_ip_config_method (connection, addr_family); + + if (addr_family == AF_INET) + indicate_addressing_running = NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + else { + indicate_addressing_running = NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP); + } + + if (indicate_addressing_running) + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), TRUE); + + return NM_DEVICE_CLASS (nm_device_wifi_p2p_parent_class)->act_stage3_ip_config_start (device, addr_family, out_config, out_failure_reason); +} + +static void +deactivate (NMDevice *device) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + int ifindex = nm_device_get_ip_ifindex (device); + + cleanup_p2p_connect_attempt (self, TRUE); + + /* Clear any critical protocol notification in the Wi-Fi stack */ + if (ifindex > 0) + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), ifindex, FALSE); +} + +static guint32 +get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source) +{ + *out_source = NM_DEVICE_MTU_SOURCE_NONE; + return 0; +} + +static const char * +get_auto_ip_config_method (NMDevice *device, int addr_family) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + /* Override the AUTO method to mean shared if we are group owner. */ + if ( priv->group_iface + && nm_supplicant_interface_get_p2p_group_owner (priv->group_iface)) { + if (addr_family == AF_INET) + return NM_SETTING_IP4_CONFIG_METHOD_SHARED; + + if (addr_family == AF_INET6) + return NM_SETTING_IP6_CONFIG_METHOD_SHARED; + } + + return NULL; +} + +static gboolean +unmanaged_on_quit (NMDevice *self) +{ + return TRUE; +} + +static void +supplicant_iface_state_cb (NMSupplicantInterface *iface, + int new_state_i, + int old_state_i, + int disconnect_reason, + gpointer user_data) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + NMDevice *device = NM_DEVICE (self); + NMSupplicantInterfaceState new_state = new_state_i; + NMSupplicantInterfaceState old_state = old_state_i; + + if (new_state == old_state) + return; + + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "supplicant management interface state: %s -> %s", + nm_supplicant_interface_state_to_string (old_state), + nm_supplicant_interface_state_to_string (new_state)); + + switch (new_state) { + case NM_SUPPLICANT_INTERFACE_STATE_READY: + _LOGD (LOGD_WIFI, "supplicant ready"); + nm_device_queue_recheck_available (device, + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + + if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) + _set_is_waiting_for_supplicant (self, FALSE); + break; + case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + supplicant_interfaces_release (self, TRUE); + nm_device_queue_recheck_available (device, + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + break; + default: + break; + } +} + +static void +supplicant_iface_peer_updated_cb (NMSupplicantInterface *iface, + const char *object_path, + GVariant *properties, + NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv; + NMWifiP2PPeer *found_peer; + + g_return_if_fail (self != NULL); + g_return_if_fail (object_path != NULL); + + priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + found_peer = nm_wifi_p2p_peers_find_by_supplicant_path (&priv->peers_lst_head, object_path); + if (found_peer) { + if (!nm_wifi_p2p_peer_update_from_properties (found_peer, object_path, properties)) + return; + + update_disconnect_on_connection_peer_missing (self); + _peer_dump (self, LOGL_DEBUG, found_peer, "updated", 0); + } else { + gs_unref_object NMWifiP2PPeer *peer = NULL; + + peer = nm_wifi_p2p_peer_new_from_properties (object_path, properties); + if (!peer) { + _LOGD (LOGD_WIFI, "invalid P2P peer properties received for %s", object_path); + return; + } + + peer_add_remove (self, TRUE, peer, TRUE); + } + + schedule_peer_list_dump (self); +} + +static void +supplicant_iface_peer_removed_cb (NMSupplicantInterface *iface, + const char *object_path, + NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv; + NMWifiP2PPeer *peer; + + g_return_if_fail (self != NULL); + g_return_if_fail (object_path != NULL); + + priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + peer = nm_wifi_p2p_peers_find_by_supplicant_path (&priv->peers_lst_head, object_path); + if (!peer) + return; + + peer_add_remove (self, FALSE, peer, TRUE); + schedule_peer_list_dump (self); +} + +static void +check_group_iface_ready (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);; + + if (!priv->group_iface) + return; + + if (nm_supplicant_interface_get_state (priv->group_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + return; + + if (!nm_supplicant_interface_get_p2p_group_joined (priv->group_iface)) + return; + + nm_clear_g_source (&priv->sup_timeout_id); + update_disconnect_on_connection_peer_missing (self); + + nm_device_activate_schedule_stage3_ip_config_start (NM_DEVICE (self)); +} + +static void +supplicant_group_iface_state_cb (NMSupplicantInterface *iface, + int new_state_i, + int old_state_i, + int disconnect_reason, + gpointer user_data) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + NMSupplicantInterfaceState new_state = new_state_i; + NMSupplicantInterfaceState old_state = old_state_i; + + if (new_state == old_state) + return; + + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "P2P Group supplicant interface state: %s -> %s", + nm_supplicant_interface_state_to_string (old_state), + nm_supplicant_interface_state_to_string (new_state)); + + switch (new_state) { + case NM_SUPPLICANT_INTERFACE_STATE_READY: + _LOGD (LOGD_WIFI, "P2P Group supplicant ready"); + + if (!nm_device_set_ip_iface (device, nm_supplicant_interface_get_ifname (priv->group_iface))) { + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + break; + } + + if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) + _set_is_waiting_for_supplicant (self, FALSE); + + check_group_iface_ready (self); + break; + case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + supplicant_group_interface_release (self); + + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + break; + default: + break; + } +} + +static void +supplicant_group_iface_group_finished_cb (NMSupplicantInterface *iface, + void *user_data) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + + supplicant_group_interface_release (self); + + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); +} + +static void +supplicant_iface_group_joined_updated_cb (NMSupplicantInterface *iface, + GParamSpec *pspec, + void *user_data) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + + check_group_iface_ready (self); +} + +static void +supplicant_iface_group_started_cb (NMSupplicantInterface *iface, + NMSupplicantInterface *group_iface, + NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv; + + g_return_if_fail (self != NULL); + + if (!nm_device_is_activating (NM_DEVICE (self))) { + _LOGW (LOGD_DEVICE | LOGD_WIFI, "P2P: WPA supplicant notified a group start but we are not trying to connect! Ignoring the event."); + return; + } + + priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + supplicant_group_interface_release (self); + priv->group_iface = g_object_ref (group_iface); + + /* We need to wait for the interface to be ready and the group + * information to be resolved. */ + g_signal_connect (priv->group_iface, + "notify::" NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED, + G_CALLBACK (supplicant_iface_group_joined_updated_cb), + self); + + g_signal_connect (priv->group_iface, + NM_SUPPLICANT_INTERFACE_STATE, + G_CALLBACK (supplicant_group_iface_state_cb), + self); + + g_signal_connect (priv->group_iface, NM_SUPPLICANT_INTERFACE_GROUP_FINISHED, + G_CALLBACK (supplicant_group_iface_group_finished_cb), + self); + + if (nm_supplicant_interface_get_state (priv->group_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + _set_is_waiting_for_supplicant (self, TRUE); + + check_group_iface_ready (self); +} + +static void +supplicant_group_interface_release (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if (!priv->group_iface) + return; + + g_signal_handlers_disconnect_by_data (priv->group_iface, self); + + nm_supplicant_interface_p2p_disconnect (priv->group_iface); + + g_clear_object (&priv->group_iface); +} + +static void +supplicant_interfaces_release (NMDeviceWifiP2P *self, gboolean set_is_waiting) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + nm_clear_g_source (&priv->peer_dump_id); + + remove_all_peers (self); + + if (priv->mgmt_iface) { + _LOGD (LOGD_DEVICE | LOGD_WIFI, "P2P: Releasing WPA supplicant interface."); + nm_supplicant_manager_set_wfd_ies (priv->sup_mgr, NULL); + g_signal_handlers_disconnect_by_data (priv->mgmt_iface, self); + g_clear_object (&priv->mgmt_iface); + nm_clear_g_source (&priv->sup_timeout_id); + } + + supplicant_group_interface_release (self); + + if (set_is_waiting) + _set_is_waiting_for_supplicant (self, TRUE); +} + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + update_disconnect_on_connection_peer_missing (self); + + if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) { + /* Clean up the supplicant interface because in these states the + * device cannot be used. + * Do not clean up for the UNMANAGED to UNAVAILABLE transition which + * will happen during initialization. + */ + if (priv->mgmt_iface && old_state > new_state) + supplicant_interfaces_release (self, TRUE); + + /* TODO: More cleanup needed? */ + } + + switch (new_state) { + case NM_DEVICE_STATE_UNMANAGED: + break; + case NM_DEVICE_STATE_UNAVAILABLE: + if ( !priv->mgmt_iface + || nm_supplicant_interface_get_state (priv->mgmt_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + _set_is_waiting_for_supplicant (self, TRUE); + + break; + case NM_DEVICE_STATE_NEED_AUTH: + /* Disconnect? */ + break; + case NM_DEVICE_STATE_IP_CHECK: + /* Clear any critical protocol notification in the wifi stack */ + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), FALSE); + break; + case NM_DEVICE_STATE_ACTIVATED: + //activation_success_handler (device); + break; + case NM_DEVICE_STATE_FAILED: + /* Clear any critical protocol notification in the wifi stack. + * At this point the IP device may have been removed already. */ + nm_supplicant_manager_set_wfd_ies (priv->sup_mgr, NULL); + if (nm_device_get_ip_ifindex (device) > 0) + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), FALSE); + break; + case NM_DEVICE_STATE_DISCONNECTED: + nm_supplicant_manager_set_wfd_ies (priv->sup_mgr, NULL); + break; + default: + break; + } +} + +static void +impl_device_wifi_p2p_start_find (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (obj); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + gs_unref_variant GVariant *options = NULL; + const char *opts_key; + GVariant *opts_val; + GVariantIter iter; + gint32 timeout = 30; + + g_variant_get (parameters, "(@a{sv})", &options); + + g_variant_iter_init (&iter, options); + while (g_variant_iter_next (&iter, "{&sv}", &opts_key, &opts_val)) { + _nm_unused gs_unref_variant GVariant *opts_val_free = opts_val; + + if (nm_streq (opts_key, "timeout")) { + if (!g_variant_is_of_type (opts_val, G_VARIANT_TYPE_INT32)) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_ARGUMENT, + "\"timeout\" must be an integer \"i\""); + return; + } + + timeout = g_variant_get_int32 (opts_val); + if (timeout <= 0 || timeout > 600) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "The timeout for a find operation needs to be in the range of 1-600s."); + return; + } + + continue; + } + + g_dbus_method_invocation_return_error (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_ARGUMENT, + "Unsupported options key \"%s\"", + opts_key); + return; + } + + if (!priv->mgmt_iface) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "WPA Supplicant management interface is currently unavailable."); + return; + } + + nm_supplicant_interface_p2p_start_find (priv->mgmt_iface, timeout); + + g_dbus_method_invocation_return_value (invocation, NULL); +} + +static void +impl_device_wifi_p2p_stop_find (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (obj); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if (!priv->mgmt_iface) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "WPA Supplicant management interface is currently unavailable."); + return; + } + + nm_supplicant_interface_p2p_stop_find (priv->mgmt_iface); + + g_dbus_method_invocation_return_value (invocation, NULL); +} + +/*****************************************************************************/ + +NMSupplicantInterface * +nm_device_wifi_p2p_get_mgmt_iface (NMDeviceWifiP2P *self) +{ + g_return_val_if_fail (NM_IS_DEVICE_WIFI_P2P (self), NULL); + + return NM_DEVICE_WIFI_P2P_GET_PRIVATE (self)->mgmt_iface; +} + +void +nm_device_wifi_p2p_set_mgmt_iface (NMDeviceWifiP2P *self, + NMSupplicantInterface *iface) +{ + NMDeviceWifiP2PPrivate *priv; + + g_return_if_fail (NM_IS_DEVICE_WIFI_P2P (self)); + g_return_if_fail (!iface || NM_IS_SUPPLICANT_INTERFACE (iface)); + + priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if (priv->mgmt_iface == iface) + goto done; + + supplicant_interfaces_release (self, FALSE); + + if (!iface) + goto done; + + _LOGD (LOGD_DEVICE | LOGD_WIFI, "P2P: WPA supplicant management interface changed to %s.", + nm_supplicant_interface_get_object_path (iface)); + + priv->mgmt_iface = g_object_ref (iface); + + g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_STATE, + G_CALLBACK (supplicant_iface_state_cb), + self); + g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_PEER_UPDATED, + G_CALLBACK (supplicant_iface_peer_updated_cb), + self); + g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_PEER_REMOVED, + G_CALLBACK (supplicant_iface_peer_removed_cb), + self); + g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_GROUP_STARTED, + G_CALLBACK (supplicant_iface_group_started_cb), + self); +done: + nm_device_queue_recheck_available (NM_DEVICE (self), + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + _set_is_waiting_for_supplicant (self, + !priv->mgmt_iface + || ( nm_supplicant_interface_get_state (priv->mgmt_iface) + < NM_SUPPLICANT_INTERFACE_STATE_READY)); +} + +void +nm_device_wifi_p2p_remove (NMDeviceWifiP2P* self) +{ + g_signal_emit_by_name (self, NM_DEVICE_REMOVED); +} + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "wifi-p2p"; +} + +/*****************************************************************************/ + +static const GDBusSignalInfo nm_signal_info_wifi_p2p_peer_added = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "PeerAdded", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("peer", "o"), + ), +); + +static const GDBusSignalInfo nm_signal_info_wifi_p2p_peer_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "PeerRemoved", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("peer", "o"), + ), +); + +static const NMDBusInterfaceInfoExtended interface_info_device_wifi_p2p = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_WIFI_P2P, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "StartFind", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("options", "a{sv}"), + ), + ), + .handle = impl_device_wifi_p2p_start_find, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "StopFind", + ), + .handle = impl_device_wifi_p2p_stop_find, + ), + ), + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_wifi_p2p_peer_added, + &nm_signal_info_wifi_p2p_peer_removed, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Peers", "ao", NM_DEVICE_WIFI_P2P_PEERS), + ), + ), + .legacy_property_changed = FALSE, +}; + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (object); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + const char **list; + + switch (prop_id) { + case PROP_PEERS: + list = nm_wifi_p2p_peers_get_paths (&priv->peers_lst_head); + g_value_take_boxed (value, nm_utils_strv_make_deep_copied (list)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_device_wifi_p2p_init (NMDeviceWifiP2P * self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + c_list_init (&priv->peers_lst_head); + + priv->sup_mgr = g_object_ref (nm_supplicant_manager_get ()); +} + +static void +constructed (GObject *object) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (object); + + G_OBJECT_CLASS (nm_device_wifi_p2p_parent_class)->constructed (object); + + _set_is_waiting_for_supplicant (self, TRUE); +} + +NMDeviceWifiP2P * +nm_device_wifi_p2p_new (const char *iface) +{ + return g_object_new (NM_TYPE_DEVICE_WIFI_P2P, + NM_DEVICE_IFACE, iface, + NM_DEVICE_TYPE_DESC, "802.11 Wi-Fi P2P", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI_P2P, + NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, + NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, + NULL); +} + +static void +dispose (GObject *object) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (object); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (object); + + g_clear_object (&priv->sup_mgr); + + supplicant_interfaces_release (self, FALSE); + + G_OBJECT_CLASS (nm_device_wifi_p2p_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceWifiP2P *peer = NM_DEVICE_WIFI_P2P (object); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (peer); + + nm_assert (c_list_is_empty (&priv->peers_lst_head)); + + G_OBJECT_CLASS (nm_device_wifi_p2p_parent_class)->finalize (object); +} + +static void +nm_device_wifi_p2p_class_init (NMDeviceWifiP2PClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_wifi_p2p); + + device_class->connection_type_supported = NM_SETTING_WIFI_P2P_SETTING_NAME; + device_class->connection_type_check_compatible = NM_SETTING_WIFI_P2P_SETTING_NAME; + device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_WIFI_P2P); + device_class->get_type_description = get_type_description; + + /* Do we need compatibility checking or is the default good enough? */ + device_class->is_available = is_available; + device_class->check_connection_compatible = check_connection_compatible; + device_class->complete_connection = complete_connection; + + device_class->act_stage1_prepare = act_stage1_prepare; + device_class->act_stage2_config = act_stage2_config; + device_class->get_configured_mtu = get_configured_mtu; + device_class->get_auto_ip_config_method = get_auto_ip_config_method; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + + device_class->deactivate = deactivate; + device_class->unmanaged_on_quit = unmanaged_on_quit; + + device_class->state_changed = device_state_changed; + + obj_properties[PROP_PEERS] = + g_param_spec_boxed (NM_DEVICE_WIFI_P2P_PEERS, "", "", + G_TYPE_STRV, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/devices/wifi/nm-device-wifi-p2p.h b/src/devices/wifi/nm-device-wifi-p2p.h new file mode 100644 index 00000000..a13eef15 --- /dev/null +++ b/src/devices/wifi/nm-device-wifi-p2p.h @@ -0,0 +1,50 @@ +/* NetworkManager -- Wi-Fi P2P Device + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_DEVICE_WIFI_P2P_H__ +#define __NM_DEVICE_WIFI_P2P_H__ + +#include "devices/nm-device.h" +#include "supplicant/nm-supplicant-interface.h" + +#define NM_TYPE_DEVICE_WIFI_P2P (nm_device_wifi_p2p_get_type ()) +#define NM_DEVICE_WIFI_P2P(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIFI_P2P, NMDeviceWifiP2P)) +#define NM_DEVICE_WIFI_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_WIFI_P2P, NMDeviceWifiP2PClass)) +#define NM_IS_DEVICE_WIFI_P2P(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIFI_P2P)) +#define NM_IS_DEVICE_WIFI_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_WIFI_P2P)) +#define NM_DEVICE_WIFI_P2P_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_WIFI_P2P, NMDeviceWifiP2PClass)) + +#define NM_DEVICE_WIFI_P2P_PEERS "peers" +#define NM_DEVICE_WIFI_P2P_GROUPS "groups" + +typedef struct _NMDeviceWifiP2P NMDeviceWifiP2P; +typedef struct _NMDeviceWifiP2PClass NMDeviceWifiP2PClass; + +GType nm_device_wifi_p2p_get_type (void); + +NMDeviceWifiP2P *nm_device_wifi_p2p_new (const char *iface); + +NMSupplicantInterface * nm_device_wifi_p2p_get_mgmt_iface (NMDeviceWifiP2P *self); +void nm_device_wifi_p2p_set_mgmt_iface (NMDeviceWifiP2P *self, + NMSupplicantInterface *iface); + +void nm_device_wifi_p2p_remove (NMDeviceWifiP2P *self); + +#endif /* __NM_DEVICE_WIFI_P2P_H__ */ diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 2ce84618..64869672 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -24,10 +24,9 @@ #include "nm-device-wifi.h" #include <netinet/in.h> -#include <string.h> #include <unistd.h> -#include <errno.h> +#include "nm-device-wifi-p2p.h" #include "nm-wifi-ap.h" #include "nm-common-macros.h" #include "devices/nm-device.h" @@ -79,6 +78,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi, enum { SCANNING_PROHIBITED, + P2P_DEVICE_CREATED, LAST_SIGNAL }; @@ -124,6 +124,8 @@ typedef struct { guint wps_timeout_id; NMSettingWirelessWakeOnWLan wowlan_restore; + + NMDeviceWifiP2P *p2p_device; } NMDeviceWifiPrivate; struct _NMDeviceWifi @@ -186,6 +188,10 @@ static void supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, GParamSpec *pspec, NMDeviceWifi *self); +static void supplicant_iface_notify_p2p_available (NMSupplicantInterface *iface, + GParamSpec *pspec, + NMDeviceWifi *self); + static void request_wireless_scan (NMDeviceWifi *self, gboolean periodic, gboolean force_if_scanning, @@ -198,6 +204,8 @@ static void ap_add_remove (NMDeviceWifi *self, static void _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset); +static void recheck_p2p_availability (NMDeviceWifi *self); + /*****************************************************************************/ static void @@ -291,6 +299,10 @@ supplicant_interface_acquire (NMDeviceWifi *self) "notify::" NM_SUPPLICANT_INTERFACE_CURRENT_BSS, G_CALLBACK (supplicant_iface_notify_current_bss), self); + g_signal_connect (priv->sup_iface, + "notify::" NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE, + G_CALLBACK (supplicant_iface_notify_p2p_available), + self); _notify_scanning (self); @@ -347,6 +359,11 @@ supplicant_interface_release (NMDeviceWifi *self) g_clear_object (&priv->sup_iface); } + if (priv->p2p_device) { + /* Signal to P2P device to also release its reference */ + nm_device_wifi_p2p_set_mgmt_iface (priv->p2p_device, NULL); + } + _notify_scanning (self); } @@ -732,7 +749,7 @@ check_connection_available (NMDevice *device, /* Hidden SSIDs obviously don't always appear in the scan list either. * - * For an explict user-activation-request, a connection is considered + * For an explicit user-activation-request, a connection is considered * available because for hidden Wi-Fi, clients didn't consistently * set the 'hidden' property to indicate hidden SSID networks. If * activating but the network isn't available let the device recheck @@ -838,7 +855,7 @@ complete_connection (NMDevice *device, ssid = nm_wifi_ap_get_ssid (ap); if (ssid == NULL) { - /* The AP must be hidden. Connecting to a WiFi AP requires the SSID + /* The AP must be hidden. Connecting to a Wi-Fi AP requires the SSID * as part of the initial handshake, so check the connection details * for the SSID. The AP object will still be used for encryption * settings and such. @@ -909,18 +926,6 @@ complete_connection (NMDevice *device, g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS); return FALSE; } - } else { - guint8 tmp[ETH_ALEN]; - - /* Lock the connection to this device by default if it uses a - * permanent MAC address (ie not a 'locally administered' one) - */ - nm_utils_hwaddr_aton (perm_hw_addr, tmp, ETH_ALEN); - if (!(tmp[0] & 0x02)) { - g_object_set (G_OBJECT (s_wifi), - NM_SETTING_WIRELESS_MAC_ADDRESS, perm_hw_addr, - NULL); - } } } @@ -981,7 +986,7 @@ can_auto_connect (NMDevice *device, g_return_val_if_fail (s_wifi, FALSE); /* Always allow autoconnect for AP and non-autoconf Ad-Hoc */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + method = nm_utils_get_ip_config_method (connection, AF_INET); mode = nm_setting_wireless_get_mode (s_wifi); if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) return TRUE; @@ -1197,7 +1202,7 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self, NM_DEVICE_AUTH_REQUEST, invocation, NULL, - NM_AUTH_PERMISSION_NETWORK_CONTROL, + NM_AUTH_PERMISSION_WIFI_SCAN, TRUE, dbus_request_scan_cb, options ? g_variant_ref (options) : NULL); @@ -1236,7 +1241,7 @@ scanning_prohibited (NMDeviceWifi *self, gboolean periodic) 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 specifc + * background scan for us, unless the connection is locked to a specific * BSSID. */ if (periodic) @@ -1685,20 +1690,37 @@ wifi_secrets_cb (NMActRequest *req, g_return_if_fail (nm_act_request_get_settings_connection (req) == connection); if (error) { - _LOGW (LOGD_WIFI, "%s", error->message); - - if (g_error_matches (error, NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_USER_CANCELED)) { - /* Don't wait for WPS timeout on an explicit cancel. */ - nm_clear_g_source (&priv->wps_timeout_id); - } + _LOGW (LOGD_WIFI, "no secrets: %s", error->message); - if (!priv->wps_timeout_id) { - /* Fail the device only if the WPS period is over too. */ - nm_device_state_changed (device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NO_SECRETS); - } + /* Even if WPS is still pending, let's abort the activation when the secret + * request returns. + * + * This means, a user can only effectively use WPS when also running a secret + * agent, and pressing the push button while being prompted for the password. + * Note, that in the secret prompt the user can see that WPS is in progress + * (via the NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE flag). + * + * Previously, WPS was not cancelled when the secret request returns. + * Note that in common use-cases WPS is enabled in the connection profile + * but it won't succeed (because it's disabled in the AP or because the + * user is not prepared to press the push button). + * That means for example, during boot we would try to autoconnect with WPS. + * At that point, there is no secret-agent running, and WPS is pending for + * full 30 seconds. If in the meantime a secret agent registers (because + * of logging into the DE), the profile is still busy waiting for WPS to time + * out. Only after that delay, autoconnect starts again (note that autoconnect gets + * not blocked in this case, because a secret agent registered in the meantime). + * + * It seems wrong to continue doing WPS if the user is not aware + * that WPS is ongoing. The user is required to perform an action (push button), + * and must be told via the secret prompt. + * If no secret-agent is running, if the user cancels the secret-request, or any + * other error to obtain secrets, the user apparently does not want WPS either. + */ + nm_clear_g_source (&priv->wps_timeout_id); + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); } else nm_device_activate_schedule_stage1_device_prepare (device); } @@ -1851,9 +1873,10 @@ need_new_8021x_secrets (NMDeviceWifi *self, NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; NMConnection *connection; - g_assert (setting_name != NULL); + g_return_val_if_fail (setting_name, FALSE); connection = nm_device_get_applied_connection (NM_DEVICE (self)); + g_return_val_if_fail (connection != NULL, FALSE); /* 802.1x stuff only happens in the supplicant's ASSOCIATED state when it's @@ -1905,10 +1928,11 @@ need_new_wpa_psk (NMDeviceWifi *self, NMConnection *connection; const char *key_mgmt = NULL; - g_assert (setting_name != NULL); + g_return_val_if_fail (setting_name, FALSE); connection = nm_device_get_applied_connection (NM_DEVICE (self)); - g_return_val_if_fail (connection != NULL, FALSE); + + g_return_val_if_fail (connection, FALSE); /* A bad PSK will cause the supplicant to disconnect during the 4-way handshake */ if (old_state != NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE) @@ -2021,6 +2045,10 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, && new_state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) priv->ssid_found = TRUE; + if ( old_state < NM_SUPPLICANT_INTERFACE_STATE_READY + && new_state >= NM_SUPPLICANT_INTERFACE_STATE_READY) + recheck_p2p_availability (self); + switch (new_state) { case NM_SUPPLICANT_INTERFACE_STATE_READY: _LOGD (LOGD_WIFI, "supplicant ready"); @@ -2040,15 +2068,12 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, * schedule the next activation stage. */ if (devstate == NM_DEVICE_STATE_CONFIG) { - NMConnection *connection; NMSettingWireless *s_wifi; GBytes *ssid; gs_free char *ssid_str = NULL; - connection = nm_device_get_applied_connection (NM_DEVICE (self)); - g_return_if_fail (connection); + s_wifi = nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS); - s_wifi = nm_connection_get_setting_wireless (connection); g_return_if_fail (s_wifi); ssid = nm_setting_wireless_get_ssid (s_wifi); @@ -2200,6 +2225,67 @@ supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, } } +/* We bind the existence of the P2P device to a wifi device that is being + * managed by NetworkManager and is capable of P2P operation. + * Note that some care must be taken here, because we don't want to re-create + * the device every time the supplicant interface is destroyed (e.g. due to + * a suspend/resume cycle). + * Therefore, this function will be called when a change in the P2P capability + * is detected and the supplicant interface has been initialised. + */ +static void +recheck_p2p_availability (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + gboolean p2p_available; + + g_object_get (priv->sup_iface, + NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE, &p2p_available, + NULL); + + if (p2p_available && !priv->p2p_device) { + gs_free char *iface_name = NULL; + + /* Create a P2P device. "p2p-dev-" is the same prefix as chosen by + * wpa_supplicant internally. + */ + iface_name = g_strconcat ("p2p-dev-", nm_device_get_iface (NM_DEVICE (self)), NULL); + + priv->p2p_device = nm_device_wifi_p2p_new (iface_name); + + nm_device_wifi_p2p_set_mgmt_iface (priv->p2p_device, priv->sup_iface); + + g_signal_emit (self, signals[P2P_DEVICE_CREATED], 0, priv->p2p_device); + g_object_add_weak_pointer (G_OBJECT (priv->p2p_device), (gpointer*) &priv->p2p_device); + g_object_unref (priv->p2p_device); + return; + } + + if (p2p_available && priv->p2p_device) { + nm_device_wifi_p2p_set_mgmt_iface (priv->p2p_device, priv->sup_iface); + return; + } + + if (!p2p_available && priv->p2p_device) { + /* Destroy the P2P device. */ + g_object_remove_weak_pointer (G_OBJECT (priv->p2p_device), (gpointer*) &priv->p2p_device); + nm_device_wifi_p2p_remove (g_steal_pointer (&priv->p2p_device)); + return; + } +} + +static void +supplicant_iface_notify_p2p_available (NMSupplicantInterface *iface, + GParamSpec *pspec, + NMDeviceWifi *self) +{ + /* Do not update when the interface is still initializing. */ + if (nm_supplicant_interface_get_state (iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + return; + + recheck_p2p_availability (self); +} + static gboolean handle_auth_or_fail (NMDeviceWifi *self, NMActRequest *req, @@ -2469,7 +2555,7 @@ wake_on_wlan_enable (NMDeviceWifi *self) NMSettingWirelessWakeOnWLan wowl; NMSettingWireless *s_wireless; - s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS); + s_wireless = nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS); if (s_wireless) { wowl = nm_setting_wireless_get_wake_on_wlan (s_wireless); if (wowl != NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT) @@ -2646,7 +2732,8 @@ set_powersave (NMDevice *device) NMSettingWireless *s_wireless; NMSettingWirelessPowersave val; - s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + s_wireless = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + g_return_if_fail (s_wireless); val = nm_setting_wireless_get_powersave (s_wireless); @@ -2787,50 +2874,29 @@ out: } static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *device, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason) -{ - NMConnection *connection; - NMSettingIPConfig *s_ip4; - const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; - - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4) - method = nm_setting_ip_config_get_method (s_ip4); - - /* Indicate that a critical protocol is about to start */ - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) - nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), TRUE); - - return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason); -} - -static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *device, - NMIP6Config **out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) { + gboolean indicate_addressing_running; NMConnection *connection; - NMSettingIPConfig *s_ip6; - const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + const char *method; connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (s_ip6) - method = nm_setting_ip_config_get_method (s_ip6); + method = nm_utils_get_ip_config_method (connection, addr_family); + if (addr_family == AF_INET) + indicate_addressing_running = NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + else { + indicate_addressing_running = NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP); + } - /* Indicate that a critical protocol is about to start */ - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 || - strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) - nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), TRUE); + if (indicate_addressing_running) + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), TRUE); - return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip6_config_start (device, out_config, out_failure_reason); + return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip_config_start (device, addr_family, out_config, out_failure_reason); } static guint32 @@ -2865,90 +2931,52 @@ is_static_wep (NMConnection *connection) } static NMActStageReturn -handle_ip_config_timeout (NMDeviceWifi *self, - NMConnection *connection, - gboolean may_fail, - gboolean *chain_up, - NMDeviceStateReason *out_failure_reason) +act_stage4_ip_config_timeout (NMDevice *device, + int addr_family, + NMDeviceStateReason *out_failure_reason) { - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMConnection *connection; + NMSettingIPConfig *s_ip; + gboolean may_fail; + + connection = nm_device_get_applied_connection (device); + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); + may_fail = nm_setting_ip_config_get_may_fail (s_ip); - g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); + if (priv->mode == NM_802_11_MODE_AP) + goto call_parent; - if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP) { - *chain_up = TRUE; - return NM_ACT_STAGE_RETURN_FAILURE; + if ( may_fail + || !is_static_wep (connection)) { + /* Not static WEP or failure allowed; let superclass handle it */ + goto call_parent; } /* If IP configuration times out and it's a static WEP connection, that * usually means the WEP key is wrong. WEP's Open System auth mode has * no provision for figuring out if the WEP key is wrong, so you just have - * to wait for DHCP to fail to figure it out. For all other WiFi security + * to wait for DHCP to fail to figure it out. For all other Wi-Fi security * types (open, WPA, 802.1x, etc) if the secrets/certs were wrong the * connection would have failed before IP configuration. - */ - if (!may_fail && is_static_wep (connection)) { - /* Activation failed, we must have bad encryption key */ - _LOGW (LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) could not get IP configuration for connection '%s'.", - nm_connection_get_id (connection)); + * + * Activation failed, we must have bad encryption key */ + _LOGW (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) could not get IP configuration for connection '%s'.", + nm_connection_get_id (connection)); - if (handle_auth_or_fail (self, NULL, TRUE)) { - _LOGI (LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) asking for new secrets"); - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); - ret = NM_ACT_STAGE_RETURN_FAILURE; - } - } else { - /* Not static WEP or failure allowed; let superclass handle it */ - *chain_up = TRUE; + if (!handle_auth_or_fail (self, NULL, TRUE)) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); + return NM_ACT_STAGE_RETURN_FAILURE; } - return ret; -} - -static NMActStageReturn -act_stage4_ip4_config_timeout (NMDevice *device, NMDeviceStateReason *out_failure_reason) -{ - NMConnection *connection; - NMSettingIPConfig *s_ip4; - gboolean may_fail = FALSE, chain_up = FALSE; - NMActStageReturn ret; - - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - may_fail = nm_setting_ip_config_get_may_fail (s_ip4); - - ret = handle_ip_config_timeout (NM_DEVICE_WIFI (device), connection, may_fail, &chain_up, out_failure_reason); - if (chain_up) - ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (device, out_failure_reason); - - return ret; -} - -static NMActStageReturn -act_stage4_ip6_config_timeout (NMDevice *device, NMDeviceStateReason *out_failure_reason) -{ - NMConnection *connection; - NMSettingIPConfig *s_ip6; - gboolean may_fail = FALSE, chain_up = FALSE; - NMActStageReturn ret; - - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - may_fail = nm_setting_ip_config_get_may_fail (s_ip6); - - ret = handle_ip_config_timeout (NM_DEVICE_WIFI (device), connection, may_fail, &chain_up, out_failure_reason); - if (chain_up) - ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (device, out_failure_reason); + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) asking for new secrets"); + return NM_ACT_STAGE_RETURN_POSTPONE; - return ret; +call_parent: + return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip_config_timeout (device, addr_family, out_failure_reason); } static void @@ -3279,7 +3307,7 @@ nm_device_wifi_new (const char *iface, NMDeviceWifiCapabilities capabilities) { return g_object_new (NM_TYPE_DEVICE_WIFI, NM_DEVICE_IFACE, iface, - NM_DEVICE_TYPE_DESC, "802.11 WiFi", + NM_DEVICE_TYPE_DESC, "802.11 Wi-Fi", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, @@ -3305,6 +3333,12 @@ dispose (GObject *object) remove_all_aps (self); + if (priv->p2p_device) { + /* Destroy the P2P device. */ + g_object_remove_weak_pointer (G_OBJECT (priv->p2p_device), (gpointer*) &priv->p2p_device); + nm_device_wifi_p2p_remove (g_steal_pointer (&priv->p2p_device)); + } + G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object); } @@ -3350,10 +3384,8 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) device_class->act_stage1_prepare = act_stage1_prepare; device_class->act_stage2_config = act_stage2_config; device_class->get_configured_mtu = get_configured_mtu; - device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; - device_class->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; - device_class->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->act_stage4_ip_config_timeout = act_stage4_ip_config_timeout; device_class->deactivate = deactivate; device_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr; device_class->unmanaged_on_quit = unmanaged_on_quit; @@ -3417,4 +3449,12 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_prohibited), NULL, NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); + + signals[P2P_DEVICE_CREATED] = + g_signal_new (NM_DEVICE_WIFI_P2P_DEVICE_CREATED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, NM_TYPE_DEVICE); } diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h index 1c555f5f..116ad11e 100644 --- a/src/devices/wifi/nm-device-wifi.h +++ b/src/devices/wifi/nm-device-wifi.h @@ -40,6 +40,7 @@ #define NM_DEVICE_WIFI_LAST_SCAN "last-scan" #define NM_DEVICE_WIFI_SCANNING_PROHIBITED "scanning-prohibited" +#define NM_DEVICE_WIFI_P2P_DEVICE_CREATED "p2p-device-created" typedef struct _NMDeviceWifi NMDeviceWifi; typedef struct _NMDeviceWifiClass NMDeviceWifiClass; diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c index a3da9791..d668f0d8 100644 --- a/src/devices/wifi/nm-iwd-manager.c +++ b/src/devices/wifi/nm-iwd-manager.c @@ -22,7 +22,6 @@ #include "nm-iwd-manager.h" -#include <string.h> #include <net/if.h> #include "nm-logging.h" @@ -48,6 +47,7 @@ typedef struct { typedef struct { NMManager *manager; + NMSettings *settings; GCancellable *cancellable; gboolean running; GDBusObjectManager *object_manager; @@ -136,6 +136,7 @@ agent_dbus_method_cb (GDBusConnection *connection, int ifindex; NMDevice *device; gs_free char *name_owner = NULL; + int errsv; /* Be paranoid and check the sender address */ name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager)); @@ -171,8 +172,9 @@ agent_dbus_method_cb (GDBusConnection *connection, ifindex = if_nametoindex (ifname); if (!ifindex) { + errsv = errno; _LOGD ("agent-request: if_nametoindex failed for Name %s for Device at %s: %i", - ifname, device_path, errno); + ifname, device_path, errsv); goto return_error; } @@ -338,6 +340,7 @@ set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy, const char *ifname; int ifindex; NMDevice *device; + int errsv; ifname = get_property_string_or_null (proxy, "Name"); if (!ifname) { @@ -349,8 +352,9 @@ set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy, ifindex = if_nametoindex (ifname); if (!ifindex) { + errsv = errno; _LOGE ("if_nametoindex failed for Name %s for Device at %s: %i", - ifname, g_dbus_proxy_get_object_path (proxy), errno); + ifname, g_dbus_proxy_get_object_path (proxy), errsv); return; } @@ -363,31 +367,35 @@ set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy, nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object); } -/* Create an in-memory NMConnection for a WPA2-Enterprise network that - * has been preprovisioned with an IWD config file so that NM autoconnect - * mechanism and the clients know this networks needs no additional EAP - * configuration from the user. Only do this if no existing connection - * SSID and security type match that network yet. +/* Look up an existing NMSettingsConnection for a WPA2-Enterprise network + * that has been preprovisioned with an IWD config file, or create a new + * in-memory connection object so that NM autoconnect mechanism and the + * clients know this networks needs no additional EAP configuration from + * the user. */ static NMSettingsConnection * mirror_8021x_connection (NMIwdManager *self, - const char *name) + const char *name, + gboolean create_new) { - NMSettings *settings = NM_SETTINGS_GET; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); NMSettingsConnection *const*iter; gs_unref_object NMConnection *connection = NULL; - NMSettingsConnection *settings_connection; + NMSettingsConnection *settings_connection = NULL; char uuid[37]; NMSetting *setting; GError *error = NULL; gs_unref_bytes GBytes *new_ssid = NULL; - for (iter = nm_settings_get_connections (settings, NULL); *iter; iter++) { + for (iter = nm_settings_get_connections (priv->settings, NULL); *iter; iter++) { NMSettingsConnection *sett_conn = *iter; NMConnection *conn = nm_settings_connection_get_connection (sett_conn); NMIwdNetworkSecurity security; gs_free char *ssid_name = NULL; NMSettingWireless *s_wifi; + NMSetting8021x *s_8021x; + gboolean external = FALSE; + guint i; security = nm_wifi_connection_get_iwd_security (conn, NULL); if (security != NM_IWD_NETWORK_SECURITY_8021X) @@ -399,14 +407,30 @@ mirror_8021x_connection (NMIwdManager *self, ssid_name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wifi)); - /* We already have an NMSettingsConnection matching this - * KnownNetwork, whether it's saved or an in-memory connection - * potentially created by ourselves. Nothing to do here. - */ - if (nm_streq (ssid_name, name)) - return NULL; + if (!nm_streq (ssid_name, name)) + continue; + + s_8021x = nm_connection_get_setting_802_1x (conn); + for (i = 0; i < nm_setting_802_1x_get_num_eap_methods (s_8021x); i++) { + if (nm_streq (nm_setting_802_1x_get_eap_method (s_8021x, i), "external")) { + external = TRUE; + break; + } + } + + /* Prefer returning connections for EAP method "external" */ + if (!settings_connection || external) + settings_connection = sett_conn; } + /* If we already have an NMSettingsConnection matching this + * KnownNetwork, whether it's saved or an in-memory connection + * potentially created by ourselves then we have nothing left to + * do here. + */ + if (settings_connection || !create_new) + return settings_connection; + connection = nm_simple_connection_new (); setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_CONNECTION, @@ -446,7 +470,7 @@ mirror_8021x_connection (NMIwdManager *self, if (!nm_connection_normalize (connection, NULL, NULL, NULL)) return NULL; - settings_connection = nm_settings_add_connection (settings, connection, + settings_connection = nm_settings_add_connection (priv->settings, connection, FALSE, &error); if (!settings_connection) { _LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s", @@ -526,16 +550,19 @@ interface_added (GDBusObjectManager *object_manager, GDBusObject *object, id = known_network_id_new (name, security); data = g_hash_table_lookup (priv->known_networks, id); - if (data) + if (data) { + _LOGW ("DBus error: KnownNetwork already exists ('%s', %s)", + name, type_str); g_free (id); - else { + nm_g_object_ref_set (&data->known_network, proxy); + } else { data = g_slice_new0 (KnownNetworkData); data->known_network = g_object_ref (proxy); g_hash_table_insert (priv->known_networks, id, data); } if (security == NM_IWD_NETWORK_SECURITY_8021X) { - sett_conn = mirror_8021x_connection (self, name); + sett_conn = mirror_8021x_connection (self, name, TRUE); if ( sett_conn && sett_conn != data->mirror_connection) { @@ -593,6 +620,55 @@ interface_removed (GDBusObjectManager *object_manager, GDBusObject *object, } } +static void +connection_removed (NMSettings *settings, + NMSettingsConnection *sett_conn, + gpointer user_data) +{ + NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + NMConnection *conn = nm_settings_connection_get_connection (sett_conn); + NMSettingWireless *s_wireless; + gboolean mapped; + KnownNetworkData *data; + KnownNetworkId id; + + id.security = nm_wifi_connection_get_iwd_security (conn, &mapped); + if (!mapped) + return; + + s_wireless = nm_connection_get_setting_wireless (conn); + id.name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wireless)); + data = g_hash_table_lookup (priv->known_networks, &id); + g_free ((char *) id.name); + if (!data) + return; + + if (id.security == NM_IWD_NETWORK_SECURITY_8021X) { + NMSettingsConnection *new_mirror_conn; + + if (data->mirror_connection != sett_conn) + return; + + g_clear_object (&data->mirror_connection); + + /* Don't call Forget for an 8021x network until there's no + * longer *any* matching NMSettingsConnection (debatable) + */ + new_mirror_conn = mirror_8021x_connection (self, id.name, FALSE); + if (new_mirror_conn) { + data->mirror_connection = g_object_ref (new_mirror_conn); + return; + } + } + + if (!priv->running) + return; + + g_dbus_proxy_call (data->known_network, "Forget", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); +} + static gboolean _om_has_name_owner (GDBusObjectManager *object_manager) { @@ -744,7 +820,7 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) &priv->agent_path, &error); if (!priv->agent_id) { - _LOGE ("failed to export the IWD Agent: PSK/8021x WiFi networks may not work: %s", + _LOGE ("failed to export the IWD Agent: PSK/8021x Wi-Fi networks may not work: %s", error->message); g_clear_error (&error); } @@ -824,6 +900,10 @@ nm_iwd_manager_init (NMIwdManager *self) g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK (device_added), self); + priv->settings = g_object_ref (nm_settings_get ()); + g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, + G_CALLBACK (connection_removed), self); + priv->cancellable = g_cancellable_new (); priv->known_networks = g_hash_table_new_full ((GHashFunc) known_network_id_hash, @@ -844,6 +924,14 @@ dispose (GObject *object) nm_clear_g_cancellable (&priv->cancellable); + if (priv->settings) { + g_signal_handlers_disconnect_by_data (priv->settings, self); + g_clear_object (&priv->settings); + } + + /* This may trigger mirror connection removals so it happens + * after the g_signal_handlers_disconnect_by_data above. + */ nm_clear_pointer (&priv->known_networks, g_hash_table_destroy); if (priv->manager) { diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index e5573383..87daff59 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -23,7 +23,6 @@ #include "nm-wifi-ap.h" -#include <string.h> #include <stdlib.h> #include "nm-setting-wireless.h" @@ -425,6 +424,8 @@ security_from_vardict (GVariant *security) g_strv_contains (array, "wpa-fils-sha256") || g_strv_contains (array, "wpa-fils-sha384")) flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; + if (g_strv_contains (array, "sae")) + flags |= NM_802_11_AP_SEC_KEY_MGMT_SAE; g_free (array); } @@ -1390,7 +1391,8 @@ nm_wifi_ap_class_init (NMWifiAPClass *ap_class) | NM_802_11_AP_SEC_GROUP_TKIP \ | NM_802_11_AP_SEC_GROUP_CCMP \ | NM_802_11_AP_SEC_KEY_MGMT_PSK \ - | NM_802_11_AP_SEC_KEY_MGMT_802_1X ) + | NM_802_11_AP_SEC_KEY_MGMT_802_1X \ + | NM_802_11_AP_SEC_KEY_MGMT_SAE ) GObjectClass *object_class = G_OBJECT_CLASS (ap_class); NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (ap_class); diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c index 6b8e5fb8..9a89bbe9 100644 --- a/src/devices/wifi/nm-wifi-factory.c +++ b/src/devices/wifi/nm-wifi-factory.c @@ -26,6 +26,7 @@ #include "nm-setting-wireless.h" #include "nm-setting-olpc-mesh.h" #include "nm-device-wifi.h" +#include "nm-device-wifi-p2p.h" #include "nm-device-olpc-mesh.h" #include "nm-device-iwd.h" #include "settings/nm-settings-connection.h" @@ -68,6 +69,18 @@ nm_device_factory_create (GError **error) /*****************************************************************************/ +static void +p2p_device_created (NMDeviceWifi *device, + NMDeviceWifiP2P *p2p_device, + NMDeviceFactory *self) +{ + nm_log_info (LOGD_PLATFORM | LOGD_WIFI, + "Wi-Fi P2P device controlled by interface %s created", + nm_device_get_iface (NM_DEVICE (device))); + + g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, p2p_device); +} + static NMDevice * create_device (NMDeviceFactory *factory, const char *iface, @@ -75,8 +88,6 @@ create_device (NMDeviceFactory *factory, NMConnection *connection, gboolean *out_ignore) { - NMDeviceWifiCapabilities capabilities; - NM80211Mode mode; gs_free char *backend = NULL; g_return_val_if_fail (iface != NULL, NULL); @@ -84,23 +95,6 @@ create_device (NMDeviceFactory *factory, g_return_val_if_fail (g_strcmp0 (iface, plink->name) == 0, NULL); g_return_val_if_fail (NM_IN_SET (plink->type, NM_LINK_TYPE_WIFI, NM_LINK_TYPE_OLPC_MESH), NULL); - if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET, - plink->ifindex, - &capabilities)) { - nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, "(%s) failed to initialize Wi-Fi driver for ifindex %d", iface, plink->ifindex); - return NULL; - } - - /* Ignore monitor-mode and other unhandled interface types. - * FIXME: keep TYPE_MONITOR devices in UNAVAILABLE state and manage - * them if/when they change to a handled type. - */ - mode = nm_platform_wifi_get_mode (NM_PLATFORM_GET, plink->ifindex); - if (mode == NM_802_11_MODE_UNKNOWN) { - *out_ignore = TRUE; - return NULL; - } - if (plink->type != NM_LINK_TYPE_WIFI) return nm_device_olpc_mesh_new (iface); @@ -116,11 +110,42 @@ create_device (NMDeviceFactory *factory, iface, NM_PRINT_FMT_QUOTE_STRING (backend), WITH_IWD ? " (iwd support enabled)" : ""); - if (!backend || !strcasecmp (backend, "wpa_supplicant")) - return nm_device_wifi_new (iface, capabilities); + if (!backend || !strcasecmp (backend, "wpa_supplicant")) { + NMDevice *device; + NMDeviceWifiCapabilities capabilities; + NM80211Mode mode; + + if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET, + plink->ifindex, + &capabilities)) { + nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, + "(%s) failed to initialize Wi-Fi driver for ifindex %d", + iface, plink->ifindex); + return NULL; + } + + /* Ignore monitor-mode and other unhandled interface types. + * FIXME: keep TYPE_MONITOR devices in UNAVAILABLE state and manage + * them if/when they change to a handled type. + */ + mode = nm_platform_wifi_get_mode (NM_PLATFORM_GET, plink->ifindex); + if (mode == NM_802_11_MODE_UNKNOWN) { + *out_ignore = TRUE; + return NULL; + } + + device = nm_device_wifi_new (iface, capabilities); + + g_signal_connect_object (device, NM_DEVICE_WIFI_P2P_DEVICE_CREATED, + G_CALLBACK (p2p_device_created), + factory, + 0); + + return device; + } #if WITH_IWD else if (!strcasecmp (backend, "iwd")) - return nm_device_iwd_new (iface, capabilities); + return nm_device_iwd_new (iface); #endif nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, "(%s) config: unknown or unsupported wifi-backend %s", iface, backend); diff --git a/src/devices/wifi/nm-wifi-p2p-peer.c b/src/devices/wifi/nm-wifi-p2p-peer.c new file mode 100644 index 00000000..4b524623 --- /dev/null +++ b/src/devices/wifi/nm-wifi-p2p-peer.c @@ -0,0 +1,796 @@ +/* NetworkManager -- Wi-Fi P2P Peer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-wifi-p2p-peer.h" + +#include <stdlib.h> + +#include "nm-setting-wireless.h" + +#include "nm-wifi-utils.h" +#include "NetworkManagerUtils.h" +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "platform/nm-platform.h" +#include "devices/nm-device.h" +#include "nm-dbus-manager.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE (NMWifiP2PPeer, + PROP_NAME, + PROP_MANUFACTURER, + PROP_MODEL, + PROP_MODEL_NUMBER, + PROP_SERIAL, + PROP_WFD_IES, + PROP_GROUPS, + PROP_HW_ADDRESS, + PROP_STRENGTH, + PROP_LAST_SEEN, + PROP_FLAGS, +); + +struct _NMWifiP2PPeerPrivate { + char *supplicant_path; /* D-Bus object path of this Peer from wpa_supplicant */ + + /* Scanned or cached values */ + char * name; + char * manufacturer; + char * model; + char * model_number; + char * serial; + + char * address; + + GBytes * wfd_ies; + char ** groups; + + guint8 strength; + + NM80211ApFlags flags; /* General flags */ + + /* Non-scanned attributes */ + gint32 last_seen; /* Timestamp when the Peer was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */ +}; + +typedef struct _NMWifiP2PPeerPrivate NMWifiP2PPeerPrivate; + +struct _NMWifiP2PPeerClass { + NMDBusObjectClass parent; +}; + +G_DEFINE_TYPE (NMWifiP2PPeer, nm_wifi_p2p_peer, NM_TYPE_DBUS_OBJECT) + +#define NM_WIFI_P2P_PEER_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMWifiP2PPeer, NM_IS_WIFI_P2P_PEER) + +/*****************************************************************************/ + +const char ** +nm_wifi_p2p_peers_get_paths (const CList *peers_lst_head) +{ + NMWifiP2PPeer *peer; + const char **list; + const char *path; + gsize i, n; + + n = c_list_length (peers_lst_head); + list = g_new (const char *, n + 1); + + i = 0; + if (n > 0) { + c_list_for_each_entry (peer, peers_lst_head, peers_lst) { + nm_assert (i < n); + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (peer)); + nm_assert (path); + + list[i++] = path; + } + nm_assert (i <= n); + } + list[i] = NULL; + return list; +} + +NMWifiP2PPeer * +nm_wifi_p2p_peers_find_first_compatible (const CList *peers_lst_head, + NMConnection *connection) +{ + NMWifiP2PPeer *peer; + + g_return_val_if_fail (connection, NULL); + + c_list_for_each_entry (peer, peers_lst_head, peers_lst) { + if (nm_wifi_p2p_peer_check_compatible (peer, connection)) + return peer; + } + return NULL; +} + +NMWifiP2PPeer * +nm_wifi_p2p_peers_find_by_supplicant_path (const CList *peers_lst_head, const char *path) +{ + NMWifiP2PPeer *peer; + + g_return_val_if_fail (path != NULL, NULL); + + c_list_for_each_entry (peer, peers_lst_head, peers_lst) { + if (nm_streq0 (path, nm_wifi_p2p_peer_get_supplicant_path (peer))) + return peer; + } + return NULL; +} + +/*****************************************************************************/ + +NMWifiP2PPeer * +nm_wifi_p2p_peer_lookup_for_device (NMDevice *device, const char *exported_path) +{ + NMWifiP2PPeer *peer; + + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + peer = (NMWifiP2PPeer *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (device)), + exported_path); + if ( !peer + || !NM_IS_WIFI_P2P_PEER (peer) + || peer->wifi_device != device) + return NULL; + + return peer; +} + +/*****************************************************************************/ + +const char * +nm_wifi_p2p_peer_get_supplicant_path (NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->supplicant_path; +} + +const char * +nm_wifi_p2p_peer_get_name (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->name; +} + +gboolean +nm_wifi_p2p_peer_set_name (NMWifiP2PPeer *peer, const char *name) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if (g_strcmp0 (name, priv->name) == 0) + return FALSE; + + g_clear_pointer (&priv->name, g_free); + if (name) + priv->name = g_strdup (name); + + _notify (peer, PROP_NAME); + return TRUE; +} + +const char * +nm_wifi_p2p_peer_get_manufacturer (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->manufacturer; +} + +gboolean +nm_wifi_p2p_peer_set_manufacturer (NMWifiP2PPeer *peer, const char *manufacturer) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if (g_strcmp0 (manufacturer, priv->manufacturer) == 0) + return FALSE; + + g_clear_pointer (&priv->manufacturer, g_free); + if (manufacturer) + priv->manufacturer = g_strdup (manufacturer); + + _notify (peer, PROP_MANUFACTURER); + return TRUE; +} + +const char * +nm_wifi_p2p_peer_get_model (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->model; +} + +gboolean +nm_wifi_p2p_peer_set_model (NMWifiP2PPeer *peer, const char *model) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if (g_strcmp0 (model, priv->model) == 0) + return FALSE; + + g_clear_pointer (&priv->model, g_free); + if (model) + priv->model = g_strdup (model); + + _notify (peer, PROP_MODEL); + return TRUE; +} + +const char * +nm_wifi_p2p_peer_get_model_number (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->model_number; +} + +gboolean +nm_wifi_p2p_peer_set_model_number (NMWifiP2PPeer *peer, const char *model_number) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if (g_strcmp0 (model_number, priv->model_number) == 0) + return FALSE; + + g_clear_pointer (&priv->model_number, g_free); + if (model_number) + priv->model_number = g_strdup (model_number); + + _notify (peer, PROP_MODEL_NUMBER); + return TRUE; +} + +const char * +nm_wifi_p2p_peer_get_serial (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->serial; +} + +gboolean +nm_wifi_p2p_peer_set_serial (NMWifiP2PPeer *peer, const char *serial) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if (g_strcmp0 (serial, priv->serial) == 0) + return FALSE; + + g_clear_pointer (&priv->serial, g_free); + if (serial) + priv->serial = g_strdup (serial); + + _notify (peer, PROP_SERIAL); + return TRUE; +} + +GBytes * +nm_wifi_p2p_peer_get_wfd_ies (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->wfd_ies; +} + +gboolean +nm_wifi_p2p_peer_set_wfd_ies (NMWifiP2PPeer *peer, GBytes *wfd_ies) +{ + NMWifiP2PPeerPrivate *priv; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + if (nm_gbytes_equal0 (priv->wfd_ies, wfd_ies)) + return FALSE; + + g_bytes_unref (priv->wfd_ies); + priv->wfd_ies = wfd_ies ? g_bytes_ref (wfd_ies) : NULL; + + _notify (peer, PROP_WFD_IES); + return TRUE; +} + +const char *const* +nm_wifi_p2p_peer_get_groups (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return (const char * const*) NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->groups; +} + +static gboolean +nm_wifi_p2p_peer_set_groups (NMWifiP2PPeer *peer, const char** groups) +{ + NMWifiP2PPeerPrivate *priv; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + g_return_val_if_fail (groups != NULL, FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + if (_nm_utils_strv_equal (priv->groups, (char **) groups)) + return FALSE; + + g_strfreev (priv->groups); + priv->groups = g_strdupv ((char**) groups); + + _notify (peer, PROP_GROUPS); + return TRUE; +} + +const char * +nm_wifi_p2p_peer_get_address (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->address; +} + +static gboolean +nm_wifi_p2p_peer_set_address_bin (NMWifiP2PPeer *peer, const guint8 addr[static ETH_ALEN]) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + if ( priv->address + && nm_utils_hwaddr_matches (addr, ETH_ALEN, priv->address, -1)) + return FALSE; + + g_free (priv->address); + priv->address = nm_utils_hwaddr_ntoa (addr, ETH_ALEN); + _notify (peer, PROP_HW_ADDRESS); + return TRUE; +} + +gboolean +nm_wifi_p2p_peer_set_address (NMWifiP2PPeer *peer, const char *addr) +{ + guint8 addr_buf[ETH_ALEN]; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if ( !addr + || !nm_utils_hwaddr_aton (addr, addr_buf, sizeof (addr_buf))) + g_return_val_if_reached (FALSE); + + return nm_wifi_p2p_peer_set_address_bin (peer, addr_buf); +} + +gint8 +nm_wifi_p2p_peer_get_strength (NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), 0); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->strength; +} + +gboolean +nm_wifi_p2p_peer_set_strength (NMWifiP2PPeer *peer, const gint8 strength) +{ + NMWifiP2PPeerPrivate *priv; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + if (priv->strength != strength) { + priv->strength = strength; + _notify (peer, PROP_STRENGTH); + return TRUE; + } + return FALSE; +} + +NM80211ApFlags +nm_wifi_p2p_peer_get_flags (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NM_802_11_AP_FLAGS_NONE); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->flags; +} + +static gboolean +nm_wifi_p2p_peer_set_last_seen (NMWifiP2PPeer *peer, gint32 last_seen) +{ + NMWifiP2PPeerPrivate *priv; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + if (priv->last_seen != last_seen) { + priv->last_seen = last_seen; + _notify (peer, PROP_LAST_SEEN); + return TRUE; + } + return FALSE; +} + + +/*****************************************************************************/ + +gboolean +nm_wifi_p2p_peer_update_from_properties (NMWifiP2PPeer *peer, + const char *supplicant_path, + GVariant *properties) +{ + NMWifiP2PPeerPrivate *priv; + const guint8 *bytes; + GVariant *v; + gsize len; + const char *s; + const char **sv; + gint32 i32; + gboolean changed = FALSE; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + g_return_val_if_fail (properties, FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + g_object_freeze_notify (G_OBJECT (peer)); + + if (g_variant_lookup (properties, "level", "i", &i32)) + changed |= nm_wifi_p2p_peer_set_strength (peer, nm_wifi_utils_level_to_quality (i32)); + + if (g_variant_lookup (properties, "DeviceName", "&s", &s)) + changed |= nm_wifi_p2p_peer_set_name (peer, s); + + if (g_variant_lookup (properties, "Manufacturer", "&s", &s)) + changed |= nm_wifi_p2p_peer_set_manufacturer (peer, s); + + if (g_variant_lookup (properties, "Model", "&s", &s)) + changed |= nm_wifi_p2p_peer_set_model (peer, s); + + if (g_variant_lookup (properties, "ModelNumber", "&s", &s)) + changed |= nm_wifi_p2p_peer_set_model_number (peer, s); + + if (g_variant_lookup (properties, "Serial", "&s", &s)) + changed |= nm_wifi_p2p_peer_set_serial (peer, s); + + v = g_variant_lookup_value (properties, "DeviceAddress", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + if ( len == ETH_ALEN + && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0 + && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0) + changed |= nm_wifi_p2p_peer_set_address_bin (peer, bytes); + g_variant_unref (v); + } + + /* The IEs property contains the WFD R1 subelements */ + v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING); + if (v) { + gs_unref_bytes GBytes *b = NULL; + + bytes = g_variant_get_fixed_array (v, &len, 1); + b = g_bytes_new (bytes, len); + changed |= nm_wifi_p2p_peer_set_wfd_ies (peer, b); + g_variant_unref (v); + } + + v = g_variant_lookup_value (properties, "Groups", G_VARIANT_TYPE_OBJECT_PATH_ARRAY); + if (v) { + sv = g_variant_get_objv (v, NULL); + changed |= nm_wifi_p2p_peer_set_groups (peer, sv); + g_free (sv); + } + + /*if (max_rate) + changed |= nm_wifi_p2p_peer_set_max_bitrate (peer, max_rate / 1000);*/ + + if (!priv->supplicant_path) { + priv->supplicant_path = g_strdup (supplicant_path); + changed = TRUE; + } + + changed |= nm_wifi_p2p_peer_set_last_seen (peer, nm_utils_get_monotonic_timestamp_s ()); + + g_object_thaw_notify (G_OBJECT (peer)); + + return changed; +} + +const char * +nm_wifi_p2p_peer_to_string (const NMWifiP2PPeer *self, + char *str_buf, + gsize buf_len, + gint32 now_s) +{ + const NMWifiP2PPeerPrivate *priv; + const char *supplicant_id = "-"; + const char* export_path; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (self), NULL); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (self); + + if (priv->supplicant_path) + supplicant_id = strrchr (priv->supplicant_path, '/') ?: supplicant_id; + + export_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self)); + if (export_path) + export_path = strrchr (export_path, '/') ?: export_path; + else + export_path = "/"; + + g_snprintf (str_buf, buf_len, + "%17s [n:%s, m:%s, mod:%s, mod_num:%s, ser:%s] %3us sup:%s [nm:%s]", + priv->address ?: "(none)", + priv->name, + priv->manufacturer, + priv->model, + priv->model_number, + priv->serial, + priv->last_seen > 0 ? ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_s ()) - priv->last_seen) : -1, + supplicant_id, + export_path); + + return str_buf; +} + +gboolean +nm_wifi_p2p_peer_check_compatible (NMWifiP2PPeer *self, + NMConnection *connection) +{ + NMWifiP2PPeerPrivate *priv; + NMSettingWifiP2P *s_wifi_p2p; + const char *hwaddr; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (self); + + s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P)); + if (s_wifi_p2p == NULL) + return FALSE; + + hwaddr = nm_setting_wifi_p2p_get_peer (s_wifi_p2p); + if ( hwaddr + && ( !priv->address + || !nm_utils_hwaddr_matches (hwaddr, -1, priv->address, -1))) + return FALSE; + + return TRUE; +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMWifiP2PPeer *self = NM_WIFI_P2P_PEER (object); + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (self); + + switch (prop_id) { + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_MANUFACTURER: + g_value_set_string (value, priv->manufacturer); + break; + case PROP_MODEL: + g_value_set_string (value, priv->model); + break; + case PROP_MODEL_NUMBER: + g_value_set_string (value, priv->model_number); + break; + case PROP_SERIAL: + g_value_set_string (value, priv->serial); + break; + case PROP_WFD_IES: + g_value_take_variant (value, nm_utils_gbytes_to_variant_ay (priv->wfd_ies)); + break; + case PROP_GROUPS: + g_value_set_variant (value, + g_variant_new_strv ( (const char*const*) priv->groups + ?: NM_PTRARRAY_EMPTY (const char *), + -1)); + break; + case PROP_HW_ADDRESS: + g_value_set_string (value, priv->address); + break; + case PROP_STRENGTH: + g_value_set_uchar (value, priv->strength); + break; + case PROP_LAST_SEEN: + g_value_set_int (value, + priv->last_seen > 0 + ? (int) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NS_PER_SECOND) + : -1); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_wifi_p2p_peer_init (NMWifiP2PPeer *self) +{ + NMWifiP2PPeerPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeerPrivate); + + self->_priv = priv; + + c_list_init (&self->peers_lst); + + priv->flags = NM_802_11_AP_FLAGS_NONE; + priv->last_seen = -1; +} + +NMWifiP2PPeer * +nm_wifi_p2p_peer_new_from_properties (const char *supplicant_path, GVariant *properties) +{ + NMWifiP2PPeer *peer; + + g_return_val_if_fail (supplicant_path != NULL, NULL); + g_return_val_if_fail (properties != NULL, NULL); + + peer = (NMWifiP2PPeer *) g_object_new (NM_TYPE_WIFI_P2P_PEER, NULL); + nm_wifi_p2p_peer_update_from_properties (peer, supplicant_path, properties); + + /* ignore peers with invalid or missing address */ + if (!nm_wifi_p2p_peer_get_address (peer)) { + g_object_unref (peer); + return NULL; + } + + return peer; +} + +static void +finalize (GObject *object) +{ + NMWifiP2PPeer *self = NM_WIFI_P2P_PEER (object); + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (self); + + nm_assert (!self->wifi_device); + nm_assert (c_list_is_empty (&self->peers_lst)); + + g_free (priv->supplicant_path); + g_free (priv->name); + g_free (priv->manufacturer); + g_free (priv->model); + g_free (priv->model_number); + g_free (priv->serial); + g_free (priv->address); + g_bytes_unref (priv->wfd_ies); + g_strfreev (priv->groups); + + G_OBJECT_CLASS (nm_wifi_p2p_peer_parent_class)->finalize (object); +} + +static const NMDBusInterfaceInfoExtended interface_info_p2p_peer = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_WIFI_P2P_PEER, + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Flags", "u", NM_WIFI_P2P_PEER_FLAGS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Name", "s", NM_WIFI_P2P_PEER_NAME), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Manufacturer", "s", NM_WIFI_P2P_PEER_MANUFACTURER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Model", "s", NM_WIFI_P2P_PEER_MODEL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("ModelNumber", "s", NM_WIFI_P2P_PEER_MODEL_NUMBER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Serial", "s", NM_WIFI_P2P_PEER_SERIAL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("WfdIEs", "ay", NM_WIFI_P2P_PEER_WFD_IES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Groups", "as", NM_WIFI_P2P_PEER_GROUPS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("HwAddress", "s", NM_WIFI_P2P_PEER_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Strength", "y", NM_WIFI_P2P_PEER_STRENGTH), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("LastSeen", "i", NM_WIFI_P2P_PEER_LAST_SEEN), + ), + ), + .legacy_property_changed = FALSE, +}; + +static void +nm_wifi_p2p_peer_class_init (NMWifiP2PPeerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMWifiP2PPeerPrivate)); + + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_WIFI_P2P_PEER); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_p2p_peer); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + obj_properties[PROP_FLAGS] = + g_param_spec_uint (NM_WIFI_P2P_PEER_FLAGS, "", "", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_FLAGS_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_NAME] = + g_param_spec_string (NM_WIFI_P2P_PEER_NAME, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_MANUFACTURER] = + g_param_spec_string (NM_WIFI_P2P_PEER_MANUFACTURER, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_MODEL] = + g_param_spec_string (NM_WIFI_P2P_PEER_MODEL, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_MODEL_NUMBER] = + g_param_spec_string (NM_WIFI_P2P_PEER_MODEL_NUMBER, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_SERIAL] = + g_param_spec_string (NM_WIFI_P2P_PEER_SERIAL, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_WFD_IES] = + g_param_spec_variant (NM_WIFI_P2P_PEER_WFD_IES, "", "", + G_VARIANT_TYPE ("ay"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_GROUPS] = + g_param_spec_variant (NM_WIFI_P2P_PEER_GROUPS, "", "", + G_VARIANT_TYPE ("as"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_HW_ADDRESS] = + g_param_spec_string (NM_WIFI_P2P_PEER_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_STRENGTH] = + g_param_spec_uchar (NM_WIFI_P2P_PEER_STRENGTH, "", "", + 0, G_MAXINT8, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_LAST_SEEN] = + g_param_spec_int (NM_WIFI_P2P_PEER_LAST_SEEN, "", "", + -1, G_MAXINT, -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/devices/wifi/nm-wifi-p2p-peer.h b/src/devices/wifi/nm-wifi-p2p-peer.h new file mode 100644 index 00000000..d6ff7abc --- /dev/null +++ b/src/devices/wifi/nm-wifi-p2p-peer.h @@ -0,0 +1,114 @@ +/* NetworkManager -- Wi-Fi P2P Peer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __NM_WIFI_P2P_PEER_H__ +#define __NM_WIFI_P2P_PEER_H__ + +#include "nm-dbus-object.h" +#include "nm-dbus-interface.h" +#include "nm-connection.h" + +#define NM_TYPE_WIFI_P2P_PEER (nm_wifi_p2p_peer_get_type ()) +#define NM_WIFI_P2P_PEER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeer)) +#define NM_WIFI_P2P_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeerClass)) +#define NM_IS_WIFI_P2P_PEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_WIFI_P2P_PEER)) +#define NM_IS_WIFI_P2P_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_WIFI_P2P_PEER)) +#define NM_WIFI_P2P_PEER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeerClass)) + +#define NM_WIFI_P2P_PEER_FLAGS "flags" +#define NM_WIFI_P2P_PEER_NAME "name" +#define NM_WIFI_P2P_PEER_MANUFACTURER "manufacturer" +#define NM_WIFI_P2P_PEER_MODEL "model" +#define NM_WIFI_P2P_PEER_MODEL_NUMBER "model-number" +#define NM_WIFI_P2P_PEER_SERIAL "serial" +#define NM_WIFI_P2P_PEER_WFD_IES "wfd-ies" +#define NM_WIFI_P2P_PEER_GROUPS "groups" +#define NM_WIFI_P2P_PEER_HW_ADDRESS "hw-address" +#define NM_WIFI_P2P_PEER_STRENGTH "strength" +#define NM_WIFI_P2P_PEER_LAST_SEEN "last-seen" + +typedef struct { + NMDBusObject parent; + NMDevice *wifi_device; + CList peers_lst; + struct _NMWifiP2PPeerPrivate *_priv; +} NMWifiP2PPeer; + +typedef struct _NMWifiP2PPeerClass NMWifiP2PPeerClass; + +GType nm_wifi_p2p_peer_get_type (void); + +NMWifiP2PPeer * nm_wifi_p2p_peer_new_from_properties (const char *supplicant_path, + GVariant *properties); + +gboolean nm_wifi_p2p_peer_update_from_properties (NMWifiP2PPeer *peer, + const char *supplicant_path, + GVariant *properties); + +gboolean nm_wifi_p2p_peer_check_compatible (NMWifiP2PPeer *self, + NMConnection *connection); + +const char * nm_wifi_p2p_peer_get_supplicant_path (NMWifiP2PPeer *peer); + +const char * nm_wifi_p2p_peer_get_name (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_name (NMWifiP2PPeer *peer, + const char *name); +const char * nm_wifi_p2p_peer_get_manufacturer (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_manufacturer (NMWifiP2PPeer *peer, + const char *manufacturer); +const char * nm_wifi_p2p_peer_get_model (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_model (NMWifiP2PPeer *peer, + const char *model); +const char * nm_wifi_p2p_peer_get_model_number (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_model_number (NMWifiP2PPeer *peer, + const char *number); +const char * nm_wifi_p2p_peer_get_serial (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_serial (NMWifiP2PPeer *peer, + const char *serial); + +GBytes * nm_wifi_p2p_peer_get_wfd_ies (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_wfd_ies (NMWifiP2PPeer *peer, + GBytes *bytes); + +const char *const*nm_wifi_p2p_peer_get_groups (const NMWifiP2PPeer *peer); + +const char * nm_wifi_p2p_peer_get_address (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_address (NMWifiP2PPeer *peer, + const char *addr); +gint8 nm_wifi_p2p_peer_get_strength (NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_strength (NMWifiP2PPeer *peer, + gint8 strength); +NM80211ApFlags nm_wifi_p2p_peer_get_flags (const NMWifiP2PPeer *self); + +const char *nm_wifi_p2p_peer_to_string (const NMWifiP2PPeer *self, + char *str_buf, + gsize buf_len, + gint32 now_s); + +const char **nm_wifi_p2p_peers_get_paths (const CList *peers_lst_head); + +NMWifiP2PPeer *nm_wifi_p2p_peers_find_first_compatible (const CList *peers_lst_head, + NMConnection *connection); + +NMWifiP2PPeer *nm_wifi_p2p_peers_find_by_supplicant_path (const CList *peers_lst_head, const char *path); + +NMWifiP2PPeer *nm_wifi_p2p_peer_lookup_for_device (NMDevice *device, const char *exported_path); + +#endif /* __NM_WIFI_P2P_PEER_H__ */ diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c index 0f7836be..c6e8b3e0 100644 --- a/src/devices/wifi/nm-wifi-utils.c +++ b/src/devices/wifi/nm-wifi-utils.c @@ -22,7 +22,6 @@ #include "nm-wifi-utils.h" -#include <string.h> #include <stdlib.h> #include "nm-utils.h" @@ -739,6 +738,12 @@ nm_wifi_utils_complete_connection (GBytes *ap_ssid, * setting. Since there's so much configuration required for it, there's * no way it can be automatically completed. */ + } else if ( (key_mgmt && !strcmp (key_mgmt, "sae")) + || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) { + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "sae", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NULL); } else if ( (key_mgmt && !strcmp (key_mgmt, "wpa-psk")) || (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { diff --git a/src/devices/wifi/tests/meson.build b/src/devices/wifi/tests/meson.build index bb8f7c27..895853c4 100644 --- a/src/devices/wifi/tests/meson.build +++ b/src/devices/wifi/tests/meson.build @@ -3,11 +3,12 @@ test_unit = 'test-general' exe = executable( 'wifi-' + test_unit, [test_unit + '.c'] + common_sources, - dependencies: test_nm_dep + dependencies: test_nm_dep, ) test( 'devices/wifi/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], + timeout: default_test_timeout, ) diff --git a/src/devices/wifi/tests/test-general.c b/src/devices/wifi/tests/test-general.c index f752bbfc..e0899837 100644 --- a/src/devices/wifi/tests/test-general.c +++ b/src/devices/wifi/tests/test-general.c @@ -20,10 +20,7 @@ #include "nm-default.h" -#include <string.h> - #include "devices/wifi/nm-wifi-utils.h" - #include "nm-core-internal.h" #include "nm-test-utils-core.h" |