NetworkManager/src/core/devices/nm-device-veth.c
Fernando Fernandez Mancera 4655b7c308 veth: fix veth activation on booting
When creating one profile for each veth during activation the creation
of the veth could fail. When the link for the first profile is created
the link for the peer is generated in kernel. Therefore when trying to
activate the second profile it will fail because the link already
exists. NetworkManager must check if the link already exists and
corresponds to the same veth, if so, it should skip the link creation.

https://bugzilla.redhat.com/show_bug.cgi?id=2036023
https://bugzilla.redhat.com/show_bug.cgi?id=2105956
2022-07-12 13:34:18 +02:00

235 lines
7.8 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2013 Red Hat, Inc.
*/
#include "src/core/nm-default-daemon.h"
#include <stdlib.h>
#include "libnm-core-intern/nm-core-internal.h"
#include "nm-device-veth.h"
#include "nm-device-private.h"
#include "nm-manager.h"
#include "libnm-platform/nm-platform.h"
#include "nm-device-factory.h"
#include "nm-setting-veth.h"
#define _NMLOG_DEVICE_TYPE NMDeviceVeth
#include "nm-device-logging.h"
/*****************************************************************************/
struct _NMDeviceVeth {
NMDeviceEthernet parent;
};
struct _NMDeviceVethClass {
NMDeviceEthernetClass parent;
};
NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceVeth, PROP_PEER, );
/*****************************************************************************/
G_DEFINE_TYPE(NMDeviceVeth, nm_device_veth, NM_TYPE_DEVICE_ETHERNET)
/*****************************************************************************/
static void
update_properties(NMDevice *device)
{
NMDevice *peer;
int ifindex, peer_ifindex;
ifindex = nm_device_get_ifindex(device);
if (ifindex <= 0
|| !nm_platform_link_veth_get_properties(nm_device_get_platform(device),
ifindex,
&peer_ifindex))
peer_ifindex = 0;
nm_device_parent_set_ifindex(device, peer_ifindex);
peer = nm_device_parent_get_device(device);
if (peer && NM_IS_DEVICE_VETH(peer) && nm_device_parent_get_ifindex(peer) <= 0)
update_properties(peer);
}
static gboolean
can_unmanaged_external_down(NMDevice *self)
{
/* Unless running in a container, an udev rule causes these to be
* unmanaged. If there's no udev then we're probably in a container
* and should IFF_UP and configure the veth ourselves even if we
* didn't create it. */
return FALSE;
}
static void
link_changed(NMDevice *device, const NMPlatformLink *pllink)
{
NM_DEVICE_CLASS(nm_device_veth_parent_class)->link_changed(device, pllink);
update_properties(device);
}
static gboolean
create_and_realize(NMDevice *device,
NMConnection *connection,
NMDevice *parent,
const NMPlatformLink **out_plink,
GError **error)
{
const char *iface = nm_device_get_iface(device);
const char *peer;
NMDevice *peer_device;
NMSettingVeth *s_veth;
int r;
s_veth = _nm_connection_get_setting(connection, NM_TYPE_SETTING_VETH);
if (!s_veth) {
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_CREATION_FAILED,
"Profile %s (%s) is not a suitable veth profile",
nm_connection_get_id(connection),
nm_connection_get_uuid(connection));
return FALSE;
}
peer = nm_setting_veth_get_peer(s_veth);
peer_device = nm_manager_get_device(NM_MANAGER_GET, peer, NM_DEVICE_TYPE_VETH);
if (peer_device) {
/* The veth device and its peer already exist. No need to create it again. */
if (nm_streq0(nm_device_get_iface(nm_device_parent_get_device(peer_device)), iface))
return TRUE;
}
r = nm_platform_link_veth_add(nm_device_get_platform(device), iface, peer, out_plink);
if (r < 0) {
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_CREATION_FAILED,
"Failed to create veth interface '%s' for '%s': %s",
iface,
nm_connection_get_id(connection),
nm_strerror(r));
return FALSE;
}
return TRUE;
}
/*****************************************************************************/
static NMDeviceCapabilities
get_generic_capabilities(NMDevice *device)
{
return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
}
/*****************************************************************************/
static void
nm_device_veth_init(NMDeviceVeth *self)
{}
static void
parent_changed_notify(NMDevice *device,
int old_ifindex,
NMDevice *old_parent,
int new_ifindex,
NMDevice *new_parent)
{
NM_DEVICE_CLASS(nm_device_veth_parent_class)
->parent_changed_notify(device, old_ifindex, old_parent, new_ifindex, new_parent);
_notify(NM_DEVICE_VETH(device), PROP_PEER);
}
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMDeviceVeth *self = NM_DEVICE_VETH(object);
NMDevice *peer;
switch (prop_id) {
case PROP_PEER:
peer = nm_device_parent_get_device(NM_DEVICE(self));
if (peer && !NM_IS_DEVICE_VETH(peer))
peer = NULL;
nm_dbus_utils_g_value_set_object_path(value, peer);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static const NMDBusInterfaceInfoExtended interface_info_device_veth = {
.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
NM_DBUS_INTERFACE_DEVICE_VETH,
.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Peer", "o", NM_DEVICE_VETH_PEER), ), ),
};
static void
nm_device_veth_class_init(NMDeviceVethClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass);
NMDeviceClass *device_class = NM_DEVICE_CLASS(klass);
object_class->get_property = get_property;
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_device_veth);
device_class->connection_type_supported = NULL;
device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_VETH);
device_class->can_unmanaged_external_down = can_unmanaged_external_down;
device_class->link_changed = link_changed;
device_class->parent_changed_notify = parent_changed_notify;
device_class->create_and_realize = create_and_realize;
device_class->get_generic_capabilities = get_generic_capabilities;
obj_properties[PROP_PEER] = g_param_spec_string(NM_DEVICE_VETH_PEER,
"",
"",
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
}
/*****************************************************************************/
#define NM_TYPE_VETH_DEVICE_FACTORY (nm_veth_device_factory_get_type())
#define NM_VETH_DEVICE_FACTORY(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VETH_DEVICE_FACTORY, NMVethDeviceFactory))
static NMDevice *
create_device(NMDeviceFactory *factory,
const char *iface,
const NMPlatformLink *plink,
NMConnection *connection,
gboolean *out_ignore)
{
return g_object_new(NM_TYPE_DEVICE_VETH,
NM_DEVICE_IFACE,
iface,
NM_DEVICE_TYPE_DESC,
"Veth",
NM_DEVICE_DEVICE_TYPE,
NM_DEVICE_TYPE_VETH,
NM_DEVICE_LINK_TYPE,
NM_LINK_TYPE_VETH,
NULL);
}
NM_DEVICE_FACTORY_DEFINE_INTERNAL(
VETH,
Veth,
veth,
NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(NM_LINK_TYPE_VETH)
NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_VETH_SETTING_NAME),
factory_class->create_device = create_device;);