Commit 6c319593 authored by Beniamino Galvani's avatar Beniamino Galvani

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.
parent 695f6cee
......@@ -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;
);
......@@ -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,
......
......@@ -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)
{
......
......@@ -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
......
......@@ -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__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment