diff options
author | Thomas Haller <thaller@redhat.com> | 2018-12-13 09:50:11 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2018-12-13 09:50:11 +0100 |
commit | 4d88334d23fd447d2c38e89ddcd8c0445253e968 (patch) | |
tree | 3f0cdbc557794b62101a9015a31955c5b6e3ac1d | |
parent | b16e09a707fd836cf1ea4ed34422158c8408d94f (diff) | |
parent | 1371414e3d39fb0974db1282a136bb96448e6287 (diff) | |
download | NetworkManager-4d88334d23fd447d2c38e89ddcd8c0445253e968.tar.gz |
wifi: merge branch 'bb/supplicant-wifi-p2p-support'
Partially merge [1].
[1] https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/24
-rw-r--r-- | src/supplicant/nm-supplicant-interface.c | 928 | ||||
-rw-r--r-- | src/supplicant/nm-supplicant-interface.h | 42 | ||||
-rw-r--r-- | src/supplicant/nm-supplicant-manager.c | 89 | ||||
-rw-r--r-- | src/supplicant/nm-supplicant-manager.h | 2 |
4 files changed, 1010 insertions, 51 deletions
diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index 5237acb263..6e4d178a05 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -22,6 +22,7 @@ #include "nm-default.h" #include "nm-supplicant-interface.h" +#include "nm-supplicant-manager.h" #include <stdio.h> #include <string.h> @@ -31,12 +32,15 @@ #include "nm-core-internal.h" #include "nm-dbus-compat.h" -#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" -#define WPAS_DBUS_IFACE_INTERFACE_WPS WPAS_DBUS_INTERFACE ".Interface.WPS" -#define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS" -#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" -#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface" -#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists" +#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" +#define WPAS_DBUS_IFACE_INTERFACE_WPS WPAS_DBUS_INTERFACE ".Interface.WPS" +#define WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE WPAS_DBUS_INTERFACE ".Interface.P2PDevice" +#define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS" +#define WPAS_DBUS_IFACE_PEER WPAS_DBUS_INTERFACE ".Peer" +#define WPAS_DBUS_IFACE_GROUP WPAS_DBUS_INTERFACE ".Group" +#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" +#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface" +#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists" /*****************************************************************************/ @@ -45,6 +49,11 @@ typedef struct { gulong change_id; } BssData; +typedef struct { + GDBusProxy *proxy; + gulong change_id; +} PeerData; + struct _AddNetworkData; typedef struct { @@ -74,26 +83,38 @@ typedef struct _AddNetworkData { } AddNetworkData; enum { - STATE, /* change in the interface's state */ - REMOVED, /* interface was removed by the supplicant */ - BSS_UPDATED, /* a new BSS appeared or an existing had properties changed */ - BSS_REMOVED, /* supplicant removed BSS from its scan list */ - SCAN_DONE, /* wifi scan is complete */ - CREDENTIALS_REQUEST, /* 802.1x identity or password requested */ - WPS_CREDENTIALS, /* WPS credentials received */ + STATE, /* change in the interface's state */ + REMOVED, /* interface was removed by the supplicant */ + BSS_UPDATED, /* a new BSS appeared or an existing had properties changed */ + BSS_REMOVED, /* supplicant removed BSS from its scan list */ + PEER_UPDATED, /* a new Peer appeared or an existing had properties changed */ + PEER_REMOVED, /* supplicant removed Peer from its scan list */ + SCAN_DONE, /* wifi scan is complete */ + CREDENTIALS_REQUEST, /* 802.1x identity or password requested */ + WPS_CREDENTIALS, /* WPS credentials received */ + GROUP_STARTED, /* a new Group (interface) was created */ + GROUP_FINISHED, /* a Group (interface) has been finished */ + GROUP_FORMATION_FAILURE, /* P2P Group formation failed */ LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface, PROP_IFACE, + PROP_OBJECT_PATH, + PROP_P2P_GROUP_JOINED, + PROP_P2P_GROUP_PATH, + PROP_P2P_GROUP_OWNER, PROP_SCANNING, PROP_CURRENT_BSS, PROP_DRIVER, + PROP_P2P_AVAILABLE, PROP_FAST_SUPPORT, PROP_AP_SUPPORT, PROP_PMF_SUPPORT, PROP_FILS_SUPPORT, + PROP_P2P_SUPPORT, + PROP_WFD_SUPPORT, ); typedef struct { @@ -104,6 +125,8 @@ typedef struct { NMSupplicantFeature ap_support; /* Lightweight AP mode support */ NMSupplicantFeature pmf_support; NMSupplicantFeature fils_support; + NMSupplicantFeature p2p_support; + NMSupplicantFeature wfd_support; guint32 max_scan_ssids; guint32 ready_count; @@ -120,6 +143,14 @@ typedef struct { GCancellable * init_cancellable; GDBusProxy * iface_proxy; GCancellable * other_cancellable; + GDBusProxy * p2p_proxy; + GDBusProxy * group_proxy; + + gboolean p2p_proxy_acquired; + gboolean group_proxy_acquired; + gboolean p2p_capable; + + gboolean p2p_group_owner; WpsData *wps_data; @@ -129,6 +160,8 @@ typedef struct { GHashTable * bss_proxies; char * current_bss; + GHashTable * peer_proxies; + gint64 last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_ms() */ } NMSupplicantInterfacePrivate; @@ -311,6 +344,122 @@ bss_add_new (NMSupplicantInterface *self, const char *object_path) self); } +static void +peer_data_destroy (gpointer user_data) +{ + PeerData *peer_data = user_data; + + nm_clear_g_signal_handler (peer_data->proxy, &peer_data->change_id); + g_object_unref (peer_data->proxy); + g_slice_free (PeerData, peer_data); +} + +static void +peer_proxy_properties_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + char **invalidated_properties, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + g_signal_emit (self, signals[PEER_UPDATED], 0, + g_dbus_proxy_get_object_path (proxy), + changed_properties); +} + +static GVariant * +peer_proxy_get_properties (NMSupplicantInterface *self, GDBusProxy *proxy) +{ + gs_strfreev char **properties = NULL; + GVariantBuilder builder; + char **iter; + + iter = properties = g_dbus_proxy_get_cached_property_names (proxy); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (iter) { + while (*iter) { + GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter); + + g_variant_builder_add (&builder, "{sv}", *iter++, copy); + g_variant_unref (copy); + } + } + return g_variant_builder_end (&builder); +} + +static void +peer_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + GVariant *props = NULL; + const char *object_path; + PeerData *peer_data; + gboolean success; + + success = g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error); + if ( !success + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (!success) { + _LOGD ("failed to acquire Peer proxy: (%s)", error->message); + g_hash_table_remove (priv->peer_proxies, + g_dbus_proxy_get_object_path (proxy)); + return; + } + + object_path = g_dbus_proxy_get_object_path (proxy); + peer_data = g_hash_table_lookup (priv->peer_proxies, object_path); + if (!peer_data) + return; + + peer_data->change_id = g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (peer_proxy_properties_changed_cb), self); + + props = peer_proxy_get_properties (self, proxy); + + g_signal_emit (self, signals[PEER_UPDATED], 0, + g_dbus_proxy_get_object_path (proxy), + g_variant_ref_sink (props)); + g_variant_unref (props); +} + +static void +peer_add_new (NMSupplicantInterface *self, const char *object_path) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + GDBusProxy *peer_proxy; + PeerData *peer_data; + + g_return_if_fail (object_path != NULL); + + if (g_hash_table_lookup (priv->peer_proxies, object_path)) + return; + + peer_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", object_path, + "g-interface-name", WPAS_DBUS_IFACE_PEER, + NULL); + peer_data = g_slice_new0 (PeerData); + peer_data->proxy = peer_proxy; + g_hash_table_insert (priv->peer_proxies, + (char *) g_dbus_proxy_get_object_path (peer_proxy), + peer_data); + g_async_initable_init_async (G_ASYNC_INITABLE (peer_proxy), + G_PRIORITY_DEFAULT, + priv->other_cancellable, + (GAsyncReadyCallback) peer_proxy_acquired_cb, + self); +} + /*****************************************************************************/ static void @@ -450,12 +599,24 @@ static void parse_capabilities (NMSupplicantInterface *self, GVariant *capabilities) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - gboolean have_active = FALSE, have_ssid = FALSE; + gboolean have_active = FALSE, have_p2p = FALSE, have_ssid = FALSE; gint32 max_scan_ssids = -1; const char **array; g_return_if_fail (capabilities && g_variant_is_of_type (capabilities, G_VARIANT_TYPE_VARDICT)); + if ( g_variant_lookup (capabilities, "Modes", "^a&s", &array) + && array) { + if (g_strv_contains (array, "p2p")) + have_p2p = TRUE; + g_free (array); + } + + if (priv->p2p_capable != have_p2p) { + priv->p2p_capable = have_p2p; + _notify (self, PROP_P2P_AVAILABLE); + } + if ( g_variant_lookup (capabilities, "Scan", "^a&s", &array) && array) { if (g_strv_contains (array, "active")) @@ -555,6 +716,29 @@ iface_check_netreply_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ iface_check_ready (self); } +gboolean +nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->group_proxy_acquired; +} + +const char* +nm_supplicant_interface_get_p2p_group_path (NMSupplicantInterface *self) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (priv->group_proxy_acquired) + return g_dbus_proxy_get_object_path (priv->group_proxy); + else + return NULL; +} + +gboolean +nm_supplicant_interface_get_p2p_group_owner (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->p2p_group_owner; +} + NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self) { @@ -573,6 +757,18 @@ nm_supplicant_interface_get_fils_support (NMSupplicantInterface *self) return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->fils_support; } +NMSupplicantFeature +nm_supplicant_interface_get_p2p_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->p2p_support; +} + +NMSupplicantFeature +nm_supplicant_interface_get_wfd_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->wfd_support; +} + void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature ap_support) @@ -613,6 +809,24 @@ nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self, priv->fils_support = fils_support; } +void +nm_supplicant_interface_set_p2p_support (NMSupplicantInterface *self, + NMSupplicantFeature p2p_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->p2p_support = p2p_support; +} + +void +nm_supplicant_interface_set_wfd_support (NMSupplicantInterface *self, + NMSupplicantFeature wfd_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->wfd_support = wfd_support; +} + /*****************************************************************************/ static void @@ -1100,10 +1314,260 @@ props_changed_cb (GDBusProxy *proxy, _LOGW ("connection disconnected (reason %d)", priv->disconnect_reason); } + /* We may not have priv->dev set yet if this interface was created from a + * known wpa_supplicant interface without knowing the device name. + */ + if (priv->dev == NULL && g_variant_lookup (changed_properties, "Ifname", "&s", &s)) { + priv->dev = g_strdup (s); + _notify (self, PROP_IFACE); + } + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +group_props_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + char **invalidated_properties, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + char *s; + + g_object_freeze_notify (G_OBJECT (self)); + +#if 0 + v = g_variant_lookup_value (properties, "BSSID", 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) + nm_wifi_p2p_group_set_bssid_bin (group, bytes); + g_variant_unref (v); + } + + v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + len = MIN (32, len); + + /* Stupid ieee80211 layer uses <hidden> */ + if ( bytes && len + && !(((len == 8) || (len == 9)) && !memcmp (bytes, "<hidden>", 8)) + && !nm_utils_is_empty_ssid (bytes, len)) + nm_wifi_p2p_group_set_ssid (group, bytes, len); + + g_variant_unref (v); + } +#endif + + if (g_variant_lookup (changed_properties, "Role", "s", &s)) { + priv->p2p_group_owner = g_strcmp0 (s, "GO") == 0; + _notify (self, PROP_P2P_GROUP_OWNER); + g_free (s); + } + + /* NOTE: We do not seem to get any property change notifications for the Members + * property. However, we can keep track of these indirectly either by querying + * the groups that each peer is in or listening to the Join/Disconnect + * notifications. + */ + g_object_thaw_notify (G_OBJECT (self)); } static void +group_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + gboolean success; + + success = g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error); + if ( !success + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (!success) { + _LOGD ("failed to acquire Group proxy: (%s)", error->message); + g_clear_object (&priv->group_proxy); + return; + } + + priv->group_proxy_acquired = TRUE; + _notify (self, PROP_P2P_GROUP_JOINED); + _notify (self, PROP_P2P_GROUP_PATH); + + iface_check_ready (self); +} + +static void +p2p_props_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + const char **array, **iter; + const char *path = NULL; + + g_object_freeze_notify (G_OBJECT (self)); + + if (g_variant_lookup (changed_properties, "Peers", "^a&o", &array)) { + iter = array; + while (*iter) + peer_add_new (self, *iter++); + g_free (array); + } + + if (g_variant_lookup (changed_properties, "Group", "&o", &path)) { + if (priv->group_proxy && g_strcmp0 (path, g_dbus_proxy_get_object_path (priv->group_proxy)) == 0) { + /* We already have the proxy, nothing to do. */ + } else if (path && g_strcmp0 (path, "/") != 0) { + if (priv->group_proxy != NULL) { + _LOGW ("P2P: Unexpected udpate of the group object path"); + priv->group_proxy_acquired = FALSE; + _notify (self, PROP_P2P_GROUP_JOINED); + _notify (self, PROP_P2P_GROUP_PATH); + g_clear_object (&priv->group_proxy); + } + + /* Delay ready state if we have not reached it yet. */ + if (priv->ready_count) + priv->ready_count++; + + priv->group_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", path, + "g-interface-name", WPAS_DBUS_IFACE_GROUP, + NULL); + g_signal_connect (priv->group_proxy, "g-properties-changed", G_CALLBACK (group_props_changed_cb), self); + g_async_initable_init_async (G_ASYNC_INITABLE (priv->group_proxy), + G_PRIORITY_DEFAULT, + priv->other_cancellable, + (GAsyncReadyCallback) group_proxy_acquired_cb, + self); + } else { + priv->group_proxy_acquired = FALSE; + _notify (self, PROP_P2P_GROUP_JOINED); + _notify (self, PROP_P2P_GROUP_PATH); + g_clear_object (&priv->group_proxy); + } + } + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +p2p_device_found (GDBusProxy *proxy, + const char *path, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + peer_add_new (self, path); +} + +static void +p2p_device_lost (GDBusProxy *proxy, + const char *path, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + PeerData *peer_data; + + peer_data = g_hash_table_lookup (priv->peer_proxies, path); + if (!peer_data) + return; + g_hash_table_steal (priv->peer_proxies, path); + g_signal_emit (self, signals[PEER_REMOVED], 0, path); + peer_data_destroy (peer_data); +} + +static void +p2p_group_started (GDBusProxy *proxy, + GVariant *params, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInterface *iface = NULL; + char *group_path = NULL; + char *iface_path = NULL; + + /* There is one more parameter: the role, but we don't really care about that here. */ + if (!g_variant_lookup (params, "group_object", "&o", &group_path)) { + _LOGW ("P2P: GroupStarted signal is missing the \"group_object\" parameter"); + return; + } + + if (!g_variant_lookup (params, "interface_object", "&o", &iface_path)) { + _LOGW ("P2P: GroupStarted signal is missing the \"interface\" parameter"); + return; + } + + if (g_strcmp0 (iface_path, priv->object_path) == 0) { + _LOGW ("P2P: GroupStarted on existing interface"); + iface = g_object_ref (self); + } else { + iface = nm_supplicant_manager_create_interface_from_path (nm_supplicant_manager_get (), + iface_path); + if (iface == NULL) { + _LOGW ("P2P: Group interface already exists in GroupStarted handler, aborting further processing."); + return; + } + } + + /* Signal existance of the (new) interface. */ + g_signal_emit (self, signals[GROUP_STARTED], 0, iface); + g_object_unref (iface); +} + +static void +p2p_group_formation_failure (GDBusProxy *proxy, + const char *group, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + g_signal_emit (self, signals[GROUP_FORMATION_FAILURE], 0, group); +} + +static void +p2p_group_finished (GDBusProxy *proxy, + GVariant *params, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + const char *iface_path = NULL; + /* TODO: Group finished is called on the management interface! + * This means the signal consumer will currently need to assume which + * interface is finishing or it needs to match the object paths. + */ + + if (!g_variant_lookup (params, "interface_object", "&o", &iface_path)) { + _LOGW ("P2P: GroupFinished signal is missing the \"interface\" parameter"); + return; + } + + _LOGD ("P2P: GroupFinished signal on interface %s for interface %s", priv->object_path, iface_path); + + /* Signal group finish interface (on management interface). */ + g_signal_emit (self, signals[GROUP_FINISHED], 0, iface_path); +} + +static void on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { NMSupplicantInterface *self; @@ -1156,7 +1620,6 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ NULL); /* Check whether NetworkReply and AP mode are supported */ - priv->ready_count = 1; g_dbus_proxy_call (priv->iface_proxy, "NetworkReply", g_variant_new ("(oss)", @@ -1188,13 +1651,63 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ } static void +on_p2p_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + + if (!g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + _LOGW ("failed to acquire wpa_supplicant p2p proxy: (%s)", error->message); + + g_clear_object (&priv->p2p_proxy); + + iface_check_ready (self); + } + return; + } + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + _nm_dbus_signal_connect (priv->p2p_proxy, "DeviceFound", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (p2p_device_found), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "DeviceLost", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (p2p_device_lost), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupStarted", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (p2p_group_started), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupFormationFailure", G_VARIANT_TYPE ("(s)"), + G_CALLBACK (p2p_group_formation_failure), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupFinished", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (p2p_group_finished), self); + /* TODO: + * * WpsFailed + * * FindStopped + * * GONegotationFailure + * * InvitationReceived + */ + + priv->p2p_proxy_acquired = TRUE; + _notify (self, PROP_P2P_AVAILABLE); + + iface_check_ready (self); +} + +static void interface_add_done (NMSupplicantInterface *self, const char *path) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); _LOGD ("interface added to supplicant"); + /* Iface ready check happens in iface_check_netreply_cb */ + priv->ready_count = 1; + priv->object_path = g_strdup (path); + _notify (self, PROP_OBJECT_PATH); priv->iface_proxy = g_object_new (G_TYPE_DBUS_PROXY, "g-bus-type", G_BUS_TYPE_SYSTEM, "g-flags", G_DBUS_PROXY_FLAGS_NONE, @@ -1208,6 +1721,23 @@ interface_add_done (NMSupplicantInterface *self, const char *path) priv->init_cancellable, (GAsyncReadyCallback) on_iface_proxy_acquired, self); + + if (priv->p2p_support == NM_SUPPLICANT_FEATURE_YES) { + priv->ready_count++; + priv->p2p_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", priv->object_path, + "g-interface-name", WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, + NULL); + g_signal_connect (priv->p2p_proxy, "g-properties-changed", G_CALLBACK (p2p_props_changed_cb), self); + g_async_initable_init_async (G_ASYNC_INITABLE (priv->p2p_proxy), + G_PRIORITY_DEFAULT, + priv->init_cancellable, + (GAsyncReadyCallback) on_p2p_proxy_acquired, + self); + } } static void @@ -1289,6 +1819,39 @@ interface_add_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) } } +static void +interface_removed_cb (GDBusProxy *proxy, + const char *path, + gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (g_strcmp0 (priv->object_path, path) != 0) + return; + + _LOGD ("Received interface removed signal"); + + /* The interface may lose its last reference during signal handling otherwise. */ + g_object_ref (self); + + /* Invalidate the object path to prevent the manager from trying to remove + * a non-existing interface. */ + g_clear_pointer (&priv->object_path, g_free); + _notify (self, PROP_OBJECT_PATH); + + /* No need to clean up everything now, that will happen at dispose time. */ + + /* Interface is down and has been removed. */ + set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN); + g_signal_emit (self, signals[REMOVED], 0); + + g_object_unref (self); +} + #if HAVE_WEXT #define DEFAULT_WIFI_DRIVER "nl80211,wext" #else @@ -1303,7 +1866,6 @@ on_wpas_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_d gs_free_error GError *error = NULL; GDBusProxy *wpas_proxy; GVariantBuilder props; - const char *driver_name = NULL; wpas_proxy = g_dbus_proxy_new_for_bus_finish (result, &error); if (!wpas_proxy) { @@ -1320,41 +1882,53 @@ on_wpas_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_d priv->wpas_proxy = wpas_proxy; + /* Watch for interface removal. */ + _nm_dbus_signal_connect (priv->wpas_proxy, "InterfaceRemoved", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (interface_removed_cb), self); + /* Try to add the interface to the supplicant. If the supplicant isn't * running, this will start it via D-Bus activation and return the response * when the supplicant has started. */ - switch (priv->driver) { - case NM_SUPPLICANT_DRIVER_WIRELESS: - driver_name = DEFAULT_WIFI_DRIVER; - break; - case NM_SUPPLICANT_DRIVER_WIRED: - driver_name = "wired"; - break; - case NM_SUPPLICANT_DRIVER_MACSEC: - driver_name = "macsec_linux"; - break; - } + if (priv->dev != NULL) { + const char *driver_name = NULL; + + switch (priv->driver) { + case NM_SUPPLICANT_DRIVER_WIRELESS: + driver_name = DEFAULT_WIFI_DRIVER; + break; + case NM_SUPPLICANT_DRIVER_WIRED: + driver_name = "wired"; + break; + case NM_SUPPLICANT_DRIVER_MACSEC: + driver_name = "macsec_linux"; + break; + } - g_return_if_fail (driver_name); + g_return_if_fail (driver_name); - g_variant_builder_init (&props, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&props, "{sv}", - "Driver", - g_variant_new_string (driver_name)); - g_variant_builder_add (&props, "{sv}", - "Ifname", - g_variant_new_string (priv->dev)); + g_variant_builder_init (&props, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&props, "{sv}", + "Driver", + g_variant_new_string (driver_name)); + g_variant_builder_add (&props, "{sv}", + "Ifname", + g_variant_new_string (priv->dev)); - g_dbus_proxy_call (priv->wpas_proxy, - "CreateInterface", - g_variant_new ("(a{sv})", &props), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->init_cancellable, - (GAsyncReadyCallback) interface_add_cb, - self); + g_dbus_proxy_call (priv->wpas_proxy, + "CreateInterface", + g_variant_new ("(a{sv})", &props), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + (GAsyncReadyCallback) interface_add_cb, + self); + } else if (priv->object_path) { + interface_add_done (self, priv->object_path); + } else { + g_assert_not_reached (); + } } static void @@ -1374,8 +1948,7 @@ interface_add (NMSupplicantInterface *self) priv->init_cancellable = g_cancellable_new (); g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, WPAS_DBUS_SERVICE, WPAS_DBUS_PATH, @@ -1863,6 +2436,142 @@ nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self) /*****************************************************************************/ +void +nm_supplicant_interface_p2p_start_find (NMSupplicantInterface *self, + guint timeout) +{ + NMSupplicantInterfacePrivate *priv; + GVariantBuilder builder; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + g_return_if_fail (timeout > 0 && timeout <= 600); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Find parameters */ + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&builder, "{sv}", "Timeout", g_variant_new_int32 (timeout)); + + g_dbus_proxy_call (priv->p2p_proxy, + "Find", + g_variant_new ("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + self); +} + +void +nm_supplicant_interface_p2p_stop_find (NMSupplicantInterface *self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + g_dbus_proxy_call (priv->p2p_proxy, + "StopFind", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) scan_request_cb, + self); +} + +/*****************************************************************************/ + +void +nm_supplicant_interface_p2p_connect (NMSupplicantInterface * self, + const char * peer, + const char * wps_method, + const char * wps_pin) +{ + NMSupplicantInterfacePrivate *priv; + GVariantBuilder builder; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Don't do anything if there is no connection to the supplicant yet. */ + if (!priv->p2p_proxy || !priv->object_path) + return; + + /* Connect parameters */ + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + + g_variant_builder_add (&builder, "{sv}", "wps_method", g_variant_new_string (wps_method)); + + if (wps_pin) + g_variant_builder_add (&builder, "{sv}", "pin", g_variant_new_string (wps_pin)); + + g_variant_builder_add (&builder, "{sv}", "peer", g_variant_new_object_path (peer)); + + g_variant_builder_add (&builder, "{sv}", "join", g_variant_new_boolean (FALSE)); + g_variant_builder_add (&builder, "{sv}", "persistent", g_variant_new_boolean (FALSE)); + g_variant_builder_add (&builder, "{sv}", "go_intent", g_variant_new_int32 (7)); + + g_dbus_proxy_call (priv->p2p_proxy, + "Connect", + g_variant_new ("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + "p2p connect"); +} + +void +nm_supplicant_interface_p2p_cancel_connect (NMSupplicantInterface * self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Don't do anything if there is no connection to the supplicant yet. */ + if (!priv->p2p_proxy || !priv->object_path) + return; + + g_dbus_proxy_call (priv->p2p_proxy, + "Cancel", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + "cancel p2p connect"); +} + +void +nm_supplicant_interface_p2p_disconnect (NMSupplicantInterface * self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Don't do anything if there is no connection to the supplicant. */ + if (!priv->p2p_proxy || !priv->object_path) + return; + + g_dbus_proxy_call (priv->p2p_proxy, + "Disconnect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + "p2p disconnect"); +} + +/*****************************************************************************/ + static void get_property (GObject *object, guint prop_id, @@ -1878,6 +2587,18 @@ get_property (GObject *object, case PROP_CURRENT_BSS: g_value_set_string (value, priv->current_bss); break; + case PROP_P2P_GROUP_JOINED: + g_value_set_boolean (value, priv->p2p_capable && priv->group_proxy_acquired); + break; + case PROP_P2P_GROUP_PATH: + g_value_set_string (value, nm_supplicant_interface_get_p2p_group_path (NM_SUPPLICANT_INTERFACE (object))); + break; + case PROP_P2P_GROUP_OWNER: + g_value_set_boolean (value, priv->p2p_group_owner); + break; + case PROP_P2P_AVAILABLE: + g_value_set_boolean (value, priv->p2p_capable && priv->p2p_proxy_acquired); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1896,7 +2617,10 @@ set_property (GObject *object, case PROP_IFACE: /* construct-only */ priv->dev = g_value_dup_string (value); - g_return_if_fail (priv->dev); + break; + case PROP_OBJECT_PATH: + /* construct-only */ + priv->object_path = g_value_dup_string (value); break; case PROP_DRIVER: /* construct-only */ @@ -1918,6 +2642,14 @@ set_property (GObject *object, /* construct-only */ priv->fils_support = g_value_get_int (value); break; + case PROP_P2P_SUPPORT: + /* construct-only */ + priv->p2p_support = g_value_get_int (value); + break; + case PROP_WFD_SUPPORT: + /* construct-only */ + priv->wfd_support = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1931,25 +2663,34 @@ nm_supplicant_interface_init (NMSupplicantInterface * self) priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT; priv->bss_proxies = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, bss_data_destroy); + priv->peer_proxies = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, peer_data_destroy); } NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, + const char *object_path, NMSupplicantDriver driver, NMSupplicantFeature fast_support, NMSupplicantFeature ap_support, NMSupplicantFeature pmf_support, - NMSupplicantFeature fils_support) + NMSupplicantFeature fils_support, + NMSupplicantFeature p2p_support, + NMSupplicantFeature wfd_support) { - g_return_val_if_fail (ifname != NULL, NULL); + /* One of ifname or path need to be set */ + g_return_val_if_fail (ifname != NULL || object_path != NULL, NULL); + g_return_val_if_fail (ifname == NULL || object_path == NULL, NULL); return g_object_new (NM_TYPE_SUPPLICANT_INTERFACE, NM_SUPPLICANT_INTERFACE_IFACE, ifname, + NM_SUPPLICANT_INTERFACE_OBJECT_PATH, object_path, NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver, NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support, NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support, NM_SUPPLICANT_INTERFACE_PMF_SUPPORT, (int) pmf_support, NM_SUPPLICANT_INTERFACE_FILS_SUPPORT, (int) fils_support, + NM_SUPPLICANT_INTERFACE_P2P_SUPPORT, (int) p2p_support, + NM_SUPPLICANT_INTERFACE_WFD_SUPPORT, (int) wfd_support, NULL); } @@ -1979,12 +2720,21 @@ dispose (GObject *object) if (priv->iface_proxy) g_signal_handlers_disconnect_by_data (priv->iface_proxy, object); g_clear_object (&priv->iface_proxy); + if (priv->p2p_proxy) + g_signal_handlers_disconnect_by_data (priv->p2p_proxy, object); + g_clear_object (&priv->p2p_proxy); + if (priv->group_proxy) + g_signal_handlers_disconnect_by_data (priv->group_proxy, object); + g_clear_object (&priv->group_proxy); nm_clear_g_cancellable (&priv->init_cancellable); nm_clear_g_cancellable (&priv->other_cancellable); + if (priv->wpas_proxy) + g_signal_handlers_disconnect_by_data (priv->wpas_proxy, object); g_clear_object (&priv->wpas_proxy); g_clear_pointer (&priv->bss_proxies, g_hash_table_destroy); + g_clear_pointer (&priv->peer_proxies, g_hash_table_destroy); g_clear_pointer (&priv->net_path, g_free); g_clear_pointer (&priv->dev, g_free); @@ -2019,12 +2769,38 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_OBJECT_PATH] = + g_param_spec_string (NM_SUPPLICANT_INTERFACE_OBJECT_PATH, "", "", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_JOINED] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_PATH] = + g_param_spec_string (NM_SUPPLICANT_INTERFACE_P2P_GROUP_PATH, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_OWNER] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_GROUP_OWNER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_DRIVER] = g_param_spec_uint (NM_SUPPLICANT_INTERFACE_DRIVER, "", "", 0, G_MAXUINT, NM_SUPPLICANT_DRIVER_WIRELESS, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_AVAILABLE] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_FAST_SUPPORT] = g_param_spec_int (NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, "", "", NM_SUPPLICANT_FEATURE_UNKNOWN, @@ -2057,6 +2833,22 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_P2P_SUPPORT, "", "", + NM_SUPPLICANT_FEATURE_UNKNOWN, + NM_SUPPLICANT_FEATURE_YES, + NM_SUPPLICANT_FEATURE_UNKNOWN, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_WFD_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_WFD_SUPPORT, "", "", + NM_SUPPLICANT_FEATURE_UNKNOWN, + NM_SUPPLICANT_FEATURE_YES, + NM_SUPPLICANT_FEATURE_UNKNOWN, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); @@ -2092,6 +2884,22 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); + signals[PEER_UPDATED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_PEER_UPDATED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT); + + signals[PEER_REMOVED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_PEER_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); + signals[SCAN_DONE] = g_signal_new (NM_SUPPLICANT_INTERFACE_SCAN_DONE, G_OBJECT_CLASS_TYPE (object_class), @@ -2115,4 +2923,28 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_VARIANT); + + signals[GROUP_STARTED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_STARTED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, NM_TYPE_SUPPLICANT_INTERFACE); + + signals[GROUP_FINISHED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_FINISHED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); + + signals[GROUP_FORMATION_FAILURE] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_FORMATION_FAILURE, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_VARIANT); } diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h index 0365fdcd63..0aa7732ac4 100644 --- a/src/supplicant/nm-supplicant-interface.h +++ b/src/supplicant/nm-supplicant-interface.h @@ -55,33 +55,48 @@ typedef enum { /* Properties */ #define NM_SUPPLICANT_INTERFACE_IFACE "iface" +#define NM_SUPPLICANT_INTERFACE_OBJECT_PATH "object-path" #define NM_SUPPLICANT_INTERFACE_SCANNING "scanning" #define NM_SUPPLICANT_INTERFACE_CURRENT_BSS "current-bss" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED "p2p-group-joined" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_PATH "p2p-group-path" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_OWNER "p2p-group-owner" #define NM_SUPPLICANT_INTERFACE_DRIVER "driver" +#define NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE "p2p-available" #define NM_SUPPLICANT_INTERFACE_FAST_SUPPORT "fast-support" #define NM_SUPPLICANT_INTERFACE_AP_SUPPORT "ap-support" #define NM_SUPPLICANT_INTERFACE_PMF_SUPPORT "pmf-support" #define NM_SUPPLICANT_INTERFACE_FILS_SUPPORT "fils-support" +#define NM_SUPPLICANT_INTERFACE_P2P_SUPPORT "p2p-support" +#define NM_SUPPLICANT_INTERFACE_WFD_SUPPORT "wfd-support" /* Signals */ #define NM_SUPPLICANT_INTERFACE_STATE "state" #define NM_SUPPLICANT_INTERFACE_REMOVED "removed" #define NM_SUPPLICANT_INTERFACE_BSS_UPDATED "bss-updated" #define NM_SUPPLICANT_INTERFACE_BSS_REMOVED "bss-removed" +#define NM_SUPPLICANT_INTERFACE_PEER_UPDATED "peer-updated" +#define NM_SUPPLICANT_INTERFACE_PEER_REMOVED "peer-removed" #define NM_SUPPLICANT_INTERFACE_SCAN_DONE "scan-done" #define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request" #define NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS "wps-credentials" +#define NM_SUPPLICANT_INTERFACE_GROUP_STARTED "group-started" +#define NM_SUPPLICANT_INTERFACE_GROUP_FINISHED "group-finished" +#define NM_SUPPLICANT_INTERFACE_GROUP_FORMATION_FAILURE "group-formation-failure" typedef struct _NMSupplicantInterfaceClass NMSupplicantInterfaceClass; GType nm_supplicant_interface_get_type (void); NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, + const char *object_path, NMSupplicantDriver driver, NMSupplicantFeature fast_support, NMSupplicantFeature ap_support, NMSupplicantFeature pmf_support, - NMSupplicantFeature fils_support); + NMSupplicantFeature fils_support, + NMSupplicantFeature p2p_support, + NMSupplicantFeature wfd_support); void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self, gboolean available); @@ -120,14 +135,33 @@ guint nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self); gboolean nm_supplicant_interface_get_has_credentials_request (NMSupplicantInterface *self); +gboolean nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self); + +const char* nm_supplicant_interface_get_p2p_group_path (NMSupplicantInterface *self); + +gboolean nm_supplicant_interface_get_p2p_group_owner (NMSupplicantInterface *self); + gboolean nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self, const char *field, const char *value, GError **error); +void nm_supplicant_interface_p2p_start_find (NMSupplicantInterface *self, + guint timeout); +void nm_supplicant_interface_p2p_stop_find (NMSupplicantInterface *self); + +void nm_supplicant_interface_p2p_connect (NMSupplicantInterface * self, + const char * peer, + const char * wps_method, + const char * wps_pin); +void nm_supplicant_interface_p2p_cancel_connect (NMSupplicantInterface * self); +void nm_supplicant_interface_p2p_disconnect (NMSupplicantInterface * self); + NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self); NMSupplicantFeature nm_supplicant_interface_get_pmf_support (NMSupplicantInterface *self); NMSupplicantFeature nm_supplicant_interface_get_fils_support (NMSupplicantInterface *self); +NMSupplicantFeature nm_supplicant_interface_get_p2p_support (NMSupplicantInterface *self); +NMSupplicantFeature nm_supplicant_interface_get_wfd_support (NMSupplicantInterface *self); void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature apmode); @@ -141,6 +175,12 @@ void nm_supplicant_interface_set_pmf_support (NMSupplicantInterface *self, void nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self, NMSupplicantFeature fils_support); +void nm_supplicant_interface_set_p2p_support (NMSupplicantInterface *self, + NMSupplicantFeature p2p_support); + +void nm_supplicant_interface_set_wfd_support (NMSupplicantInterface *self, + NMSupplicantFeature wfd_support); + void nm_supplicant_interface_enroll_wps (NMSupplicantInterface *self, const char *const type, const char *bssid, diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c index 5ab96f88f1..64a057f384 100644 --- a/src/supplicant/nm-supplicant-manager.c +++ b/src/supplicant/nm-supplicant-manager.c @@ -41,6 +41,8 @@ typedef struct { NMSupplicantFeature ap_support; NMSupplicantFeature pmf_support; NMSupplicantFeature fils_support; + NMSupplicantFeature p2p_support; + NMSupplicantFeature wfd_support; guint die_count_reset_id; guint die_count; } NMSupplicantManagerPrivate; @@ -159,11 +161,71 @@ nm_supplicant_manager_create_interface (NMSupplicantManager *self, } iface = nm_supplicant_interface_new (ifname, + NULL, driver, priv->fast_support, priv->ap_support, priv->pmf_support, - priv->fils_support); + priv->fils_support, + priv->p2p_support, + priv->wfd_support); + + priv->ifaces = g_slist_prepend (priv->ifaces, iface); + g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self); + + /* If we're making the supplicant take a time out for a bit, don't + * let the supplicant interface start immediately, just let it hang + * around in INIT state until we're ready to talk to the supplicant + * again. + */ + if (is_available (self)) + nm_supplicant_interface_set_supplicant_available (iface, TRUE); + + return iface; +} + +/** + * nm_supplicant_manager_create_interface_from_path: + * @self: the #NMSupplicantManager + * @object_path: the DBus object path for which to obtain the supplicant interface + * + * Note: the manager owns a reference to the instance and the only way to + * get the manager to release it, is by dropping all other references + * to the supplicant-interface (or destroying the manager). + * + * Returns: (transfer full): returns a #NMSupplicantInterface or %NULL. + * Must be unrefed at the end. + * */ +NMSupplicantInterface * +nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self, + const char *object_path) +{ + NMSupplicantManagerPrivate *priv; + NMSupplicantInterface *iface; + GSList *ifaces; + + g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL); + g_return_val_if_fail (object_path != NULL, NULL); + + priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + _LOGD ("creating new supplicant interface for dbus path %s", object_path); + + /* assert against not requesting duplicate interfaces. */ + for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) { + if (g_strcmp0 (nm_supplicant_interface_get_object_path (ifaces->data), object_path) == 0) + g_return_val_if_reached (NULL); + } + + iface = nm_supplicant_interface_new (NULL, + object_path, + NM_SUPPLICANT_DRIVER_WIRELESS, + priv->fast_support, + priv->ap_support, + priv->pmf_support, + priv->fils_support, + priv->p2p_support, + priv->wfd_support); priv->ifaces = g_slist_prepend (priv->ifaces, iface); g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self); @@ -199,6 +261,8 @@ update_capabilities (NMSupplicantManager *self) priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->fils_support = NM_SUPPLICANT_FEATURE_UNKNOWN; + /* P2P support is newer than the capabilities property */ + priv->p2p_support = NM_SUPPLICANT_FEATURE_NO; value = g_dbus_proxy_get_cached_property (priv->proxy, "Capabilities"); if (value) { @@ -207,6 +271,7 @@ update_capabilities (NMSupplicantManager *self) priv->ap_support = NM_SUPPLICANT_FEATURE_NO; priv->pmf_support = NM_SUPPLICANT_FEATURE_NO; priv->fils_support = NM_SUPPLICANT_FEATURE_NO; + priv->p2p_support = NM_SUPPLICANT_FEATURE_NO; if (array) { if (g_strv_contains (array, "ap")) priv->ap_support = NM_SUPPLICANT_FEATURE_YES; @@ -214,17 +279,20 @@ update_capabilities (NMSupplicantManager *self) priv->pmf_support = NM_SUPPLICANT_FEATURE_YES; if (g_strv_contains (array, "fils")) priv->fils_support = NM_SUPPLICANT_FEATURE_YES; + if (g_strv_contains (array, "p2p")) + priv->p2p_support = NM_SUPPLICANT_FEATURE_YES; g_free (array); } } g_variant_unref (value); } - /* Tell all interfaces about results of the AP/PMF/FILS check */ + /* Tell all interfaces about results of the AP/PMF/FILS/P2P check */ for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) { nm_supplicant_interface_set_ap_support (ifaces->data, priv->ap_support); nm_supplicant_interface_set_pmf_support (ifaces->data, priv->pmf_support); nm_supplicant_interface_set_fils_support (ifaces->data, priv->fils_support); + nm_supplicant_interface_set_p2p_support (ifaces->data, priv->p2p_support); } _LOGD ("AP mode is %ssupported", @@ -236,6 +304,9 @@ update_capabilities (NMSupplicantManager *self) _LOGD ("FILS is %ssupported", (priv->fils_support == NM_SUPPLICANT_FEATURE_YES) ? "" : (priv->fils_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + _LOGD ("P2P is %ssupported", + (priv->p2p_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->p2p_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); /* EAP-FAST */ priv->fast_support = NM_SUPPLICANT_FEATURE_NO; @@ -264,6 +335,20 @@ update_capabilities (NMSupplicantManager *self) _LOGD ("EAP-FAST is %ssupported", (priv->fast_support == NM_SUPPLICANT_FEATURE_YES) ? "" : (priv->fast_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + + priv->wfd_support = NM_SUPPLICANT_FEATURE_NO; + value = g_dbus_proxy_get_cached_property (priv->proxy, "WFDIEs"); + if (value) { + priv->wfd_support = NM_SUPPLICANT_FEATURE_YES; + g_variant_unref (value); + } + + for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) + nm_supplicant_interface_set_wfd_support (ifaces->data, priv->fast_support); + + _LOGD ("WFD is %ssupported", + (priv->wfd_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->wfd_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); } static void diff --git a/src/supplicant/nm-supplicant-manager.h b/src/supplicant/nm-supplicant-manager.h index 8928cf206b..7225a36b58 100644 --- a/src/supplicant/nm-supplicant-manager.h +++ b/src/supplicant/nm-supplicant-manager.h @@ -41,5 +41,7 @@ NMSupplicantManager *nm_supplicant_manager_get (void); NMSupplicantInterface *nm_supplicant_manager_create_interface (NMSupplicantManager *mgr, const char *ifname, NMSupplicantDriver driver); +NMSupplicantInterface *nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self, + const char *object_path); #endif /* __NETWORKMANAGER_SUPPLICANT_MANAGER_H__ */ |