summary refs log tree commit diff
path: root/src/devices/nm-device-macsec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/nm-device-macsec.c')
-rw-r--r--src/devices/nm-device-macsec.c332
1 files changed, 200 insertions, 132 deletions
diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c
index c9592a49..f1878078 100644
--- a/src/devices/nm-device-macsec.c
+++ b/src/devices/nm-device-macsec.c
@@ -23,16 +23,9 @@ _LOG_DECLARE_SELF(NMDeviceMacsec);
 
 /*****************************************************************************/
 
-typedef struct Supplicant {
-	NMSupplicantManager *mgr;
-	NMSupplicantInterface *iface;
+#define SUPPLICANT_LNK_TIMEOUT_SEC 15
 
-	/* signal handler ids */
-	gulong iface_state_id;
-
-	/* Timeouts and idles */
-	guint con_timeout_id;
-} Supplicant;
+/*****************************************************************************/
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceMacsec,
 	PROP_SCI,
@@ -53,8 +46,20 @@ typedef struct {
 	NMPlatformLnkMacsec props;
 	gulong parent_state_id;
 	gulong parent_mtu_id;
-	Supplicant supplicant;
-	guint supplicant_timeout_id;
+
+	struct {
+		NMSupplicantManager *mgr;
+		NMSupplMgrCreateIfaceHandle *create_handle;
+		NMSupplicantInterface *iface;
+
+		gulong iface_state_id;
+
+		guint con_timeout_id;
+		guint lnk_timeout_id;
+
+		bool is_associated:1;
+	} supplicant;
+
 	NMActRequestGetSecretsCallId *macsec_secrets_id;
 } NMDeviceMacsecPrivate;
 
@@ -69,7 +74,7 @@ struct _NMDeviceMacsecClass {
 
 G_DEFINE_TYPE (NMDeviceMacsec, nm_device_macsec, NM_TYPE_DEVICE)
 
-#define NM_DEVICE_MACSEC_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceMacsec, NM_IS_DEVICE_MACSEC)
+#define NM_DEVICE_MACSEC_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceMacsec, NM_IS_DEVICE_MACSEC, NMDevice)
 
 /******************************************************************/
 
@@ -77,7 +82,8 @@ static void macsec_secrets_cancel (NMDeviceMacsec *self);
 
 /******************************************************************/
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (validation_mode_to_string, guint8,
+static
+NM_UTILS_LOOKUP_STR_DEFINE (validation_mode_to_string, guint8,
 	NM_UTILS_LOOKUP_DEFAULT_WARN ("<unknown>"),
 	NM_UTILS_LOOKUP_STR_ITEM (0, "disable"),
 	NM_UTILS_LOOKUP_STR_ITEM (1, "check"),
@@ -226,7 +232,7 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error)
 	mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)),
 	                                nm_device_get_ifindex (NM_DEVICE (self)));
 
-	config = nm_supplicant_config_new (FALSE, FALSE, FALSE, FALSE);
+	config = nm_supplicant_config_new (NM_SUPPL_CAP_MASK_NONE);
 
 	s_macsec = nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_MACSEC);
 
@@ -253,7 +259,9 @@ supplicant_interface_release (NMDeviceMacsec *self)
 {
 	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
 
-	nm_clear_g_source (&priv->supplicant_timeout_id);
+	nm_clear_pointer (&priv->supplicant.create_handle, nm_supplicant_manager_create_interface_cancel);
+
+	nm_clear_g_source (&priv->supplicant.lnk_timeout_id);
 	nm_clear_g_source (&priv->supplicant.con_timeout_id);
 	nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_state_id);
 
@@ -264,21 +272,6 @@ supplicant_interface_release (NMDeviceMacsec *self)
 }
 
 static void
-supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
-                           GError *error,
-                           gpointer user_data)
-{
-	NMDeviceMacsec *self = NM_DEVICE_MACSEC (user_data);
-
-	if (error && !nm_utils_error_is_cancelled (error, TRUE)) {
-		supplicant_interface_release (self);
-		nm_device_queue_state (NM_DEVICE (self),
-		                       NM_DEVICE_STATE_FAILED,
-		                       NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
-	}
-}
-
-static void
 macsec_secrets_cb (NMActRequest *req,
                    NMActRequestGetSecretsCallId *call_id,
                    NMSettingsConnection *connection,
@@ -313,7 +306,7 @@ macsec_secrets_cb (NMActRequest *req,
 		return;
 	}
 
-	nm_device_activate_schedule_stage1_device_prepare (device);
+	nm_device_activate_schedule_stage1_device_prepare (device, FALSE);
 }
 
 static void
@@ -350,7 +343,7 @@ macsec_secrets_get_secrets (NMDeviceMacsec *self,
 }
 
 static gboolean
-link_timeout_cb (gpointer user_data)
+supplicant_lnk_timeout_cb (gpointer user_data)
 {
 	NMDeviceMacsec *self = NM_DEVICE_MACSEC (user_data);
 	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
@@ -359,7 +352,7 @@ link_timeout_cb (gpointer user_data)
 	NMConnection *applied_connection;
 	const char *setting_name;
 
-	priv->supplicant_timeout_id = 0;
+	priv->supplicant.lnk_timeout_id = 0;
 
 	req = nm_device_get_act_request (dev);
 
@@ -367,7 +360,7 @@ link_timeout_cb (gpointer user_data)
 		nm_device_state_changed (dev,
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT);
-		return FALSE;
+		return G_SOURCE_REMOVE;
 	}
 
 	/* Disconnect event during initial authentication and credentials
@@ -391,13 +384,98 @@ link_timeout_cb (gpointer user_data)
 	nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
 	macsec_secrets_get_secrets (self, setting_name, NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW);
 
-	return FALSE;
+	return G_SOURCE_REMOVE;
 
 time_out:
 	_LOGW (LOGD_DEVICE | LOGD_ETHER, "link timed out.");
 	nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
 
-	return FALSE;
+	return G_SOURCE_REMOVE;
+}
+
+static void
+supplicant_iface_state_is_completed (NMDeviceMacsec *self,
+                                     NMSupplicantInterfaceState state)
+{
+	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
+
+	if (state == NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) {
+		nm_clear_g_source (&priv->supplicant.lnk_timeout_id);
+		nm_clear_g_source (&priv->supplicant.con_timeout_id);
+
+		nm_device_bring_up (NM_DEVICE (self), TRUE, NULL);
+
+		/* If this is the initial association during device activation,
+		 * schedule the next activation stage.
+		 */
+		if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_CONFIG) {
+			_LOGI (LOGD_DEVICE,
+			       "Activation: Stage 2 of 5 (Device Configure) successful.");
+			nm_device_activate_schedule_stage3_ip_config_start (NM_DEVICE (self));
+		}
+		return;
+	}
+
+	if (   !priv->supplicant.lnk_timeout_id
+	    && !priv->supplicant.con_timeout_id)
+		priv->supplicant.lnk_timeout_id = g_timeout_add_seconds (SUPPLICANT_LNK_TIMEOUT_SEC, supplicant_lnk_timeout_cb, self);
+}
+
+static void
+supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
+                           GError *error,
+                           gpointer user_data)
+{
+	NMDeviceMacsec *self;
+	NMDeviceMacsecPrivate *priv;
+
+	if (nm_utils_error_is_cancelled_or_disposing (error))
+		return;
+
+	self = user_data;
+	priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
+
+	if (error) {
+		supplicant_interface_release (self);
+		nm_device_queue_state (NM_DEVICE (self),
+		                       NM_DEVICE_STATE_FAILED,
+		                       NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+		return;
+	}
+
+	nm_assert (!priv->supplicant.lnk_timeout_id);
+	nm_assert (!priv->supplicant.is_associated);
+
+	priv->supplicant.is_associated = TRUE;
+	supplicant_iface_state_is_completed (self,
+	                                     nm_supplicant_interface_get_state (priv->supplicant.iface));
+}
+
+static gboolean
+supplicant_iface_start (NMDeviceMacsec *self)
+{
+	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
+	gs_unref_object NMSupplicantConfig *config = NULL;
+	gs_free_error GError *error = NULL;
+
+	config = build_supplicant_config (self, &error);
+	if (!config) {
+		_LOGE (LOGD_DEVICE,
+		       "Activation: couldn't build security configuration: %s",
+		       error->message);
+		supplicant_interface_release (self);
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+		return FALSE;
+	}
+
+	nm_supplicant_interface_disconnect (priv->supplicant.iface);
+	nm_supplicant_interface_assoc (priv->supplicant.iface,
+	                               config,
+	                               supplicant_iface_assoc_cb,
+	                               self);
+	return TRUE;
 }
 
 static void
@@ -409,76 +487,31 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 {
 	NMDeviceMacsec *self = NM_DEVICE_MACSEC (user_data);
 	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
-	NMDevice *device = NM_DEVICE (self);
-	NMSupplicantConfig *config;
-	NMDeviceState devstate;
-	GError *error = NULL;
 	NMSupplicantInterfaceState new_state = new_state_i;
 	NMSupplicantInterfaceState old_state = old_state_i;
 
-	if (new_state == old_state)
-		return;
-
 	_LOGI (LOGD_DEVICE, "supplicant interface state: %s -> %s",
 	       nm_supplicant_interface_state_to_string (old_state),
 	       nm_supplicant_interface_state_to_string (new_state));
 
-	devstate = nm_device_get_state (device);
-
-	switch (new_state) {
-	case NM_SUPPLICANT_INTERFACE_STATE_READY:
-		config = build_supplicant_config (self, &error);
-		if (config) {
-			nm_supplicant_interface_assoc (priv->supplicant.iface, config,
-			                               supplicant_iface_assoc_cb, self);
-			g_object_unref (config);
-		} else {
-			_LOGE (LOGD_DEVICE,
-			       "Activation: couldn't build security configuration: %s",
-			       error->message);
-			g_clear_error (&error);
-
-			nm_device_state_changed (device,
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
-		}
-		break;
-	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
-		nm_clear_g_source (&priv->supplicant_timeout_id);
-		nm_clear_g_source (&priv->supplicant.con_timeout_id);
-		nm_device_bring_up (device, TRUE, NULL);
-
-		/* If this is the initial association during device activation,
-		 * schedule the next activation stage.
-		 */
-		if (devstate == NM_DEVICE_STATE_CONFIG) {
-			_LOGI (LOGD_DEVICE,
-			       "Activation: Stage 2 of 5 (Device Configure) successful.");
-			nm_device_activate_schedule_stage3_ip_config_start (device);
-		}
-		break;
-	case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
-		if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) {
-			/* Start the link timeout so we allow some time for reauthentication */
-			if (!priv->supplicant_timeout_id)
-				priv->supplicant_timeout_id = g_timeout_add_seconds (15, link_timeout_cb, device);
-		}
-		break;
-	case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
+	if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
 		supplicant_interface_release (self);
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+		return;
+	}
 
-		if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) {
-			nm_device_state_changed (device,
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-		}
-		break;
-	default:
-		;
+	if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) {
+		if (!supplicant_iface_start (self))
+			return;
 	}
+
+	if (priv->supplicant.is_associated)
+		supplicant_iface_state_is_completed (self, new_state);
 }
 
-static NMActStageReturn
+static gboolean
 handle_auth_or_fail (NMDeviceMacsec *self,
                      NMActRequest *req,
                      gboolean new_secrets)
@@ -487,7 +520,7 @@ handle_auth_or_fail (NMDeviceMacsec *self,
 	NMConnection *applied_connection;
 
 	if (!nm_device_auth_retries_try_next (NM_DEVICE (self)))
-		return NM_ACT_STAGE_RETURN_FAILURE;
+		return FALSE;
 
 	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
 
@@ -497,13 +530,13 @@ handle_auth_or_fail (NMDeviceMacsec *self,
 	setting_name = nm_connection_need_secrets (applied_connection, NULL);
 	if (!setting_name) {
 		_LOGI (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
-		return NM_ACT_STAGE_RETURN_FAILURE;
+		return FALSE;
 	}
 
 	macsec_secrets_get_secrets (self, setting_name,
 	                              NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
 	                            | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0));
-	return NM_ACT_STAGE_RETURN_POSTPONE;
+	return TRUE;
 }
 
 static gboolean
@@ -526,11 +559,10 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	       "Activation: (macsec) association took too long.");
 
 	supplicant_interface_release (self);
-	req = nm_device_get_act_request (device);
-	g_assert (req);
 
+	req = nm_device_get_act_request (device);
 	connection = nm_act_request_get_settings_connection (req);
-	g_assert (connection);
+	g_return_val_if_fail (connection, G_SOURCE_REMOVE);
 
 	/* Ask for new secrets only if we've never activated this connection
 	 * before.  If we've connected before, don't bother the user with dialogs,
@@ -539,48 +571,73 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	if (nm_settings_connection_get_timestamp (connection, &timestamp))
 		new_secrets = !timestamp;
 
-	if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE)
-		_LOGW (LOGD_DEVICE, "Activation: (macsec) asking for new secrets");
-	else
+	if (!handle_auth_or_fail (self, req, new_secrets)) {
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS);
+		return G_SOURCE_REMOVE;
+	}
+
+	_LOGW (LOGD_DEVICE, "Activation: (macsec) asking for new secrets");
 
-	return FALSE;
+	if (   !priv->supplicant.lnk_timeout_id
+	    && priv->supplicant.iface) {
+		NMSupplicantInterfaceState state;
+
+		state = nm_supplicant_interface_get_state (priv->supplicant.iface);
+		if (state != NM_SUPPLICANT_INTERFACE_STATE_COMPLETED
+		    && nm_supplicant_interface_state_is_operational (state))
+			priv->supplicant.lnk_timeout_id = g_timeout_add_seconds (SUPPLICANT_LNK_TIMEOUT_SEC, supplicant_lnk_timeout_cb, self);
+	}
+
+	return G_SOURCE_REMOVE;
 }
 
-static gboolean
-supplicant_interface_init (NMDeviceMacsec *self)
+static void
+supplicant_interface_create_cb (NMSupplicantManager *supplicant_manager,
+                                NMSupplMgrCreateIfaceHandle *handle,
+                                NMSupplicantInterface *iface,
+                                GError *error,
+                                gpointer user_data)
 {
-	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
-	NMDevice *parent;
+	NMDeviceMacsec *self;
+	NMDeviceMacsecPrivate *priv;
 	guint timeout;
 
-	parent = nm_device_parent_get_device (NM_DEVICE (self));
-	g_return_val_if_fail (parent, FALSE);
+	if (nm_utils_error_is_cancelled (error))
+		return;
 
-	supplicant_interface_release (self);
+	self = user_data;
+	priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
 
-	priv->supplicant.iface = nm_supplicant_manager_create_interface (priv->supplicant.mgr,
-	                                                                 nm_device_get_iface (parent),
-	                                                                 NM_SUPPLICANT_DRIVER_MACSEC);
+	nm_assert (priv->supplicant.create_handle == handle);
 
-	if (!priv->supplicant.iface) {
+	priv->supplicant.create_handle = NULL;
+
+	if (error) {
 		_LOGE (LOGD_DEVICE,
-		       "Couldn't initialize supplicant interface");
-		return FALSE;
+		       "Couldn't initialize supplicant interface: %s",
+		       error->message);
+		supplicant_interface_release (self);
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+		return;
 	}
 
-	/* Listen for its state signals */
+	priv->supplicant.iface = g_object_ref (iface);
+	priv->supplicant.is_associated = FALSE;
+
 	priv->supplicant.iface_state_id = g_signal_connect (priv->supplicant.iface,
 	                                                    NM_SUPPLICANT_INTERFACE_STATE,
 	                                                    G_CALLBACK (supplicant_iface_state_cb),
 	                                                    self);
 
-	/* Set up a timeout on the connection attempt  */
 	timeout = nm_device_get_supplicant_timeout (NM_DEVICE (self));
 	priv->supplicant.con_timeout_id = g_timeout_add_seconds (timeout,
 	                                                         supplicant_connection_timeout_cb,
 	                                                         self);
-	return TRUE;
+
+	if (nm_supplicant_interface_state_is_operational (nm_supplicant_interface_get_state (iface)))
+		supplicant_iface_start (self);
 }
 
 static NMActStageReturn
@@ -589,8 +646,9 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	NMDeviceMacsec *self = NM_DEVICE_MACSEC (device);
 	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
 	NMConnection *connection;
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	NMDevice *parent;
 	const char *setting_name;
+	int ifindex;
 
 	connection = nm_device_get_applied_connection (NM_DEVICE (self));
 
@@ -608,21 +666,31 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		       "Activation: connection '%s' has security, but secrets are required.",
 		       nm_connection_get_id (connection));
 
-		ret = handle_auth_or_fail (self, req, FALSE);
-		if (ret != NM_ACT_STAGE_RETURN_POSTPONE)
+		if (!handle_auth_or_fail (self, req, FALSE)) {
 			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
-	} else {
-		_LOGI (LOGD_DEVICE | LOGD_ETHER,
-		       "Activation: connection '%s' requires no security. No secrets needed.",
-		       nm_connection_get_id (connection));
+			return NM_ACT_STAGE_RETURN_FAILURE;
+		}
 
-		if (supplicant_interface_init (self))
-			ret = NM_ACT_STAGE_RETURN_POSTPONE;
-		else
-			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+		return NM_ACT_STAGE_RETURN_POSTPONE;
 	}
 
-	return ret;
+	_LOGI (LOGD_DEVICE | LOGD_ETHER,
+	       "Activation: connection '%s' requires no security. No secrets needed.",
+	       nm_connection_get_id (connection));
+
+	supplicant_interface_release (self);
+
+	parent = nm_device_parent_get_device (NM_DEVICE (self));
+	g_return_val_if_fail (parent, NM_ACT_STAGE_RETURN_FAILURE);
+	ifindex = nm_device_get_ifindex (parent);
+	g_return_val_if_fail (ifindex > 0, NM_ACT_STAGE_RETURN_FAILURE);
+
+	priv->supplicant.create_handle = nm_supplicant_manager_create_interface (priv->supplicant.mgr,
+	                                                                         ifindex,
+	                                                                         NM_SUPPLICANT_DRIVER_MACSEC,
+	                                                                         supplicant_interface_create_cb,
+	                                                                         self);
+	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
 static void