wifi/iwd: merge branch 'balrog-kun:iwd-p2p'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1017
This commit is contained in:
Thomas Haller 2022-01-21 11:16:31 +01:00
commit 2395bedd63
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
12 changed files with 1894 additions and 33 deletions

View file

@ -3935,6 +3935,8 @@ if WITH_IWD
src_core_devices_wifi_libnm_wifi_base_la_SOURCES += \
src/core/devices/wifi/nm-device-iwd.c \
src/core/devices/wifi/nm-device-iwd.h \
src/core/devices/wifi/nm-device-iwd-p2p.c \
src/core/devices/wifi/nm-device-iwd-p2p.h \
src/core/devices/wifi/nm-iwd-manager.c \
src/core/devices/wifi/nm-iwd-manager.h \
$(NULL)

View file

@ -4,6 +4,7 @@ iwd_sources = files()
if enable_iwd
iwd_sources += files(
'nm-device-iwd.c',
'nm-device-iwd-p2p.c',
'nm-iwd-manager.c',
)
endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2021 Intel Corporation
*/
#ifndef __NM_DEVICE_IWD_P2P_H__
#define __NM_DEVICE_IWD_P2P_H__
#include "devices/nm-device.h"
#include "nm-device-wifi-p2p.h"
#define NM_TYPE_DEVICE_IWD_P2P (nm_device_iwd_p2p_get_type())
#define NM_DEVICE_IWD_P2P(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_IWD_P2P, NMDeviceIwdP2P))
#define NM_DEVICE_IWD_P2P_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_IWD_P2P, NMDeviceIwdP2PClass))
#define NM_IS_DEVICE_IWD_P2P(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_IWD_P2P))
#define NM_IS_DEVICE_IWD_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE_IWD_P2P))
#define NM_DEVICE_IWD_P2P_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_IWD_P2P, NMDeviceIwdP2PClass))
#define NM_DEVICE_IWD_P2P_PEERS NM_DEVICE_WIFI_P2P_PEERS
#define NM_DEVICE_IWD_P2P_GROUPS NM_DEVICE_WIFI_P2P_GROUPS
typedef struct _NMDeviceIwdP2P NMDeviceIwdP2P;
typedef struct _NMDeviceIwdP2PClass NMDeviceIwdP2PClass;
GType nm_device_iwd_p2p_get_type(void);
NMDeviceIwdP2P *nm_device_iwd_p2p_new(GDBusObject *object);
void nm_device_iwd_p2p_remove(NMDeviceIwdP2P *p2p);
void nm_device_iwd_p2p_peer_add_remove(NMDeviceIwdP2P *p2p, GDBusObject *peer_obj, bool add);
#endif /* __NM_DEVICE_IWD_P2P_H__ */

View file

@ -168,7 +168,7 @@ check_connection_peer_joined(NMDeviceWifiP2P *device)
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);
peer = nm_wifi_p2p_peers_find_first_compatible(&priv->peers_lst_head, conn, FALSE);
if (!peer)
return FALSE;
@ -369,7 +369,7 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
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);
peer = nm_wifi_p2p_peers_find_first_compatible(&priv->peers_lst_head, connection, FALSE);
if (!peer) {
/* Set up a timeout on the find attempt and run a find for the same period of time */
if (priv->find_peer_timeout_id == 0) {
@ -436,7 +436,7 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
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);
peer = nm_wifi_p2p_peers_find_first_compatible(&priv->peers_lst_head, connection, FALSE);
if (!peer) {
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_PEER_NOT_FOUND);
return NM_ACT_STAGE_RETURN_FAILURE;
@ -521,7 +521,8 @@ peer_add_remove(NMDeviceWifiP2P *self,
connection = nm_device_get_applied_connection(device);
nm_assert(NM_IS_CONNECTION(connection));
peer = nm_wifi_p2p_peers_find_first_compatible(&priv->peers_lst_head, connection);
peer =
nm_wifi_p2p_peers_find_first_compatible(&priv->peers_lst_head, connection, FALSE);
if (peer) {
/* A peer for the connection was found, cancel the timeout and go to configure state. */
nm_clear_g_source(&priv->find_peer_timeout_id);

View file

@ -15,6 +15,7 @@
#include "libnm-core-intern/nm-core-internal.h"
#include "nm-manager.h"
#include "nm-device-iwd.h"
#include "nm-device-iwd-p2p.h"
#include "nm-wifi-utils.h"
#include "libnm-glib-aux/nm-uuid.h"
#include "libnm-glib-aux/nm-random-utils.h"
@ -25,6 +26,14 @@
/*****************************************************************************/
enum {
P2P_DEVICE_ADDED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
typedef struct {
const char *name;
NMIwdNetworkSecurity security;
@ -49,6 +58,10 @@ typedef struct {
NMDeviceIwd *last_agent_call_device;
char *last_state_dir;
char *warned_state_dir;
bool netconfig_enabled;
GHashTable *p2p_devices;
NMIwdWfdInfo wfd_info;
guint wfd_use_count;
} NMIwdManagerPrivate;
struct _NMIwdManager {
@ -419,6 +432,66 @@ set_device_dbus_object(NMIwdManager *self, GDBusProxy *proxy, GDBusObject *objec
nm_device_iwd_set_dbus_object(NM_DEVICE_IWD(device), object);
}
static void
add_p2p_device(NMIwdManager *self, GDBusProxy *proxy, GDBusObject *object)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
const char *path = g_dbus_object_get_object_path(object);
NMDeviceIwdP2P *p2p;
gs_unref_object GDBusInterface *wiphy = NULL;
const char *phy_name;
if (g_hash_table_contains(priv->p2p_devices, path))
return;
wiphy = g_dbus_object_get_interface(object, NM_IWD_WIPHY_INTERFACE);
if (!wiphy)
return;
phy_name = get_property_string_or_null(G_DBUS_PROXY(wiphy), "Name");
if (!phy_name) {
_LOGE("Name not cached for phy at %s", path);
return;
}
p2p = nm_device_iwd_p2p_new(object);
if (!p2p) {
_LOGE("Can't create NMDeviceIwdP2P for phy at %s", path);
return;
}
g_hash_table_insert(priv->p2p_devices, g_strdup(path), p2p);
g_signal_emit(self, signals[P2P_DEVICE_ADDED], 0, p2p, phy_name);
/* There should be no peer objects before the device object appeared so don't
* try to look for them and notify the new device. */
}
static void
remove_p2p_device(NMIwdManager *self, GDBusProxy *proxy, GDBusObject *object)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
const char *path = g_dbus_object_get_object_path(object);
NMDeviceIwdP2P *p2p = g_hash_table_lookup(priv->p2p_devices, path);
if (!p2p)
return;
g_hash_table_remove(priv->p2p_devices, path);
}
static NMDeviceIwdP2P *
get_p2p_device_from_peer(NMIwdManager *self, GDBusProxy *proxy)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
const char *device_path = get_property_string_or_null(proxy, "Device");
if (!device_path)
return NULL;
return g_hash_table_lookup(priv->p2p_devices, device_path);
}
static void
known_network_update_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
@ -998,6 +1071,22 @@ interface_added(GDBusObjectManager *object_manager,
return;
}
if (nm_streq(iface_name, NM_IWD_P2P_INTERFACE)) {
add_p2p_device(self, proxy, object);
return;
}
if (nm_streq(iface_name, NM_IWD_P2P_PEER_INTERFACE)) {
NMDeviceIwdP2P *p2p = get_p2p_device_from_peer(self, proxy);
/* This is more conveniently done with a direct call than a signal because
* this way we only notify the interested NMDeviceIwdP2P. */
if (p2p)
nm_device_iwd_p2p_peer_add_remove(p2p, object, TRUE);
return;
}
}
static void
@ -1051,6 +1140,20 @@ interface_removed(GDBusObjectManager *object_manager,
return;
}
if (nm_streq(iface_name, NM_IWD_P2P_INTERFACE)) {
remove_p2p_device(self, proxy, object);
return;
}
if (nm_streq(iface_name, NM_IWD_P2P_PEER_INTERFACE)) {
NMDeviceIwdP2P *p2p = get_p2p_device_from_peer(self, proxy);
if (p2p)
nm_device_iwd_p2p_peer_add_remove(p2p, object, FALSE);
return;
}
}
static void
@ -1469,6 +1572,15 @@ get_daemon_info_cb(GObject *source, GAsyncResult *res, gpointer user_data)
nm_clear_g_free(&priv->last_state_dir);
priv->last_state_dir = g_variant_dup_string(value, NULL);
} else if (nm_streq(key, "NetworkConfigurationEnabled")) {
if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
_LOGE("Daemon.GetInfo property %s is typed '%s' instead of 'b'",
key,
g_variant_get_type_string(value));
goto next;
}
priv->netconfig_enabled = g_variant_get_boolean(value);
}
next:
@ -1543,6 +1655,8 @@ got_object_manager(GObject *object, GAsyncResult *result, gpointer user_data)
if (priv->agent_id)
register_agent(self);
priv->netconfig_enabled = false; /* Assume false until GetInfo() results come in */
daemon = g_dbus_object_manager_get_interface(object_manager,
"/net/connman/iwd", /* IWD 1.15+ */
NM_IWD_DAEMON_INTERFACE);
@ -1639,6 +1753,121 @@ nm_iwd_manager_get_dbus_interface(NMIwdManager *self, const char *path, const ch
return interface ? G_DBUS_PROXY(interface) : NULL;
}
gboolean
nm_iwd_manager_get_netconfig_enabled(NMIwdManager *self)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
return priv->netconfig_enabled;
}
/* IWD's net.connman.iwd.p2p.ServiceManager.RegisterDisplayService() is global so
* two local Wi-Fi P2P devices can't be connected to (or even scanning for) WFD
* peers using different WFD IE contents, e.g. one as a sink and one as a source.
* If one device is connected to a peer without a WFD service, another can try
* to establish a WFD connection to a peer since this won't disturb the first
* connection. Similarly if one device is connected to a peer with WFD, another
* can make a connection to a non-WFD peer (if that exists...) because a non-WFD
* peer will simply ignore the WFD IEs, but it cannot connect to or search for a
* peer that's WFD capable without passing our own WFD IEs, i.e. if the new
* NMSettingsConnection has no WFD IEs and we're already in a WFD connection on
* another device, we can't activate that new connection. We expose methods
* for the NMDeviceIwdP2P's to register/unregister the service and one to check
* if there's already an incompatible connection active.
*/
gboolean
nm_iwd_manager_check_wfd_info_compatible(NMIwdManager *self, const NMIwdWfdInfo *wfd_info)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
if (priv->wfd_use_count == 0)
return TRUE;
return nm_wifi_utils_wfd_info_eq(&priv->wfd_info, wfd_info);
}
gboolean
nm_iwd_manager_register_wfd(NMIwdManager *self, const NMIwdWfdInfo *wfd_info)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
gs_unref_object GDBusInterface *service_manager = NULL;
GVariantBuilder builder;
nm_assert(nm_iwd_manager_check_wfd_info_compatible(self, wfd_info));
if (!priv->object_manager)
return FALSE;
service_manager = g_dbus_object_manager_get_interface(priv->object_manager,
"/net/connman/iwd",
NM_IWD_P2P_SERVICE_MANAGER_INTERFACE);
if (!service_manager) {
_LOGE("IWD P2P service manager not found");
return FALSE;
}
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(&builder, "{sv}", "Source", g_variant_new_boolean(wfd_info->source));
g_variant_builder_add(&builder, "{sv}", "Sink", g_variant_new_boolean(wfd_info->sink));
if (wfd_info->source)
g_variant_builder_add(&builder, "{sv}", "Port", g_variant_new_uint16(wfd_info->port));
if (wfd_info->sink && wfd_info->has_audio)
g_variant_builder_add(&builder, "{sv}", "HasAudio", g_variant_new_boolean(TRUE));
if (wfd_info->has_uibc)
g_variant_builder_add(&builder, "{sv}", "HasUIBC", g_variant_new_boolean(TRUE));
if (wfd_info->has_cp)
g_variant_builder_add(&builder,
"{sv}",
"HasContentProtection",
g_variant_new_boolean(TRUE));
g_dbus_proxy_call(G_DBUS_PROXY(service_manager),
"RegisterDisplayService",
g_variant_new("(a{sv})", &builder),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
NULL,
NULL);
memcpy(&priv->wfd_info, wfd_info, sizeof(priv->wfd_info));
priv->wfd_use_count++;
return TRUE;
}
void
nm_iwd_manager_unregister_wfd(NMIwdManager *self)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
gs_unref_object GDBusInterface *service_manager = NULL;
nm_assert(priv->wfd_use_count > 0);
priv->wfd_use_count--;
if (!priv->object_manager)
return;
service_manager = g_dbus_object_manager_get_interface(priv->object_manager,
"/net/connman/iwd",
NM_IWD_P2P_SERVICE_MANAGER_INTERFACE);
if (!service_manager)
return;
g_dbus_proxy_call(G_DBUS_PROXY(service_manager),
"UnregisterDisplayService",
g_variant_new("()"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
NULL,
NULL);
}
/*****************************************************************************/
NM_DEFINE_SINGLETON_GETTER(NMIwdManager, nm_iwd_manager_get, NM_TYPE_IWD_MANAGER);
@ -1685,6 +1914,8 @@ nm_iwd_manager_init(NMIwdManager *self)
g_free,
(GDestroyNotify) known_network_data_free);
priv->p2p_devices = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_object_unref);
prepare_object_manager(self);
}
@ -1718,6 +1949,8 @@ dispose(GObject *object)
nm_clear_g_free(&priv->last_state_dir);
nm_clear_g_free(&priv->warned_state_dir);
g_hash_table_unref(nm_steal_pointer(&priv->p2p_devices));
G_OBJECT_CLASS(nm_iwd_manager_parent_class)->dispose(object);
}
@ -1727,4 +1960,16 @@ nm_iwd_manager_class_init(NMIwdManagerClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->dispose = dispose;
signals[P2P_DEVICE_ADDED] = g_signal_new(NM_IWD_MANAGER_P2P_DEVICE_ADDED,
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
2,
NM_TYPE_DEVICE,
G_TYPE_STRING);
}

View file

@ -13,18 +13,22 @@
#define NM_IWD_BUS_TYPE G_BUS_TYPE_SYSTEM
#define NM_IWD_SERVICE "net.connman.iwd"
#define NM_IWD_DAEMON_INTERFACE "net.connman.iwd.Daemon"
#define NM_IWD_AGENT_MANAGER_INTERFACE "net.connman.iwd.AgentManager"
#define NM_IWD_WIPHY_INTERFACE "net.connman.iwd.Adapter"
#define NM_IWD_DEVICE_INTERFACE "net.connman.iwd.Device"
#define NM_IWD_NETWORK_INTERFACE "net.connman.iwd.Network"
#define NM_IWD_AGENT_INTERFACE "net.connman.iwd.Agent"
#define NM_IWD_WSC_INTERFACE "net.connman.iwd.WiFiSimpleConfiguration"
#define NM_IWD_KNOWN_NETWORK_INTERFACE "net.connman.iwd.KnownNetwork"
#define NM_IWD_SIGNAL_AGENT_INTERFACE "net.connman.iwd.SignalLevelAgent"
#define NM_IWD_AP_INTERFACE "net.connman.iwd.AccessPoint"
#define NM_IWD_ADHOC_INTERFACE "net.connman.iwd.AdHoc"
#define NM_IWD_STATION_INTERFACE "net.connman.iwd.Station"
#define NM_IWD_DAEMON_INTERFACE "net.connman.iwd.Daemon"
#define NM_IWD_AGENT_MANAGER_INTERFACE "net.connman.iwd.AgentManager"
#define NM_IWD_WIPHY_INTERFACE "net.connman.iwd.Adapter"
#define NM_IWD_DEVICE_INTERFACE "net.connman.iwd.Device"
#define NM_IWD_NETWORK_INTERFACE "net.connman.iwd.Network"
#define NM_IWD_AGENT_INTERFACE "net.connman.iwd.Agent"
#define NM_IWD_WSC_INTERFACE "net.connman.iwd.SimpleConfiguration"
#define NM_IWD_KNOWN_NETWORK_INTERFACE "net.connman.iwd.KnownNetwork"
#define NM_IWD_SIGNAL_AGENT_INTERFACE "net.connman.iwd.SignalLevelAgent"
#define NM_IWD_AP_INTERFACE "net.connman.iwd.AccessPoint"
#define NM_IWD_ADHOC_INTERFACE "net.connman.iwd.AdHoc"
#define NM_IWD_STATION_INTERFACE "net.connman.iwd.Station"
#define NM_IWD_P2P_INTERFACE "net.connman.iwd.p2p.Device"
#define NM_IWD_P2P_PEER_INTERFACE "net.connman.iwd.p2p.Peer"
#define NM_IWD_P2P_SERVICE_MANAGER_INTERFACE "net.connman.iwd.p2p.ServiceManager"
#define NM_IWD_P2P_WFD_INTERFACE "net.connman.iwd.p2p.Display"
#define NM_TYPE_IWD_MANAGER (nm_iwd_manager_get_type())
#define NM_IWD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IWD_MANAGER, NMIwdManager))
@ -35,6 +39,8 @@
#define NM_IWD_MANAGER_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_IWD_MANAGER, NMIwdManagerClass))
#define NM_IWD_MANAGER_P2P_DEVICE_ADDED "p2p-device-added"
typedef struct _NMIwdManager NMIwdManager;
typedef struct _NMIwdManagerClass NMIwdManagerClass;
@ -51,4 +57,10 @@ NMSettingsConnection *nm_iwd_manager_get_ap_mirror_connection(NMIwdManager *self
GDBusProxy *
nm_iwd_manager_get_dbus_interface(NMIwdManager *self, const char *path, const char *name);
gboolean nm_iwd_manager_get_netconfig_enabled(NMIwdManager *self);
gboolean nm_iwd_manager_check_wfd_info_compatible(NMIwdManager *self, const NMIwdWfdInfo *wfd_info);
gboolean nm_iwd_manager_register_wfd(NMIwdManager *self, const NMIwdWfdInfo *wfd_info);
void nm_iwd_manager_unregister_wfd(NMIwdManager *self);
#endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */

View file

@ -14,6 +14,8 @@
#include "nm-device-wifi-p2p.h"
#include "nm-device-olpc-mesh.h"
#include "nm-device-iwd.h"
#include "nm-device-iwd-p2p.h"
#include "nm-iwd-manager.h"
#include "settings/nm-settings-connection.h"
#include "libnm-platform/nm-platform.h"
#include "nm-config.h"
@ -67,6 +69,19 @@ p2p_device_created(NMDeviceWifi *device, NMDeviceWifiP2P *p2p_device, NMDeviceFa
g_signal_emit_by_name(self, NM_DEVICE_FACTORY_DEVICE_ADDED, p2p_device);
}
#if WITH_IWD
static void
iwd_p2p_device_added(NMIwdManager *iwd,
NMDeviceIwdP2P *p2p_device,
const char *phy_name,
NMDeviceFactory *self)
{
nm_log_info(LOGD_PLATFORM | LOGD_WIFI, "Wi-Fi P2P device added on %s", phy_name);
g_signal_emit_by_name(self, NM_DEVICE_FACTORY_DEVICE_ADDED, p2p_device);
}
#endif
static NMDevice *
create_device(NMDeviceFactory *factory,
const char *iface,
@ -76,6 +91,7 @@ create_device(NMDeviceFactory *factory,
{
gs_free char *backend_free = NULL;
const char *backend;
_NM80211Mode mode;
g_return_val_if_fail(iface != NULL, NULL);
g_return_val_if_fail(plink != NULL, NULL);
@ -85,6 +101,20 @@ create_device(NMDeviceFactory *factory,
if (plink->type != NM_LINK_TYPE_WIFI)
return nm_device_olpc_mesh_new(iface);
/* 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 (!NM_IN_SET(mode,
_NM_802_11_MODE_INFRA,
_NM_802_11_MODE_ADHOC,
_NM_802_11_MODE_AP,
_NM_802_11_MODE_MESH)) {
*out_ignore = TRUE;
return NULL;
}
backend = nm_config_data_get_device_config_by_pllink(NM_CONFIG_GET_DATA,
NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND,
plink,
@ -103,7 +133,6 @@ create_device(NMDeviceFactory *factory,
if (!g_ascii_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,
@ -113,16 +142,6 @@ create_device(NMDeviceFactory *factory,
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,
@ -134,8 +153,23 @@ create_device(NMDeviceFactory *factory,
return device;
}
#if WITH_IWD
else if (!g_ascii_strcasecmp(backend, "iwd"))
else if (!g_ascii_strcasecmp(backend, "iwd")) {
NMIwdManager *iwd = nm_iwd_manager_get();
if (!g_signal_handler_find(iwd,
G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
0,
0,
NULL,
G_CALLBACK(iwd_p2p_device_added),
factory))
g_signal_connect(iwd,
NM_IWD_MANAGER_P2P_DEVICE_ADDED,
G_CALLBACK(iwd_p2p_device_added),
factory);
return nm_device_iwd_new(iface);
}
#endif
nm_log_warn(LOGD_PLATFORM | LOGD_WIFI,

View file

@ -18,6 +18,7 @@
#include "nm-setting-wireless.h"
#include "nm-utils.h"
#include "nm-wifi-utils.h"
#include "nm-iwd-manager.h"
#include "libnm-platform/nm-platform.h"
#include "supplicant/nm-supplicant-types.h"
@ -100,14 +101,16 @@ 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)
nm_wifi_p2p_peers_find_first_compatible(const CList *peers_lst_head,
NMConnection *connection,
gboolean check_wfd)
{
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))
if (nm_wifi_p2p_peer_check_compatible(peer, connection, check_wfd))
return peer;
}
return NULL;
@ -419,6 +422,90 @@ nm_wifi_p2p_peer_update_from_properties(NMWifiP2PPeer *peer, const NMSupplicantP
return changed;
}
gboolean
nm_wifi_p2p_peer_update_from_iwd_object(NMWifiP2PPeer *peer, GDBusObject *obj)
{
NMWifiP2PPeerPrivate *priv;
gboolean changed = FALSE;
nm_auto_ref_string NMRefString *peer_path = NULL;
gs_unref_object GDBusProxy *peer_proxy = NULL;
gs_unref_object GDBusProxy *wfd_proxy = NULL;
GVariant *value;
gs_unref_bytes GBytes *wfd_ies = NULL;
g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), FALSE);
peer_proxy = G_DBUS_PROXY(g_dbus_object_get_interface(obj, NM_IWD_P2P_PEER_INTERFACE));
wfd_proxy = G_DBUS_PROXY(g_dbus_object_get_interface(obj, NM_IWD_P2P_WFD_INTERFACE));
g_return_val_if_fail(peer_proxy, FALSE);
peer_path = nm_ref_string_new(g_dbus_object_get_object_path(obj));
priv = NM_WIFI_P2P_PEER_GET_PRIVATE(peer);
nm_assert(!priv->supplicant_path || priv->supplicant_path == peer_path);
g_object_freeze_notify(G_OBJECT(peer));
if (!priv->supplicant_path) {
priv->supplicant_path = g_steal_pointer(&peer_path);
changed = TRUE;
}
value = g_dbus_proxy_get_cached_property(peer_proxy, "Name");
if (value && g_variant_is_of_type(value, G_VARIANT_TYPE_STRING))
changed |= nm_wifi_p2p_peer_set_name(peer, g_variant_get_string(value, NULL));
else
changed |= nm_wifi_p2p_peer_set_name(peer, "");
nm_g_variant_unref(value);
value = g_dbus_proxy_get_cached_property(peer_proxy, "Address");
if (value && g_variant_is_of_type(value, G_VARIANT_TYPE_STRING))
changed |= nm_wifi_p2p_peer_set_address(peer, g_variant_get_string(value, NULL));
nm_g_variant_unref(value);
if (wfd_proxy) {
NMIwdWfdInfo wfd = {};
value = g_dbus_proxy_get_cached_property(wfd_proxy, "Source");
wfd.source = value && g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)
&& g_variant_get_boolean(value);
nm_g_variant_unref(value);
value = g_dbus_proxy_get_cached_property(wfd_proxy, "Sink");
wfd.sink = value && g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)
&& g_variant_get_boolean(value);
nm_g_variant_unref(value);
value = g_dbus_proxy_get_cached_property(wfd_proxy, "Port");
wfd.port = (value && g_variant_is_of_type(value, G_VARIANT_TYPE_UINT16))
? g_variant_get_uint16(value)
: 0;
nm_g_variant_unref(value);
value = g_dbus_proxy_get_cached_property(wfd_proxy, "HasAudio");
wfd.has_audio = value && g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)
&& g_variant_get_boolean(value);
nm_g_variant_unref(value);
value = g_dbus_proxy_get_cached_property(wfd_proxy, "HasUIBC");
wfd.has_uibc = value && g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)
&& g_variant_get_boolean(value);
nm_g_variant_unref(value);
value = g_dbus_proxy_get_cached_property(wfd_proxy, "HasContentProtection");
wfd.has_cp = value && g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)
&& g_variant_get_boolean(value);
nm_g_variant_unref(value);
wfd_ies = nm_wifi_utils_build_wfd_ies(&wfd);
}
changed |= nm_wifi_p2p_peer_set_wfd_ies(peer, wfd_ies);
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)
{
@ -458,7 +545,7 @@ nm_wifi_p2p_peer_to_string(const NMWifiP2PPeer *self, char *str_buf, gsize buf_l
}
gboolean
nm_wifi_p2p_peer_check_compatible(NMWifiP2PPeer *self, NMConnection *connection)
nm_wifi_p2p_peer_check_compatible(NMWifiP2PPeer *self, NMConnection *connection, gboolean check_wfd)
{
NMWifiP2PPeerPrivate *priv;
NMSettingWifiP2P *s_wifi_p2p;
@ -478,6 +565,10 @@ nm_wifi_p2p_peer_check_compatible(NMWifiP2PPeer *self, NMConnection *connection)
if (hwaddr && (!priv->address || !nm_utils_hwaddr_matches(hwaddr, -1, priv->address, -1)))
return FALSE;
if (check_wfd && nm_setting_wifi_p2p_get_wfd_ies(s_wifi_p2p)
&& !nm_wifi_p2p_peer_get_wfd_ies(self))
return FALSE;
return TRUE;
}
@ -559,6 +650,17 @@ nm_wifi_p2p_peer_new_from_properties(const NMSupplicantPeerInfo *peer_info)
return peer;
}
NMWifiP2PPeer *
nm_wifi_p2p_peer_new_from_iwd_object(GDBusObject *obj)
{
NMWifiP2PPeer *peer;
/* TODO: Set the flags here */
peer = g_object_new(NM_TYPE_WIFI_P2P_PEER, NULL);
nm_wifi_p2p_peer_update_from_iwd_object(peer, obj);
return peer;
}
static void
finalize(GObject *object)
{

View file

@ -45,11 +45,15 @@ struct _NMSupplicantPeerInfo;
GType nm_wifi_p2p_peer_get_type(void);
NMWifiP2PPeer *nm_wifi_p2p_peer_new_from_properties(const struct _NMSupplicantPeerInfo *peer_info);
NMWifiP2PPeer *nm_wifi_p2p_peer_new_from_iwd_object(GDBusObject *obj);
gboolean nm_wifi_p2p_peer_update_from_properties(NMWifiP2PPeer *peer,
const struct _NMSupplicantPeerInfo *peer_info);
gboolean nm_wifi_p2p_peer_update_from_iwd_object(NMWifiP2PPeer *peer, GDBusObject *obj);
gboolean nm_wifi_p2p_peer_check_compatible(NMWifiP2PPeer *self, NMConnection *connection);
gboolean nm_wifi_p2p_peer_check_compatible(NMWifiP2PPeer *self,
NMConnection *connection,
gboolean check_wfd);
const char *nm_wifi_p2p_peer_get_supplicant_path(NMWifiP2PPeer *peer);
@ -81,7 +85,8 @@ nm_wifi_p2p_peer_to_string(const NMWifiP2PPeer *self, char *str_buf, gsize buf_l
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);
NMConnection *connection,
gboolean check_wfd);
NMWifiP2PPeer *nm_wifi_p2p_peers_find_by_supplicant_path(const CList *peers_lst_head,
const char *path);

View file

@ -1800,3 +1800,151 @@ nm_wifi_utils_connection_to_iwd_config(NMConnection *connection,
return g_steal_pointer(&file);
}
/* Wi-Fi Display Technical Specification v2.1.0 Table 27 */
enum wfd_subelem_type {
WFD_SUBELEM_WFD_DEVICE_INFORMATION = 0,
WFD_SUBELEM_ASSOCIATED_BSSID = 1,
WFD_SUBELEM_COUPLED_SINK_INFORMATION = 6,
WFD_SUBELEM_EXTENDED_CAPABILITY = 7,
WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
WFD_SUBELEM_SESION_INFORMATION = 9,
WFD_SUBELEM_ALTERNATIVE_MAC_ADDRESS = 10,
WFD_SUBELEM_R2_DEVICE_INFORMATION = 11,
};
bool
nm_wifi_utils_parse_wfd_ies(GBytes *ies, NMIwdWfdInfo *out_wfd)
{
size_t len;
const uint8_t *data = g_bytes_get_data(ies, &len);
const uint8_t *dev_info = NULL;
uint16_t dev_info_len = 0;
uint16_t dev_info_flags;
const uint8_t *ext_capability = NULL;
uint16_t ext_capability_len = 0;
/* The single WFD IEs array provided by the client is supposed to be sent to
* the peer in the different frame types that may include the WFD IE: Probe
* Request/Response, Beacon, (Re)Association Request/Response, GO
* Negotiation Request/Response/Confirm and Provision Discovery
* Request/Response.
*
* It's going to be a subset of the elements allowed in all those frames.
* Validate that it contains at least a valid WFD Device Information (with
* the Session Available bit true) and that the sequence of subelements is
* valid.
*/
while (len) {
uint8_t subelem_id;
uint16_t subelem_len;
/* Does the subelement header fit */
if (len < 3)
return FALSE;
subelem_id = data[0];
subelem_len = (data[1] << 8) | data[2];
data += 3;
len -= 3;
if (subelem_len > len)
return FALSE;
if (subelem_id == WFD_SUBELEM_WFD_DEVICE_INFORMATION) {
/* Is there a duplicate WFD Device Information */
if (dev_info)
return FALSE;
dev_info = data;
dev_info_len = subelem_len;
}
if (subelem_id == WFD_SUBELEM_EXTENDED_CAPABILITY) {
/* Is there a duplicate WFD Extended Capability */
if (ext_capability)
return FALSE;
ext_capability = data;
ext_capability_len = subelem_len;
}
data += subelem_len;
len -= subelem_len;
}
if (!dev_info || dev_info_len != 6)
return FALSE;
dev_info_flags = (dev_info[0] << 8) | dev_info[1];
/* Secondary sink not supported */
if ((dev_info_flags & 3) == 2)
return FALSE;
/* Must be available for WFD Session */
if (((dev_info_flags >> 4) & 3) != 1)
return FALSE;
/* TDLS persistent group re-invocation not supported */
if ((dev_info_flags >> 13) & 1)
return FALSE;
/* All other flags indicate support but not a requirement for something
* so not preserving them in the IEs IWD eventually sends doesn't break
* basic functionality.
*/
if (ext_capability && ext_capability_len != 2)
return FALSE;
if (!out_wfd)
return TRUE;
out_wfd->source = NM_IN_SET(dev_info_flags & 3, 0, 3);
out_wfd->sink = NM_IN_SET(dev_info_flags & 3, 1, 3);
out_wfd->port = (dev_info[2] << 8) | dev_info[3];
out_wfd->has_audio =
out_wfd->sink ? ((dev_info_flags >> 10) & 1) == 0 : (((dev_info_flags >> 11) & 1) == 1);
out_wfd->has_uibc = ext_capability && (ext_capability[1] & 1) == 1;
out_wfd->has_cp = ((dev_info_flags >> 8) & 1) == 1;
return TRUE;
}
GBytes *
nm_wifi_utils_build_wfd_ies(const NMIwdWfdInfo *wfd)
{
uint8_t data[64];
uint8_t *ptr = data;
*ptr++ = WFD_SUBELEM_WFD_DEVICE_INFORMATION;
*ptr++ = 0; /* WFD Subelement length */
*ptr++ = 6;
*ptr++ = 0; /* WFD Device Information bitmap: */
*ptr++ = (wfd->source ? (wfd->sink ? 3 : 0) : 1) | 0x10 | /* WFD Session Available */
(wfd->has_cp ? 0x100 : 0) | (wfd->has_audio ? 0 : 0x400);
*ptr++ = wfd->port >> 8;
*ptr++ = wfd->port & 255;
*ptr++ = 0; /* WFD Device Maximum throughput */
*ptr++ = 10;
if (wfd->has_uibc) {
*ptr++ = WFD_SUBELEM_EXTENDED_CAPABILITY;
*ptr++ = 0; /* WFD Subelement length */
*ptr++ = 2;
*ptr++ = 0x00; /* WFD Extended Capability Bitmap: */
*ptr++ = 0x10; /* UIBC Support */
}
return g_bytes_new(data, ptr - data);
}
bool
nm_wifi_utils_wfd_info_eq(const NMIwdWfdInfo *a, const NMIwdWfdInfo *b)
{
if (!a || !b)
return a == b;
return a->source == b->source && a->sink == b->sink && a->port == b->port
&& a->has_audio == b->has_audio && a->has_uibc == b->has_uibc && a->has_cp == b->has_cp;
}

View file

@ -20,6 +20,15 @@ typedef enum {
NM_IWD_NETWORK_SECURITY_8021X,
} NMIwdNetworkSecurity;
typedef struct {
bool source;
bool sink;
uint16_t port;
bool has_audio;
bool has_uibc;
bool has_cp;
} NMIwdWfdInfo;
gboolean nm_wifi_utils_complete_connection(GBytes *ssid,
const char *bssid,
_NM80211Mode mode,
@ -43,4 +52,8 @@ char *nm_wifi_utils_get_iwd_config_filename(const char *ssid,
GKeyFile *
nm_wifi_utils_connection_to_iwd_config(NMConnection *conn, char **out_filename, GError **error);
bool nm_wifi_utils_parse_wfd_ies(GBytes *ies, NMIwdWfdInfo *out_wfd);
GBytes *nm_wifi_utils_build_wfd_ies(const NMIwdWfdInfo *wfd);
bool nm_wifi_utils_wfd_info_eq(const NMIwdWfdInfo *a, const NMIwdWfdInfo *b);
#endif /* __NM_WIFI_UTILS_H__ */