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

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/670
This commit is contained in:
Thomas Haller 2020-11-19 10:12:13 +01:00
commit f37db2c2a5
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
10 changed files with 1415 additions and 360 deletions

View file

@ -1090,8 +1090,25 @@ managed=1
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>wifi.iwd.autoconnect</varname></term>
<listitem>
<para>
If <literal>wifi.backend</literal> is <literal>iwd</literal>, setting this to
<literal>false</literal> forces IWD's autoconnect mechanism to be disabled for
this device and connections will only be initiated by NetworkManager whether
commaned by a client or automatically. Leaving it <literal>true</literal> (default)
stops NetworkManager from automatically initiating connections and allows
IWD to use its network ranking and scanning logic to decide the best networks
to autoconnect to next. Connections' <literal>autoconnect-priority</literal>,
<literal>autoconnect-retries</literal> settings will be ignored. Other settings
like <literal>permissions</literal> or <literal>multi-connect</literal> may interfere
with IWD connection attempts.
</para>
</listitem>
</varlistentry>
<varlistentry id="sriov-num-vfs">
, <term><varname>sriov-num-vfs</varname></term>
<term><varname>sriov-num-vfs</varname></term>
<listitem>
<para>
Specify the number of virtual functions (VF) to enable

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,7 @@
#include "nm-wifi-utils.h"
#include "nm-glib-aux/nm-random-utils.h"
#include "settings/nm-settings.h"
#include "nm-std-aux/nm-dbus-compat.h"
/*****************************************************************************/
@ -39,6 +40,7 @@ typedef struct {
guint agent_id;
char * agent_path;
GHashTable * known_networks;
NMDeviceIwd * last_agent_call_device;
} NMIwdManagerPrivate;
struct _NMIwdManager {
@ -87,7 +89,8 @@ G_DEFINE_TYPE(NMIwdManager, nm_iwd_manager, G_TYPE_OBJECT)
/*****************************************************************************/
static void mirror_8021x_connection_take_and_delete(NMSettingsConnection *sett_conn);
static void mirror_connection_take_and_delete(NMSettingsConnection *sett_conn,
KnownNetworkData * data);
/*****************************************************************************/
@ -117,6 +120,21 @@ get_property_string_or_null(GDBusProxy *proxy, const char *property)
return get_variant_string_or_null(value);
}
static gboolean
get_property_bool(GDBusProxy *proxy, const char *property, gboolean default_val)
{
gs_unref_variant GVariant *value = NULL;
if (!proxy || !property)
return default_val;
value = g_dbus_proxy_get_cached_property(proxy, property);
if (!value || !g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN))
return default_val;
return g_variant_get_boolean(value);
}
static NMDeviceIwd *
get_device_from_network(NMIwdManager *self, GDBusProxy *network)
{
@ -178,6 +196,25 @@ agent_dbus_method_cb(GDBusConnection * connection,
if (!nm_streq0(name_owner, sender))
goto return_error;
if (!strcmp(method_name, "Cancel")) {
const char *reason = NULL;
g_variant_get(parameters, "(&s)", &reason);
_LOGD("agent-request: Cancel reason: %s", reason);
if (!priv->last_agent_call_device)
goto return_error;
if (nm_device_iwd_agent_query(priv->last_agent_call_device, NULL)) {
priv->last_agent_call_device = NULL;
g_dbus_method_invocation_return_value(invocation, NULL);
return;
}
priv->last_agent_call_device = NULL;
goto return_error;
}
if (!strcmp(method_name, "RequestUserPassword"))
g_variant_get(parameters, "(&os)", &network_path, NULL);
else
@ -197,8 +234,10 @@ agent_dbus_method_cb(GDBusConnection * connection,
goto return_error;
}
if (nm_device_iwd_agent_query(device, invocation))
if (nm_device_iwd_agent_query(device, invocation)) {
priv->last_agent_call_device = device;
return;
}
_LOGD("agent-request: device %s did not handle the IWD Agent request",
nm_device_get_iface(NM_DEVICE(device)));
@ -229,10 +268,12 @@ static const GDBusInterfaceInfo iwd_agent_iface_info = NM_DEFINE_GDBUS_INTERFACE
NM_DEFINE_GDBUS_ARG_INFO("password", "s"), ), ),
NM_DEFINE_GDBUS_METHOD_INFO(
"RequestUserPassword",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("network", "o"),
.in_args = NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("network", "o"),
NM_DEFINE_GDBUS_ARG_INFO("user", "s"), ),
.out_args =
NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("password", "s"), ), ), ), );
.out_args = NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("password", "s"), ), ),
NM_DEFINE_GDBUS_METHOD_INFO("Cancel",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS(
NM_DEFINE_GDBUS_ARG_INFO("reason", "s"), ), ), ), );
static guint
iwd_agent_export(GDBusConnection *connection, gpointer user_data, char **agent_path, GError **error)
@ -329,7 +370,7 @@ known_network_data_free(KnownNetworkData *network)
return;
g_object_unref(network->known_network);
mirror_8021x_connection_take_and_delete(network->mirror_connection);
mirror_connection_take_and_delete(network->mirror_connection, network);
g_slice_free(KnownNetworkData, network);
}
@ -370,14 +411,72 @@ set_device_dbus_object(NMIwdManager *self, GDBusProxy *proxy, GDBusObject *objec
nm_device_iwd_set_dbus_object(NM_DEVICE_IWD(device), object);
}
/* 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 void
known_network_update_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
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_log_warn(LOGD_WIFI,
"Updating %s on IWD known network %s failed: %s",
(const char *) user_data,
g_dbus_proxy_get_object_path(G_DBUS_PROXY(source)),
error->message);
}
}
static void
sett_conn_changed(NMSettingsConnection *sett_conn, guint update_reason, KnownNetworkData *data)
{
NMSettingsConnectionIntFlags flags;
NMConnection * conn = nm_settings_connection_get_connection(sett_conn);
NMSettingConnection * s_conn = nm_connection_get_setting_connection(conn);
gboolean nm_autoconnectable = nm_setting_connection_get_autoconnect(s_conn);
gboolean iwd_autoconnectable = get_property_bool(data->known_network, "AutoConnect", TRUE);
nm_assert(sett_conn == data->mirror_connection);
if (iwd_autoconnectable == nm_autoconnectable)
return;
/* If this is a generated connection it may be ourselves updating it */
flags = nm_settings_connection_get_flags(data->mirror_connection);
if (NM_FLAGS_HAS(flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
return;
nm_log_dbg(LOGD_WIFI,
"Updating AutoConnect on known network at %s based on connection %s",
g_dbus_proxy_get_object_path(data->known_network),
nm_settings_connection_get_id(data->mirror_connection));
g_dbus_proxy_call(data->known_network,
DBUS_INTERFACE_PROPERTIES ".Set",
g_variant_new("(ssv)",
NM_IWD_KNOWN_NETWORK_INTERFACE,
"AutoConnect",
g_variant_new_boolean(nm_autoconnectable)),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
known_network_update_cb,
"AutoConnect");
}
/* Look up an existing NMSettingsConnection for a network that has been
* preprovisioned with an IWD config file or has been connected to before,
* or create a new in-memory NMSettingsConnection object. This will let
* users control the few supported properties (mainly make it
* IWD-autoconnectable or not), remove/forget the network, or, for a
* WPA2-Enterprise type network it will inform the NM autoconnect mechanism
* and the clients that this networks needs no additional EAP configuration
* from the user.
*/
static NMSettingsConnection *
mirror_8021x_connection(NMIwdManager *self, const char *name, gboolean create_new)
mirror_connection(NMIwdManager * self,
const KnownNetworkId *id,
gboolean create_new,
GDBusProxy * known_network)
{
NMIwdManagerPrivate * priv = NM_IWD_MANAGER_GET_PRIVATE(self);
NMSettingsConnection *const *iter;
@ -385,43 +484,126 @@ mirror_8021x_connection(NMIwdManager *self, const char *name, gboolean create_ne
NMSettingsConnection * settings_connection = NULL;
char uuid[37];
NMSetting * setting;
GError * error = NULL;
gs_unref_bytes GBytes *new_ssid = NULL;
gs_free_error GError *error = NULL;
gs_unref_bytes GBytes *new_ssid = NULL;
gsize ssid_len = strlen(id->name);
gboolean autoconnectable = TRUE;
gboolean hidden = FALSE;
gboolean exact_match = TRUE;
const char * key_mgmt = NULL;
if (known_network) {
autoconnectable = get_property_bool(known_network, "AutoConnect", TRUE);
hidden = get_property_bool(known_network, "Hidden", FALSE);
}
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;
const guint8 * ssid_bytes;
gsize ssid_len2;
security = nm_wifi_connection_get_iwd_security(conn, NULL);
if (security != NM_IWD_NETWORK_SECURITY_8021X)
if (!nm_wifi_connection_get_iwd_ssid_and_security(conn, NULL, &security))
continue;
if (security != id->security)
continue;
s_wifi = nm_connection_get_setting_wireless(conn);
if (!s_wifi)
continue;
ssid_name = _nm_utils_ssid_to_utf8(nm_setting_wireless_get_ssid(s_wifi));
if (!nm_streq(ssid_name, name))
/* The SSID must be UTF-8 if it matches since id->name is known to be
* valid UTF-8, so just memcmp them.
*/
ssid_bytes = g_bytes_get_data(nm_setting_wireless_get_ssid(s_wifi), &ssid_len2);
if (!ssid_bytes || ssid_len2 != ssid_len || memcmp(ssid_bytes, id->name, ssid_len))
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;
}
exact_match = TRUE;
if (known_network) {
NMSettingConnection *s_conn = nm_connection_get_setting_connection(conn);
if (nm_setting_connection_get_autoconnect(s_conn) != autoconnectable
|| nm_setting_wireless_get_hidden(s_wifi) != hidden)
exact_match = FALSE;
}
/* Prefer returning connections for EAP method "external" */
if (!settings_connection || external)
switch (id->security) {
case NM_IWD_NETWORK_SECURITY_WEP:
case NM_IWD_NETWORK_SECURITY_NONE:
case NM_IWD_NETWORK_SECURITY_PSK:
break;
case NM_IWD_NETWORK_SECURITY_8021X:
{
NMSetting8021x *s_8021x = nm_connection_get_setting_802_1x(conn);
gboolean external = FALSE;
guint i;
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 with EAP method "external" */
if (!external)
exact_match = FALSE;
}
}
if (!settings_connection || exact_match)
settings_connection = sett_conn;
if (exact_match)
break;
}
if (settings_connection && known_network && !exact_match) {
NMSettingsConnectionIntFlags flags = nm_settings_connection_get_flags(settings_connection);
/* If we found a connection and it's generated (likely by ourselves)
* it may have been created on a request by
* nm_iwd_manager_get_ap_mirror_connection() when no Known Network
* was available so we didn't have access to its properties other
* than Name and Security. Copy their values to the generated
* NMConnection.
* TODO: avoid notify signals triggering our own watch.
*
* If on the other hand this is a user-created NMConnection we
* should try to copy the properties from it to IWD's Known Network
* using the Properties DBus interface in case the user created an
* NM connection before IWD appeared on the bus, or before IWD
* created its Known Network object.
*/
if (NM_FLAGS_HAS(flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) {
NMConnection *tmp_conn = nm_settings_connection_get_connection(settings_connection);
NMSettingConnection *s_conn = nm_connection_get_setting_connection(tmp_conn);
NMSettingWireless * s_wifi = nm_connection_get_setting_wireless(tmp_conn);
g_object_set(G_OBJECT(s_conn),
NM_SETTING_CONNECTION_AUTOCONNECT,
autoconnectable,
NULL);
g_object_set(G_OBJECT(s_wifi), NM_SETTING_WIRELESS_HIDDEN, hidden, NULL);
} else {
KnownNetworkData data = {known_network, settings_connection};
sett_conn_changed(settings_connection, 0, &data);
}
}
if (settings_connection && known_network) {
/* Reset NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL now that the
* connection is going to be referenced by a known network, we don't
* want it to be deleted when activation fails anymore.
*/
nm_settings_connection_set_flags_full(settings_connection,
NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL,
0);
}
/* If we already have an NMSettingsConnection matching this
@ -438,58 +620,82 @@ mirror_8021x_connection(NMIwdManager *self, const char *name, gboolean create_ne
NM_SETTING_CONNECTION_TYPE,
NM_SETTING_WIRELESS_SETTING_NAME,
NM_SETTING_CONNECTION_ID,
name,
id->name,
NM_SETTING_CONNECTION_UUID,
nm_utils_uuid_generate_buf(uuid),
NM_SETTING_CONNECTION_AUTOCONNECT,
autoconnectable,
NULL));
nm_connection_add_setting(connection, setting);
new_ssid = g_bytes_new(name, strlen(name));
new_ssid = g_bytes_new(id->name, ssid_len);
setting = NM_SETTING(g_object_new(NM_TYPE_SETTING_WIRELESS,
NM_SETTING_WIRELESS_SSID,
new_ssid,
NM_SETTING_WIRELESS_MODE,
NM_SETTING_WIRELESS_MODE_INFRA,
NM_SETTING_WIRELESS_HIDDEN,
hidden,
NULL));
nm_connection_add_setting(connection, setting);
setting = NM_SETTING(g_object_new(NM_TYPE_SETTING_WIRELESS_SECURITY,
NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
"open",
NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
"wpa-eap",
NULL));
nm_connection_add_setting(connection, setting);
switch (id->security) {
case NM_IWD_NETWORK_SECURITY_WEP:
key_mgmt = "none";
break;
case NM_IWD_NETWORK_SECURITY_NONE:
key_mgmt = NULL;
break;
case NM_IWD_NETWORK_SECURITY_PSK:
key_mgmt = "wpa-psk";
break;
case NM_IWD_NETWORK_SECURITY_8021X:
key_mgmt = "wpa-eap";
break;
}
/* "password" and "private-key-password" may be requested by the IWD agent
* from NM and IWD will implement a specific secret cache policy so by
* default respect that policy and don't save copies of those secrets in
* NM settings. The saved values can not be used anyway because of our
* use of NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW.
*/
setting = NM_SETTING(g_object_new(NM_TYPE_SETTING_802_1X,
NM_SETTING_802_1X_PASSWORD_FLAGS,
NM_SETTING_SECRET_FLAG_NOT_SAVED,
NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS,
NM_SETTING_SECRET_FLAG_NOT_SAVED,
NULL));
nm_setting_802_1x_add_eap_method(NM_SETTING_802_1X(setting), "external");
nm_connection_add_setting(connection, setting);
if (key_mgmt) {
setting = NM_SETTING(g_object_new(NM_TYPE_SETTING_WIRELESS_SECURITY,
NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
"open",
NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
key_mgmt,
NULL));
nm_connection_add_setting(connection, setting);
}
if (id->security == NM_IWD_NETWORK_SECURITY_8021X) {
/* "password" and "private-key-password" may be requested by the IWD agent
* from NM and IWD will implement a specific secret cache policy so by
* default respect that policy and don't save copies of those secrets in
* NM settings. The saved values can not be used anyway because of our
* use of NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW.
*/
setting = NM_SETTING(g_object_new(NM_TYPE_SETTING_802_1X,
NM_SETTING_802_1X_PASSWORD_FLAGS,
NM_SETTING_SECRET_FLAG_NOT_SAVED,
NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS,
NM_SETTING_SECRET_FLAG_NOT_SAVED,
NULL));
nm_setting_802_1x_add_eap_method(NM_SETTING_802_1X(setting), "external");
nm_connection_add_setting(connection, setting);
}
if (!nm_connection_normalize(connection, NULL, NULL, NULL))
return NULL;
if (!nm_settings_add_connection(priv->settings,
connection,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
&settings_connection,
&error)) {
if (!nm_settings_add_connection(
priv->settings,
connection,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
| (known_network ? 0 : NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL),
&settings_connection,
&error)) {
_LOGW("failed to add a mirror NMConnection for IWD's Known Network '%s': %s",
name,
id->name,
error->message);
g_error_free(error);
return NULL;
}
@ -497,7 +703,7 @@ mirror_8021x_connection(NMIwdManager *self, const char *name, gboolean create_ne
}
static void
mirror_8021x_connection_take_and_delete(NMSettingsConnection *sett_conn)
mirror_connection_take_and_delete(NMSettingsConnection *sett_conn, KnownNetworkData *data)
{
NMSettingsConnectionIntFlags flags;
@ -511,6 +717,7 @@ mirror_8021x_connection_take_and_delete(NMSettingsConnection *sett_conn)
if (NM_FLAGS_HAS(flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
nm_settings_connection_delete(sett_conn, FALSE);
g_signal_handlers_disconnect_by_data(sett_conn, data);
g_object_unref(sett_conn);
}
@ -540,6 +747,7 @@ interface_added(GDBusObjectManager *object_manager,
if (nm_streq(iface_name, NM_IWD_KNOWN_NETWORK_INTERFACE)) {
KnownNetworkId * id;
KnownNetworkId * orig_id;
KnownNetworkData * data;
NMIwdNetworkSecurity security;
const char * type_str, *name;
@ -561,28 +769,33 @@ interface_added(GDBusObjectManager *object_manager,
id = known_network_id_new(name, security);
data = g_hash_table_lookup(priv->known_networks, id);
if (data) {
if (g_hash_table_lookup_extended(priv->known_networks,
id,
(void **) &orig_id,
(void **) &data)) {
_LOGW("DBus error: KnownNetwork already exists ('%s', %s)", name, type_str);
g_free(id);
nm_g_object_ref_set(&data->known_network, proxy);
g_free(id);
id = orig_id;
} 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, TRUE);
sett_conn = mirror_connection(self, id, TRUE, proxy);
if (sett_conn && sett_conn != data->mirror_connection) {
NMSettingsConnection *sett_conn_old = data->mirror_connection;
if (sett_conn && sett_conn != data->mirror_connection) {
NMSettingsConnection *sett_conn_old = data->mirror_connection;
data->mirror_connection = nm_g_object_ref(sett_conn);
mirror_8021x_connection_take_and_delete(sett_conn_old);
}
} else
mirror_8021x_connection_take_and_delete(g_steal_pointer(&data->mirror_connection));
data->mirror_connection = nm_g_object_ref(sett_conn);
mirror_connection_take_and_delete(sett_conn_old, data);
g_signal_connect(sett_conn,
NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
G_CALLBACK(sett_conn_changed),
data);
}
return;
}
@ -685,42 +898,47 @@ object_removed(GDBusObjectManager *object_manager, GDBusObject *object, gpointer
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;
gs_free char * ssid_str = NULL;
NMIwdManager * self = user_data;
NMIwdManagerPrivate * priv = NM_IWD_MANAGER_GET_PRIVATE(self);
NMConnection * conn = nm_settings_connection_get_connection(sett_conn);
NMSettingWireless * s_wireless;
KnownNetworkData * data;
KnownNetworkId id;
char ssid_buf[33];
const guint8 * ssid_bytes;
gsize ssid_len;
NMSettingsConnection *new_mirror_conn;
id.security = nm_wifi_connection_get_iwd_security(conn, &mapped);
if (!mapped)
if (!nm_wifi_connection_get_iwd_ssid_and_security(conn, NULL, &id.security))
return;
s_wireless = nm_connection_get_setting_wireless(conn);
ssid_str = _nm_utils_ssid_to_utf8(nm_setting_wireless_get_ssid(s_wireless));
id.name = ssid_str;
data = g_hash_table_lookup(priv->known_networks, &id);
if (!s_wireless)
return;
ssid_bytes = g_bytes_get_data(nm_setting_wireless_get_ssid(s_wireless), &ssid_len);
if (!ssid_bytes || ssid_len > 32 || memchr(ssid_bytes, 0, ssid_len))
return;
memcpy(ssid_buf, ssid_bytes, ssid_len);
ssid_buf[ssid_len] = '\0';
id.name = ssid_buf;
data = g_hash_table_lookup(priv->known_networks, &id);
if (!data)
return;
if (id.security == NM_IWD_NETWORK_SECURITY_8021X) {
NMSettingsConnection *new_mirror_conn;
if (data->mirror_connection != sett_conn)
return;
if (data->mirror_connection != sett_conn)
return;
g_clear_object(&data->mirror_connection);
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;
}
/* Don't call Forget on the Known Network until there's no longer *any*
* matching NMSettingsConnection (debatable)
*/
new_mirror_conn = mirror_connection(self, &id, FALSE, NULL);
if (new_mirror_conn) {
data->mirror_connection = g_object_ref(new_mirror_conn);
return;
}
if (!priv->running)
@ -820,7 +1038,28 @@ device_added(NMManager *manager, NMDevice *device, gpointer user_data)
if (!priv->running)
return;
/* Here we handle a potential scenario where IWD's DBus objects for the
* new device popped up before the NMDevice. The
* interface_added/object_added signals have been received already and
* the handlers couldn't do much because the NMDevice wasn't there yet
* so now we go over the Network and Device interfaces again. In this
* exact order for "object path" property consistency -- see reasoning
* in object_compare_interfaces.
*/
objects = g_dbus_object_manager_get_objects(priv->object_manager);
for (iter = objects; iter; iter = iter->next) {
GDBusObject * object = G_DBUS_OBJECT(iter->data);
gs_unref_object GDBusInterface *interface = NULL;
interface = g_dbus_object_get_interface(object, NM_IWD_NETWORK_INTERFACE);
if (!interface)
continue;
if (NM_DEVICE_IWD(device) == get_device_from_network(self, (GDBusProxy *) interface))
nm_device_iwd_network_add_remove(NM_DEVICE_IWD(device), (GDBusProxy *) interface, TRUE);
}
for (iter = objects; iter; iter = iter->next) {
GDBusObject * object = G_DBUS_OBJECT(iter->data);
gs_unref_object GDBusInterface *interface = NULL;
@ -839,6 +1078,73 @@ device_added(NMManager *manager, NMDevice *device, gpointer user_data)
g_list_free_full(objects, g_object_unref);
}
static void
device_removed(NMManager *manager, NMDevice *device, gpointer user_data)
{
NMIwdManager * self = user_data;
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
if (!NM_IS_DEVICE_IWD(device))
return;
if (priv->last_agent_call_device == NM_DEVICE_IWD(device))
priv->last_agent_call_device = NULL;
}
/* This is used to sort the list of objects returned by GetManagedObjects()
* based on the DBus interfaces available on these objects in such a way that
* the interface_added calls happen in the right order. The order is defined
* by how some DBus interfaces point to interfaces on other objects using
* DBus properties of the type "object path" ("o" signature). This creates
* "dependencies" between objects.
*
* When NM and IWD are running, the InterfacesAdded signals should come in
* an order that ensures consistency of those object paths. For example
* when a Network interface is added with a KnownNetwork property, or that
* property is assigned a new value, the KnownNetwork object pointed to by
* it will have been added in an earlier InterfacesAdded signal. Similarly
* Station.ConnectedNetwork and Station.GetOrdereNetworks() only point to
* existing Network objects. (There may be circular dependencies but during
* initialization we only need a subset of those properties that doesn't
* have this problem.)
*
* But GetManagedObjects doesn't guarantee this kind of consistency so we
* order the returned object list ourselves to simplify the job of
* interface_added(). Objects that don't have any interfaces listed in
* interface_order are moved to the end of the list.
*/
static int
object_compare_interfaces(gconstpointer a, gconstpointer b)
{
static const char *interface_order[] = {
NM_IWD_KNOWN_NETWORK_INTERFACE,
NM_IWD_NETWORK_INTERFACE,
NM_IWD_DEVICE_INTERFACE,
};
int rank_a = G_N_ELEMENTS(interface_order);
int rank_b = G_N_ELEMENTS(interface_order);
guint pos;
for (pos = 0; interface_order[pos]; pos++) {
GDBusInterface *iface_a;
GDBusInterface *iface_b;
if (rank_a == G_N_ELEMENTS(interface_order)
&& (iface_a = g_dbus_object_get_interface(G_DBUS_OBJECT(a), interface_order[pos]))) {
rank_a = pos;
g_object_unref(iface_a);
}
if (rank_b == G_N_ELEMENTS(interface_order)
&& (iface_b = g_dbus_object_get_interface(G_DBUS_OBJECT(b), interface_order[pos]))) {
rank_b = pos;
g_object_unref(iface_b);
}
}
return rank_a - rank_b;
}
static void
got_object_manager(GObject *object, GAsyncResult *result, gpointer user_data)
{
@ -894,6 +1200,7 @@ got_object_manager(GObject *object, GAsyncResult *result, gpointer user_data)
g_hash_table_remove_all(priv->known_networks);
objects = g_dbus_object_manager_get_objects(object_manager);
objects = g_list_sort(objects, object_compare_interfaces);
for (iter = objects; iter; iter = iter->next)
object_added(NULL, G_DBUS_OBJECT(iter->data), self);
@ -930,6 +1237,47 @@ nm_iwd_manager_is_known_network(NMIwdManager *self, const char *name, NMIwdNetwo
return g_hash_table_contains(priv->known_networks, &kn_id);
}
NMSettingsConnection *
nm_iwd_manager_get_ap_mirror_connection(NMIwdManager *self, NMWifiAP *ap)
{
NMIwdManagerPrivate * priv = NM_IWD_MANAGER_GET_PRIVATE(self);
KnownNetworkData * data;
char name_buf[33];
KnownNetworkId kn_id = {name_buf, NM_IWD_NETWORK_SECURITY_NONE};
const guint8 * ssid_bytes;
gsize ssid_len;
NM80211ApFlags flags = nm_wifi_ap_get_flags(ap);
NM80211ApSecurityFlags sec_flags = nm_wifi_ap_get_wpa_flags(ap) | nm_wifi_ap_get_rsn_flags(ap);
ssid_bytes = g_bytes_get_data(nm_wifi_ap_get_ssid(ap), &ssid_len);
ssid_len = MIN(ssid_len, 32);
memcpy(name_buf, ssid_bytes, ssid_len);
name_buf[ssid_len] = '\0';
if (flags & NM_802_11_AP_FLAGS_PRIVACY)
kn_id.security = NM_IWD_NETWORK_SECURITY_WEP;
if (sec_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
kn_id.security = NM_IWD_NETWORK_SECURITY_PSK;
else if (sec_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
kn_id.security = NM_IWD_NETWORK_SECURITY_8021X;
/* Right now it's easier for us to do a name+security lookup than to use
* the Network.KnownNetwork property to look up by path.
*/
data = g_hash_table_lookup(priv->known_networks, &kn_id);
if (data)
return data->mirror_connection;
/* We have no KnownNetwork for this AP, we're probably connecting to it for
* the first time. This is not a usual/supported scenario so we don't need
* to bother too much about creating a great mirror connection, we don't
* even have any more information than the Name & Type properties on the
* Network interface. This *should* never happen for an 8021x type network.
*/
return mirror_connection(self, &kn_id, TRUE, NULL);
}
GDBusProxy *
nm_iwd_manager_get_dbus_interface(NMIwdManager *self, const char *path, const char *name)
{
@ -955,6 +1303,7 @@ nm_iwd_manager_init(NMIwdManager *self)
priv->manager = g_object_ref(NM_MANAGER_GET);
g_signal_connect(priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK(device_added), self);
g_signal_connect(priv->manager, NM_MANAGER_DEVICE_REMOVED, G_CALLBACK(device_removed), self);
priv->settings = g_object_ref(NM_SETTINGS_GET);
g_signal_connect(priv->settings,
@ -997,6 +1346,8 @@ dispose(GObject *object)
g_clear_object(&priv->manager);
}
priv->last_agent_call_device = NULL;
G_OBJECT_CLASS(nm_iwd_manager_parent_class)->dispose(object);
}

View file

@ -8,6 +8,7 @@
#include "devices/nm-device.h"
#include "nm-wifi-utils.h"
#include "nm-wifi-ap.h"
#define NM_IWD_BUS_TYPE G_BUS_TYPE_SYSTEM
#define NM_IWD_SERVICE "net.connman.iwd"
@ -44,6 +45,8 @@ gboolean nm_iwd_manager_is_known_network(NMIwdManager * self,
const char * name,
NMIwdNetworkSecurity security);
NMSettingsConnection *nm_iwd_manager_get_ap_mirror_connection(NMIwdManager *self, NMWifiAP *ap);
GDBusProxy *
nm_iwd_manager_get_dbus_interface(NMIwdManager *self, const char *path, const char *name);

View file

@ -342,6 +342,18 @@ nm_wifi_ap_get_metered(const NMWifiAP *self)
return NM_WIFI_AP_GET_PRIVATE(self)->metered;
}
NM80211ApSecurityFlags
nm_wifi_ap_get_wpa_flags(const NMWifiAP *self)
{
return NM_WIFI_AP_GET_PRIVATE(self)->wpa_flags;
}
NM80211ApSecurityFlags
nm_wifi_ap_get_rsn_flags(const NMWifiAP *self)
{
return NM_WIFI_AP_GET_PRIVATE(self)->rsn_flags;
}
/*****************************************************************************/
gboolean

View file

@ -64,22 +64,24 @@ nm_wifi_ap_get_supplicant_path(NMWifiAP *ap)
return ap->_supplicant_path;
}
GBytes * nm_wifi_ap_get_ssid(const NMWifiAP *ap);
gboolean nm_wifi_ap_set_ssid(NMWifiAP *ap, GBytes *ssid);
const char * nm_wifi_ap_get_address(const NMWifiAP *ap);
gboolean nm_wifi_ap_set_address(NMWifiAP *ap, const char *addr);
NM80211Mode nm_wifi_ap_get_mode(NMWifiAP *ap);
gboolean nm_wifi_ap_is_hotspot(NMWifiAP *ap);
gint8 nm_wifi_ap_get_strength(NMWifiAP *ap);
gboolean nm_wifi_ap_set_strength(NMWifiAP *ap, gint8 strength);
guint32 nm_wifi_ap_get_freq(NMWifiAP *ap);
gboolean nm_wifi_ap_set_freq(NMWifiAP *ap, guint32 freq);
guint32 nm_wifi_ap_get_max_bitrate(NMWifiAP *ap);
gboolean nm_wifi_ap_set_max_bitrate(NMWifiAP *ap, guint32 bitrate);
gboolean nm_wifi_ap_get_fake(const NMWifiAP *ap);
gboolean nm_wifi_ap_set_fake(NMWifiAP *ap, gboolean fake);
NM80211ApFlags nm_wifi_ap_get_flags(const NMWifiAP *self);
gboolean nm_wifi_ap_get_metered(const NMWifiAP *self);
GBytes * nm_wifi_ap_get_ssid(const NMWifiAP *ap);
gboolean nm_wifi_ap_set_ssid(NMWifiAP *ap, GBytes *ssid);
const char * nm_wifi_ap_get_address(const NMWifiAP *ap);
gboolean nm_wifi_ap_set_address(NMWifiAP *ap, const char *addr);
NM80211Mode nm_wifi_ap_get_mode(NMWifiAP *ap);
gboolean nm_wifi_ap_is_hotspot(NMWifiAP *ap);
gint8 nm_wifi_ap_get_strength(NMWifiAP *ap);
gboolean nm_wifi_ap_set_strength(NMWifiAP *ap, gint8 strength);
guint32 nm_wifi_ap_get_freq(NMWifiAP *ap);
gboolean nm_wifi_ap_set_freq(NMWifiAP *ap, guint32 freq);
guint32 nm_wifi_ap_get_max_bitrate(NMWifiAP *ap);
gboolean nm_wifi_ap_set_max_bitrate(NMWifiAP *ap, guint32 bitrate);
gboolean nm_wifi_ap_get_fake(const NMWifiAP *ap);
gboolean nm_wifi_ap_set_fake(NMWifiAP *ap, gboolean fake);
NM80211ApFlags nm_wifi_ap_get_flags(const NMWifiAP *self);
gboolean nm_wifi_ap_get_metered(const NMWifiAP *self);
NM80211ApSecurityFlags nm_wifi_ap_get_wpa_flags(const NMWifiAP *self);
NM80211ApSecurityFlags nm_wifi_ap_get_rsn_flags(const NMWifiAP *self);
const char *
nm_wifi_ap_to_string(const NMWifiAP *self, char *str_buf, gulong buf_len, gint64 now_msec);

View file

@ -886,34 +886,49 @@ nm_wifi_utils_is_manf_default_ssid(GBytes *ssid)
return FALSE;
}
NMIwdNetworkSecurity
nm_wifi_connection_get_iwd_security(NMConnection *connection, gboolean *mapped)
/* To be used for connections where the SSID has been validated before */
gboolean
nm_wifi_connection_get_iwd_ssid_and_security(NMConnection * connection,
char ** ssid,
NMIwdNetworkSecurity *security)
{
NMSettingWireless * s_wireless;
NMSettingWirelessSecurity *s_wireless_sec;
const char * key_mgmt = NULL;
if (!nm_connection_get_setting_wireless(connection))
goto error;
s_wireless = nm_connection_get_setting_wireless(connection);
if (!s_wireless)
return FALSE;
NM_SET_OUT(mapped, TRUE);
if (ssid) {
GBytes * bytes = nm_setting_wireless_get_ssid(s_wireless);
gsize ssid_len;
const char *ssid_str = (const char *) g_bytes_get_data(bytes, &ssid_len);
nm_assert(bytes && g_utf8_validate(ssid_str, ssid_len, NULL));
NM_SET_OUT(ssid, g_strndup(ssid_str, ssid_len));
}
if (!security)
return TRUE;
s_wireless_sec = nm_connection_get_setting_wireless_security(connection);
if (!s_wireless_sec)
return NM_IWD_NETWORK_SECURITY_NONE;
if (!s_wireless_sec) {
NM_SET_OUT(security, NM_IWD_NETWORK_SECURITY_NONE);
return TRUE;
}
key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wireless_sec);
nm_assert(key_mgmt);
if (NM_IN_STRSET(key_mgmt, "none", "ieee8021x"))
return NM_IWD_NETWORK_SECURITY_WEP;
NM_SET_OUT(security, NM_IWD_NETWORK_SECURITY_WEP);
else if (nm_streq(key_mgmt, "wpa-psk"))
NM_SET_OUT(security, NM_IWD_NETWORK_SECURITY_PSK);
else if (nm_streq(key_mgmt, "wpa-eap"))
NM_SET_OUT(security, NM_IWD_NETWORK_SECURITY_8021X);
else
return FALSE;
if (nm_streq(key_mgmt, "wpa-psk"))
return NM_IWD_NETWORK_SECURITY_PSK;
if (nm_streq(key_mgmt, "wpa-eap"))
return NM_IWD_NETWORK_SECURITY_8021X;
error:
NM_SET_OUT(mapped, FALSE);
return NM_IWD_NETWORK_SECURITY_NONE;
return TRUE;
}

View file

@ -32,7 +32,8 @@ gboolean nm_wifi_utils_complete_connection(GBytes * ssid,
gboolean nm_wifi_utils_is_manf_default_ssid(GBytes *ssid);
NMIwdNetworkSecurity nm_wifi_connection_get_iwd_security(NMConnection *connection,
gboolean * mapped);
gboolean nm_wifi_connection_get_iwd_ssid_and_security(NMConnection * connection,
char ** ssid,
NMIwdNetworkSecurity *security);
#endif /* __NM_WIFI_UTILS_H__ */

View file

@ -884,6 +884,7 @@ static const ConfigGroup config_groups[] = {
NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS,
NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND,
NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS,
NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_IWD_AUTOCONNECT,
NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE,
NM_CONFIG_KEYFILE_KEY_STOP_MATCH, ),
},

View file

@ -86,6 +86,7 @@
#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND "wifi.backend"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS "wifi.scan-rand-mac-address"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT "carrier-wait-timeout"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_IWD_AUTOCONNECT "wifi.iwd.autoconnect"
#define NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE "match-device"
#define NM_CONFIG_KEYFILE_KEY_STOP_MATCH "stop-match"