diff --git a/docs/api/network-manager-docs.xml b/docs/api/network-manager-docs.xml index a7cf24610..caff6fa4f 100644 --- a/docs/api/network-manager-docs.xml +++ b/docs/api/network-manager-docs.xml @@ -186,6 +186,7 @@ + diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml index 1b37fdd25..4ed079393 100644 --- a/docs/libnm/libnm-docs.xml +++ b/docs/libnm/libnm-docs.xml @@ -325,6 +325,7 @@ print ("NetworkManager version " + client.get_version())]]> + @@ -378,6 +379,7 @@ print ("NetworkManager version " + client.get_version())]]> + diff --git a/introspection/meson.build b/introspection/meson.build index 81afa7d67..16bb5ed42 100644 --- a/introspection/meson.build +++ b/introspection/meson.build @@ -18,6 +18,7 @@ ifaces = [ 'org.freedesktop.NetworkManager.Device.Hsr', 'org.freedesktop.NetworkManager.Device.IPTunnel', 'org.freedesktop.NetworkManager.Device.Infiniband', + 'org.freedesktop.NetworkManager.Device.Ipvlan', 'org.freedesktop.NetworkManager.Device.Loopback', 'org.freedesktop.NetworkManager.Device.Lowpan', 'org.freedesktop.NetworkManager.Device.Macsec', diff --git a/introspection/org.freedesktop.NetworkManager.Device.Ipvlan.xml b/introspection/org.freedesktop.NetworkManager.Device.Ipvlan.xml new file mode 100644 index 000000000..db7a779d1 --- /dev/null +++ b/introspection/org.freedesktop.NetworkManager.Device.Ipvlan.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/core/devices/nm-device-factory.c b/src/core/devices/nm-device-factory.c index 22c8fa5aa..bd771b7cf 100644 --- a/src/core/devices/nm-device-factory.c +++ b/src/core/devices/nm-device-factory.c @@ -415,6 +415,7 @@ nm_device_factory_manager_load_factories(NMDeviceFactoryManagerFactoryFunc callb _ADD_INTERNAL(nm_hsr_device_factory_get_type); _ADD_INTERNAL(nm_infiniband_device_factory_get_type); _ADD_INTERNAL(nm_ip_tunnel_device_factory_get_type); + _ADD_INTERNAL(nm_ipvlan_device_factory_get_type); _ADD_INTERNAL(nm_loopback_device_factory_get_type); _ADD_INTERNAL(nm_macsec_device_factory_get_type); _ADD_INTERNAL(nm_macvlan_device_factory_get_type); diff --git a/src/core/devices/nm-device-ipvlan.c b/src/core/devices/nm-device-ipvlan.c new file mode 100644 index 000000000..662e3ff28 --- /dev/null +++ b/src/core/devices/nm-device-ipvlan.c @@ -0,0 +1,487 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nm-device-ipvlan.h" + +#include + +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-device-private.h" +#include "settings/nm-settings.h" +#include "nm-act-request.h" +#include "nm-manager.h" +#include "libnm-core-aux-intern/nm-libnm-core-utils.h" +#include "libnm-platform/nm-platform.h" +#include "nm-device-factory.h" +#include "nm-setting-ipvlan.h" +#include "nm-setting-wired.h" +#include "nm-active-connection.h" +#include "nm-utils.h" + +#define _NMLOG_DEVICE_TYPE NMDeviceIpvlan +#include "nm-device-logging.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceIpvlan, PROP_MODE, PROP_PRIVATE, PROP_VEPA, ); + +typedef struct { + NMPlatformLnkIpvlan props; +} NMDeviceIpvlanPrivate; + +struct _NMDeviceIpvlan { + NMDevice parent; + NMDeviceIpvlanPrivate _priv; +}; + +struct _NMDeviceIpvlanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceIpvlan, nm_device_ipvlan, NM_TYPE_DEVICE); + +#define NM_DEVICE_IPVLAN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceIpvlan, NM_IS_DEVICE_IPVLAN, NMDevice) + +/*****************************************************************************/ + +static int modes[][2] = { + {NM_SETTING_IPVLAN_MODE_L2, IPVLAN_MODE_L2}, + {NM_SETTING_IPVLAN_MODE_L3, IPVLAN_MODE_L3}, + {NM_SETTING_IPVLAN_MODE_L3S, IPVLAN_MODE_L3S}, +}; + +static int +setting_mode_to_platform(int mode) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS(modes); i++) { + if (modes[i][0] == mode) + return modes[i][1]; + } + + return -1; +} + +static int +platform_mode_to_setting(int mode) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS(modes); i++) { + if (modes[i][1] == mode) + return modes[i][0]; + } + + return 0; +} + +static const char * +platform_mode_to_string(guint mode) +{ + switch (mode) { + case IPVLAN_MODE_L2: + return "l2"; + case IPVLAN_MODE_L3: + return "l3"; + case IPVLAN_MODE_L3S: + return "l3s"; + default: + return "unknown"; + } +} + +/*****************************************************************************/ + +static void +update_properties(NMDevice *device) +{ + NMDeviceIpvlan *self = NM_DEVICE_IPVLAN(device); + NMDeviceIpvlanPrivate *priv = NM_DEVICE_IPVLAN_GET_PRIVATE(self); + GObject *object = G_OBJECT(device); + const NMPlatformLnkIpvlan *props; + const NMPlatformLink *plink; + + props = nm_platform_link_get_lnk_ipvlan(nm_device_get_platform(device), + nm_device_get_ifindex(device), + &plink); + + if (!props) { + _LOGW(LOGD_PLATFORM, "could not get IPVLAN properties"); + return; + } + + g_object_freeze_notify(object); + + nm_device_parent_set_ifindex(device, plink->parent); + +#define CHECK_PROPERTY_CHANGED(field, prop) \ + G_STMT_START \ + { \ + if (priv->props.field != props->field) { \ + priv->props.field = props->field; \ + _notify(self, prop); \ + } \ + } \ + G_STMT_END + + CHECK_PROPERTY_CHANGED(mode, PROP_MODE); + CHECK_PROPERTY_CHANGED(private_flag, PROP_PRIVATE); + CHECK_PROPERTY_CHANGED(vepa, PROP_VEPA); + + g_object_thaw_notify(object); +} + +static void +link_changed(NMDevice *device, const NMPlatformLink *pllink) +{ + NM_DEVICE_CLASS(nm_device_ipvlan_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); + NMSettingIpvlan *s_ipvlan; + NMPlatformLnkIpvlan lnk = {}; + int parent_ifindex; + int r; + + s_ipvlan = _nm_connection_get_setting(connection, NM_TYPE_SETTING_IPVLAN); + nm_assert(s_ipvlan); + + if (!parent) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_MISSING_DEPENDENCIES, + "IPVLAN device cannot be created without a parent interface"); + return FALSE; + } + + parent_ifindex = nm_device_get_ifindex(parent); + if (parent_ifindex <= 0) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_MISSING_DEPENDENCIES, + "cannot retrieve ifindex of interface %s (%s)", + nm_device_get_iface(parent), + nm_device_get_type_desc(parent)); + return FALSE; + } + + if (setting_mode_to_platform(nm_setting_ipvlan_get_mode(s_ipvlan)) < 0) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + "unsupported IPVLAN mode %u in connection %s", + nm_setting_ipvlan_get_mode(s_ipvlan), + nm_connection_get_uuid(connection)); + return FALSE; + } + lnk.mode = setting_mode_to_platform(nm_setting_ipvlan_get_mode(s_ipvlan)); + lnk.private_flag = nm_setting_ipvlan_get_private(s_ipvlan); + lnk.vepa = nm_setting_ipvlan_get_vepa(s_ipvlan); + + r = nm_platform_link_ipvlan_add(nm_device_get_platform(device), + iface, + parent_ifindex, + &lnk, + out_plink); + + if (r < 0) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create IPVLAN 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 gboolean +is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags) +{ + if (!nm_device_parent_get_device(device)) + return FALSE; + return NM_DEVICE_CLASS(nm_device_ipvlan_parent_class)->is_available(device, flags); +} + +/*****************************************************************************/ + +static gboolean +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) +{ + NMDeviceIpvlanPrivate *priv = NM_DEVICE_IPVLAN_GET_PRIVATE(device); + NMSettingIpvlan *s_ipvlan; + const char *parent = NULL; + + if (!NM_DEVICE_CLASS(nm_device_ipvlan_parent_class) + ->check_connection_compatible(device, connection, check_properties, error)) + return FALSE; + + s_ipvlan = _nm_connection_get_setting(connection, NM_TYPE_SETTING_IPVLAN); + + if (check_properties && nm_device_is_real(device)) { + if (setting_mode_to_platform(nm_setting_ipvlan_get_mode(s_ipvlan)) != priv->props.mode) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IPVLAN mode setting differs"); + return FALSE; + } + + if (nm_setting_ipvlan_get_private(s_ipvlan) != priv->props.private_flag) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IPVLAN private flag setting differs"); + return FALSE; + } + if (nm_setting_ipvlan_get_vepa(s_ipvlan) != priv->props.vepa) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IPVLAN VEPA flag setting differs"); + return FALSE; + } + + /* Check parent interface; could be an interface name or a UUID */ + parent = nm_setting_ipvlan_get_parent(s_ipvlan); + if (parent) { + if (!nm_device_match_parent(device, parent)) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IPVLAN parent setting differs"); + return FALSE; + } + } else { + /* Parent could be a MAC address in an NMSettingWired */ + if (!nm_device_match_parent_hwaddr(device, connection, TRUE)) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IPVLAN parent mac setting differs"); + return FALSE; + } + } + } + return TRUE; +} + +static void +update_connection(NMDevice *device, NMConnection *connection) +{ + NMDeviceIpvlanPrivate *priv = NM_DEVICE_IPVLAN_GET_PRIVATE(device); + NMSettingIpvlan *s_ipvlan = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_IPVLAN); + + if (priv->props.mode != setting_mode_to_platform(nm_setting_ipvlan_get_mode(s_ipvlan))) + g_object_set(s_ipvlan, + NM_SETTING_IPVLAN_MODE, + platform_mode_to_setting(priv->props.mode), + NULL); + + if (priv->props.private_flag != nm_setting_ipvlan_get_private(s_ipvlan)) + g_object_set(s_ipvlan, NM_SETTING_IPVLAN_PRIVATE, priv->props.private_flag, NULL); + + if (priv->props.vepa != nm_setting_ipvlan_get_vepa(s_ipvlan)) + g_object_set(s_ipvlan, NM_SETTING_IPVLAN_VEPA, priv->props.vepa, NULL); + + g_object_set( + s_ipvlan, + NM_SETTING_IPVLAN_PARENT, + nm_device_parent_find_for_connection(device, nm_setting_ipvlan_get_parent(s_ipvlan)), + NULL); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceIpvlanPrivate *priv = NM_DEVICE_IPVLAN_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_MODE: + g_value_set_string(value, platform_mode_to_string(priv->props.mode)); + break; + case PROP_PRIVATE: + g_value_set_boolean(value, priv->props.private_flag); + break; + case PROP_VEPA: + g_value_set_boolean(value, priv->props.vepa); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_device_ipvlan_init(NMDeviceIpvlan *self) +{} + +static const NMDBusInterfaceInfoExtended interface_info_device_ipvlan = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( + NM_DBUS_INTERFACE_DEVICE_IPVLAN, + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Parent", "o", NM_DEVICE_PARENT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Mode", "s", NM_DEVICE_IPVLAN_MODE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Private", + "b", + NM_DEVICE_IPVLAN_PRIVATE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Vepa", + "b", + NM_DEVICE_IPVLAN_VEPA), ), ), +}; + +static void +nm_device_ipvlan_class_init(NMDeviceIpvlanClass *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_ipvlan); + + device_class->connection_type_supported = NM_SETTING_IPVLAN_SETTING_NAME; + device_class->connection_type_check_compatible = NM_SETTING_IPVLAN_SETTING_NAME; + device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_IPVLAN); + + device_class->check_connection_compatible = check_connection_compatible; + device_class->create_and_realize = create_and_realize; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->is_available = is_available; + device_class->link_changed = link_changed; + device_class->update_connection = update_connection; + + obj_properties[PROP_MODE] = g_param_spec_string(NM_DEVICE_IPVLAN_MODE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_PRIVATE] = g_param_spec_boolean(NM_DEVICE_IPVLAN_PRIVATE, + "", + "", + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_VEPA] = g_param_spec_boolean(NM_DEVICE_IPVLAN_VEPA, + "", + "", + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} + +/*****************************************************************************/ + +#define NM_TYPE_IPVLAN_DEVICE_FACTORY (nm_ipvlan_device_factory_get_type()) +#define NM_IPVLAN_DEVICE_FACTORY(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IPVLAN_DEVICE_FACTORY, NMIpvlanDeviceFactory)) + +static NMDevice * +create_device(NMDeviceFactory *factory, + const char *iface, + const NMPlatformLink *plink, + NMConnection *connection, + gboolean *out_ignore) +{ + NMSettingIpvlan *s_ipvlan; + + if (connection) { + s_ipvlan = _nm_connection_get_setting(connection, NM_TYPE_SETTING_IPVLAN); + nm_assert(s_ipvlan); + } + + return g_object_new(NM_TYPE_DEVICE_IPVLAN, + NM_DEVICE_IFACE, + iface, + NM_DEVICE_TYPE_DESC, + "Ipvlan", + NM_DEVICE_DEVICE_TYPE, + NM_DEVICE_TYPE_IPVLAN, + NM_DEVICE_LINK_TYPE, + NM_LINK_TYPE_IPVLAN, + NULL); +} + +static const char * +get_connection_parent(NMDeviceFactory *factory, NMConnection *connection) +{ + NMSettingIpvlan *s_ipvlan; + NMSettingWired *s_wired; + const char *parent = NULL; + + g_return_val_if_fail(nm_connection_is_type(connection, NM_SETTING_IPVLAN_SETTING_NAME), NULL); + + s_ipvlan = _nm_connection_get_setting(connection, NM_TYPE_SETTING_IPVLAN); + nm_assert(s_ipvlan); + + parent = nm_setting_ipvlan_get_parent(s_ipvlan); + if (parent) + return parent; + + /* Try the hardware address from the IPVLAN connection's hardware setting */ + s_wired = nm_connection_get_setting_wired(connection); + if (s_wired) + return nm_setting_wired_get_mac_address(s_wired); + + return NULL; +} + +static char * +get_connection_iface(NMDeviceFactory *factory, NMConnection *connection, const char *parent_iface) +{ + NMSettingIpvlan *s_ipvlan; + const char *ifname; + + g_return_val_if_fail(nm_connection_is_type(connection, NM_SETTING_IPVLAN_SETTING_NAME), NULL); + + s_ipvlan = _nm_connection_get_setting(connection, NM_TYPE_SETTING_IPVLAN); + nm_assert(s_ipvlan); + + if (!parent_iface) + return NULL; + + ifname = nm_connection_get_interface_name(connection); + return g_strdup(ifname); +} + +NM_DEVICE_FACTORY_DEFINE_INTERNAL( + IPVLAN, + Ipvlan, + ipvlan, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(NM_LINK_TYPE_IPVLAN) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_IPVLAN_SETTING_NAME), + factory_class->create_device = create_device; + factory_class->get_connection_parent = get_connection_parent; + factory_class->get_connection_iface = get_connection_iface;); diff --git a/src/core/devices/nm-device-ipvlan.h b/src/core/devices/nm-device-ipvlan.h new file mode 100644 index 000000000..6a228abe8 --- /dev/null +++ b/src/core/devices/nm-device-ipvlan.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_DEVICE_IPVLAN_H__ +#define __NETWORKMANAGER_DEVICE_IPVLAN_H__ + +#include "nm-device.h" + +#define NM_TYPE_DEVICE_IPVLAN (nm_device_ipvlan_get_type()) +#define NM_DEVICE_IPVLAN(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_IPVLAN, NMDeviceIpvlan)) +#define NM_DEVICE_IPVLAN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_IPVLAN, NMDeviceIpvlanClass)) +#define NM_IS_DEVICE_IPVLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_IPVLAN)) +#define NM_IS_DEVICE_IPVLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE_IPVLAN)) +#define NM_DEVICE_IPVLAN_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_IPVLAN, NMDeviceIpvlanClass)) + +#define NM_DEVICE_IPVLAN_PARENT "parent" +#define NM_DEVICE_IPVLAN_MODE "mode" +#define NM_DEVICE_IPVLAN_PRIVATE "private" +#define NM_DEVICE_IPVLAN_VEPA "vepa" + +typedef struct _NMDeviceIpvlan NMDeviceIpvlan; +typedef struct _NMDeviceIpvlanClass NMDeviceIpvlanClass; + +GType nm_device_ipvlan_get_type(void); + +#endif /* __NETWORKMANAGER_DEVICE_IPVLAN_H__ */ diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 927a33fa0..803ea7371 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -5632,6 +5632,8 @@ nm_device_get_route_metric_default(NMDeviceType device_type) return 400; case NM_DEVICE_TYPE_MACVLAN: return 410; + case NM_DEVICE_TYPE_IPVLAN: + return 420; case NM_DEVICE_TYPE_BRIDGE: return 425; case NM_DEVICE_TYPE_TUN: @@ -18700,7 +18702,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps nm_assert(priv->type == NM_DEVICE_TYPE_UNKNOWN); priv->type = g_value_get_uint(value); nm_assert(priv->type > NM_DEVICE_TYPE_UNKNOWN); - nm_assert(priv->type <= NM_DEVICE_TYPE_HSR); + nm_assert(priv->type <= NM_DEVICE_TYPE_IPVLAN); break; case PROP_LINK_TYPE: /* construct-only */ diff --git a/src/core/meson.build b/src/core/meson.build index 4419ff629..6dd60be87 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -105,6 +105,7 @@ libNetworkManager = static_library( 'devices/nm-device-hsr.c', 'devices/nm-device-infiniband.c', 'devices/nm-device-ip-tunnel.c', + 'devices/nm-device-ipvlan.c', 'devices/nm-device-loopback.c', 'devices/nm-device-macsec.c', 'devices/nm-device-macvlan.c', diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver index 23e6042fb..eb3645b2c 100644 --- a/src/libnm-client-impl/libnm.ver +++ b/src/libnm-client-impl/libnm.ver @@ -2007,4 +2007,20 @@ libnm_1_50_0 { global: nm_setting_wireless_channel_width_get_type; nm_setting_wireless_get_channel_width; -} libnm_1_48_0; \ No newline at end of file +} libnm_1_48_0; + +libnm_1_52_0 { +global: + nm_device_ipvlan_get_mode; + nm_device_ipvlan_get_parent; + nm_device_ipvlan_get_private; + nm_device_ipvlan_get_type; + nm_device_ipvlan_get_vepa; + nm_setting_ipvlan_get_mode; + nm_setting_ipvlan_get_parent; + nm_setting_ipvlan_get_private; + nm_setting_ipvlan_get_type; + nm_setting_ipvlan_get_vepa; + nm_setting_ipvlan_mode_get_type; + nm_setting_ipvlan_new; +} libnm_1_50_0; diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build index 3dd2338a8..e50e8fbdb 100644 --- a/src/libnm-client-impl/meson.build +++ b/src/libnm-client-impl/meson.build @@ -20,6 +20,7 @@ libnm_client_impl_sources = files( 'nm-device-hsr.c', 'nm-device-infiniband.c', 'nm-device-ip-tunnel.c', + 'nm-device-ipvlan.c', 'nm-device-loopback.c', 'nm-device-macsec.c', 'nm-device-macvlan.c', diff --git a/src/libnm-client-impl/nm-client.c b/src/libnm-client-impl/nm-client.c index 4ecc83899..9715981fe 100644 --- a/src/libnm-client-impl/nm-client.c +++ b/src/libnm-client-impl/nm-client.c @@ -32,6 +32,7 @@ #include "nm-device-hsr.h" #include "nm-device-infiniband.h" #include "nm-device-ip-tunnel.h" +#include "nm-device-ipvlan.h" #include "nm-device-loopback.h" #include "nm-device-macsec.h" #include "nm-device-macvlan.h" diff --git a/src/libnm-client-impl/nm-device-ipvlan.c b/src/libnm-client-impl/nm-device-ipvlan.c new file mode 100644 index 000000000..85d799c26 --- /dev/null +++ b/src/libnm-client-impl/nm-device-ipvlan.c @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ipvlan.h" + +#include "nm-setting-connection.h" +#include "nm-setting-ipvlan.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT, PROP_MODE, PROP_PRIVATE, PROP_VEPA, ); + +typedef struct { + NMLDBusPropertyO parent; + char *mode; + bool private_flag; + bool vepa; +} NMDeviceIpvlanPrivate; + +struct _NMDeviceIpvlan { + NMDevice parent; + NMDeviceIpvlanPrivate _priv; +}; + +struct _NMDeviceIpvlanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceIpvlan, nm_device_ipvlan, NM_TYPE_DEVICE) + +#define NM_DEVICE_IPVLAN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceIpvlan, NM_IS_DEVICE_IPVLAN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_ipvlan_get_parent: + * @device: a #NMDeviceIpvlan + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.52 + **/ +NMDevice * +nm_device_ipvlan_get_parent(NMDeviceIpvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IPVLAN(device), FALSE); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_IPVLAN_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_ipvlan_get_mode: + * @device: a #NMDeviceIpvlan + * + * Gets the IPVLAN mode of the device. + * + * Returns: the IPVLAN mode. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.52 + **/ +const char * +nm_device_ipvlan_get_mode(NMDeviceIpvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IPVLAN(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_IPVLAN_GET_PRIVATE(device)->mode); +} + +/** + * nm_device_ipvlan_get_private + * @device: a #NMDeviceIpvlan + * + * Gets the private flag of the device. + * + * Returns: the private flag of the device. + * + * Since: 1.52 + **/ +gboolean +nm_device_ipvlan_get_private(NMDeviceIpvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IPVLAN(device), FALSE); + + return NM_DEVICE_IPVLAN_GET_PRIVATE(device)->private_flag; +} + +/** + * nm_device_ipvlan_get_vepa + * @device: a #NMDeviceIpvlan + * + * Gets the VEPA flag of the device. + * + * Returns: the VEPA flag of the device. + * + * Since: 1.52 + **/ +gboolean +nm_device_ipvlan_get_vepa(NMDeviceIpvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IPVLAN(device), FALSE); + + return NM_DEVICE_IPVLAN_GET_PRIVATE(device)->vepa; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceIpvlan *device = NM_DEVICE_IPVLAN(object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, nm_device_ipvlan_get_parent(device)); + break; + case PROP_MODE: + g_value_set_string(value, nm_device_ipvlan_get_mode(device)); + break; + case PROP_PRIVATE: + g_value_set_boolean(value, nm_device_ipvlan_get_private(device)); + break; + case PROP_VEPA: + g_value_set_boolean(value, nm_device_ipvlan_get_vepa(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nm_device_ipvlan_init(NMDeviceIpvlan *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceIpvlanPrivate *priv = NM_DEVICE_IPVLAN_GET_PRIVATE(object); + + g_free(priv->mode); + + G_OBJECT_CLASS(nm_device_ipvlan_parent_class)->finalize(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ipvlan = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_IPVLAN, + nm_device_ipvlan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_S("Mode", PROP_MODE, NMDeviceIpvlan, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceIpvlan, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_B("Private", PROP_PRIVATE, NMDeviceIpvlan, _priv.private_flag), + NML_DBUS_META_PROPERTY_INIT_B("Vepa", PROP_VEPA, NMDeviceIpvlan, _priv.vepa), ), ); + +static void +nm_device_ipvlan_class_init(NMDeviceIpvlanClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceIpvlan); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceIpvlanPrivate, parent); + + /** + * NMDeviceIpvlan:parent: + * + * The devices's parent device. + * + * Since: 1.52 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_IPVLAN_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIpvlan:mode: + * + * The IPVLAN mode. + * + * Since: 1.52 + **/ + obj_properties[PROP_MODE] = g_param_spec_string(NM_DEVICE_IPVLAN_MODE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIpvlan:private: + * + * Whether the device has the private flag. + * + * Since: 1.52 + **/ + obj_properties[PROP_PRIVATE] = g_param_spec_boolean(NM_DEVICE_IPVLAN_PRIVATE, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIpvlan:vepa: + * + * Whether the device has the VEPA flag. + * + * Since: 1.52 + **/ + obj_properties[PROP_VEPA] = g_param_spec_boolean(NM_DEVICE_IPVLAN_VEPA, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_ipvlan); +} diff --git a/src/libnm-client-impl/nm-device.c b/src/libnm-client-impl/nm-device.c index dc868f5b9..a95501894 100644 --- a/src/libnm-client-impl/nm-device.c +++ b/src/libnm-client-impl/nm-device.c @@ -314,6 +314,7 @@ coerce_type(NMDeviceType type) case NM_DEVICE_TYPE_VRF: case NM_DEVICE_TYPE_LOOPBACK: case NM_DEVICE_TYPE_HSR: + case NM_DEVICE_TYPE_IPVLAN: return type; } return NM_DEVICE_TYPE_UNKNOWN; @@ -1817,6 +1818,8 @@ get_type_name(NMDevice *device) return _("Loopback"); case NM_DEVICE_TYPE_HSR: return _("HSR"); + case NM_DEVICE_TYPE_IPVLAN: + return _("IPVLAN"); case NM_DEVICE_TYPE_GENERIC: case NM_DEVICE_TYPE_UNUSED1: case NM_DEVICE_TYPE_UNUSED2: diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c index 8af234cea..1b25c92ca 100644 --- a/src/libnm-client-impl/nm-libnm-utils.c +++ b/src/libnm-client-impl/nm-libnm-utils.c @@ -785,6 +785,7 @@ const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[] = { &_nml_dbus_meta_iface_nm_device_hsr, &_nml_dbus_meta_iface_nm_device_iptunnel, &_nml_dbus_meta_iface_nm_device_infiniband, + &_nml_dbus_meta_iface_nm_device_ipvlan, &_nml_dbus_meta_iface_nm_device_loopback, &_nml_dbus_meta_iface_nm_device_lowpan, &_nml_dbus_meta_iface_nm_device_macsec, diff --git a/src/libnm-client-impl/nm-libnm-utils.h b/src/libnm-client-impl/nm-libnm-utils.h index 61ff442d1..f47b1a509 100644 --- a/src/libnm-client-impl/nm-libnm-utils.h +++ b/src/libnm-client-impl/nm-libnm-utils.h @@ -579,7 +579,7 @@ struct _NMLDBusMetaIface { NML_DBUS_META_IFACE_OBJ_PROPERTIES(), \ ##__VA_ARGS__) -extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[46]; +extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[47]; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint; @@ -596,6 +596,7 @@ extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_generic; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_hsr; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_infiniband; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_iptunnel; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ipvlan; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_loopback; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_lowpan; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macsec; diff --git a/src/libnm-client-public/NetworkManager.h b/src/libnm-client-public/NetworkManager.h index 646431f62..3608168f9 100644 --- a/src/libnm-client-public/NetworkManager.h +++ b/src/libnm-client-public/NetworkManager.h @@ -45,6 +45,7 @@ #include "nm-setting-ip6-config.h" #include "nm-setting-ip-config.h" #include "nm-setting-ip-tunnel.h" +#include "nm-setting-ipvlan.h" #include "nm-setting-link.h" #include "nm-setting-loopback.h" #include "nm-setting-macsec.h" @@ -115,6 +116,7 @@ #include "nm-device-hsr.h" #include "nm-device-infiniband.h" #include "nm-device-ip-tunnel.h" +#include "nm-device-ipvlan.h" #include "nm-device-loopback.h" #include "nm-device-macsec.h" #include "nm-device-macvlan.h" diff --git a/src/libnm-client-public/meson.build b/src/libnm-client-public/meson.build index dccbb11fe..b8ae9cce0 100644 --- a/src/libnm-client-public/meson.build +++ b/src/libnm-client-public/meson.build @@ -21,6 +21,7 @@ libnm_client_headers = files( 'nm-device-hsr.h', 'nm-device-infiniband.h', 'nm-device-ip-tunnel.h', + 'nm-device-ipvlan.h', 'nm-device-loopback.h', 'nm-device-macsec.h', 'nm-device-macvlan.h', diff --git a/src/libnm-client-public/nm-autoptr.h b/src/libnm-client-public/nm-autoptr.h index c38f270c0..0f6e59c4a 100644 --- a/src/libnm-client-public/nm-autoptr.h +++ b/src/libnm-client-public/nm-autoptr.h @@ -44,6 +44,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceGeneric, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceHsr, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceIPTunnel, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceInfiniband, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceIpvlan, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceLoopback, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceMacsec, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceMacvlan, g_object_unref) @@ -87,6 +88,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingIP6Config, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingIPConfig, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingIPTunnel, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingInfiniband, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingIpvlan, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingLink, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingLoopback, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingMacsec, g_object_unref) diff --git a/src/libnm-client-public/nm-device-ipvlan.h b/src/libnm-client-public/nm-device-ipvlan.h new file mode 100644 index 000000000..768abc80a --- /dev/null +++ b/src/libnm-client-public/nm-device-ipvlan.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024 Red Hat, Inc. + */ + +#ifndef __NM_DEVICE_IPVLAN_H__ +#define __NM_DEVICE_IPVLAN_H__ + +#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION) +#error "Only can be included directly." +#endif + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_IPVLAN (nm_device_ipvlan_get_type()) +#define NM_DEVICE_IPVLAN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_IPVLAN, NMDeviceIpvlan)) +#define NM_DEVICE_IPVLAN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_IPVLAN, NMDeviceIpvlanClass)) +#define NM_IS_DEVICE_IPVLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_IPVLAN)) +#define NM_IS_DEVICE_IPVLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE_IPVLAN)) +#define NM_DEVICE_IPVLAN_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_IPVLAN, NMDeviceIpvlanClass)) + +#define NM_DEVICE_IPVLAN_PARENT "parent" +#define NM_DEVICE_IPVLAN_MODE "mode" +#define NM_DEVICE_IPVLAN_PRIVATE "private" +#define NM_DEVICE_IPVLAN_VEPA "vepa" + +/** + * NMDeviceIpvlan: + * + * Since: 1.52 + */ +typedef struct _NMDeviceIpvlan NMDeviceIpvlan; +typedef struct _NMDeviceIpvlanClass NMDeviceIpvlanClass; + +NM_AVAILABLE_IN_1_52 +GType nm_device_ipvlan_get_type(void); + +NM_AVAILABLE_IN_1_52 +NMDevice *nm_device_ipvlan_get_parent(NMDeviceIpvlan *device); +NM_AVAILABLE_IN_1_52 +const char *nm_device_ipvlan_get_mode(NMDeviceIpvlan *device); +NM_AVAILABLE_IN_1_52 +gboolean nm_device_ipvlan_get_private(NMDeviceIpvlan *device); +NM_AVAILABLE_IN_1_52 +gboolean nm_device_ipvlan_get_vepa(NMDeviceIpvlan *device); + +G_END_DECLS + +#endif /* __NM_DEVICE_IPVLAN_H__ */ diff --git a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in index 6d2b92541..0b03d3628 100644 --- a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in +++ b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in @@ -1865,6 +1865,26 @@ gprop-type="gchararray" /> + + + + + + diff --git a/src/libnm-core-impl/meson.build b/src/libnm-core-impl/meson.build index e1f11f323..e068d6bd3 100644 --- a/src/libnm-core-impl/meson.build +++ b/src/libnm-core-impl/meson.build @@ -25,6 +25,7 @@ libnm_core_settings_sources = files( 'nm-setting-ip-tunnel.c', 'nm-setting-ip4-config.c', 'nm-setting-ip6-config.c', + 'nm-setting-ipvlan.c', 'nm-setting-link.c', 'nm-setting-loopback.c', 'nm-setting-macsec.c', diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c index 6dace2b73..24c28e815 100644 --- a/src/libnm-core-impl/nm-connection.c +++ b/src/libnm-core-impl/nm-connection.c @@ -3176,6 +3176,7 @@ nm_connection_is_virtual(NMConnection *connection) NM_SETTING_DUMMY_SETTING_NAME, NM_SETTING_HSR_SETTING_NAME, NM_SETTING_IP_TUNNEL_SETTING_NAME, + NM_SETTING_IPVLAN_SETTING_NAME, NM_SETTING_MACSEC_SETTING_NAME, NM_SETTING_MACVLAN_SETTING_NAME, NM_SETTING_OVS_BRIDGE_SETTING_NAME, diff --git a/src/libnm-core-impl/nm-meta-setting-base-impl.c b/src/libnm-core-impl/nm-meta-setting-base-impl.c index 34a7d22e7..c8b77bf32 100644 --- a/src/libnm-core-impl/nm-meta-setting-base-impl.c +++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c @@ -35,6 +35,7 @@ #include "nm-setting-ip-tunnel.h" #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" +#include "nm-setting-ipvlan.h" #include "nm-setting-link.h" #include "nm-setting-loopback.h" #include "nm-setting-macsec.h" @@ -371,6 +372,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_IP_TUNNEL_SETTING_NAME, .get_setting_gtype = nm_setting_ip_tunnel_get_type, }, + [NM_META_SETTING_TYPE_IPVLAN] = + { + .meta_type = NM_META_SETTING_TYPE_IPVLAN, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_IPVLAN_SETTING_NAME, + .get_setting_gtype = nm_setting_ipvlan_get_type, + }, [NM_META_SETTING_TYPE_LINK] = { .meta_type = NM_META_SETTING_TYPE_LINK, @@ -643,6 +651,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = { NM_META_SETTING_TYPE_HSR, NM_META_SETTING_TYPE_INFINIBAND, NM_META_SETTING_TYPE_IP_TUNNEL, + NM_META_SETTING_TYPE_IPVLAN, NM_META_SETTING_TYPE_LOOPBACK, NM_META_SETTING_TYPE_MACSEC, NM_META_SETTING_TYPE_MACVLAN, diff --git a/src/libnm-core-impl/nm-setting-ipvlan.c b/src/libnm-core-impl/nm-setting-ipvlan.c new file mode 100644 index 000000000..fafa37b6e --- /dev/null +++ b/src/libnm-core-impl/nm-setting-ipvlan.c @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024 Red Hat, Inc. + */ + +#include "libnm-core-impl/nm-default-libnm-core.h" + +#include "nm-setting-ipvlan.h" + +#include "nm-connection-private.h" +#include "nm-utils.h" +#include "nm-utils-private.h" + +/** + * SECTION:nm-setting-ipvlan + * @short_description: Describes connection properties for IPVLAN interfaces + * + * The #NMSettingIpvlan object is a #NMSetting subclass that describes properties + * necessary for connection to IPVLAN interfaces. + **/ + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT, PROP_MODE, PROP_PRIVATE, PROP_VEPA, ); + +typedef struct { + char *parent; + guint32 mode; + bool private_flag; + bool vepa; +} NMSettingIpvlanPrivate; + +/** + * NMSettingIpvlan: + * + * IPVLAN Settings + */ +struct _NMSettingIpvlan { + NMSetting parent; + NMSettingIpvlanPrivate _priv; +}; + +struct _NMSettingIpvlanClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE(NMSettingIpvlan, nm_setting_ipvlan, NM_TYPE_SETTING) + +#define NM_SETTING_IPVLAN_GET_PRIVATE(o) \ + _NM_GET_PRIVATE(o, NMSettingIpvlan, NM_IS_SETTING_IPVLAN, NMSetting) + +/*****************************************************************************/ + +/** + * nm_setting_ipvlan_get_parent: + * @setting: the #NMSettingIpvlan + * + * Returns: the #NMSettingIpvlan:parent property of the setting + * + * Since: 1.52 + **/ +const char * +nm_setting_ipvlan_get_parent(NMSettingIpvlan *setting) +{ + g_return_val_if_fail(NM_IS_SETTING_IPVLAN(setting), NULL); + + return NM_SETTING_IPVLAN_GET_PRIVATE(setting)->parent; +} + +/** + * nm_setting_ipvlan_get_mode: + * @setting: the #NMSettingIpvlan + * + * Returns: the #NMSettingIpvlan:mode property of the setting + * + * Since: 1.52 + **/ +NMSettingIpvlanMode +nm_setting_ipvlan_get_mode(NMSettingIpvlan *setting) +{ + g_return_val_if_fail(NM_IS_SETTING_IPVLAN(setting), NM_SETTING_IPVLAN_MODE_UNKNOWN); + + return NM_SETTING_IPVLAN_GET_PRIVATE(setting)->mode; +} + +/** + * nm_setting_ipvlan_get_private: + * @setting: the #NMSettingIpvlan + * + * Returns: the #NMSettingIpvlan:private property of the setting + * + * Since: 1.52 + **/ +gboolean +nm_setting_ipvlan_get_private(NMSettingIpvlan *setting) +{ + g_return_val_if_fail(NM_IS_SETTING_IPVLAN(setting), FALSE); + + return NM_SETTING_IPVLAN_GET_PRIVATE(setting)->private_flag; +} + +/** + * nm_setting_ipvlan_get_vepa: + * @setting: the #NMSettingIpvlan + * + * Returns: the #NMSettingIpvlan:vepa property of the setting + * + * Since: 1.52 + **/ +gboolean +nm_setting_ipvlan_get_vepa(NMSettingIpvlan *setting) +{ + g_return_val_if_fail(NM_IS_SETTING_IPVLAN(setting), FALSE); + + return NM_SETTING_IPVLAN_GET_PRIVATE(setting)->vepa; +} + +/*****************************************************************************/ + +static gboolean +verify(NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingIpvlanPrivate *priv = NM_SETTING_IPVLAN_GET_PRIVATE(setting); + NMSettingWired *s_wired = NULL; + + if (connection) + s_wired = nm_connection_get_setting_wired(connection); + + if (priv->parent) { + if (!nm_utils_is_uuid(priv->parent) && !nm_utils_ifname_valid_kernel(priv->parent, NULL)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is neither an UUID nor an interface name"), + priv->parent); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_IPVLAN_SETTING_NAME, + NM_SETTING_IPVLAN_PARENT); + return FALSE; + } + } else { + /* If parent is NULL, the parent must be specified via NMSettingWired:mac-address. */ + if (connection && (!s_wired || !nm_setting_wired_get_mac_address(s_wired))) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is not specified and neither is '%s:%s'"), + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_MAC_ADDRESS); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_IPVLAN_SETTING_NAME, + NM_SETTING_IPVLAN_PARENT); + return FALSE; + } + } + + if (priv->private_flag && priv->vepa) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("private and VEPA cannot be enabled at the same time")); + g_prefix_error(error, "%s: ", NM_SETTING_IPVLAN_SETTING_NAME); + return FALSE; + } + + if (!_nm_connection_verify_required_interface_name(connection, error)) + return FALSE; + + return TRUE; +} + +/*****************************************************************************/ + +static void +nm_setting_ipvlan_init(NMSettingIpvlan *self) +{} + +/** + * nm_setting_ipvlan_new: + * + * Creates a new #NMSettingIpvlan object with default values. + * + * Returns: (transfer full): the new empty #NMSettingIpvlan object + * + * Since: 1.52 + **/ +NMSetting * +nm_setting_ipvlan_new(void) +{ + return g_object_new(NM_TYPE_SETTING_IPVLAN, NULL); +} + +static void +nm_setting_ipvlan_class_init(NMSettingIpvlanClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + NMSettingClass *setting_class = NM_SETTING_CLASS(klass); + GArray *properties_override = _nm_sett_info_property_override_create_array(); + + object_class->get_property = _nm_setting_property_get_property_direct; + object_class->set_property = _nm_setting_property_set_property_direct; + + setting_class->verify = verify; + + /** + * NMSettingIpvlan:parent: + * + * If given, specifies the parent interface name or parent connection UUID + * from which this IPVLAN interface should be created. If this property is + * not specified, the connection must contain an #NMSettingWired setting + * with a #NMSettingWired:mac-address property. + * + * Since: 1.52 + **/ + _nm_setting_property_define_direct_string(properties_override, + obj_properties, + NM_SETTING_IPVLAN_PARENT, + PROP_PARENT, + NM_SETTING_PARAM_INFERRABLE, + NMSettingIpvlanPrivate, + parent, + .direct_string_allow_empty = TRUE); + + /** + * NMSettingIpvlan:mode: + * + * The IPVLAN mode. Valid values: %NM_SETTING_IPVLAN_MODE_L2, + * %NM_SETTING_IPVLAN_MODE_L3 and %NM_SETTING_IPVLAN_MODE_L3S. + * + * Since: 1.52 + **/ + /* ---nmcli--- + * property: mode + * description: + * The IPVLAN mode. Valid values: l2 (1), l3 (2), l3s (3) + * ---end--- + */ + _nm_setting_property_define_direct_uint32(properties_override, + obj_properties, + NM_SETTING_IPVLAN_MODE, + PROP_MODE, + 0, + G_MAXUINT32, + NM_SETTING_IPVLAN_MODE_UNKNOWN, + NM_SETTING_PARAM_INFERRABLE, + NMSettingIpvlanPrivate, + mode); + + /** + * NMSettingIpvlan:private: + * + * Whether the interface should be put in private mode. + * + * Since: 1.52 + **/ + _nm_setting_property_define_direct_boolean(properties_override, + obj_properties, + NM_SETTING_IPVLAN_PRIVATE, + PROP_PRIVATE, + FALSE, + NM_SETTING_PARAM_INFERRABLE, + NMSettingIpvlanPrivate, + private_flag); + + /** + * NMSettingIpvlan:vepa: + * + * Whether the interface should be put in VEPA mode. + * + * Since: 1.52 + **/ + _nm_setting_property_define_direct_boolean(properties_override, + obj_properties, + NM_SETTING_IPVLAN_VEPA, + PROP_VEPA, + FALSE, + NM_SETTING_PARAM_INFERRABLE, + NMSettingIpvlanPrivate, + vepa); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + _nm_setting_class_commit(setting_class, + NM_META_SETTING_TYPE_IPVLAN, + NULL, + properties_override, + G_STRUCT_OFFSET(NMSettingIpvlan, _priv)); +} diff --git a/src/libnm-core-intern/nm-core-internal.h b/src/libnm-core-intern/nm-core-internal.h index 4677fb163..69a435ea1 100644 --- a/src/libnm-core-intern/nm-core-internal.h +++ b/src/libnm-core-intern/nm-core-internal.h @@ -44,6 +44,7 @@ #include "nm-setting-ip-tunnel.h" #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" +#include "nm-setting-ipvlan.h" #include "nm-setting-link.h" #include "nm-setting-loopback.h" #include "nm-setting-macsec.h" diff --git a/src/libnm-core-intern/nm-meta-setting-base-impl.h b/src/libnm-core-intern/nm-meta-setting-base-impl.h index 9226183fe..c03285ebf 100644 --- a/src/libnm-core-intern/nm-meta-setting-base-impl.h +++ b/src/libnm-core-intern/nm-meta-setting-base-impl.h @@ -128,6 +128,7 @@ typedef enum _nm_packed { NM_META_SETTING_TYPE_IP_TUNNEL, NM_META_SETTING_TYPE_IP4_CONFIG, NM_META_SETTING_TYPE_IP6_CONFIG, + NM_META_SETTING_TYPE_IPVLAN, NM_META_SETTING_TYPE_LINK, NM_META_SETTING_TYPE_LOOPBACK, NM_META_SETTING_TYPE_MACSEC, diff --git a/src/libnm-core-public/meson.build b/src/libnm-core-public/meson.build index b5ed71e8c..97888467e 100644 --- a/src/libnm-core-public/meson.build +++ b/src/libnm-core-public/meson.build @@ -30,6 +30,7 @@ libnm_core_headers = files( 'nm-setting-ip-tunnel.h', 'nm-setting-ip4-config.h', 'nm-setting-ip6-config.h', + 'nm-setting-ipvlan.h', 'nm-setting-link.h', 'nm-setting-loopback.h', 'nm-setting-macsec.h', diff --git a/src/libnm-core-public/nm-core-types.h b/src/libnm-core-public/nm-core-types.h index d9a8225e8..a0d6bc82d 100644 --- a/src/libnm-core-public/nm-core-types.h +++ b/src/libnm-core-public/nm-core-types.h @@ -35,6 +35,7 @@ typedef struct _NMSettingIP4Config NMSettingIP4Config; typedef struct _NMSettingIP6Config NMSettingIP6Config; typedef struct _NMSettingIPConfig NMSettingIPConfig; typedef struct _NMSettingIPTunnel NMSettingIPTunnel; +typedef struct _NMSettingIpvlan NMSettingIpvlan; typedef struct _NMSettingInfiniband NMSettingInfiniband; typedef struct _NMSettingLink NMSettingLink; typedef struct _NMSettingLoopback NMSettingLoopback; diff --git a/src/libnm-core-public/nm-dbus-interface.h b/src/libnm-core-public/nm-dbus-interface.h index 5eedd7da3..64136161d 100644 --- a/src/libnm-core-public/nm-dbus-interface.h +++ b/src/libnm-core-public/nm-dbus-interface.h @@ -40,6 +40,7 @@ #define NM_DBUS_INTERFACE_DEVICE_HSR NM_DBUS_INTERFACE_DEVICE ".Hsr" #define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband" #define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL NM_DBUS_INTERFACE_DEVICE ".IPTunnel" +#define NM_DBUS_INTERFACE_DEVICE_IPVLAN NM_DBUS_INTERFACE_DEVICE ".Ipvlan" #define NM_DBUS_INTERFACE_DEVICE_LOOPBACK NM_DBUS_INTERFACE_DEVICE ".Loopback" #define NM_DBUS_INTERFACE_DEVICE_MACSEC NM_DBUS_INTERFACE_DEVICE ".Macsec" #define NM_DBUS_INTERFACE_DEVICE_MACVLAN NM_DBUS_INTERFACE_DEVICE ".Macvlan" @@ -236,6 +237,7 @@ typedef enum { * @NM_DEVICE_TYPE_VRF: A VRF (Virtual Routing and Forwarding) interface. Since: 1.24. * @NM_DEVICE_TYPE_LOOPBACK: a loopback interface. Since: 1.42. * @NM_DEVICE_TYPE_HSR: A HSR/PRP device. Since: 1.46. + * @NM_DEVICE_TYPE_IPVLAN: A IPVLAN device. Since: 1.52. * * #NMDeviceType values indicate the type of hardware represented by a * device object. @@ -275,6 +277,7 @@ typedef enum { NM_DEVICE_TYPE_VRF = 31, NM_DEVICE_TYPE_LOOPBACK = 32, NM_DEVICE_TYPE_HSR = 33, + NM_DEVICE_TYPE_IPVLAN = 34, } NMDeviceType; /** diff --git a/src/libnm-core-public/nm-setting-ipvlan.h b/src/libnm-core-public/nm-setting-ipvlan.h new file mode 100644 index 000000000..e332585b0 --- /dev/null +++ b/src/libnm-core-public/nm-setting-ipvlan.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024 Red Hat, Inc. + */ + +#ifndef __NM_SETTING_IPVLAN_H__ +#define __NM_SETTING_IPVLAN_H__ + +#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION) +#error "Only can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_IPVLAN (nm_setting_ipvlan_get_type()) +#define NM_SETTING_IPVLAN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTING_IPVLAN, NMSettingIpvlan)) +#define NM_IS_SETTING_IPVLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTING_IPVLAN)) +#define NM_IS_SETTING_IPVLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_SETTING_IPVLAN)) +#define NM_SETTING_IPVLAN_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_SETTING_IPVLAN, NMSettingIpvlanClass)) + +#define NM_SETTING_IPVLAN_SETTING_NAME "ipvlan" + +#define NM_SETTING_IPVLAN_PARENT "parent" +#define NM_SETTING_IPVLAN_MODE "mode" +#define NM_SETTING_IPVLAN_PRIVATE "private" +#define NM_SETTING_IPVLAN_VEPA "vepa" + +typedef struct _NMSettingIpvlanClass NMSettingIpvlanClass; + +/** + * NMSettingIpvlanMode: + * @NM_SETTING_IPVLAN_MODE_UNKNOWN: unknown/unset mode + * @NM_SETTING_IPVLAN_MODE_L2: L2 mode, device receives and responds to ARP. + * @NM_SETTING_IPVLAN_MODE_L3: L3 mode, device process only L3 traffic and above. + * @NM_SETTING_IPVLAN_MODE_L3S: L3S mode, same way as L3 mode but egress and ingress + * lands on netfilter chain. + * + * Since: 1.52 + **/ +typedef enum { + NM_SETTING_IPVLAN_MODE_UNKNOWN = 0, + NM_SETTING_IPVLAN_MODE_L2 = 1, + NM_SETTING_IPVLAN_MODE_L3 = 2, + NM_SETTING_IPVLAN_MODE_L3S = 3, + _NM_SETTING_IPVLAN_MODE_NUM, /*< skip >*/ + NM_SETTING_IPVLAN_MODE_LAST = _NM_SETTING_IPVLAN_MODE_NUM - 1, /*< skip >*/ +} NMSettingIpvlanMode; + +NM_AVAILABLE_IN_1_52 +GType nm_setting_ipvlan_get_type(void); +NM_AVAILABLE_IN_1_52 +NMSetting *nm_setting_ipvlan_new(void); + +NM_AVAILABLE_IN_1_52 +const char *nm_setting_ipvlan_get_parent(NMSettingIpvlan *setting); +NM_AVAILABLE_IN_1_52 +NMSettingIpvlanMode nm_setting_ipvlan_get_mode(NMSettingIpvlan *setting); +NM_AVAILABLE_IN_1_52 +gboolean nm_setting_ipvlan_get_private(NMSettingIpvlan *setting); +NM_AVAILABLE_IN_1_52 +gboolean nm_setting_ipvlan_get_vepa(NMSettingIpvlan *setting); + +G_END_DECLS + +#endif /* __NM_SETTING_IPVLAN_H__ */ diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index 1dbd50232..93be743fb 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -137,6 +137,7 @@ typedef enum { NM_LINK_TYPE_IP6GRE, NM_LINK_TYPE_IP6GRETAP, NM_LINK_TYPE_IPIP, + NM_LINK_TYPE_IPVLAN, NM_LINK_TYPE_LOOPBACK, NM_LINK_TYPE_MACSEC, NM_LINK_TYPE_MACVLAN, diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index bd495fe27..a738f9d9e 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -861,6 +861,7 @@ static const LinkDesc link_descs[] = { [NM_LINK_TYPE_IP6GRE] = {"ip6gre", "ip6gre", NULL}, [NM_LINK_TYPE_IP6GRETAP] = {"ip6gretap", "ip6gretap", NULL}, [NM_LINK_TYPE_IPIP] = {"ipip", "ipip", NULL}, + [NM_LINK_TYPE_IPVLAN] = {"ipvlan", "ipvlan", NULL}, [NM_LINK_TYPE_LOOPBACK] = {"loopback", NULL, NULL}, [NM_LINK_TYPE_MACSEC] = {"macsec", "macsec", NULL}, [NM_LINK_TYPE_MACVLAN] = {"macvlan", "macvlan", NULL}, @@ -908,6 +909,7 @@ _link_type_from_rtnl_type(const char *name) NM_LINK_TYPE_IP6GRETAP, /* "ip6gretap" */ NM_LINK_TYPE_IP6TNL, /* "ip6tnl" */ NM_LINK_TYPE_IPIP, /* "ipip" */ + NM_LINK_TYPE_IPVLAN, /* "ipvlan" */ NM_LINK_TYPE_MACSEC, /* "macsec" */ NM_LINK_TYPE_MACVLAN, /* "macvlan" */ NM_LINK_TYPE_MACVTAP, /* "macvtap" */ @@ -2106,6 +2108,40 @@ _parse_lnk_ipip(const char *kind, struct nlattr *info_data) /*****************************************************************************/ +static NMPObject * +_parse_lnk_ipvlan(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_IPVLAN_MODE] = {.type = NLA_U16}, + [IFLA_IPVLAN_FLAGS] = {.type = NLA_U16}, + }; + NMPlatformLnkIpvlan *props; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + NMPObject *obj; + + if (!info_data || !kind) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + if (!tb[IFLA_IPVLAN_MODE]) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_IPVLAN, NULL); + props = &obj->lnk_ipvlan; + props->mode = nla_get_u16(tb[IFLA_IPVLAN_MODE]); + + if (tb[IFLA_IPVLAN_FLAGS]) { + props->private_flag = NM_FLAGS_HAS(nla_get_u16(tb[IFLA_IPVLAN_FLAGS]), IPVLAN_F_PRIVATE); + props->vepa = NM_FLAGS_HAS(nla_get_u16(tb[IFLA_IPVLAN_FLAGS]), IPVLAN_F_VEPA); + } + + return obj; +} + +/*****************************************************************************/ + static NMPObject * _parse_lnk_macvlan(const char *kind, struct nlattr *info_data) { @@ -3670,6 +3706,9 @@ _new_from_nl_link(NMPlatform *platform, case NM_LINK_TYPE_IPIP: lnk_data = _parse_lnk_ipip(nl_info_kind, nl_info_data); break; + case NM_LINK_TYPE_IPVLAN: + lnk_data = _parse_lnk_ipvlan(nl_info_kind, nl_info_data); + break; case NM_LINK_TYPE_MACSEC: lnk_data = _parse_lnk_macsec(nl_info_kind, nl_info_data); break; @@ -5248,6 +5287,26 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery); break; } + case NM_LINK_TYPE_IPVLAN: + { + const NMPlatformLnkIpvlan *props = extra_data; + guint16 flags = 0; + + nm_assert(props); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->private_flag) + flags |= IPVLAN_F_PRIVATE; + + if (props->vepa) + flags |= IPVLAN_F_VEPA; + + NLA_PUT_U16(msg, IFLA_IPVLAN_MODE, props->mode); + NLA_PUT_U16(msg, IFLA_IPVLAN_FLAGS, flags); + break; + } case NM_LINK_TYPE_MACSEC: { const NMPlatformLnkMacsec *props = extra_data; diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index af04f29fa..a751aeeb3 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -1418,6 +1418,12 @@ nm_platform_link_add(NMPlatform *self, buf_p, buf_len); break; + case NM_LINK_TYPE_IPVLAN: + nm_strbuf_append_str(&buf_p, &buf_len, ", "); + nm_platform_lnk_ipvlan_to_string((const NMPlatformLnkIpvlan *) extra_data, + buf_p, + buf_len); + break; case NM_LINK_TYPE_MACSEC: nm_strbuf_append_str(&buf_p, &buf_len, ", "); nm_platform_lnk_macsec_to_string((const NMPlatformLnkMacsec *) extra_data, @@ -2604,6 +2610,12 @@ nm_platform_link_get_lnk_ipip(NMPlatform *self, int ifindex, const NMPlatformLin return _link_get_lnk(self, ifindex, NM_LINK_TYPE_IPIP, out_link); } +const NMPlatformLnkIpvlan * +nm_platform_link_get_lnk_ipvlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk(self, ifindex, NM_LINK_TYPE_IPVLAN, out_link); +} + const NMPlatformLnkMacsec * nm_platform_link_get_lnk_macsec(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) { @@ -6670,6 +6682,21 @@ nm_platform_lnk_macvlan_to_string(const NMPlatformLnkMacvlan *lnk, char *buf, gs return buf; } +const char * +nm_platform_lnk_ipvlan_to_string(const NMPlatformLnkIpvlan *lnk, char *buf, gsize len) +{ + if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) + return buf; + + g_snprintf(buf, + len, + "mode %u%s%s", + lnk->mode, + lnk->private_flag ? " private" : "", + lnk->vepa ? " vepa" : ""); + return buf; +} + const char * nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf, gsize len) { @@ -8557,6 +8584,22 @@ nm_platform_lnk_macvlan_cmp(const NMPlatformLnkMacvlan *a, const NMPlatformLnkMa return 0; } +void +nm_platform_lnk_ipvlan_hash_update(const NMPlatformLnkIpvlan *obj, NMHashState *h) +{ + nm_hash_update_vals(h, obj->mode, NM_HASH_COMBINE_BOOLS(guint8, obj->private_flag, obj->vepa)); +} + +int +nm_platform_lnk_ipvlan_cmp(const NMPlatformLnkIpvlan *a, const NMPlatformLnkIpvlan *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, mode); + NM_CMP_FIELD_UNSAFE(a, b, private_flag); + NM_CMP_FIELD_UNSAFE(a, b, vepa); + return 0; +} + void nm_platform_lnk_sit_hash_update(const NMPlatformLnkSit *obj, NMHashState *h) { diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index e33be8135..97dac246f 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -879,6 +879,12 @@ typedef struct { bool path_mtu_discovery : 1; } _nm_alignas(NMPlatformObject) NMPlatformLnkIpIp; +typedef struct { + guint16 mode; + bool private_flag : 1; + bool vepa : 1; +} _nm_alignas(NMPlatformObject) NMPlatformLnkIpvlan; + typedef struct { int parent_ifindex; in_addr_t local; @@ -1935,6 +1941,27 @@ nm_platform_link_macvlan_add(NMPlatform *self, out_link); } +static inline int +nm_platform_link_ipvlan_add(NMPlatform *self, + const char *name, + int parent, + const NMPlatformLnkIpvlan *props, + const NMPlatformLink **out_link) +{ + g_return_val_if_fail(props, -NME_BUG); + g_return_val_if_fail(parent > 0, -NME_BUG); + + return nm_platform_link_add(self, + NM_LINK_TYPE_IPVLAN, + name, + parent, + NULL, + 0, + 0, + props, + out_link); +} + gboolean nm_platform_link_delete(NMPlatform *self, int ifindex); gboolean nm_platform_link_set_netns(NMPlatform *self, int ifindex, int netns_fd); @@ -2117,6 +2144,8 @@ const NMPlatformLnkMacsec * nm_platform_link_get_lnk_macsec(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkMacvlan * nm_platform_link_get_lnk_macvlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkIpvlan * +nm_platform_link_get_lnk_ipvlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkMacvlan * nm_platform_link_get_lnk_macvtap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkSit * @@ -2431,6 +2460,7 @@ const char *nm_platform_lnk_ipip_to_string(const NMPlatformLnkIpIp *lnk, char *b const char *nm_platform_lnk_macsec_to_string(const NMPlatformLnkMacsec *lnk, char *buf, gsize len); const char * nm_platform_lnk_macvlan_to_string(const NMPlatformLnkMacvlan *lnk, char *buf, gsize len); +const char *nm_platform_lnk_ipvlan_to_string(const NMPlatformLnkIpvlan *lnk, char *buf, gsize len); const char *nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf, gsize len); const char *nm_platform_lnk_tun_to_string(const NMPlatformLnkTun *lnk, char *buf, gsize len); const char *nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len); @@ -2483,6 +2513,7 @@ int nm_platform_lnk_infiniband_cmp(const NMPlatformLnkInfiniband *a, int nm_platform_lnk_ip6tnl_cmp(const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b); int nm_platform_lnk_ipip_cmp(const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b); int nm_platform_lnk_macsec_cmp(const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b); +int nm_platform_lnk_ipvlan_cmp(const NMPlatformLnkIpvlan *a, const NMPlatformLnkIpvlan *b); int nm_platform_lnk_macvlan_cmp(const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b); int nm_platform_lnk_sit_cmp(const NMPlatformLnkSit *a, const NMPlatformLnkSit *b); int nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b); @@ -2555,6 +2586,7 @@ void nm_platform_lnk_ip6tnl_hash_update(const NMPlatformLnkIp6Tnl *obj, NMHashSt void nm_platform_lnk_ipip_hash_update(const NMPlatformLnkIpIp *obj, NMHashState *h); void nm_platform_lnk_macsec_hash_update(const NMPlatformLnkMacsec *obj, NMHashState *h); void nm_platform_lnk_macvlan_hash_update(const NMPlatformLnkMacvlan *obj, NMHashState *h); +void nm_platform_lnk_ipvlan_hash_update(const NMPlatformLnkIpvlan *obj, NMHashState *h); void nm_platform_lnk_sit_hash_update(const NMPlatformLnkSit *obj, NMHashState *h); void nm_platform_lnk_tun_hash_update(const NMPlatformLnkTun *obj, NMHashState *h); void nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h); diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h index c7d487e23..4f675a81b 100644 --- a/src/libnm-platform/nmp-base.h +++ b/src/libnm-platform/nmp-base.h @@ -174,6 +174,7 @@ typedef enum _nm_packed { NMP_OBJECT_TYPE_LNK_IP6GRE, NMP_OBJECT_TYPE_LNK_IP6GRETAP, NMP_OBJECT_TYPE_LNK_IPIP, + NMP_OBJECT_TYPE_LNK_IPVLAN, NMP_OBJECT_TYPE_LNK_MACSEC, NMP_OBJECT_TYPE_LNK_MACVLAN, NMP_OBJECT_TYPE_LNK_MACVTAP, diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c index 1fdaa2755..f41cc95ab 100644 --- a/src/libnm-platform/nmp-object.c +++ b/src/libnm-platform/nmp-object.c @@ -3565,6 +3565,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ipip_hash_update, .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ipip_cmp, }, + [NMP_OBJECT_TYPE_LNK_IPVLAN - 1] = + { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_IPVLAN, + .sizeof_data = sizeof(NMPObjectLnkIpvlan), + .sizeof_public = sizeof(NMPlatformLnkIpvlan), + .obj_type_name = "ipvlan", + .lnk_link_type = NM_LINK_TYPE_IPVLAN, + .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_ipvlan_to_string, + .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ipvlan_hash_update, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ipvlan_cmp, + }, [NMP_OBJECT_TYPE_LNK_MACSEC - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h index cd1793d45..b526fc3be 100644 --- a/src/libnm-platform/nmp-object.h +++ b/src/libnm-platform/nmp-object.h @@ -270,6 +270,10 @@ typedef struct { NMPlatformLnkIpIp _public; } NMPObjectLnkIpIp; +typedef struct { + NMPlatformLnkIpvlan _public; +} NMPObjectLnkIpvlan; + typedef struct { NMPlatformLnkMacsec _public; } NMPObjectLnkMacsec; @@ -394,6 +398,9 @@ struct _NMPObject { NMPlatformLnkIp6Tnl lnk_ip6tnl; NMPObjectLnkIp6Tnl _lnk_ip6tnl; + NMPlatformLnkIpvlan lnk_ipvlan; + NMPObjectLnkIpvlan _lnk_ipvlan; + NMPlatformLnkMacsec lnk_macsec; NMPObjectLnkMacsec _lnk_macsec; @@ -544,6 +551,7 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type) case NMP_OBJECT_TYPE_LNK_IP6GRE: case NMP_OBJECT_TYPE_LNK_IP6GRETAP: case NMP_OBJECT_TYPE_LNK_IPIP: + case NMP_OBJECT_TYPE_LNK_IPVLAN: case NMP_OBJECT_TYPE_LNK_MACSEC: case NMP_OBJECT_TYPE_LNK_MACVLAN: case NMP_OBJECT_TYPE_LNK_MACVTAP: diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.c b/src/libnmc-setting/nm-meta-setting-base-impl.c index 34a7d22e7..c8b77bf32 100644 --- a/src/libnmc-setting/nm-meta-setting-base-impl.c +++ b/src/libnmc-setting/nm-meta-setting-base-impl.c @@ -35,6 +35,7 @@ #include "nm-setting-ip-tunnel.h" #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" +#include "nm-setting-ipvlan.h" #include "nm-setting-link.h" #include "nm-setting-loopback.h" #include "nm-setting-macsec.h" @@ -371,6 +372,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_IP_TUNNEL_SETTING_NAME, .get_setting_gtype = nm_setting_ip_tunnel_get_type, }, + [NM_META_SETTING_TYPE_IPVLAN] = + { + .meta_type = NM_META_SETTING_TYPE_IPVLAN, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_IPVLAN_SETTING_NAME, + .get_setting_gtype = nm_setting_ipvlan_get_type, + }, [NM_META_SETTING_TYPE_LINK] = { .meta_type = NM_META_SETTING_TYPE_LINK, @@ -643,6 +651,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = { NM_META_SETTING_TYPE_HSR, NM_META_SETTING_TYPE_INFINIBAND, NM_META_SETTING_TYPE_IP_TUNNEL, + NM_META_SETTING_TYPE_IPVLAN, NM_META_SETTING_TYPE_LOOPBACK, NM_META_SETTING_TYPE_MACSEC, NM_META_SETTING_TYPE_MACVLAN, diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.h b/src/libnmc-setting/nm-meta-setting-base-impl.h index 9226183fe..c03285ebf 100644 --- a/src/libnmc-setting/nm-meta-setting-base-impl.h +++ b/src/libnmc-setting/nm-meta-setting-base-impl.h @@ -128,6 +128,7 @@ typedef enum _nm_packed { NM_META_SETTING_TYPE_IP_TUNNEL, NM_META_SETTING_TYPE_IP4_CONFIG, NM_META_SETTING_TYPE_IP6_CONFIG, + NM_META_SETTING_TYPE_IPVLAN, NM_META_SETTING_TYPE_LINK, NM_META_SETTING_TYPE_LOOPBACK, NM_META_SETTING_TYPE_MACSEC, diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c index b3a51ba00..01aa271db 100644 --- a/src/libnmc-setting/nm-meta-setting-desc.c +++ b/src/libnmc-setting/nm-meta-setting-desc.c @@ -6077,7 +6077,6 @@ static const NMMetaPropertyInfo *const property_infos_HSR[] = { NULL }; - #undef _CURRENT_NM_META_SETTING_TYPE #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_HOSTNAME static const NMMetaPropertyInfo *const property_infos_HOSTNAME[] = { @@ -6799,6 +6798,37 @@ static const NMMetaPropertyInfo *const property_infos_IP_TUNNEL[] = { NULL }; +#undef _CURRENT_NM_META_SETTING_TYPE +#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_IPVLAN +static const NMMetaPropertyInfo *const property_infos_IPVLAN[] = { + PROPERTY_INFO_WITH_DESC (NM_SETTING_IPVLAN_PARENT, + .is_cli_option = TRUE, + .property_alias = "dev", + .inf_flags = NM_META_PROPERTY_INF_FLAG_REQD, + .prompt = N_("IPVLAN parent device or connection UUID"), + .property_type = &_pt_gobject_devices, + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_IPVLAN_MODE, + .is_cli_option = TRUE, + .property_alias = "mode", + .inf_flags = NM_META_PROPERTY_INF_FLAG_REQD, + .prompt = NM_META_TEXT_PROMPT_IPVLAN_MODE, + .property_type = &_pt_gobject_enum, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_enum, + .get_gtype = nm_setting_ipvlan_mode_get_type, + .min = NM_SETTING_IPVLAN_MODE_UNKNOWN + 1, + .max = G_MAXINT, + ), + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_IPVLAN_PRIVATE, + .property_type = &_pt_gobject_bool, + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_IPVLAN_VEPA, + .property_type = &_pt_gobject_bool, + ), + NULL +}; + #undef _CURRENT_NM_META_SETTING_TYPE #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_LOOPBACK static const NMMetaPropertyInfo *const property_infos_LOOPBACK[] = { @@ -8713,6 +8743,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN) #define SETTING_PRETTY_NAME_IP4_CONFIG N_("IPv4 protocol") #define SETTING_PRETTY_NAME_IP6_CONFIG N_("IPv6 protocol") #define SETTING_PRETTY_NAME_IP_TUNNEL N_("IP-tunnel settings") +#define SETTING_PRETTY_NAME_IPVLAN N_("IPVLAN settings") #define SETTING_PRETTY_NAME_LINK N_("Link settings") #define SETTING_PRETTY_NAME_LOOPBACK N_("Loopback settings") #define SETTING_PRETTY_NAME_MACSEC N_("MACsec connection") @@ -8881,6 +8912,14 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), + SETTING_INFO (IPVLAN, + .valid_parts = NM_META_SETTING_VALID_PARTS ( + NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), + NM_META_SETTING_VALID_PART_ITEM (IPVLAN, TRUE), + NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), + ), + ), SETTING_INFO (LINK), SETTING_INFO (LOOPBACK, .valid_parts = NM_META_SETTING_VALID_PARTS ( diff --git a/src/libnmc-setting/nm-meta-setting-desc.h b/src/libnmc-setting/nm-meta-setting-desc.h index 21d913826..e867f628c 100644 --- a/src/libnmc-setting/nm-meta-setting-desc.h +++ b/src/libnmc-setting/nm-meta-setting-desc.h @@ -69,6 +69,8 @@ struct _NMDevice; #define NM_META_TEXT_PROMPT_IP_TUNNEL_MODE N_("IP Tunnel mode") +#define NM_META_TEXT_PROMPT_IPVLAN_MODE N_("IPVLAN mode") + #define NM_META_TEXT_PROMPT_MACVLAN_MODE N_("MACVLAN mode") #define NM_META_TEXT_PROMPT_MACSEC_MODE N_("MACsec mode") diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in index 02b5d5fb3..8198caa45 100644 --- a/src/libnmc-setting/settings-docs.h.in +++ b/src/libnmc-setting/settings-docs.h.in @@ -249,6 +249,10 @@ #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_REMOTE N_("The remote endpoint of the tunnel; the value must contain an IPv4 or IPv6 address.") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_TOS N_("The type of service (IPv4) or traffic class (IPv6) field to be set on tunneled packets.") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_TTL N_("The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value.") +#define DESCRIBE_DOC_NM_SETTING_IPVLAN_MODE N_("The IPVLAN mode. Valid values: l2 (1), l3 (2), l3s (3)") +#define DESCRIBE_DOC_NM_SETTING_IPVLAN_PARENT N_("If given, specifies the parent interface name or parent connection UUID from which this IPVLAN interface should be created. If this property is not specified, the connection must contain an \"802-3-ethernet\" setting with a \"mac-address\" property.") +#define DESCRIBE_DOC_NM_SETTING_IPVLAN_PRIVATE N_("Whether the interface should be put in private mode.") +#define DESCRIBE_DOC_NM_SETTING_IPVLAN_VEPA N_("Whether the interface should be put in VEPA mode.") #define DESCRIBE_DOC_NM_SETTING_MACSEC_ENCRYPT N_("Whether the transmitted traffic must be encrypted.") #define DESCRIBE_DOC_NM_SETTING_MACSEC_MKA_CAK N_("The pre-shared CAK (Connectivity Association Key) for MACsec Key Agreement. Must be a string of 32 hexadecimal characters.") #define DESCRIBE_DOC_NM_SETTING_MACSEC_MKA_CAK_FLAGS N_("Flags indicating how to handle the \"mka-cak\" property.") diff --git a/src/nmcli/connections.c b/src/nmcli/connections.c index 002dd4027..1d9b9e39d 100644 --- a/src/nmcli/connections.c +++ b/src/nmcli/connections.c @@ -1075,7 +1075,7 @@ const NmcMetaGenericInfo "," NM_SETTING_PROXY_SETTING_NAME "," NM_SETTING_TC_CONFIG_SETTING_NAME \ "," NM_SETTING_SRIOV_SETTING_NAME "," NM_SETTING_ETHTOOL_SETTING_NAME \ "," NM_SETTING_OVS_DPDK_SETTING_NAME "," NM_SETTING_HOSTNAME_SETTING_NAME \ - "," NM_SETTING_HSR_SETTING_NAME + "," NM_SETTING_HSR_SETTING_NAME "," NM_SETTING_IPVLAN_SETTING_NAME /* NM_SETTING_DUMMY_SETTING_NAME NM_SETTING_WIMAX_SETTING_NAME */ const NmcMetaGenericInfo *const nmc_fields_con_active_details_groups[] = { diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in index ca90b4107..e6018a8ad 100644 --- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in +++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in @@ -629,7 +629,7 @@ alias="type" nmcli-description="Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc)." format="string" - values="6lowpan, 802-11-olpc-mesh, 802-11-wireless, 802-3-ethernet, adsl, bluetooth, bond, bridge, cdma, dummy, generic, gsm, hsr, infiniband, ip-tunnel, loopback, macsec, macvlan, ovs-bridge, ovs-dpdk, ovs-interface, ovs-patch, ovs-port, pppoe, team, tun, veth, vlan, vpn, vrf, vxlan, wifi-p2p, wimax, wireguard, wpan" /> + values="6lowpan, 802-11-olpc-mesh, 802-11-wireless, 802-3-ethernet, adsl, bluetooth, bond, bridge, cdma, dummy, generic, gsm, hsr, infiniband, ip-tunnel, ipvlan, loopback, macsec, macvlan, ovs-bridge, ovs-dpdk, ovs-interface, ovs-patch, ovs-port, pppoe, team, tun, veth, vlan, vpn, vrf, vxlan, wifi-p2p, wimax, wireguard, wpan" /> + + + + + +