diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2017-07-03 16:24:59 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2017-08-05 08:03:16 +0200 |
commit | 6c3195931e94cab70208ce97f3b834f5d9f5ff62 (patch) | |
tree | b6fce96b2e395052833c65f574507d2f82c4db85 | |
parent | 695f6ceebb7c2cc96e50e3156e7938616e021553 (diff) | |
download | NetworkManager-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.c | 240 | ||||
-rw-r--r-- | src/devices/nm-device-private.h | 2 | ||||
-rw-r--r-- | src/devices/nm-device.c | 34 | ||||
-rw-r--r-- | src/nm-manager.c | 18 | ||||
-rw-r--r-- | src/nm-manager.h | 2 |
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__ */ |