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,12 +52,185 @@ 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) { return NM_DEVICE_CAP_IS_SOFTWARE; } +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__ */