summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Zaborowski <andrew.zaborowski@intel.com>2022-08-13 02:37:18 +0200
committerThomas Haller <thaller@redhat.com>2022-08-24 20:44:46 +0200
commit824f2f26db465c67339260e10a728ef6f25a4c7f (patch)
tree37667f264d15906343f8887193dc51691aa5af1c
parentf6cec3b584ed263986ca9ececf345d3a52a8c8f9 (diff)
iwd: Work around timing when new 802.1x connection activated
Try work around the issue documented by Emil Velikov in https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1264 When we mirror an 802.1x connection to an IWD config file and there's an AP in range with matching SSID, that connection should become available for activation. In IWD terms when an 802.1x network becomes a Known Network, it can be connected to using the .Connect D-Bus method. However there's a delay between writing the IWD config file and receiving the InterfaceAdded event for the Known Network so we don't immediately find out that the network can now be used. If an NM client creates a new connection for an 802.1x AP and tries to activate it quickly enough, NMDeviceIWD will not allow it to because it won't know the network is known yet. To work around this, we save the SSIDs of 802.1x connections we recently mirrored to IWD config files, for an arbitrary 2 seconds period, and we treat them as Known Networks in that period since in theory activations should succeed. The alternative proposed in the !1264 is to drop NMDeviceIWD checks that there's a Known Network for the 802.1x connection being activated since IWD will eventually perform the same checks and IWD is the ultimate authority on whether the profile is IWD-connectable.
-rw-r--r--src/core/devices/wifi/nm-device-iwd.c11
-rw-r--r--src/core/devices/wifi/nm-iwd-manager.c82
-rw-r--r--src/core/devices/wifi/nm-iwd-manager.h2
3 files changed, 92 insertions, 3 deletions
diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c
index 73232e3414..300ec7c94c 100644
--- a/src/core/devices/wifi/nm-device-iwd.c
+++ b/src/core/devices/wifi/nm-device-iwd.c
@@ -799,7 +799,8 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
* thus are Known Networks.
*/
if (security == NM_IWD_NETWORK_SECURITY_8021X) {
- if (!is_connection_known_network(connection)) {
+ if (!is_connection_known_network(connection)
+ && !nm_iwd_manager_is_recently_mirrored(nm_iwd_manager_get(), ssid)) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
"802.1x connections must have IWD provisioning files");
@@ -932,7 +933,9 @@ check_connection_available(NMDevice *device,
*/
if (nm_wifi_connection_get_iwd_ssid_and_security(connection, NULL, &security)
&& security == NM_IWD_NETWORK_SECURITY_8021X) {
- if (!is_ap_known_network(ap)) {
+ if (!is_ap_known_network(ap)
+ && !nm_iwd_manager_is_recently_mirrored(nm_iwd_manager_get(),
+ nm_setting_wireless_get_ssid(s_wifi))) {
nm_utils_error_set_literal(
error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
@@ -2327,7 +2330,9 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
* fail, for other combinations we will let the Connect call fail
* or ask us for any missing secrets through the Agent.
*/
- if (nm_connection_get_setting_802_1x(connection) && !is_ap_known_network(ap)) {
+ if (nm_connection_get_setting_802_1x(connection) && !is_ap_known_network(ap)
+ && !nm_iwd_manager_is_recently_mirrored(nm_iwd_manager_get(),
+ nm_setting_wireless_get_ssid(s_wireless))) {
_LOGI(LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) access point '%s' has 802.1x security but is not configured "
"in IWD.",
diff --git a/src/core/devices/wifi/nm-iwd-manager.c b/src/core/devices/wifi/nm-iwd-manager.c
index 2e0d51e5d8..ec111329a2 100644
--- a/src/core/devices/wifi/nm-iwd-manager.c
+++ b/src/core/devices/wifi/nm-iwd-manager.c
@@ -47,6 +47,11 @@ typedef struct {
} KnownNetworkData;
typedef struct {
+ GBytes *ssid;
+ gint64 timestamp;
+} RecentlyMirroredData;
+
+typedef struct {
NMManager *manager;
NMSettings *settings;
GCancellable *cancellable;
@@ -62,6 +67,7 @@ typedef struct {
GHashTable *p2p_devices;
NMIwdWfdInfo wfd_info;
guint wfd_use_count;
+ GSList *recently_mirrored;
} NMIwdManagerPrivate;
struct _NMIwdManager {
@@ -353,6 +359,70 @@ register_agent(NMIwdManager *self)
/*****************************************************************************/
+static void
+recently_mirrored_data_free(void *data)
+{
+ RecentlyMirroredData *rmd = data;
+
+ g_bytes_unref(rmd->ssid);
+ g_free(rmd);
+}
+
+/* When we mirror an 802.1x connection to an IWD config file, and there's an
+ * AP in range with matching SSID, that connection should become available
+ * for activation. In IWD terms when an 802.1x network becomes a Known
+ * Network, it can be connected to using the .Connect D-Bus method.
+ *
+ * However there's a delay between writing the IWD config file and receiving
+ * the InterfaceAdded event for the Known Network so we don't immediately
+ * find out that the network can now be used. If an NM client creates a
+ * new connection for an 802.1x AP and tries to activate it immediately,
+ * NMDeviceIWD will not allow it to because it doesn't know the network is
+ * known yet. To work around this, we save the SSIDs of 802.1x connections
+ * we recently mirrored to IWD config files, for 2 seconds, and we treat
+ * them as Known Networks in that period since in theory activations should
+ * succeed.
+ */
+bool
+nm_iwd_manager_is_recently_mirrored(NMIwdManager *self, const GBytes *ssid)
+{
+ NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
+ gint64 now = nm_utils_get_monotonic_timestamp_nsec();
+ GSList *iter;
+ RecentlyMirroredData *rmd;
+
+ /* Drop entries older than 2 seconds */
+ while (priv->recently_mirrored) {
+ rmd = priv->recently_mirrored->data;
+ if (now < rmd->timestamp + 2000000000)
+ break;
+
+ priv->recently_mirrored = g_slist_remove(priv->recently_mirrored, rmd);
+ recently_mirrored_data_free(rmd);
+ }
+
+ for (iter = priv->recently_mirrored; iter; iter = iter->next) {
+ rmd = iter->data;
+ if (g_bytes_equal(ssid, rmd->ssid))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+save_mirrored(NMIwdManager *self, GBytes *ssid)
+{
+ NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
+ RecentlyMirroredData *rmd = g_malloc(sizeof(RecentlyMirroredData));
+
+ rmd->ssid = g_bytes_ref(ssid);
+ rmd->timestamp = nm_utils_get_monotonic_timestamp_nsec();
+ priv->recently_mirrored = g_slist_append(priv->recently_mirrored, rmd);
+}
+
+/*****************************************************************************/
+
static KnownNetworkId *
known_network_id_new(const char *name, NMIwdNetworkSecurity security)
{
@@ -721,6 +791,9 @@ sett_conn_changed(NMSettingsConnection *sett_conn,
"iwd: changed Wi-Fi connection %s mirrored as IWD profile %s",
nm_settings_connection_get_id(sett_conn),
full_path);
+
+ if (security == NM_IWD_NETWORK_SECURITY_8021X)
+ save_mirrored(nm_iwd_manager_get(), ssid);
}
/* Look up an existing NMSettingsConnection for a network that has been
@@ -1283,6 +1356,7 @@ connection_added(NMSettings *settings, NMSettingsConnection *sett_conn, gpointer
gs_free_error GError *error = NULL;
nm_auto_unref_keyfile GKeyFile *iwd_config = NULL;
NMSettingsConnectionIntFlags flags;
+ NMIwdNetworkSecurity security;
if (!nm_streq(nm_settings_connection_get_connection_type(sett_conn), "802-11-wireless"))
return;
@@ -1338,6 +1412,12 @@ connection_added(NMSettings *settings, NMSettingsConnection *sett_conn, gpointer
_LOGD("New Wi-Fi connection %s mirrored as IWD profile %s",
nm_settings_connection_get_id(sett_conn),
full_path);
+
+ if (nm_wifi_connection_get_iwd_ssid_and_security(conn, NULL, &security)
+ && security == NM_IWD_NETWORK_SECURITY_8021X) {
+ NMSettingWireless *s_wifi = nm_connection_get_setting_wireless(conn);
+ save_mirrored(nm_iwd_manager_get(), nm_setting_wireless_get_ssid(s_wifi));
+ }
}
static gboolean
@@ -1952,6 +2032,8 @@ dispose(GObject *object)
g_hash_table_unref(nm_steal_pointer(&priv->p2p_devices));
+ g_slist_free_full(nm_steal_pointer(&priv->recently_mirrored), recently_mirrored_data_free);
+
G_OBJECT_CLASS(nm_iwd_manager_parent_class)->dispose(object);
}
diff --git a/src/core/devices/wifi/nm-iwd-manager.h b/src/core/devices/wifi/nm-iwd-manager.h
index 02cd6bba50..80a2753462 100644
--- a/src/core/devices/wifi/nm-iwd-manager.h
+++ b/src/core/devices/wifi/nm-iwd-manager.h
@@ -63,4 +63,6 @@ gboolean nm_iwd_manager_check_wfd_info_compatible(NMIwdManager *self, const NMIw
gboolean nm_iwd_manager_register_wfd(NMIwdManager *self, const NMIwdWfdInfo *wfd_info);
void nm_iwd_manager_unregister_wfd(NMIwdManager *self);
+bool nm_iwd_manager_is_recently_mirrored(NMIwdManager *self, const GBytes *ssid);
+
#endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */