summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2017-07-03 16:24:59 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2017-08-05 08:03:16 +0200
commit6c3195931e94cab70208ce97f3b834f5d9f5ff62 (patch)
treeb6fce96b2e395052833c65f574507d2f82c4db85
parent695f6ceebb7c2cc96e50e3156e7938616e021553 (diff)
downloadNetworkManager-6c3195931e94cab70208ce97f3b834f5d9f5ff62.tar.gz
core: implement activation of PPP devices
Add code to NMPppDevice to activate new-style PPPoE connections. This is a bit tricky because we can't create the link as usual in create_and_realize(). Instead, we create a device without ifindex and start pppd in stage2; when pppd reports a new configuration, we rename the platform link to the correct name and set the ifindex into the device. This mechanism is inherently racy, but there is no way to tell pppd to create an arbitrary interface name.
-rw-r--r--src/devices/nm-device-ppp.c240
-rw-r--r--src/devices/nm-device-private.h2
-rw-r--r--src/devices/nm-device.c34
-rw-r--r--src/nm-manager.c18
-rw-r--r--src/nm-manager.h2
5 files changed, 293 insertions, 3 deletions
diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c
index d0f2c9526e..7c034f5f85 100644
--- a/src/devices/nm-device-ppp.c
+++ b/src/devices/nm-device-ppp.c
@@ -16,9 +16,15 @@
#include "nm-device-ppp.h"
+#include "nm-act-request.h"
#include "nm-device-factory.h"
#include "nm-device-private.h"
+#include "nm-manager.h"
+#include "nm-setting-pppoe.h"
#include "platform/nm-platform.h"
+#include "ppp/nm-ppp-manager.h"
+#include "ppp/nm-ppp-manager-call.h"
+#include "ppp/nm-ppp-status.h"
#include "introspection/org.freedesktop.NetworkManager.Device.Ppp.h"
@@ -28,7 +34,9 @@ _LOG_DECLARE_SELF(NMDevicePpp);
/*****************************************************************************/
typedef struct _NMDevicePppPrivate {
- int dummy;
+ NMPPPManager *ppp_manager;
+ NMIP4Config *pending_ip4_config;
+ char *pending_ifname;
} NMDevicePppPrivate;
struct _NMDevicePpp {
@@ -44,6 +52,24 @@ G_DEFINE_TYPE (NMDevicePpp, nm_device_ppp, NM_TYPE_DEVICE)
#define NM_DEVICE_PPP_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDevicePpp, NM_IS_DEVICE_PPP)
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+ NMSettingPppoe *s_pppoe;
+
+ if (!NM_DEVICE_CLASS (nm_device_ppp_parent_class)->check_connection_compatible (device, connection))
+ return FALSE;
+
+ if (!nm_streq0 (nm_connection_get_connection_type (connection),
+ NM_SETTING_PPPOE_SETTING_NAME))
+ return FALSE;
+
+ s_pppoe = nm_connection_get_setting_pppoe (connection);
+ nm_assert (s_pppoe);
+
+ return !!nm_setting_pppoe_get_parent (s_pppoe);
+}
+
static NMDeviceCapabilities
get_generic_capabilities (NMDevice *device)
{
@@ -51,6 +77,161 @@ get_generic_capabilities (NMDevice *device)
}
static void
+ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data)
+{
+ NMDevice *device = NM_DEVICE (user_data);
+
+ switch (status) {
+ case NM_PPP_STATUS_DISCONNECT:
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_DISCONNECT);
+ break;
+ case NM_PPP_STATUS_DEAD:
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED);
+ break;
+ case NM_PPP_STATUS_RUNNING:
+ nm_device_activate_schedule_stage3_ip_config_start (device);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+ppp_ip4_config (NMPPPManager *ppp_manager,
+ const char *iface,
+ NMIP4Config *config,
+ gpointer user_data)
+{
+ NMDevice *device = NM_DEVICE (user_data);
+ NMDevicePpp *self = NM_DEVICE_PPP (device);
+ NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self);
+
+ _LOGT (LOGD_DEVICE | LOGD_PPP, "received IPv4 config from pppd");
+
+ if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
+ if (nm_device_activate_ip4_state_in_conf (device)) {
+ if (!nm_device_take_over_link (device, iface)) {
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+ return;
+ }
+ nm_manager_remove_device (nm_manager_get (), iface);
+ nm_device_activate_schedule_ip4_config_result (device, config);
+ return;
+ }
+ } else {
+ if (priv->pending_ip4_config)
+ g_object_unref (priv->pending_ip4_config);
+ priv->pending_ip4_config = g_object_ref (config);
+ g_free (priv->pending_ifname);
+ priv->pending_ifname = g_strdup (iface);
+ }
+}
+
+static NMActStageReturn
+act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
+{
+ NMDevicePpp *self = NM_DEVICE_PPP (device);
+ NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self);
+ NMSettingPppoe *s_pppoe;
+ NMActRequest *req;
+ GError *err = NULL;
+
+ req = nm_device_get_act_request (NM_DEVICE (self));
+ g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
+
+ s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE);
+ g_return_val_if_fail (s_pppoe, NM_ACT_STAGE_RETURN_FAILURE);
+
+ g_clear_object (&priv->pending_ip4_config);
+ nm_clear_g_free (&priv->pending_ifname);
+
+ priv->ppp_manager = nm_ppp_manager_create (nm_setting_pppoe_get_parent (s_pppoe), &err);
+
+ if ( !priv->ppp_manager
+ || !nm_ppp_manager_start (priv->ppp_manager, req,
+ nm_setting_pppoe_get_username (s_pppoe),
+ 30, 0, &err)) {
+ _LOGW (LOGD_DEVICE | LOGD_PPP, "PPPoE failed to start: %s", err->message);
+ g_error_free (err);
+
+ g_clear_object (&priv->ppp_manager);
+
+ NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED);
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
+
+ g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
+ G_CALLBACK (ppp_state_changed),
+ self);
+ g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
+ G_CALLBACK (ppp_ip4_config),
+ self);
+
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+static NMActStageReturn
+act_stage3_ip4_config_start (NMDevice *device,
+ NMIP4Config **out_config,
+ NMDeviceStateReason *out_failure_reason)
+{
+ NMDevicePpp *self = NM_DEVICE_PPP (device);
+ NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self);
+
+ if (priv->pending_ip4_config) {
+ if (!nm_device_take_over_link (device, priv->pending_ifname))
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ nm_manager_remove_device (nm_manager_get (), priv->pending_ifname);
+ if (out_config)
+ *out_config = g_steal_pointer (&priv->pending_ip4_config);
+ else
+ g_clear_object (&priv->pending_ip4_config);
+ return NM_ACT_STAGE_RETURN_SUCCESS;
+ }
+
+ /* Wait IPCP termination */
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+ NMConnection *connection,
+ NMDevice *parent,
+ const NMPlatformLink **out_plink,
+ GError **error)
+{
+ int parent_ifindex;
+
+ if (!parent) {
+ g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+ "PPP devices can not be created without a parent interface");
+ return FALSE;
+ }
+
+ parent_ifindex = nm_device_get_ifindex (parent);
+ g_warn_if_fail (parent_ifindex > 0);
+
+ nm_device_parent_set_ifindex (device, parent_ifindex);
+
+ /* The interface is created later */
+
+ return TRUE;
+}
+
+static void
+deactivate (NMDevice *device)
+{
+ NMDevicePpp *self = NM_DEVICE_PPP (device);
+ NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self);
+
+ if (priv->ppp_manager) {
+ nm_ppp_manager_stop_sync (priv->ppp_manager);
+ g_clear_object (&priv->ppp_manager);
+ }
+}
+
+static void
nm_device_ppp_init (NMDevicePpp *self)
{
}
@@ -58,6 +239,12 @@ nm_device_ppp_init (NMDevicePpp *self)
static void
dispose (GObject *object)
{
+ NMDevicePpp *self = NM_DEVICE_PPP (object);
+ NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self);
+
+ g_clear_object (&priv->pending_ip4_config);
+ nm_clear_g_free (&priv->pending_ifname);
+
G_OBJECT_CLASS (nm_device_ppp_parent_class)->dispose (object);
}
@@ -67,9 +254,15 @@ nm_device_ppp_class_init (NMDevicePppClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
- NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_PPP)
+ NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_PPPOE_SETTING_NAME, NM_LINK_TYPE_PPP)
object_class->dispose = dispose;
+
+ parent_class->act_stage2_config = act_stage2_config;
+ parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+ parent_class->check_connection_compatible = check_connection_compatible;
+ parent_class->create_and_realize = create_and_realize;
+ parent_class->deactivate = deactivate;
parent_class->get_generic_capabilities = get_generic_capabilities;
nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
@@ -97,7 +290,48 @@ create_device (NMDeviceFactory *factory,
NULL);
}
+static gboolean
+match_connection (NMDeviceFactory *factory, NMConnection *connection)
+{
+ NMSettingPppoe *s_pppoe;
+
+ s_pppoe = nm_connection_get_setting_pppoe (connection);
+ nm_assert (s_pppoe);
+
+ return !!nm_setting_pppoe_get_parent (s_pppoe);
+}
+
+static const char *
+get_connection_parent (NMDeviceFactory *factory, NMConnection *connection)
+{
+ NMSettingPppoe *s_pppoe;
+
+ nm_assert (nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME));
+
+ s_pppoe = nm_connection_get_setting_pppoe (connection);
+ nm_assert (s_pppoe);
+
+ return nm_setting_pppoe_get_parent (s_pppoe);
+}
+
+static char *
+get_connection_iface (NMDeviceFactory *factory,
+ NMConnection *connection,
+ const char *parent_iface)
+{
+ nm_assert (nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME));
+
+ if (!parent_iface)
+ return NULL;
+
+ return g_strdup (nm_connection_get_interface_name (connection));
+}
+
NM_DEVICE_FACTORY_DEFINE_INTERNAL (PPP, Ppp, ppp,
- NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_PPP),
+ NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_PPP)
+ NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_PPPOE_SETTING_NAME),
+ factory_class->get_connection_parent = get_connection_parent;
+ factory_class->get_connection_iface = get_connection_iface;
factory_class->create_device = create_device;
+ factory_class->match_connection = match_connection;
);
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index 05cb3f7d84..a02cab3159 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -57,6 +57,8 @@ gboolean nm_device_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmwar
void nm_device_take_down (NMDevice *self, gboolean block);
+gboolean nm_device_take_over_link (NMDevice *self, const char *ifname);
+
gboolean nm_device_hw_addr_set (NMDevice *device,
const char *addr,
const char *detail,
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 321f7cf8ca..3934c18f3c 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -1001,6 +1001,40 @@ nm_device_get_iface (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->iface;
}
+gboolean
+nm_device_take_over_link (NMDevice *self, const char *ifname)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ const NMPlatformLink *plink;
+ NMPlatform *platform;
+ gboolean up, success;
+ int ifindex;
+
+ g_return_val_if_fail (priv->ifindex <= 0, FALSE);
+
+ platform = nm_device_get_platform (self);
+ plink = nm_platform_link_get_by_ifname (platform, ifname);
+ if (!plink)
+ return FALSE;
+
+ ifindex = plink->ifindex;
+ up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP);
+
+ /* Rename the link to the device ifname */
+ if (up)
+ nm_platform_link_set_down (platform, ifindex);
+ success = nm_platform_link_set_name (platform, ifindex, nm_device_get_iface (self));
+ if (up)
+ nm_platform_link_set_up (platform, ifindex, NULL);
+
+ if (success) {
+ priv->ifindex = ifindex;
+ _notify (self, PROP_IFINDEX);
+ }
+
+ return success;
+}
+
int
nm_device_get_ifindex (NMDevice *self)
{
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 3a8ada4360..e0cce262dc 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -1258,6 +1258,24 @@ nm_manager_iface_for_uuid (NMManager *self, const char *uuid)
return nm_connection_get_interface_name (NM_CONNECTION (connection));
}
+gboolean
+nm_manager_remove_device (NMManager *self, const char *ifname)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GSList *iter;
+ NMDevice *d;
+
+ for (iter = priv->devices; iter; iter = iter->next) {
+ d = iter->data;
+ if (nm_streq0 (nm_device_get_iface (d), ifname)) {
+ remove_device (self, d, FALSE, FALSE);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
/**
* system_create_virtual_device:
* @self: the #NMManager
diff --git a/src/nm-manager.h b/src/nm-manager.h
index fbbaffc314..0fdca8d458 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -124,4 +124,6 @@ gboolean nm_manager_deactivate_connection (NMManager *manager,
void nm_manager_set_capability (NMManager *self, NMCapability cap);
+gboolean nm_manager_remove_device (NMManager *self, const char *ifname);
+
#endif /* __NETWORKMANAGER_MANAGER_H__ */