summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2017-05-23 18:14:19 +0200
committerLubomir Rintel <lkundrak@v3.sk>2017-06-01 11:28:57 +0200
commitd6f2a2e73c1a98da6f1959a2360810ac258260ad (patch)
tree1f8eed1a1c30c6185004348253dc9c1f12240eb8
parent53482c38e20c213cf48d58ce2b9e2a27f46848d6 (diff)
downloadNetworkManager-d6f2a2e73c1a98da6f1959a2360810ac258260ad.tar.gz
bluetooth: expose known HCI adapters to devices
They may register the Bluetooth NAP enabled bridges for Bluez to enslave their BNEP links to.
-rw-r--r--src/devices/bluetooth/nm-bluez-common.h2
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c213
2 files changed, 213 insertions, 2 deletions
diff --git a/src/devices/bluetooth/nm-bluez-common.h b/src/devices/bluetooth/nm-bluez-common.h
index 19344d36c4..d72bea8131 100644
--- a/src/devices/bluetooth/nm-bluez-common.h
+++ b/src/devices/bluetooth/nm-bluez-common.h
@@ -32,6 +32,7 @@
#define NM_BLUEZ5_ADAPTER_INTERFACE "org.bluez.Adapter1"
#define NM_BLUEZ5_DEVICE_INTERFACE "org.bluez.Device1"
#define NM_BLUEZ5_NETWORK_INTERFACE "org.bluez.Network1"
+#define NM_BLUEZ5_NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1"
#define NM_BLUEZ4_MANAGER_INTERFACE "org.bluez.Manager"
#define NM_BLUEZ4_ADAPTER_INTERFACE "org.bluez.Adapter"
@@ -40,5 +41,6 @@
#define NM_BLUEZ4_NETWORK_INTERFACE "org.bluez.Network"
#define NM_BLUEZ_MANAGER_BDADDR_ADDED "bdaddr-added"
+#define NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED "network-server-added"
#endif /* NM_BLUEZ_COMMON_H */
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
index 1d3abb2905..2dfc521bc6 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2013 Red Hat, Inc.
+ * Copyright (C) 2007 - 2017 Red Hat, Inc.
* Copyright (C) 2013 Intel Corporation.
*/
@@ -30,14 +30,17 @@
#include "nm-core-internal.h"
+#include "nm-utils/c-list.h"
#include "nm-bluez-device.h"
#include "nm-bluez-common.h"
+#include "devices/nm-device.h"
#include "settings/nm-settings.h"
/*****************************************************************************/
enum {
BDADDR_ADDED,
+ NETWORK_SERVER_ADDED,
LAST_SIGNAL,
};
@@ -49,10 +52,13 @@ typedef struct {
GDBusProxy *proxy;
GHashTable *devices;
+
+ CList network_servers;
} NMBluez5ManagerPrivate;
struct _NMBluez5Manager {
GObject parent;
+ NMBtVTableNetworkServer network_server_vtable;
NMBluez5ManagerPrivate _priv;
};
@@ -64,6 +70,10 @@ G_DEFINE_TYPE (NMBluez5Manager, nm_bluez5_manager, G_TYPE_OBJECT)
#define NM_BLUEZ5_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluez5Manager, NM_IS_BLUEZ5_MANAGER)
+#define NM_BLUEZ5_MANAGER_GET_NETWORK_SERVER_VTABLE(self) (&(self)->network_server_vtable)
+#define NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER(vtable) \
+ NM_BLUEZ5_MANAGER(((char *)(vtable)) - offsetof (struct _NMBluez5Manager, network_server_vtable))
+
/*****************************************************************************/
#define _NMLOG_DOMAIN LOGD_BT
@@ -76,6 +86,174 @@ static void device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Man
/*****************************************************************************/
+typedef struct {
+ char *path;
+ char *addr;
+ char *uuid;
+ NMDevice *device;
+ CList network_servers;
+} NetworkServer;
+
+static NetworkServer*
+_find_network_server (NMBluez5Manager *self,
+ const gchar *path, const gchar *addr, NMDevice *device)
+{
+ NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+ NetworkServer *network_server;
+ CList *iter;
+
+ c_list_for_each (iter, &priv->network_servers) {
+ network_server = c_list_entry (iter, NetworkServer, network_servers);
+
+ /* Device and path matches are exact. */
+ if ( (path && !strcmp (network_server->path, path))
+ || (device && network_server->device == device))
+ return network_server;
+
+ /* The address lookups need a server not assigned to a device
+ * and tolerate an empty address as a wildcard for "any". */
+ if ( (!path && !device)
+ && !network_server->device
+ && (!addr || !strcmp (network_server->addr, addr)))
+ return network_server;
+ }
+
+ return NULL;
+}
+
+static void
+_network_server_unregister (NMBluez5Manager *self, NetworkServer *network_server)
+{
+ NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+
+ if (!network_server->uuid) {
+ /* Not connected. */
+ return;
+ }
+
+ _LOGI ("NAP: unregistering %s from %s",
+ nm_device_get_iface (network_server->device),
+ network_server->addr);
+
+ g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy),
+ NM_BLUEZ_SERVICE,
+ network_server->path,
+ NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
+ "Unregister",
+ g_variant_new ("(s)", network_server->uuid),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+
+ g_clear_pointer (&network_server->uuid, g_free);
+ g_clear_object (&network_server->device);
+}
+
+static void
+_network_server_free (NMBluez5Manager *self, NetworkServer *network_server)
+{
+ _network_server_unregister (self, network_server);
+ c_list_unlink (&network_server->network_servers);
+ g_free (network_server->path);
+ g_free (network_server->addr);
+ g_slice_free (NetworkServer, network_server);
+}
+
+static gboolean
+network_server_is_available (const NMBtVTableNetworkServer *vtable,
+ const char *addr)
+{
+ NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
+
+ return !!_find_network_server (self, NULL, addr, NULL);
+}
+
+static gboolean
+network_server_register_bridge (const NMBtVTableNetworkServer *vtable,
+ const char *addr, const char *uuid, NMDevice *device)
+{
+ NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
+ NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+ NetworkServer *network_server = _find_network_server (self, NULL, addr, NULL);
+
+ if (!network_server) {
+ /* The device checked that a network server is available, before
+ * starting the activation, but for some reason it no longer is.
+ * Indicate that the activation should not proceed. */
+ _LOGI ("NAP: %s is not available for %s", addr, nm_device_get_iface (device));
+ return FALSE;
+ }
+
+ _LOGI ("NAP: registering %s on %s", nm_device_get_iface (device), network_server->addr);
+
+ g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy),
+ NM_BLUEZ_SERVICE,
+ network_server->path,
+ NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
+ "Register",
+ g_variant_new ("(ss)", uuid, nm_device_get_iface (device)),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+
+ network_server->device = g_object_ref (device);
+ network_server->uuid = g_strdup (uuid);
+
+ return TRUE;
+}
+
+static gboolean
+network_server_unregister_bridge (const NMBtVTableNetworkServer *vtable,
+ NMDevice *device)
+{
+ NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
+ NetworkServer *network_server = _find_network_server (self, NULL, NULL, device);
+
+ if (network_server)
+ _network_server_unregister (self, network_server);
+
+ return TRUE;
+}
+
+static void
+network_server_removed (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
+{
+ NetworkServer *network_server;
+
+ network_server = _find_network_server (self, path, NULL, NULL);
+ if (!network_server)
+ return;
+
+ if (network_server->device) {
+ nm_device_queue_state (network_server->device, NM_DEVICE_STATE_DISCONNECTED,
+ NM_DEVICE_STATE_REASON_BT_FAILED);
+ }
+ _LOGI ("NAP: removed interface %s", network_server->addr);
+ _network_server_free (self, network_server);
+}
+
+static void
+network_server_added (GDBusProxy *proxy, const gchar *path, const char *addr, NMBluez5Manager *self)
+{
+ NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+ NetworkServer *network_server;
+
+ /* If BlueZ messes up and announces a single network server twice,
+ * make sure we get rid of the older instance first. */
+ network_server_removed (proxy, path, self);
+
+ network_server = g_slice_new0 (NetworkServer);
+ network_server->path = g_strdup (path);
+ network_server->addr = g_strdup (addr);
+ c_list_link_before (&priv->network_servers, &network_server->network_servers);
+
+ _LOGI ("NAP: added interface %s", addr);
+
+ g_signal_emit (self, signals[NETWORK_SERVER_ADDED], 0);
+}
+
+/*****************************************************************************/
+
static void
emit_bdaddr_added (NMBluez5Manager *self, NMBluezDevice *device)
{
@@ -193,6 +371,14 @@ object_manager_interfaces_added (GDBusProxy *proxy,
{
if (g_variant_lookup (dict, NM_BLUEZ5_DEVICE_INTERFACE, "a{sv}", NULL))
device_added (proxy, path, self);
+ if (g_variant_lookup (dict, NM_BLUEZ5_NETWORK_SERVER_INTERFACE, "a{sv}", NULL)) {
+ GVariant *adapter = g_variant_lookup_value (dict, NM_BLUEZ5_ADAPTER_INTERFACE, G_VARIANT_TYPE_DICTIONARY);
+ const char *address;
+
+ g_variant_lookup (adapter, "Address", "&s", &address);
+ network_server_added (proxy, path, address, self);
+ g_variant_unref (adapter);
+ }
}
static void
@@ -203,6 +389,8 @@ object_manager_interfaces_removed (GDBusProxy *proxy,
{
if (ifaces && g_strv_contains (ifaces, NM_BLUEZ5_DEVICE_INTERFACE))
device_removed (proxy, path, self);
+ if (ifaces && g_strv_contains (ifaces, NM_BLUEZ5_NETWORK_SERVER_INTERFACE))
+ network_server_removed (proxy, path, self);
}
static void
@@ -314,11 +502,20 @@ static void
nm_bluez5_manager_init (NMBluez5Manager *self)
{
NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+ NMBtVTableNetworkServer *network_server_vtable = NM_BLUEZ5_MANAGER_GET_NETWORK_SERVER_VTABLE (self);
bluez_connect (self);
priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, g_object_unref);
+
+ c_list_init (&priv->network_servers);
+
+ nm_assert (!nm_bt_vtable_network_server);
+ network_server_vtable->is_available = network_server_is_available;
+ network_server_vtable->register_bridge = network_server_register_bridge;
+ network_server_vtable->unregister_bridge = network_server_unregister_bridge;
+ nm_bt_vtable_network_server = network_server_vtable;
}
NMBluez5Manager *
@@ -338,6 +535,10 @@ dispose (GObject *object)
{
NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+ CList *iter, *safe;
+
+ c_list_for_each_safe (iter, safe, &priv->network_servers)
+ _network_server_free (self, c_list_entry (iter, NetworkServer, network_servers));
if (priv->proxy) {
g_signal_handlers_disconnect_by_func (priv->proxy, G_CALLBACK (name_owner_changed_cb), self);
@@ -352,7 +553,8 @@ dispose (GObject *object)
static void
finalize (GObject *object)
{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE ((NMBluez5Manager *) object);
+ NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
+ NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
g_hash_table_destroy (priv->devices);
@@ -376,4 +578,11 @@ nm_bluez5_manager_class_init (NMBluez5ManagerClass *klass)
0, NULL, NULL, NULL,
G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
+
+ signals[NETWORK_SERVER_ADDED] =
+ g_signal_new (NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
}