diff --git a/NEWS b/NEWS
index 65260a14dc..ffd9dc43d9 100644
--- a/NEWS
+++ b/NEWS
@@ -47,6 +47,7 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
* WIFI connections using wpa-psk respect the setting connection.auth-retry
and only prompt for new secrets during the last authentication attempt before
failing.
+* Add support for GENEVE interface.
=============================================
NetworkManager-1.56
diff --git a/docs/api/network-manager-docs.xml b/docs/api/network-manager-docs.xml
index caff6fa4fc..9916427399 100644
--- a/docs/api/network-manager-docs.xml
+++ b/docs/api/network-manager-docs.xml
@@ -183,6 +183,7 @@
+
diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml
index 39f62417d1..719d8998a1 100644
--- a/docs/libnm/libnm-docs.xml
+++ b/docs/libnm/libnm-docs.xml
@@ -317,6 +317,7 @@ print ("NetworkManager version " + client.get_version())]]>
+
@@ -377,6 +378,7 @@ print ("NetworkManager version " + client.get_version())]]>
+
diff --git a/introspection/meson.build b/introspection/meson.build
index 16bb5ed423..7703b46983 100644
--- a/introspection/meson.build
+++ b/introspection/meson.build
@@ -15,6 +15,7 @@ ifaces = [
'org.freedesktop.NetworkManager.Device.Bridge',
'org.freedesktop.NetworkManager.Device.Dummy',
'org.freedesktop.NetworkManager.Device.Generic',
+ 'org.freedesktop.NetworkManager.Device.Geneve',
'org.freedesktop.NetworkManager.Device.Hsr',
'org.freedesktop.NetworkManager.Device.IPTunnel',
'org.freedesktop.NetworkManager.Device.Infiniband',
diff --git a/introspection/org.freedesktop.NetworkManager.Device.Geneve.xml b/introspection/org.freedesktop.NetworkManager.Device.Geneve.xml
new file mode 100644
index 0000000000..8d24c5a578
--- /dev/null
+++ b/introspection/org.freedesktop.NetworkManager.Device.Geneve.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 20bd8f253a..b66ca45263 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -11,6 +11,7 @@ src/core/devices/nm-device-bridge.c
src/core/devices/nm-device-dummy.c
src/core/devices/nm-device-ethernet-utils.c
src/core/devices/nm-device-ethernet.c
+src/core/devices/nm-device-geneve.c
src/core/devices/nm-device-infiniband.c
src/core/devices/nm-device-ip-tunnel.c
src/core/devices/nm-device-loopback.c
@@ -46,6 +47,7 @@ src/libnm-client-impl/nm-device-bt.c
src/libnm-client-impl/nm-device-dummy.c
src/libnm-client-impl/nm-device-ethernet.c
src/libnm-client-impl/nm-device-generic.c
+src/libnm-client-impl/nm-device-geneve.c
src/libnm-client-impl/nm-device-hsr.c
src/libnm-client-impl/nm-device-infiniband.c
src/libnm-client-impl/nm-device-ip-tunnel.c
@@ -90,6 +92,7 @@ src/libnm-core-impl/nm-setting-connection.c
src/libnm-core-impl/nm-setting-dcb.c
src/libnm-core-impl/nm-setting-ethtool.c
src/libnm-core-impl/nm-setting-generic.c
+src/libnm-core-impl/nm-setting-geneve.c
src/libnm-core-impl/nm-setting-gsm.c
src/libnm-core-impl/nm-setting-hsr.c
src/libnm-core-impl/nm-setting-infiniband.c
diff --git a/src/core/devices/nm-device-factory.c b/src/core/devices/nm-device-factory.c
index 1585836281..24755a5f00 100644
--- a/src/core/devices/nm-device-factory.c
+++ b/src/core/devices/nm-device-factory.c
@@ -412,6 +412,7 @@ nm_device_factory_manager_load_factories(NMDeviceFactoryManagerFactoryFunc callb
_ADD_INTERNAL(nm_dummy_device_factory_get_type);
_ADD_INTERNAL(nm_ethernet_device_factory_get_type);
_ADD_INTERNAL(nm_generic_device_factory_get_type);
+ _ADD_INTERNAL(nm_geneve_device_factory_get_type);
_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);
diff --git a/src/core/devices/nm-device-geneve.c b/src/core/devices/nm-device-geneve.c
new file mode 100644
index 0000000000..0968a2fb9b
--- /dev/null
+++ b/src/core/devices/nm-device-geneve.c
@@ -0,0 +1,487 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Red Hat, Inc.
+ */
+
+#include "src/core/nm-default-daemon.h"
+
+#include "nm-manager.h"
+#include "nm-device-geneve.h"
+
+#include "libnm-core-intern/nm-core-internal.h"
+#include "nm-act-request.h"
+#include "nm-device-private.h"
+#include "nm-setting-geneve.h"
+#include "libnm-platform/nm-platform.h"
+#include "nm-device-factory.h"
+
+#define _NMLOG_DEVICE_TYPE NMDeviceGeneve
+#include "nm-device-logging.h"
+
+NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceGeneve,
+ PROP_ID,
+ PROP_REMOTE,
+ PROP_TOS,
+ PROP_TTL,
+ PROP_DF,
+ PROP_DST_PORT, );
+
+typedef struct {
+ NMPlatformLnkGeneve props;
+} NMDeviceGenevePrivate;
+
+struct _NMDeviceGeneve {
+ NMDevice parent;
+ NMDeviceGenevePrivate _priv;
+};
+
+struct _NMDeviceGeneveClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE(NMDeviceGeneve, nm_device_geneve, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_GENEVE_GET_PRIVATE(self) \
+ _NM_GET_PRIVATE(self, NMDeviceGeneve, NM_IS_DEVICE_GENEVE, NMDevice)
+
+/*****************************************************************************/
+
+static NMDeviceCapabilities
+get_generic_capabilities(NMDevice *dev)
+{
+ return NM_DEVICE_CAP_IS_SOFTWARE;
+}
+
+static void
+update_properties(NMDevice *device)
+{
+ NMDeviceGeneve *self;
+ NMDeviceGenevePrivate *priv;
+ const NMPlatformLink *plink;
+ const NMPlatformLnkGeneve *props;
+ int ifindex;
+
+ g_return_if_fail(NM_IS_DEVICE_GENEVE(device));
+ self = NM_DEVICE_GENEVE(device);
+ priv = NM_DEVICE_GENEVE_GET_PRIVATE(self);
+
+ ifindex = nm_device_get_ifindex(device);
+ g_return_if_fail(ifindex > 0);
+ props = nm_platform_link_get_lnk_geneve(nm_device_get_platform(device), ifindex, &plink);
+
+ if (!props) {
+ _LOGW(LOGD_PLATFORM, "could not get GENEVE properties");
+ return;
+ }
+
+ g_object_freeze_notify((GObject *) device);
+
+#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
+
+#define CHECK_PROPERTY_CHANGED_IN6ADDR(field, prop) \
+ G_STMT_START \
+ { \
+ if (memcmp(&priv->props.field, &props->field, sizeof(props->field)) != 0) { \
+ priv->props.field = props->field; \
+ _notify(self, prop); \
+ } \
+ } \
+ G_STMT_END
+
+ CHECK_PROPERTY_CHANGED(id, PROP_ID);
+ CHECK_PROPERTY_CHANGED(remote, PROP_REMOTE);
+ CHECK_PROPERTY_CHANGED_IN6ADDR(remote6, PROP_REMOTE);
+ CHECK_PROPERTY_CHANGED(tos, PROP_TOS);
+ CHECK_PROPERTY_CHANGED(ttl, PROP_TTL);
+ CHECK_PROPERTY_CHANGED(df, PROP_DF);
+ CHECK_PROPERTY_CHANGED(dst_port, PROP_DST_PORT);
+
+ g_object_thaw_notify((GObject *) device);
+}
+
+static void
+link_changed(NMDevice *device, const NMPlatformLink *pllink)
+{
+ NM_DEVICE_CLASS(nm_device_geneve_parent_class)->link_changed(device, pllink);
+ update_properties(device);
+}
+
+static void
+unrealize_notify(NMDevice *device)
+{
+ NMDeviceGeneve *self = NM_DEVICE_GENEVE(device);
+ NMDeviceGenevePrivate *priv = NM_DEVICE_GENEVE_GET_PRIVATE(self);
+ guint i;
+
+ NM_DEVICE_CLASS(nm_device_geneve_parent_class)->unrealize_notify(device);
+
+ memset(&priv->props, 0, sizeof(NMPlatformLnkGeneve));
+
+ for (i = 1; i < _PROPERTY_ENUMS_LAST; i++)
+ g_object_notify_by_pspec(G_OBJECT(self), obj_properties[i]);
+}
+
+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);
+ NMPlatformLnkGeneve props = {};
+ NMSettingGeneve *s_geneve;
+ const char *str;
+ int r;
+
+ s_geneve = nm_connection_get_setting_geneve(connection);
+ g_return_val_if_fail(s_geneve, FALSE);
+
+ props.id = nm_setting_geneve_get_id(s_geneve);
+
+ str = nm_setting_geneve_get_remote(s_geneve);
+ if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.remote)
+ && !nm_inet_parse_bin(AF_INET6, str, NULL, &props.remote6)) {
+ return nm_assert_unreachable_val(FALSE);
+ }
+ props.tos = nm_setting_geneve_get_tos(s_geneve);
+ props.ttl = nm_setting_geneve_get_ttl(s_geneve);
+ props.df = nm_setting_geneve_get_df(s_geneve);
+ props.dst_port = nm_setting_geneve_get_destination_port(s_geneve);
+
+ r = nm_platform_link_geneve_add(nm_device_get_platform(device), iface, &props, out_plink);
+ if (r < 0) {
+ g_set_error(error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_CREATION_FAILED,
+ "Failed to create geneve interface '%s' for '%s': %s",
+ iface,
+ nm_connection_get_id(connection),
+ nm_strerror(r));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+address_matches(const char *candidate, in_addr_t addr4, struct in6_addr *addr6)
+{
+ NMIPAddr candidate_addr;
+ int addr_family;
+
+ if (!candidate)
+ return addr4 == 0u && IN6_IS_ADDR_UNSPECIFIED(addr6);
+
+ if (!nm_inet_parse_bin(AF_UNSPEC, candidate, &addr_family, &candidate_addr))
+ return FALSE;
+
+ if (!nm_ip_addr_equal(addr_family,
+ &candidate_addr,
+ NM_IS_IPv4(addr_family) ? (gpointer) &addr4 : addr6))
+ return FALSE;
+
+ if (NM_IS_IPv4(addr_family))
+ return IN6_IS_ADDR_UNSPECIFIED(addr6);
+ else
+ return addr4 == 0u;
+}
+
+static gboolean
+check_connection_compatible(NMDevice *device,
+ NMConnection *connection,
+ gboolean check_properties,
+ GError **error)
+{
+ NMDeviceGenevePrivate *priv = NM_DEVICE_GENEVE_GET_PRIVATE(device);
+ NMSettingGeneve *s_geneve;
+
+ if (!NM_DEVICE_CLASS(nm_device_geneve_parent_class)
+ ->check_connection_compatible(device, connection, check_properties, error))
+ return FALSE;
+
+ if (check_properties && nm_device_is_real(device)) {
+ s_geneve = nm_connection_get_setting_geneve(connection);
+
+ if (priv->props.id != nm_setting_geneve_get_id(s_geneve)) {
+ nm_utils_error_set_literal(error,
+ NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "geneve id mismatches");
+ return FALSE;
+ }
+
+ if (!address_matches(nm_setting_geneve_get_remote(s_geneve),
+ priv->props.remote,
+ &priv->props.remote6)) {
+ nm_utils_error_set_literal(error,
+ NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "geneve remote address mismatches");
+ return FALSE;
+ }
+
+ if (priv->props.dst_port != nm_setting_geneve_get_destination_port(s_geneve)) {
+ nm_utils_error_set_literal(error,
+ NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "geneve destination port mismatches");
+ return FALSE;
+ }
+
+ if (priv->props.tos != nm_setting_geneve_get_tos(s_geneve)) {
+ nm_utils_error_set_literal(error,
+ NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "geneve TOS mismatches");
+ return FALSE;
+ }
+
+ if (priv->props.ttl != nm_setting_geneve_get_ttl(s_geneve)) {
+ nm_utils_error_set_literal(error,
+ NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "geneve TTL mismatches");
+ return FALSE;
+ }
+
+ if (priv->props.df != nm_setting_geneve_get_df(s_geneve)) {
+ nm_utils_error_set_literal(error,
+ NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "geneve DF mismatches");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+complete_connection(NMDevice *device,
+ NMConnection *connection,
+ const char *specific_object,
+ NMConnection *const *existing_connections,
+ GError **error)
+{
+ NMSettingGeneve *s_geneve;
+
+ nm_utils_complete_generic(nm_device_get_platform(device),
+ connection,
+ NM_SETTING_GENEVE_SETTING_NAME,
+ existing_connections,
+ NULL,
+ _("Geneve connection"),
+ NULL,
+ NULL);
+
+ s_geneve = nm_connection_get_setting_geneve(connection);
+ if (!s_geneve) {
+ g_set_error_literal(error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_INVALID_CONNECTION,
+ "A 'geneve' setting is required.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+update_connection(NMDevice *device, NMConnection *connection)
+{
+ NMDeviceGenevePrivate *priv = NM_DEVICE_GENEVE_GET_PRIVATE(device);
+ NMSettingGeneve *s_geneve = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_GENEVE);
+ char sbuf[NM_INET_ADDRSTRLEN];
+
+ if (priv->props.id != nm_setting_geneve_get_id(s_geneve))
+ g_object_set(G_OBJECT(s_geneve), NM_SETTING_GENEVE_ID, priv->props.id, NULL);
+
+ /* Handle remote (IPv4 or IPv6) */
+ if (priv->props.remote) {
+ g_object_set(s_geneve,
+ NM_SETTING_GENEVE_REMOTE,
+ nm_inet4_ntop(priv->props.remote, sbuf),
+ NULL);
+ } else if (memcmp(&priv->props.remote6, &in6addr_any, sizeof(in6addr_any))) {
+ g_object_set(s_geneve,
+ NM_SETTING_GENEVE_REMOTE,
+ nm_inet6_ntop(&priv->props.remote6, sbuf),
+ NULL);
+ }
+
+ if (priv->props.dst_port != nm_setting_geneve_get_destination_port(s_geneve))
+ g_object_set(G_OBJECT(s_geneve),
+ NM_SETTING_GENEVE_DESTINATION_PORT,
+ priv->props.dst_port,
+ NULL);
+
+ if (priv->props.tos != nm_setting_geneve_get_tos(s_geneve))
+ g_object_set(G_OBJECT(s_geneve), NM_SETTING_GENEVE_TOS, priv->props.tos, NULL);
+
+ if (priv->props.ttl != nm_setting_geneve_get_ttl(s_geneve))
+ g_object_set(G_OBJECT(s_geneve), NM_SETTING_GENEVE_TTL, priv->props.ttl, NULL);
+
+ if (priv->props.df != nm_setting_geneve_get_df(s_geneve))
+ g_object_set(G_OBJECT(s_geneve), NM_SETTING_GENEVE_DF, priv->props.df, NULL);
+}
+
+static void
+get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ NMDeviceGenevePrivate *priv = NM_DEVICE_GENEVE_GET_PRIVATE(object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ g_value_set_uint(value, priv->props.id);
+ break;
+ case PROP_REMOTE:
+ if (priv->props.remote)
+ g_value_take_string(value, nm_inet4_ntop_dup(priv->props.remote));
+ else if (!IN6_IS_ADDR_UNSPECIFIED(&priv->props.remote6))
+ g_value_take_string(value, nm_inet6_ntop_dup(&priv->props.remote6));
+ break;
+ case PROP_TOS:
+ g_value_set_uchar(value, priv->props.tos);
+ break;
+ case PROP_TTL:
+ g_value_set_uchar(value, priv->props.ttl);
+ break;
+ case PROP_DF:
+ g_value_set_uint(value, priv->props.df);
+ break;
+ case PROP_DST_PORT:
+ g_value_set_uint(value, priv->props.dst_port);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_geneve_init(NMDeviceGeneve *self)
+{}
+
+static const NMDBusInterfaceInfoExtended interface_info_device_geneve = {
+ .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
+ NM_DBUS_INTERFACE_DEVICE_GENEVE,
+ .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
+ NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Id", "u", NM_DEVICE_GENEVE_ID),
+ NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Remote", "s", NM_DEVICE_GENEVE_REMOTE),
+ NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Tos", "y", NM_DEVICE_GENEVE_TOS),
+ NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ttl", "y", NM_DEVICE_GENEVE_TTL),
+ NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Df", "u", NM_DEVICE_GENEVE_DF),
+ NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DstPort",
+ "q",
+ NM_DEVICE_GENEVE_DST_PORT), ), ),
+};
+
+static void
+nm_device_geneve_class_init(NMDeviceGeneveClass *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_geneve);
+
+ device_class->connection_type_supported = NM_SETTING_GENEVE_SETTING_NAME;
+ device_class->connection_type_check_compatible = NM_SETTING_GENEVE_SETTING_NAME;
+ device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_GENEVE);
+
+ device_class->link_changed = link_changed;
+ device_class->unrealize_notify = unrealize_notify;
+ device_class->create_and_realize = create_and_realize;
+ device_class->check_connection_compatible = check_connection_compatible;
+ device_class->complete_connection = complete_connection;
+ device_class->get_generic_capabilities = get_generic_capabilities;
+ device_class->update_connection = update_connection;
+
+ obj_properties[PROP_ID] = g_param_spec_uint(NM_DEVICE_GENEVE_ID,
+ "",
+ "",
+ 0,
+ G_MAXUINT32,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_REMOTE] = g_param_spec_string(NM_DEVICE_GENEVE_REMOTE,
+ "",
+ "",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_TOS] = g_param_spec_uchar(NM_DEVICE_GENEVE_TOS,
+ "",
+ "",
+ 0,
+ 255,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_TTL] = g_param_spec_uchar(NM_DEVICE_GENEVE_TTL,
+ "",
+ "",
+ 0,
+ 255,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_DF] = g_param_spec_uint(NM_DEVICE_GENEVE_DF,
+ "",
+ "",
+ 0,
+ 2,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_DST_PORT] = g_param_spec_uint(NM_DEVICE_GENEVE_DST_PORT,
+ "",
+ "",
+ 0,
+ 65535,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+}
+
+/*****************************************************************************/
+
+#define NM_TYPE_GENEVE_DEVICE_FACTORY (nm_geneve_device_factory_get_type())
+#define NM_GENEVE_DEVICE_FACTORY(obj) \
+ (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_GENEVE_DEVICE_FACTORY, NMGeneveDeviceFactory))
+
+static NMDevice *
+create_device(NMDeviceFactory *factory,
+ const char *iface,
+ const NMPlatformLink *plink,
+ NMConnection *connection,
+ gboolean *out_ignore)
+{
+ return g_object_new(NM_TYPE_DEVICE_GENEVE,
+ NM_DEVICE_IFACE,
+ iface,
+ NM_DEVICE_TYPE_DESC,
+ "Geneve",
+ NM_DEVICE_DEVICE_TYPE,
+ NM_DEVICE_TYPE_GENEVE,
+ NM_DEVICE_LINK_TYPE,
+ NM_LINK_TYPE_GENEVE,
+ NULL);
+}
+
+NM_DEVICE_FACTORY_DEFINE_INTERNAL(
+ GENEVE,
+ Geneve,
+ geneve,
+ NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(NM_LINK_TYPE_GENEVE)
+ NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_GENEVE_SETTING_NAME),
+ factory_class->create_device = create_device;);
diff --git a/src/core/devices/nm-device-geneve.h b/src/core/devices/nm-device-geneve.h
new file mode 100644
index 0000000000..d0a44e9dbb
--- /dev/null
+++ b/src/core/devices/nm-device-geneve.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_GENEVE_H__
+#define __NETWORKMANAGER_DEVICE_GENEVE_H__
+
+#include "nm-device.h"
+
+#define NM_TYPE_DEVICE_GENEVE (nm_device_geneve_get_type())
+#define NM_DEVICE_GENEVE(obj) \
+ (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_GENEVE, NMDeviceGeneve))
+#define NM_DEVICE_GENEVE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_GENEVE, NMDeviceGeneveClass))
+#define NM_IS_DEVICE_GENEVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_GENEVE))
+#define NM_IS_DEVICE_GENEVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE_GENEVE))
+#define NM_DEVICE_GENEVE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_GENEVE, NMDeviceGeneveClass))
+
+#define NM_DEVICE_GENEVE_ID "id"
+#define NM_DEVICE_GENEVE_REMOTE "remote"
+#define NM_DEVICE_GENEVE_TOS "tos"
+#define NM_DEVICE_GENEVE_TTL "ttl"
+#define NM_DEVICE_GENEVE_DF "df"
+#define NM_DEVICE_GENEVE_DST_PORT "dst-port"
+
+typedef struct _NMDeviceGeneve NMDeviceGeneve;
+typedef struct _NMDeviceGeneveClass NMDeviceGeneveClass;
+
+GType nm_device_geneve_get_type(void);
+
+#endif /* __NETWORKMANAGER_DEVICE_GENEVE_H__ */
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index 434683d6b4..f3f09db18c 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -5962,7 +5962,6 @@ nm_device_get_route_metric_default(NMDeviceType device_type)
* in some aspects a VPN. */
case NM_DEVICE_TYPE_WIREGUARD:
return NM_VPN_ROUTE_METRIC_DEFAULT;
-
case NM_DEVICE_TYPE_ETHERNET:
case NM_DEVICE_TYPE_VETH:
return 100;
@@ -5996,6 +5995,8 @@ nm_device_get_route_metric_default(NMDeviceType device_type)
return 470;
case NM_DEVICE_TYPE_VXLAN:
return 500;
+ case NM_DEVICE_TYPE_GENEVE:
+ return 525;
case NM_DEVICE_TYPE_DUMMY:
return 550;
case NM_DEVICE_TYPE_WIFI:
@@ -19567,7 +19568,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_IPVLAN);
+ nm_assert(priv->type <= NM_DEVICE_TYPE_GENEVE);
break;
case PROP_LINK_TYPE:
/* construct-only */
diff --git a/src/core/meson.build b/src/core/meson.build
index 26d27f96e2..4a3dfd8e11 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -111,6 +111,7 @@ libNetworkManager = static_library(
'devices/nm-device-ethernet-utils.c',
'devices/nm-device-factory.c',
'devices/nm-device-generic.c',
+ 'devices/nm-device-geneve.c',
'devices/nm-device-hsr.c',
'devices/nm-device-infiniband.c',
'devices/nm-device-ip-tunnel.c',
diff --git a/src/core/nm-ip-config.c b/src/core/nm-ip-config.c
index 51a069f801..4616f2e4a3 100644
--- a/src/core/nm-ip-config.c
+++ b/src/core/nm-ip-config.c
@@ -56,7 +56,7 @@ G_DEFINE_ABSTRACT_TYPE(NMIPConfig, nm_ip_config, NM_TYPE_DBUS_OBJECT)
/*****************************************************************************/
-static void _handle_platform_change(NMIPConfig *self, guint32 obj_type_flags, gboolean is_init);
+static void _handle_platform_change(NMIPConfig *self, guint64 obj_type_flags, gboolean is_init);
static void _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd);
/*****************************************************************************/
@@ -77,7 +77,7 @@ static void
_notify_platform_handle(NMIPConfig *self, gint64 now_msec)
{
NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self);
- guint32 obj_type_flags;
+ guint64 obj_type_flags;
nm_clear_g_source_inst(&priv->notify_platform_timeout_source);
@@ -98,7 +98,7 @@ _notify_platform_cb(gpointer user_data)
}
static void
-_notify_platform(NMIPConfig *self, guint32 obj_type_flags)
+_notify_platform(NMIPConfig *self, guint64 obj_type_flags)
{
const int addr_family = nm_ip_config_get_addr_family(self);
const int IS_IPv4 = NM_IS_IPv4(addr_family);
@@ -953,7 +953,7 @@ _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd)
}
static void
-_handle_platform_change(NMIPConfig *self, guint32 obj_type_flags, gboolean is_init)
+_handle_platform_change(NMIPConfig *self, guint64 obj_type_flags, gboolean is_init)
{
const int addr_family = nm_ip_config_get_addr_family(self);
const int IS_IPv4 = NM_IS_IPv4(addr_family);
diff --git a/src/core/nm-ip-config.h b/src/core/nm-ip-config.h
index 47a40bd223..02d654c4fd 100644
--- a/src/core/nm-ip-config.h
+++ b/src/core/nm-ip-config.h
@@ -35,7 +35,7 @@ struct _NMIPConfigPrivate {
GSource *notify_platform_timeout_source;
gint64 notify_platform_rlimited_until_msec;
gulong l3cfg_notify_id;
- guint32 notify_platform_obj_type_flags;
+ guint64 notify_platform_obj_type_flags;
};
struct _NMIPConfig {
diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c
index 9bb272df10..59fc6e4d7f 100644
--- a/src/core/nm-l3cfg.c
+++ b/src/core/nm-l3cfg.c
@@ -657,7 +657,7 @@ _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data,
case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE:
nm_strbuf_append(&s,
&l,
- ", obj-type-flags=0x%x",
+ ", obj-type-flags=0x%" G_GINT64_MODIFIER "x",
notify_data->platform_change_on_idle.obj_type_flags);
break;
case NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT:
@@ -1604,7 +1604,7 @@ _load_link(NML3Cfg *self, gboolean initial)
/*****************************************************************************/
void
-_nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint32 obj_type_flags)
+_nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint64 obj_type_flags)
{
NML3ConfigNotifyData notify_data;
diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h
index 5f0721da3c..c103c9f233 100644
--- a/src/core/nm-l3cfg.h
+++ b/src/core/nm-l3cfg.h
@@ -178,7 +178,7 @@ typedef struct {
} platform_change;
struct {
- guint32 obj_type_flags;
+ guint64 obj_type_flags;
} platform_change_on_idle;
struct {
@@ -207,7 +207,7 @@ struct _NML3Cfg {
* NML3Cfg instance. We track some per-l3cfg-data that is only
* relevant to NMNetns here. */
struct {
- guint32 signal_pending_obj_type_flags;
+ guint64 signal_pending_obj_type_flags;
CList signal_pending_lst;
CList ecmp_track_ifindex_lst_head;
} internal_netns;
@@ -223,7 +223,7 @@ NML3Cfg *nm_l3cfg_new(NMNetns *netns, int ifindex);
gboolean nm_l3cfg_is_ready(NML3Cfg *self);
-void _nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint32 obj_type_flags);
+void _nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint64 obj_type_flags);
void _nm_l3cfg_notify_platform_change(NML3Cfg *self,
NMPlatformSignalChangeType change_type,
diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c
index 91445b30f8..f1f7caee7a 100644
--- a/src/core/platform/tests/test-common.c
+++ b/src/core/platform/tests/test-common.c
@@ -1230,7 +1230,7 @@ out:
}
gboolean
-nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolean do_assert)
+nmtstp_check_platform_full(NMPlatform *platform, guint64 obj_type_flags, gboolean do_assert)
{
static const NMPObjectType obj_types[] = {
NMP_OBJECT_TYPE_IP4_ADDRESS,
@@ -1265,7 +1265,7 @@ nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolea
for (i_obj_types = 0; i_obj_types < (int) G_N_ELEMENTS(obj_types); i_obj_types++) {
const NMPObjectType obj_type = obj_types[i_obj_types];
- const guint32 i_obj_type_flags = nmp_object_type_to_flags(obj_type);
+ const guint64 i_obj_type_flags = nmp_object_type_to_flags(obj_type);
gs_unref_ptrarray GPtrArray *arr1 = NULL;
gs_unref_ptrarray GPtrArray *arr2 = NULL;
NMPLookup lookup;
@@ -1408,7 +1408,7 @@ nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolea
}
void
-nmtstp_check_platform(NMPlatform *platform, guint32 obj_type_flags)
+nmtstp_check_platform(NMPlatform *platform, guint64 obj_type_flags)
{
if (!nmtstp_check_platform_full(platform, obj_type_flags, FALSE)) {
/* It's unclear why this failure sometimes happens. It happens
@@ -2393,6 +2393,61 @@ nmtstp_link_dummy_add(NMPlatform *platform, int external_command, const char *na
return pllink;
}
+const NMPlatformLink *
+nmtstp_link_geneve_add(NMPlatform *platform,
+ int external_command,
+ const char *name,
+ const NMPlatformLnkGeneve *lnk)
+{
+ const NMPlatformLink *pllink = NULL;
+ int success;
+
+ g_assert(nm_utils_ifname_valid_kernel(name, NULL));
+ g_assert(lnk->remote || !IN6_IS_ADDR_UNSPECIFIED(&lnk->remote6));
+
+ external_command = nmtstp_run_command_check_external(external_command);
+
+ _init_platform(&platform, external_command);
+
+ if (external_command) {
+ char remote[NM_INET_ADDRSTRLEN];
+ char remote6[NM_INET_ADDRSTRLEN];
+ char str_ttl[30];
+
+ if (lnk->remote)
+ nm_inet4_ntop(lnk->remote, remote);
+ else
+ remote[0] = '\0';
+
+ if (memcmp(&lnk->remote6, &in6addr_any, sizeof(in6addr_any)))
+ nm_inet6_ntop(&lnk->remote6, remote6);
+ else
+ remote6[0] = '\0';
+
+ success = !nmtstp_run_command(
+ "ip link add %s type geneve id %u remote %s %s tos %02x dstport %u%s",
+ name,
+ lnk->id,
+ remote[0] ? remote : remote6,
+ lnk->ttl > 0 ? nm_sprintf_buf(str_ttl, "ttl %u", lnk->ttl & 0xff)
+ : lnk->ttl == 0 ? "ttl auto"
+ : "ttl inherit",
+ lnk->tos,
+ lnk->dst_port,
+ lnk->df == 1 ? " df set "
+ : lnk->df == 2 ? " df inherit "
+ : "");
+
+ if (success)
+ pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_GENEVE, 100);
+ } else
+ success = NMTST_NM_ERR_SUCCESS(nm_platform_link_geneve_add(platform, name, lnk, &pllink));
+
+ _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_GENEVE);
+
+ return pllink;
+}
+
const NMPlatformLink *
nmtstp_link_gre_add(NMPlatform *platform,
int external_command,
@@ -3024,8 +3079,6 @@ nmtstp_link_vxlan_add(NMPlatform *platform,
return pllink;
}
-/*****************************************************************************/
-
const NMPlatformLink *
nmtstp_link_get_typed(NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type)
{
diff --git a/src/core/platform/tests/test-common.h b/src/core/platform/tests/test-common.h
index 85ed79615b..251fa51090 100644
--- a/src/core/platform/tests/test-common.h
+++ b/src/core/platform/tests/test-common.h
@@ -140,9 +140,9 @@ int nmtstp_run_command(const char *format, ...) _nm_printf(1, 2);
/*****************************************************************************/
gboolean
-nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolean do_assert);
+nmtstp_check_platform_full(NMPlatform *platform, guint64 obj_type_flags, gboolean do_assert);
-void nmtstp_check_platform(NMPlatform *platform, guint32 obj_type_flags);
+void nmtstp_check_platform(NMPlatform *platform, guint64 obj_type_flags);
/*****************************************************************************/
@@ -474,6 +474,10 @@ const NMPlatformLink *nmtstp_link_veth_add(NMPlatform *platform,
const char *peer);
const NMPlatformLink *
nmtstp_link_dummy_add(NMPlatform *platform, int external_command, const char *name);
+const NMPlatformLink *nmtstp_link_geneve_add(NMPlatform *platform,
+ int external_command,
+ const char *name,
+ const NMPlatformLnkGeneve *lnk);
const NMPlatformLink *nmtstp_link_gre_add(NMPlatform *platform,
int external_command,
const char *name,
diff --git a/src/core/platform/tests/test-link.c b/src/core/platform/tests/test-link.c
index 77e7e64121..5618308f80 100644
--- a/src/core/platform/tests/test-link.c
+++ b/src/core/platform/tests/test-link.c
@@ -1388,10 +1388,11 @@ test_software_detect(gconstpointer user_data)
const gboolean ext = test_data->external_command;
NMPlatformLnkBridge lnk_bridge = {};
NMPlatformLnkTun lnk_tun;
- NMPlatformLnkGre lnk_gre = {};
- NMPlatformLnkVti lnk_vti = {};
- NMPlatformLnkVti6 lnk_vti6 = {};
- nm_auto_close int tun_fd = -1;
+ NMPlatformLnkGeneve lnk_geneve = {};
+ NMPlatformLnkGre lnk_gre = {};
+ NMPlatformLnkVti lnk_vti = {};
+ NMPlatformLnkVti6 lnk_vti6 = {};
+ nm_auto_close int tun_fd = -1;
gboolean module_loaded;
nmtstp_run_command_check("ip link add %s type dummy", PARENT_NAME);
@@ -1434,6 +1435,31 @@ test_software_detect(gconstpointer user_data)
g_error("Failed adding Bridge interface");
break;
+ case NM_LINK_TYPE_GENEVE:
+ {
+ switch (test_data->test_mode) {
+ case 0:
+ lnk_geneve.id = 42;
+ lnk_geneve.remote = nmtst_inet4_from_string("192.168.1.100");
+ lnk_geneve.ttl = 64;
+ lnk_geneve.tos = 0;
+ lnk_geneve.dst_port = 6081;
+ lnk_geneve.df = 0;
+ break;
+ case 1:
+ lnk_geneve.id = 12345;
+ lnk_geneve.remote6 = nmtst_inet6_from_string("2001:db8::1");
+ lnk_geneve.ttl = 128;
+ lnk_geneve.tos = 16;
+ lnk_geneve.dst_port = 6082;
+ lnk_geneve.df = 1;
+ break;
+ }
+
+ g_assert(nmtstp_link_geneve_add(NULL, ext, DEVICE_NAME, &lnk_geneve));
+ break;
+ }
+
case NM_LINK_TYPE_GRE:
module_loaded = nmtstp_ensure_module("ip_gre");
@@ -2208,6 +2234,34 @@ test_software_detect(gconstpointer user_data)
}
break;
}
+ case NM_LINK_TYPE_GENEVE:
+ {
+ const NMPlatformLnkGeneve *plnk = &lnk->lnk_geneve;
+
+ g_assert(plnk == nm_platform_link_get_lnk_geneve(NM_PLATFORM_GET, ifindex, NULL));
+
+ switch (test_data->test_mode) {
+ case 0:
+ g_assert_cmpint(plnk->id, ==, 42);
+ nmtst_assert_ip4_address(plnk->remote, "192.168.1.100");
+ nmtst_assert_ip6_address(&plnk->remote6, "::");
+ g_assert_cmpint(plnk->ttl, ==, 64);
+ g_assert_cmpint(plnk->tos, ==, 0);
+ g_assert_cmpint(plnk->dst_port, ==, 6081);
+ g_assert_cmpint(plnk->df, ==, 0);
+ break;
+ case 1:
+ g_assert_cmpint(plnk->id, ==, 12345);
+ nmtst_assert_ip4_address(plnk->remote, "0.0.0.0");
+ nmtst_assert_ip6_address(&plnk->remote6, "2001:db8::1");
+ g_assert_cmpint(plnk->ttl, ==, 128);
+ g_assert_cmpint(plnk->tos, ==, 16);
+ g_assert_cmpint(plnk->dst_port, ==, 6082);
+ g_assert_cmpint(plnk->df, ==, 1);
+ break;
+ }
+ break;
+ }
case NM_LINK_TYPE_WIREGUARD:
{
const NMPlatformLnkWireGuard *plnk = &lnk->lnk_wireguard;
@@ -4143,6 +4197,8 @@ _nmtstp_setup_tests(void)
g_test_add_func("/link/external", test_external);
test_software_detect_add("/link/software/detect/bridge", NM_LINK_TYPE_BRIDGE, 0);
+ test_software_detect_add("/link/software/detect/geneve/0", NM_LINK_TYPE_GENEVE, 0);
+ test_software_detect_add("/link/software/detect/geneve/1", NM_LINK_TYPE_GENEVE, 1);
test_software_detect_add("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0);
test_software_detect_add("/link/software/detect/gretap", NM_LINK_TYPE_GRETAP, 0);
test_software_detect_add("/link/software/detect/ip6tnl/0", NM_LINK_TYPE_IP6TNL, 0);
diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver
index 5678a90d09..e9850ea6e5 100644
--- a/src/libnm-client-impl/libnm.ver
+++ b/src/libnm-client-impl/libnm.ver
@@ -2106,8 +2106,25 @@ global:
libnm_1_58_0 {
global:
+ nm_connection_get_setting_geneve;
+ nm_device_geneve_get_df;
+ nm_device_geneve_get_dst_port;
+ nm_device_geneve_get_id;
+ nm_device_geneve_get_remote;
+ nm_device_geneve_get_tos;
+ nm_device_geneve_get_ttl;
+ nm_device_geneve_get_type;
nm_ip_config_get_clat_address;
nm_ip_config_get_clat_pref64;
+ nm_setting_geneve_df_get_type;
+ nm_setting_geneve_get_destination_port;
+ nm_setting_geneve_get_df;
+ nm_setting_geneve_get_id;
+ nm_setting_geneve_get_remote;
+ nm_setting_geneve_get_tos;
+ nm_setting_geneve_get_ttl;
+ nm_setting_geneve_get_type;
+ nm_setting_geneve_new;
nm_setting_ip4_config_clat_get_type;
nm_setting_ip4_config_get_clat;
nm_utils_wifi_6ghz_freqs;
diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build
index b49366292f..3352ebfee0 100644
--- a/src/libnm-client-impl/meson.build
+++ b/src/libnm-client-impl/meson.build
@@ -16,6 +16,7 @@ libnm_client_impl_sources = files(
'nm-device-bt.c',
'nm-device-dummy.c',
'nm-device-ethernet.c',
+ 'nm-device-geneve.c',
'nm-device-generic.c',
'nm-device-hsr.c',
'nm-device-infiniband.c',
diff --git a/src/libnm-client-impl/nm-client.c b/src/libnm-client-impl/nm-client.c
index b81ac6e506..f13835269e 100644
--- a/src/libnm-client-impl/nm-client.c
+++ b/src/libnm-client-impl/nm-client.c
@@ -29,6 +29,7 @@
#include "nm-device-dummy.h"
#include "nm-device-ethernet.h"
#include "nm-device-generic.h"
+#include "nm-device-geneve.h"
#include "nm-device-hsr.h"
#include "nm-device-infiniband.h"
#include "nm-device-ip-tunnel.h"
diff --git a/src/libnm-client-impl/nm-device-geneve.c b/src/libnm-client-impl/nm-device-geneve.c
new file mode 100644
index 0000000000..415e978d16
--- /dev/null
+++ b/src/libnm-client-impl/nm-device-geneve.c
@@ -0,0 +1,344 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Red Hat, Inc.
+ */
+
+#include "libnm-client-impl/nm-default-libnm.h"
+
+#include "nm-device-geneve.h"
+
+#include "nm-setting-connection.h"
+#include "nm-setting-geneve.h"
+#include "nm-utils.h"
+#include "nm-object-private.h"
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_ID,
+ PROP_REMOTE,
+ PROP_TOS,
+ PROP_TTL,
+ PROP_DST_PORT,
+ PROP_DF, );
+
+typedef struct {
+ char *remote;
+ guint32 id;
+ gint32 ttl;
+ guint16 dst_port;
+ guint8 df;
+ guint8 tos;
+} NMDeviceGenevePrivate;
+
+struct _NMDeviceGeneve {
+ NMDevice parent;
+ NMDeviceGenevePrivate _priv;
+};
+
+struct _NMDeviceGeneveClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE(NMDeviceGeneve, nm_device_geneve, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_GENEVE_GET_PRIVATE(self) \
+ _NM_GET_PRIVATE(self, NMDeviceGeneve, NM_IS_DEVICE_GENEVE, NMObject, NMDevice)
+
+/*****************************************************************************/
+
+/**
+ * nm_device_geneve_get_id:
+ * @device: a #NMDeviceGeneve
+ *
+ * Returns: the device's GENEVE ID.
+ *
+ * Since: 1.58
+ **/
+guint
+nm_device_geneve_get_id(NMDeviceGeneve *device)
+{
+ g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), 0);
+
+ return NM_DEVICE_GENEVE_GET_PRIVATE(device)->id;
+}
+
+/**
+ * nm_device_geneve_get_remote:
+ * @device: a #NMDeviceGeneve
+ *
+ * Returns: the IP address of the remote tunnel endpoint
+ *
+ * Since: 1.58
+ **/
+const char *
+nm_device_geneve_get_remote(NMDeviceGeneve *device)
+{
+ g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), NULL);
+
+ return _nml_coerce_property_str_not_empty(NM_DEVICE_GENEVE_GET_PRIVATE(device)->remote);
+}
+
+/**
+ * nm_device_geneve_get_dst_port:
+ * @device: a #NMDeviceGeneve
+ *
+ * Returns: the UDP destination port
+ *
+ * Since: 1.58
+ **/
+guint
+nm_device_geneve_get_dst_port(NMDeviceGeneve *device)
+{
+ g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), 0);
+
+ return NM_DEVICE_GENEVE_GET_PRIVATE(device)->dst_port;
+}
+
+/**
+ * nm_device_geneve_get_tos:
+ * @device: a #NMDeviceGeneve
+ *
+ * Returns: the TOS value to use in outgoing packets
+ *
+ * Since: 1.58
+ **/
+guint
+nm_device_geneve_get_tos(NMDeviceGeneve *device)
+{
+ g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), 0);
+
+ return NM_DEVICE_GENEVE_GET_PRIVATE(device)->tos;
+}
+
+/**
+ * nm_device_geneve_get_ttl:
+ * @device: a #NMDeviceGeneve
+ *
+ * Returns: the time-to-live value to use in outgoing packets
+ *
+ * Since: 1.58
+ **/
+guint
+nm_device_geneve_get_ttl(NMDeviceGeneve *device)
+{
+ g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), 0);
+
+ return NM_DEVICE_GENEVE_GET_PRIVATE(device)->ttl;
+}
+
+/**
+ * nm_device_geneve_get_df:
+ * @device: a #NMDeviceGeneve
+ *
+ * Returns: the Don't Fragment (DF) bit to set in outgoing packets
+ *
+ * Since: 1.58
+ **/
+guint
+nm_device_geneve_get_df(NMDeviceGeneve *device)
+{
+ g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), 0);
+
+ return NM_DEVICE_GENEVE_GET_PRIVATE(device)->df;
+}
+
+static gboolean
+connection_compatible(NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingGeneve *s_geneve;
+
+ if (!NM_DEVICE_CLASS(nm_device_geneve_parent_class)
+ ->connection_compatible(device, connection, error))
+ return FALSE;
+
+ if (!nm_connection_is_type(connection, NM_SETTING_GENEVE_SETTING_NAME)) {
+ g_set_error_literal(error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+ _("The connection was not a GENEVE connection."));
+ return FALSE;
+ }
+
+ s_geneve = nm_connection_get_setting_geneve(connection);
+ if (nm_setting_geneve_get_id(s_geneve) != nm_device_geneve_get_id(NM_DEVICE_GENEVE(device))) {
+ g_set_error_literal(
+ error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+ _("The GENEVE identifiers of the device and the connection didn't match."));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GType
+get_setting_type(NMDevice *device)
+{
+ return NM_TYPE_SETTING_GENEVE;
+}
+
+/*****************************************************************************/
+
+static void
+get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ NMDeviceGeneve *device = NM_DEVICE_GENEVE(object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ g_value_set_uint(value, nm_device_geneve_get_id(device));
+ break;
+ case PROP_REMOTE:
+ g_value_set_string(value, nm_device_geneve_get_remote(device));
+ break;
+ case PROP_TOS:
+ g_value_set_uint(value, nm_device_geneve_get_tos(device));
+ break;
+ case PROP_TTL:
+ g_value_set_int(value, nm_device_geneve_get_ttl(device));
+ break;
+ case PROP_DST_PORT:
+ g_value_set_uint(value, nm_device_geneve_get_dst_port(device));
+ break;
+ case PROP_DF:
+ g_value_set_uint(value, nm_device_geneve_get_df(device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_geneve_init(NMDeviceGeneve *device)
+{}
+
+static void
+finalize(GObject *object)
+{
+ NMDeviceGenevePrivate *priv = NM_DEVICE_GENEVE_GET_PRIVATE(object);
+
+ g_free(priv->remote);
+
+ G_OBJECT_CLASS(nm_device_geneve_parent_class)->finalize(object);
+}
+
+const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_geneve = NML_DBUS_META_IFACE_INIT_PROP(
+ NM_DBUS_INTERFACE_DEVICE_GENEVE,
+ nm_device_geneve_get_type,
+ NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30,
+ NML_DBUS_META_IFACE_DBUS_PROPERTIES(
+ NML_DBUS_META_PROPERTY_INIT_Y("Df", PROP_DF, NMDeviceGeneve, _priv.df),
+ NML_DBUS_META_PROPERTY_INIT_Q("DstPort", PROP_DST_PORT, NMDeviceGeneve, _priv.dst_port),
+ NML_DBUS_META_PROPERTY_INIT_U("Id", PROP_ID, NMDeviceGeneve, _priv.id),
+ NML_DBUS_META_PROPERTY_INIT_S("Remote", PROP_REMOTE, NMDeviceGeneve, _priv.remote),
+ NML_DBUS_META_PROPERTY_INIT_Y("Tos", PROP_TOS, NMDeviceGeneve, _priv.tos),
+ NML_DBUS_META_PROPERTY_INIT_I("Ttl", PROP_TTL, NMDeviceGeneve, _priv.ttl), ), );
+
+static void
+nm_device_geneve_class_init(NMDeviceGeneveClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS(klass);
+
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceGeneve);
+
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+
+ /**
+ * NMDeviceGeneve:id:
+ *
+ * The device's GENEVE ID.
+ *
+ * Since: 1.58
+ **/
+ obj_properties[PROP_ID] = g_param_spec_uint(NM_DEVICE_GENEVE_ID,
+ "",
+ "",
+ 0,
+ (1 << 24) - 1,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * NMDeviceGeneve:remote:
+ *
+ * The IP address of the remote tunnel endpoint.
+ *
+ * Since: 1.58
+ */
+ obj_properties[PROP_REMOTE] = g_param_spec_string(NM_DEVICE_GENEVE_REMOTE,
+ "",
+ "",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * NMDeviceGeneve:tos:
+ *
+ * The TOS value to use in outgoing packets.
+ *
+ * Since: 1.58
+ */
+ obj_properties[PROP_TOS] = g_param_spec_uchar(NM_DEVICE_GENEVE_TOS,
+ "",
+ "",
+ 0,
+ 255,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * NMDeviceGeneve:ttl:
+ *
+ * The time-to-live value to use in outgoing packets.
+ *
+ * Since: 1.58
+ */
+ obj_properties[PROP_TTL] = g_param_spec_int(NM_DEVICE_GENEVE_TTL,
+ "",
+ "",
+ -1,
+ 255,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * NMDeviceGeneve:dst-port:
+ *
+ * The UDP destination port used to communicate with the remote GENEVE tunnel
+ * endpoint.
+ *
+ * Since: 1.58
+ */
+ obj_properties[PROP_DST_PORT] = g_param_spec_uint(NM_DEVICE_GENEVE_DST_PORT,
+ "",
+ "",
+ 0,
+ 65535,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * NMDeviceGeneve:df:
+ *
+ * The Don't Fragment (DF) bit to set in outgoing packets.
+ *
+ * Since: 1.58
+ */
+ obj_properties[PROP_DF] = g_param_spec_uchar(NM_DEVICE_GENEVE_DF,
+ "",
+ "",
+ 0,
+ 2,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_geneve);
+}
diff --git a/src/libnm-client-impl/nm-device.c b/src/libnm-client-impl/nm-device.c
index 1712efa5bb..9203fd6f13 100644
--- a/src/libnm-client-impl/nm-device.c
+++ b/src/libnm-client-impl/nm-device.c
@@ -302,6 +302,7 @@ coerce_type(NMDeviceType type)
case NM_DEVICE_TYPE_TUN:
case NM_DEVICE_TYPE_VETH:
case NM_DEVICE_TYPE_GENERIC:
+ case NM_DEVICE_TYPE_GENEVE:
case NM_DEVICE_TYPE_UNUSED1:
case NM_DEVICE_TYPE_UNUSED2:
case NM_DEVICE_TYPE_UNKNOWN:
@@ -1792,6 +1793,8 @@ get_type_name(NMDevice *device)
return _("MACVLAN");
case NM_DEVICE_TYPE_VXLAN:
return _("VXLAN");
+ case NM_DEVICE_TYPE_GENEVE:
+ return _("GENEVE");
case NM_DEVICE_TYPE_IP_TUNNEL:
return _("IPTunnel");
case NM_DEVICE_TYPE_TUN:
diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c
index c2fa2addef..9f1b515c2e 100644
--- a/src/libnm-client-impl/nm-libnm-utils.c
+++ b/src/libnm-client-impl/nm-libnm-utils.c
@@ -789,6 +789,7 @@ const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[] = {
&_nml_dbus_meta_iface_nm_device_bridge,
&_nml_dbus_meta_iface_nm_device_dummy,
&_nml_dbus_meta_iface_nm_device_generic,
+ &_nml_dbus_meta_iface_nm_device_geneve,
&_nml_dbus_meta_iface_nm_device_hsr,
&_nml_dbus_meta_iface_nm_device_iptunnel,
&_nml_dbus_meta_iface_nm_device_infiniband,
diff --git a/src/libnm-client-impl/nm-libnm-utils.h b/src/libnm-client-impl/nm-libnm-utils.h
index 7dcf8c18dc..27b77b6009 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[47];
+extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[48];
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint;
@@ -593,6 +593,7 @@ extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bond;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bridge;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_dummy;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_generic;
+extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_geneve;
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;
diff --git a/src/libnm-client-public/NetworkManager.h b/src/libnm-client-public/NetworkManager.h
index 9e9a4eb839..880bac6ed7 100644
--- a/src/libnm-client-public/NetworkManager.h
+++ b/src/libnm-client-public/NetworkManager.h
@@ -37,6 +37,7 @@
#include "nm-setting-dummy.h"
#include "nm-setting-ethtool.h"
#include "nm-setting-generic.h"
+#include "nm-setting-geneve.h"
#include "nm-setting-gsm.h"
#include "nm-setting-hostname.h"
#include "nm-setting-hsr.h"
@@ -114,6 +115,7 @@
#include "nm-device-dummy.h"
#include "nm-device-ethernet.h"
#include "nm-device-generic.h"
+#include "nm-device-geneve.h"
#include "nm-device-hsr.h"
#include "nm-device-infiniband.h"
#include "nm-device-ip-tunnel.h"
diff --git a/src/libnm-client-public/meson.build b/src/libnm-client-public/meson.build
index b8ae9cce07..5aa6de2518 100644
--- a/src/libnm-client-public/meson.build
+++ b/src/libnm-client-public/meson.build
@@ -18,6 +18,7 @@ libnm_client_headers = files(
'nm-device-dummy.h',
'nm-device-ethernet.h',
'nm-device-generic.h',
+ 'nm-device-geneve.h',
'nm-device-hsr.h',
'nm-device-infiniband.h',
'nm-device-ip-tunnel.h',
diff --git a/src/libnm-client-public/nm-autoptr.h b/src/libnm-client-public/nm-autoptr.h
index f4321ad3a6..12379fe1aa 100644
--- a/src/libnm-client-public/nm-autoptr.h
+++ b/src/libnm-client-public/nm-autoptr.h
@@ -41,6 +41,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceBt, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceDummy, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceEthernet, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceGeneric, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceGeneve, 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)
@@ -80,6 +81,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingDcb, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingDummy, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingEthtool, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingGeneric, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingGeneve, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingGsm, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingHostname, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingHsr, g_object_unref)
diff --git a/src/libnm-client-public/nm-device-geneve.h b/src/libnm-client-public/nm-device-geneve.h
new file mode 100644
index 0000000000..5f7c92b30b
--- /dev/null
+++ b/src/libnm-client-public/nm-device-geneve.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_DEVICE_GENEVE_H__
+#define __NM_DEVICE_GENEVE_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_GENEVE (nm_device_geneve_get_type())
+#define NM_DEVICE_GENEVE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_GENEVE, NMDeviceGeneve))
+#define NM_DEVICE_GENEVE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_GENEVE, NMDeviceGeneveClass))
+#define NM_IS_DEVICE_GENEVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_GENEVE))
+#define NM_IS_DEVICE_GENEVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE_GENEVE))
+#define NM_DEVICE_GENEVE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_GENEVE, NMDeviceGeneveClass))
+
+#define NM_DEVICE_GENEVE_ID "id"
+#define NM_DEVICE_GENEVE_REMOTE "remote"
+#define NM_DEVICE_GENEVE_TOS "tos"
+#define NM_DEVICE_GENEVE_TTL "ttl"
+#define NM_DEVICE_GENEVE_DST_PORT "dst-port"
+#define NM_DEVICE_GENEVE_DF "df"
+
+/**
+ * NMDeviceGeneve:
+ *
+ * Since: 1.58
+ */
+typedef struct _NMDeviceGeneve NMDeviceGeneve;
+typedef struct _NMDeviceGeneveClass NMDeviceGeneveClass;
+
+NM_AVAILABLE_IN_1_58
+GType nm_device_geneve_get_type(void);
+
+NM_AVAILABLE_IN_1_58
+guint nm_device_geneve_get_id(NMDeviceGeneve *device);
+NM_AVAILABLE_IN_1_58
+const char *nm_device_geneve_get_remote(NMDeviceGeneve *device);
+NM_AVAILABLE_IN_1_58
+guint nm_device_geneve_get_dst_port(NMDeviceGeneve *device);
+NM_AVAILABLE_IN_1_58
+guint nm_device_geneve_get_tos(NMDeviceGeneve *device);
+NM_AVAILABLE_IN_1_58
+guint nm_device_geneve_get_ttl(NMDeviceGeneve *device);
+NM_AVAILABLE_IN_1_58
+guint nm_device_geneve_get_df(NMDeviceGeneve *device);
+
+G_END_DECLS
+
+#endif /* __NM_DEVICE_GENEVE_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 0131ba76bf..7308db4d64 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
@@ -1378,6 +1378,34 @@
gprop-type="gchararray"
/>
+
+
+
+
+
+
+
+
diff --git a/src/libnm-core-impl/meson.build b/src/libnm-core-impl/meson.build
index 610e22792e..1eee3184e5 100644
--- a/src/libnm-core-impl/meson.build
+++ b/src/libnm-core-impl/meson.build
@@ -17,6 +17,7 @@ libnm_core_settings_sources = files(
'nm-setting-dummy.c',
'nm-setting-ethtool.c',
'nm-setting-generic.c',
+ 'nm-setting-geneve.c',
'nm-setting-gsm.c',
'nm-setting-hostname.c',
'nm-setting-hsr.c',
diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c
index 0fbadfb85c..9ce72b4145 100644
--- a/src/libnm-core-impl/nm-connection.c
+++ b/src/libnm-core-impl/nm-connection.c
@@ -3272,6 +3272,7 @@ nm_connection_is_virtual(NMConnection *connection)
NM_SETTING_BOND_SETTING_NAME,
NM_SETTING_BRIDGE_SETTING_NAME,
NM_SETTING_DUMMY_SETTING_NAME,
+ NM_SETTING_GENEVE_SETTING_NAME,
NM_SETTING_HSR_SETTING_NAME,
NM_SETTING_IP_TUNNEL_SETTING_NAME,
NM_SETTING_IPVLAN_SETTING_NAME,
@@ -3526,6 +3527,22 @@ nm_connection_get_setting_generic(NMConnection *connection)
return _nm_connection_get_setting_by_metatype(connection, NM_META_SETTING_TYPE_GENERIC);
}
+/**
+ * nm_connection_get_setting_geneve:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingGeneve the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingGeneve if the connection contains one, otherwise NULL
+ *
+ * Since: 1.58
+ **/
+NMSettingGeneve *
+nm_connection_get_setting_geneve(NMConnection *connection)
+{
+ return _nm_connection_get_setting_by_metatype(connection, NM_META_SETTING_TYPE_GENEVE);
+}
+
/**
* nm_connection_get_setting_gsm:
* @connection: the #NMConnection
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 52af9ad259..d84490d238 100644
--- a/src/libnm-core-impl/nm-meta-setting-base-impl.c
+++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c
@@ -27,6 +27,7 @@
#include "nm-setting-dummy.h"
#include "nm-setting-ethtool.h"
#include "nm-setting-generic.h"
+#include "nm-setting-geneve.h"
#include "nm-setting-gsm.h"
#include "nm-setting-hostname.h"
#include "nm-setting-hsr.h"
@@ -324,6 +325,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
.setting_name = NM_SETTING_GENERIC_SETTING_NAME,
.get_setting_gtype = nm_setting_generic_get_type,
},
+ [NM_META_SETTING_TYPE_GENEVE] =
+ {
+ .meta_type = NM_META_SETTING_TYPE_GENEVE,
+ .setting_priority = NM_SETTING_PRIORITY_HW_BASE,
+ .setting_name = NM_SETTING_GENEVE_SETTING_NAME,
+ .get_setting_gtype = nm_setting_geneve_get_type,
+ },
[NM_META_SETTING_TYPE_GSM] =
{
.meta_type = NM_META_SETTING_TYPE_GSM,
@@ -655,6 +663,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
NM_META_SETTING_TYPE_CDMA,
NM_META_SETTING_TYPE_DUMMY,
NM_META_SETTING_TYPE_GENERIC,
+ NM_META_SETTING_TYPE_GENEVE,
NM_META_SETTING_TYPE_GSM,
NM_META_SETTING_TYPE_HSR,
NM_META_SETTING_TYPE_INFINIBAND,
diff --git a/src/libnm-core-impl/nm-setting-geneve.c b/src/libnm-core-impl/nm-setting-geneve.c
new file mode 100644
index 0000000000..d6426a06ff
--- /dev/null
+++ b/src/libnm-core-impl/nm-setting-geneve.c
@@ -0,0 +1,354 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Red Hat, Inc.
+ */
+
+#include "libnm-core-impl/nm-default-libnm-core.h"
+
+#include "nm-setting-geneve.h"
+
+#include
+
+#include "nm-utils.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-geneve
+ * @short_description: Describes connection properties for GENEVE interfaces
+ *
+ * The #NMSettingGeneve object is a #NMSetting subclass that describes properties
+ * necessary for connection to GENEVE interfaces.
+ **/
+
+#define DST_PORT_DEFAULT 6081
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_ID,
+ PROP_REMOTE,
+ PROP_DESTINATION_PORT,
+ PROP_TOS,
+ PROP_TTL,
+ PROP_DF, );
+
+typedef struct {
+ char *remote;
+ guint32 id;
+ guint32 destination_port;
+ guint32 tos;
+ gint32 ttl;
+ int df;
+} NMSettingGenevePrivate;
+
+/**
+ * NMSettingGeneve:
+ *
+ * GENEVE Settings
+ */
+struct _NMSettingGeneve {
+ NMSetting parent;
+ NMSettingGenevePrivate _priv;
+};
+
+struct _NMSettingGeneveClass {
+ NMSettingClass parent;
+};
+
+G_DEFINE_TYPE(NMSettingGeneve, nm_setting_geneve, NM_TYPE_SETTING)
+
+#define NM_SETTING_GENEVE_GET_PRIVATE(o) \
+ _NM_GET_PRIVATE(o, NMSettingGeneve, NM_IS_SETTING_GENEVE, NMSetting)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_geneve_get_id:
+ * @setting: the #NMSettingGeneve
+ *
+ * Returns: the #NMSettingGeneve:id property of the setting
+ *
+ * Since: 1.58
+ **/
+guint
+nm_setting_geneve_get_id(NMSettingGeneve *setting)
+{
+ g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), 0);
+
+ return NM_SETTING_GENEVE_GET_PRIVATE(setting)->id;
+}
+
+/**
+ * nm_setting_geneve_get_remote:
+ * @setting: the #NMSettingGeneve
+ *
+ * Returns: the #NMSettingGeneve:remote property of the setting
+ *
+ * Since: 1.58
+ **/
+const char *
+nm_setting_geneve_get_remote(NMSettingGeneve *setting)
+{
+ g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), NULL);
+
+ return NM_SETTING_GENEVE_GET_PRIVATE(setting)->remote;
+}
+
+/**
+ * nm_setting_geneve_get_destination_port:
+ * @setting: the #NMSettingGeneve
+ *
+ * Returns: the #NMSettingGeneve:destination-port property of the setting
+ *
+ * Since: 1.58
+ **/
+guint
+nm_setting_geneve_get_destination_port(NMSettingGeneve *setting)
+{
+ g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), DST_PORT_DEFAULT);
+
+ return NM_SETTING_GENEVE_GET_PRIVATE(setting)->destination_port;
+}
+
+/**
+ * nm_setting_geneve_get_tos:
+ * @setting: the #NMSettingGeneve
+ *
+ * Returns: the #NMSettingGeneve:tos property of the setting
+ *
+ * Since: 1.58
+ **/
+guint
+nm_setting_geneve_get_tos(NMSettingGeneve *setting)
+{
+ g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), 0);
+
+ return NM_SETTING_GENEVE_GET_PRIVATE(setting)->tos;
+}
+
+/**
+ * nm_setting_geneve_get_ttl:
+ * @setting: the #NMSettingGeneve
+ *
+ * Returns: the #NMSettingGeneve:ttl property of the setting
+ *
+ * Since: 1.58
+ **/
+guint
+nm_setting_geneve_get_ttl(NMSettingGeneve *setting)
+{
+ g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), 0);
+
+ return NM_SETTING_GENEVE_GET_PRIVATE(setting)->ttl;
+}
+
+/**
+ * nm_setting_geneve_get_df:
+ * @setting: the #NMSettingGeneve
+ *
+ * Returns: the #NMSettingGeneve:df property of the setting
+ *
+ * Since: 1.58
+ **/
+NMSettingGeneveDf
+nm_setting_geneve_get_df(NMSettingGeneve *setting)
+{
+ g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), NM_SETTING_GENEVE_DF_UNSET);
+
+ return NM_SETTING_GENEVE_GET_PRIVATE(setting)->df;
+}
+
+/*****************************************************************************/
+
+static gboolean
+verify(NMSetting *setting, NMConnection *connection, GError **error)
+{
+ NMSettingGenevePrivate *priv = NM_SETTING_GENEVE_GET_PRIVATE(setting);
+
+ if (priv->id == 0) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("property is required"));
+ g_prefix_error(error, "%s.%s: ", NM_SETTING_GENEVE_SETTING_NAME, NM_SETTING_GENEVE_ID);
+ return FALSE;
+ }
+
+ if (!priv->remote) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("property is required"));
+ g_prefix_error(error, "%s.%s: ", NM_SETTING_GENEVE_SETTING_NAME, NM_SETTING_GENEVE_REMOTE);
+ return FALSE;
+ }
+
+ if (!nm_inet_parse_bin(AF_UNSPEC, priv->remote, NULL, NULL)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid IP address"),
+ priv->remote);
+ g_prefix_error(error, "%s.%s: ", NM_SETTING_GENEVE_SETTING_NAME, NM_SETTING_GENEVE_REMOTE);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_geneve_init(NMSettingGeneve *self)
+{}
+
+/**
+ * nm_setting_geneve_new:
+ *
+ * Creates a new #NMSettingGeneve object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingGeneve object
+ *
+ * Since: 1.58
+ **/
+NMSetting *
+nm_setting_geneve_new(void)
+{
+ return g_object_new(NM_TYPE_SETTING_GENEVE, NULL);
+}
+
+static void
+nm_setting_geneve_class_init(NMSettingGeneveClass *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;
+
+ /**
+ * NMSettingGeneve:id:
+ *
+ * Specifies the GENEVE Network Identifier (or GENEVE Segment Identifier) to
+ * use.
+ *
+ * Since: 1.58
+ **/
+ _nm_setting_property_define_direct_uint32(properties_override,
+ obj_properties,
+ NM_SETTING_GENEVE_ID,
+ PROP_ID,
+ 0,
+ (1 << 24) - 1,
+ 0,
+ NM_SETTING_PARAM_INFERRABLE,
+ NMSettingGenevePrivate,
+ id);
+
+ /**
+ * NMSettingGeneve:remote:
+ *
+ * Specifies the unicast destination IP address to use in outgoing packets
+ * when communicating with the remote GENEVE tunnel endpoint.
+ *
+ * Since: 1.58
+ **/
+ _nm_setting_property_define_direct_string(properties_override,
+ obj_properties,
+ NM_SETTING_GENEVE_REMOTE,
+ PROP_REMOTE,
+ NM_SETTING_PARAM_REQUIRED,
+ NMSettingGenevePrivate,
+ remote,
+ .direct_set_string_ip_address_addr_family =
+ AF_UNSPEC + 1,
+ .direct_string_allow_empty = TRUE);
+
+ /**
+ * NMSettingGeneve:destination-port:
+ *
+ * Specifies the UDP destination port to communicate to the remote GENEVE
+ * tunnel endpoint.
+ *
+ * Since: 1.58
+ **/
+ _nm_setting_property_define_direct_uint32(properties_override,
+ obj_properties,
+ NM_SETTING_GENEVE_DESTINATION_PORT,
+ PROP_DESTINATION_PORT,
+ 0,
+ G_MAXUINT16,
+ DST_PORT_DEFAULT,
+ NM_SETTING_PARAM_INFERRABLE,
+ NMSettingGenevePrivate,
+ destination_port);
+
+ /**
+ * NMSettingGeneve:tos:
+ *
+ * Specifies the TOS value to use in outgoing packets.
+ * The special value "inherit" (1) means inherit from outer packet.
+ *
+ * Since: 1.58
+ **/
+ _nm_setting_property_define_direct_uint32(properties_override,
+ obj_properties,
+ NM_SETTING_GENEVE_TOS,
+ PROP_TOS,
+ 0,
+ 255,
+ 0,
+ NM_SETTING_PARAM_INFERRABLE,
+ NMSettingGenevePrivate,
+ tos);
+
+ /**
+ * NMSettingGeneve:ttl:
+ *
+ * Specifies the time-to-live value to use in outgoing packets.
+ * The special value "inherit" (-1) means inherit from outer packet, 0 means auto, 1-255 are fixed values.
+ *
+ * Since: 1.58
+ **/
+ _nm_setting_property_define_direct_int32(properties_override,
+ obj_properties,
+ NM_SETTING_GENEVE_TTL,
+ PROP_TTL,
+ -1,
+ 255,
+ 0,
+ NM_SETTING_PARAM_INFERRABLE,
+ NMSettingGenevePrivate,
+ ttl);
+
+ /**
+ * NMSettingGeneve:df:
+ *
+ * Specifies how the Don't Fragment (DF) flag should be handled in the outer IP
+ * header of GENEVE tunnel packets.
+ *
+ * %NM_SETTING_GENEVE_DF_UNSET (0): Don't set the DF flag, packets may be fragmented.
+ * %NM_SETTING_GENEVE_DF_SET (1): Always set the DF flag, packets will not be fragmented.
+ * %NM_SETTING_GENEVE_DF_INHERIT (2): Inherit the DF flag from the inner IP header.
+ *
+ * Since: 1.58
+ **/
+ _nm_setting_property_define_direct_enum(properties_override,
+ obj_properties,
+ NM_SETTING_GENEVE_DF,
+ PROP_DF,
+ NM_TYPE_SETTING_GENEVE_DF,
+ NM_SETTING_GENEVE_DF_UNSET,
+ NM_SETTING_PARAM_INFERRABLE,
+ NULL,
+ NMSettingGenevePrivate,
+ df);
+
+ g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+ _nm_setting_class_commit(setting_class,
+ NM_META_SETTING_TYPE_GENEVE,
+ NULL,
+ properties_override,
+ G_STRUCT_OFFSET(NMSettingGeneve, _priv));
+}
diff --git a/src/libnm-core-intern/nm-core-internal.h b/src/libnm-core-intern/nm-core-internal.h
index f1fc76a563..9a7633281e 100644
--- a/src/libnm-core-intern/nm-core-internal.h
+++ b/src/libnm-core-intern/nm-core-internal.h
@@ -37,6 +37,7 @@
#include "nm-setting-dcb.h"
#include "nm-setting-dummy.h"
#include "nm-setting-generic.h"
+#include "nm-setting-geneve.h"
#include "nm-setting-gsm.h"
#include "nm-setting-hsr.h"
#include "nm-setting-hostname.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 d1535e5e1f..8f512d09c6 100644
--- a/src/libnm-core-intern/nm-meta-setting-base-impl.h
+++ b/src/libnm-core-intern/nm-meta-setting-base-impl.h
@@ -121,6 +121,7 @@ typedef enum _nm_packed {
NM_META_SETTING_TYPE_DUMMY,
NM_META_SETTING_TYPE_ETHTOOL,
NM_META_SETTING_TYPE_GENERIC,
+ NM_META_SETTING_TYPE_GENEVE,
NM_META_SETTING_TYPE_GSM,
NM_META_SETTING_TYPE_HOSTNAME,
NM_META_SETTING_TYPE_HSR,
diff --git a/src/libnm-core-public/meson.build b/src/libnm-core-public/meson.build
index 086801d96d..2a4f143f75 100644
--- a/src/libnm-core-public/meson.build
+++ b/src/libnm-core-public/meson.build
@@ -22,6 +22,7 @@ libnm_core_headers = files(
'nm-setting-dummy.h',
'nm-setting-ethtool.h',
'nm-setting-generic.h',
+ 'nm-setting-geneve.h',
'nm-setting-gsm.h',
'nm-setting-hsr.h',
'nm-setting-hostname.h',
diff --git a/src/libnm-core-public/nm-connection.h b/src/libnm-core-public/nm-connection.h
index ff3e0f924a..db73711f69 100644
--- a/src/libnm-core-public/nm-connection.h
+++ b/src/libnm-core-public/nm-connection.h
@@ -206,8 +206,10 @@ NMSettingCdma *nm_connection_get_setting_cdma(NMConnection *connection);
NMSettingConnection *nm_connection_get_setting_connection(NMConnection *connection);
NMSettingDcb *nm_connection_get_setting_dcb(NMConnection *connection);
NM_AVAILABLE_IN_1_8
-NMSettingDummy *nm_connection_get_setting_dummy(NMConnection *connection);
-NMSettingGeneric *nm_connection_get_setting_generic(NMConnection *connection);
+NMSettingDummy *nm_connection_get_setting_dummy(NMConnection *connection);
+NMSettingGeneric *nm_connection_get_setting_generic(NMConnection *connection);
+NM_AVAILABLE_IN_1_58
+NMSettingGeneve *nm_connection_get_setting_geneve(NMConnection *connection);
NMSettingGsm *nm_connection_get_setting_gsm(NMConnection *connection);
NMSettingInfiniband *nm_connection_get_setting_infiniband(NMConnection *connection);
NM_AVAILABLE_IN_1_2
diff --git a/src/libnm-core-public/nm-core-types.h b/src/libnm-core-public/nm-core-types.h
index 617d6b7415..81743e31d7 100644
--- a/src/libnm-core-public/nm-core-types.h
+++ b/src/libnm-core-public/nm-core-types.h
@@ -27,6 +27,7 @@ typedef struct _NMSettingConnection NMSettingConnection;
typedef struct _NMSettingDcb NMSettingDcb;
typedef struct _NMSettingDummy NMSettingDummy;
typedef struct _NMSettingEthtool NMSettingEthtool;
+typedef struct _NMSettingGeneve NMSettingGeneve;
typedef struct _NMSettingGeneric NMSettingGeneric;
typedef struct _NMSettingGsm NMSettingGsm;
typedef struct _NMSettingHostname NMSettingHostname;
diff --git a/src/libnm-core-public/nm-dbus-interface.h b/src/libnm-core-public/nm-dbus-interface.h
index 42bff04ae0..8d34066f0e 100644
--- a/src/libnm-core-public/nm-dbus-interface.h
+++ b/src/libnm-core-public/nm-dbus-interface.h
@@ -36,6 +36,7 @@
#define NM_DBUS_INTERFACE_DEVICE_BRIDGE NM_DBUS_INTERFACE_DEVICE ".Bridge"
#define NM_DBUS_INTERFACE_DEVICE_DUMMY NM_DBUS_INTERFACE_DEVICE ".Dummy"
#define NM_DBUS_INTERFACE_DEVICE_GENERIC NM_DBUS_INTERFACE_DEVICE ".Generic"
+#define NM_DBUS_INTERFACE_DEVICE_GENEVE NM_DBUS_INTERFACE_DEVICE ".Geneve"
#define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre"
#define NM_DBUS_INTERFACE_DEVICE_HSR NM_DBUS_INTERFACE_DEVICE ".Hsr"
#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband"
@@ -250,6 +251,7 @@ typedef enum {
* @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.
+ * @NM_DEVICE_TYPE_GENEVE: A GENEVE device. Since: 1.58.
*
* #NMDeviceType values indicate the type of hardware represented by a
* device object.
@@ -290,6 +292,7 @@ typedef enum {
NM_DEVICE_TYPE_LOOPBACK = 32,
NM_DEVICE_TYPE_HSR = 33,
NM_DEVICE_TYPE_IPVLAN = 34,
+ NM_DEVICE_TYPE_GENEVE = 35,
} NMDeviceType;
/**
diff --git a/src/libnm-core-public/nm-setting-geneve.h b/src/libnm-core-public/nm-setting-geneve.h
new file mode 100644
index 0000000000..bff8fba1b2
--- /dev/null
+++ b/src/libnm-core-public/nm-setting-geneve.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_GENEVE_H__
+#define __NM_SETTING_GENEVE_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_GENEVE (nm_setting_geneve_get_type())
+#define NM_SETTING_GENEVE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTING_GENEVE, NMSettingGeneve))
+#define NM_SETTING_GENEVE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SETTING_GENEVE, NMSettingGeneveClass))
+#define NM_IS_SETTING_GENEVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTING_GENEVE))
+#define NM_IS_SETTING_GENEVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_SETTING_GENEVE))
+#define NM_SETTING_GENEVE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_SETTING_GENEVE, NMSettingGeneveClass))
+
+#define NM_SETTING_GENEVE_SETTING_NAME "geneve"
+
+#define NM_SETTING_GENEVE_ID "id"
+#define NM_SETTING_GENEVE_REMOTE "remote"
+#define NM_SETTING_GENEVE_DESTINATION_PORT "destination-port"
+#define NM_SETTING_GENEVE_TOS "tos"
+#define NM_SETTING_GENEVE_TTL "ttl"
+#define NM_SETTING_GENEVE_DF "df"
+
+/**
+ * NMSettingGeneveDf:
+ * @NM_SETTING_GENEVE_DF_UNSET: Don't set the DF flag, packets may be fragmented.
+ * @NM_SETTING_GENEVE_DF_SET: Always set the DF flag, packets will not be fragmented.
+ * @NM_SETTING_GENEVE_DF_INHERIT: Inherit the DF flag from the inner IP header.
+ *
+ * #NMSettingGeneveDf values indicate how the Don't Fragment (DF) flag should be handled
+ * in the outer IP header of GENEVE tunnel packets.
+ *
+ * Since: 1.58
+ */
+typedef enum {
+ NM_SETTING_GENEVE_DF_UNSET = 0,
+ NM_SETTING_GENEVE_DF_SET = 1,
+ NM_SETTING_GENEVE_DF_INHERIT = 2,
+} NMSettingGeneveDf;
+
+typedef struct _NMSettingGeneveClass NMSettingGeneveClass;
+
+NM_AVAILABLE_IN_1_58
+GType nm_setting_geneve_get_type(void);
+NM_AVAILABLE_IN_1_58
+NMSetting *nm_setting_geneve_new(void);
+NM_AVAILABLE_IN_1_58
+guint nm_setting_geneve_get_id(NMSettingGeneve *setting);
+NM_AVAILABLE_IN_1_58
+const char *nm_setting_geneve_get_remote(NMSettingGeneve *setting);
+NM_AVAILABLE_IN_1_58
+guint nm_setting_geneve_get_destination_port(NMSettingGeneve *setting);
+NM_AVAILABLE_IN_1_58
+guint nm_setting_geneve_get_tos(NMSettingGeneve *setting);
+NM_AVAILABLE_IN_1_58
+guint nm_setting_geneve_get_ttl(NMSettingGeneve *setting);
+NM_AVAILABLE_IN_1_58
+NMSettingGeneveDf nm_setting_geneve_get_df(NMSettingGeneve *setting);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_GENEVE_H__ */
diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h
index 1fca6015d8..0bc6e29495 100644
--- a/src/libnm-glib-aux/nm-shared-utils.h
+++ b/src/libnm-glib-aux/nm-shared-utils.h
@@ -130,6 +130,7 @@ typedef enum {
#define _NM_LINK_TYPE_SW_FIRST NM_LINK_TYPE_BNEP
NM_LINK_TYPE_BNEP, /* Bluetooth Ethernet emulation */
NM_LINK_TYPE_DUMMY,
+ NM_LINK_TYPE_GENEVE,
NM_LINK_TYPE_GRE,
NM_LINK_TYPE_GRETAP,
NM_LINK_TYPE_IFB,
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index cc5b99e095..cde96326ca 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -862,6 +862,7 @@ static const LinkDesc link_descs[] = {
[NM_LINK_TYPE_BNEP] = {"bluetooth", NULL, "bluetooth"},
[NM_LINK_TYPE_DUMMY] = {"dummy", "dummy", NULL},
+ [NM_LINK_TYPE_GENEVE] = {"geneve", "geneve", "geneve"},
[NM_LINK_TYPE_GRE] = {"gre", "gre", NULL},
[NM_LINK_TYPE_GRETAP] = {"gretap", "gretap", NULL},
[NM_LINK_TYPE_IFB] = {"ifb", "ifb", NULL},
@@ -909,6 +910,7 @@ _link_type_from_rtnl_type(const char *name)
NM_LINK_TYPE_BOND, /* "bond" */
NM_LINK_TYPE_BRIDGE, /* "bridge" */
NM_LINK_TYPE_DUMMY, /* "dummy" */
+ NM_LINK_TYPE_GENEVE, /* "geneve" */
NM_LINK_TYPE_GRE, /* "gre" */
NM_LINK_TYPE_GRETAP, /* "gretap" */
NM_LINK_TYPE_HSR, /* "hsr" */
@@ -987,6 +989,7 @@ _link_type_from_devtype(const char *name)
NM_LINK_TYPE_BNEP, /* "bluetooth" */
NM_LINK_TYPE_BOND, /* "bond" */
NM_LINK_TYPE_BRIDGE, /* "bridge" */
+ NM_LINK_TYPE_GENEVE, /* "geneve" */
NM_LINK_TYPE_HSR, /* "hsr" */
NM_LINK_TYPE_PPP, /* "ppp" */
NM_LINK_TYPE_VLAN, /* "vlan" */
@@ -1856,6 +1859,57 @@ _parse_lnk_gre(const char *kind, struct nlattr *info_data)
/*****************************************************************************/
+static NMPObject *
+_parse_lnk_geneve(const char *kind, struct nlattr *info_data)
+{
+ static const struct nla_policy policy[] = {
+ [IFLA_GENEVE_ID] = {.type = NLA_U32},
+ [IFLA_GENEVE_REMOTE] = {.type = NLA_U32},
+ [IFLA_GENEVE_REMOTE6] = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)},
+ [IFLA_GENEVE_TTL] = {.type = NLA_U8},
+ [IFLA_GENEVE_TOS] = {.type = NLA_U8},
+ [IFLA_GENEVE_TTL_INHERIT] = {.type = NLA_U8},
+ [IFLA_GENEVE_PORT] = {.type = NLA_U16},
+ [IFLA_GENEVE_DF] = {.type = NLA_U8},
+ };
+
+ struct nlattr *tb[G_N_ELEMENTS(policy)];
+ NMPObject *obj;
+ NMPlatformLnkGeneve *props;
+
+ if (!info_data || !nm_streq0(kind, "geneve"))
+ return NULL;
+
+ if (nla_parse_nested_arr(tb, info_data, policy) < 0)
+ return NULL;
+
+ obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_GENEVE, NULL);
+ props = &obj->lnk_geneve;
+
+ if (tb[IFLA_GENEVE_ID])
+ props->id = nla_get_u32(tb[IFLA_GENEVE_ID]);
+ if (tb[IFLA_GENEVE_REMOTE])
+ props->remote = nla_get_u32(tb[IFLA_GENEVE_REMOTE]);
+ if (tb[IFLA_GENEVE_REMOTE6])
+ props->remote6 = *nla_data_as(struct in6_addr, tb[IFLA_GENEVE_REMOTE6]);
+
+ if (tb[IFLA_GENEVE_TTL_INHERIT] && nla_get_u8(tb[IFLA_GENEVE_TTL_INHERIT]))
+ props->ttl = -1;
+ else if (tb[IFLA_GENEVE_TTL])
+ props->ttl = nla_get_u8(tb[IFLA_GENEVE_TTL]);
+
+ if (tb[IFLA_GENEVE_TOS])
+ props->tos = nla_get_u8(tb[IFLA_GENEVE_TOS]);
+ if (tb[IFLA_GENEVE_PORT])
+ props->dst_port = ntohs(nla_get_u16(tb[IFLA_GENEVE_PORT]));
+ if (tb[IFLA_GENEVE_DF])
+ props->df = nla_get_u8(tb[IFLA_GENEVE_DF]);
+
+ return obj;
+}
+
+/*****************************************************************************/
+
static NMPObject *
_parse_lnk_hsr(const char *kind, struct nlattr *info_data)
{
@@ -3694,6 +3748,9 @@ _new_from_nl_link(NMPlatform *platform,
case NM_LINK_TYPE_BOND:
lnk_data = _parse_lnk_bond(nl_info_kind, nl_info_data);
break;
+ case NM_LINK_TYPE_GENEVE:
+ lnk_data = _parse_lnk_geneve(nl_info_kind, nl_info_data);
+ break;
case NM_LINK_TYPE_GRE:
case NM_LINK_TYPE_GRETAP:
lnk_data = _parse_lnk_gre(nl_info_kind, nl_info_data);
@@ -5182,6 +5239,35 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo
nla_nest_end(msg, info_peer);
break;
}
+ case NM_LINK_TYPE_GENEVE:
+ {
+ const NMPlatformLnkGeneve *props = extra_data;
+
+ nm_assert(props);
+
+ if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+ goto nla_put_failure;
+
+ NLA_PUT_U32(msg, IFLA_GENEVE_ID, props->id);
+
+ if (props->remote) {
+ NLA_PUT_U32(msg, IFLA_GENEVE_REMOTE, props->remote);
+ } else if (!IN6_IS_ADDR_UNSPECIFIED(&props->remote6)) {
+ NLA_PUT(msg, IFLA_GENEVE_REMOTE6, sizeof(props->remote6), &props->remote6);
+ }
+ NLA_PUT_U16(msg, IFLA_GENEVE_PORT, htons(props->dst_port));
+ if (props->ttl == -1) {
+ NLA_PUT_U8(msg, IFLA_GENEVE_TTL_INHERIT, 1);
+ } else {
+ /* When you want to specify a TTL value,
+ * don't add TTL_INHERIT to the message */
+ NLA_PUT_U8(msg, IFLA_GENEVE_TTL, props->ttl & 0xff);
+ }
+ NLA_PUT_U8(msg, IFLA_GENEVE_TOS, props->tos);
+ NLA_PUT_U8(msg, IFLA_GENEVE_DF, props->df);
+ break;
+ }
+
case NM_LINK_TYPE_GRE:
case NM_LINK_TYPE_GRETAP:
{
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index 853157d204..1f98a9a9c3 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -1388,6 +1388,12 @@ nm_platform_link_add(NMPlatform *self,
case NM_LINK_TYPE_VETH:
nm_sprintf_buf(buf, ", veth-peer \"%s\"", (const char *) extra_data);
break;
+ case NM_LINK_TYPE_GENEVE:
+ nm_strbuf_append_str(&buf_p, &buf_len, ", ");
+ nm_platform_lnk_geneve_to_string((const NMPlatformLnkGeneve *) extra_data,
+ buf_p,
+ buf_len);
+ break;
case NM_LINK_TYPE_GRE:
case NM_LINK_TYPE_GRETAP:
nm_strbuf_append_str(&buf_p, &buf_len, ", ");
@@ -2565,6 +2571,12 @@ nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformL
return _link_get_lnk(self, ifindex, NM_LINK_TYPE_BRIDGE, out_link);
}
+const NMPlatformLnkGeneve *
+nm_platform_link_get_lnk_geneve(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
+{
+ return _link_get_lnk(self, ifindex, NM_LINK_TYPE_GENEVE, out_link);
+}
+
const NMPlatformLnkGre *
nm_platform_link_get_lnk_gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
{
@@ -6494,6 +6506,52 @@ nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize le
return buf;
}
+const char *
+nm_platform_lnk_geneve_to_string(const NMPlatformLnkGeneve *lnk, char *buf, gsize len)
+{
+ char str_remote[NM_INET_ADDRSTRLEN];
+ char str_remote1[30 + NM_INET_ADDRSTRLEN];
+ char str_remote6[NM_INET_ADDRSTRLEN];
+ char str_remote6_1[30 + NM_INET_ADDRSTRLEN];
+ char str_ttl[30];
+ char str_tos[30];
+ char str_id[30];
+ char str_dstport[30];
+
+ if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
+ return buf;
+
+ g_snprintf(
+ buf,
+ len,
+ "geneve"
+ "%s" /* id */
+ "%s" /* remote */
+ "%s" /* remote6 */
+ "%s" /* dst_port */
+ "%s" /* ttl */
+ "%s" /* tos */
+ "%s" /* df */
+ "",
+ lnk->id ? nm_sprintf_buf(str_id, " id %u", lnk->id) : "",
+ lnk->remote
+ ? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1))
+ : "",
+ !IN6_IS_ADDR_UNSPECIFIED(&lnk->remote6)
+ ? nm_sprintf_buf(str_remote6, " remote %s", nm_inet6_ntop(&lnk->remote6, str_remote6_1))
+ : "",
+ lnk->dst_port ? nm_sprintf_buf(str_dstport, " dstport %u", lnk->dst_port) : "",
+ lnk->ttl > 0 ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl & 0xff)
+ : lnk->ttl == 0 ? "ttl auto"
+ : "ttl inherit",
+ lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf(str_tos, " tos 0x%x", lnk->tos))
+ : "",
+ lnk->df == 1 ? " df set "
+ : lnk->df == 2 ? " df inherit "
+ : "");
+ return buf;
+}
+
const char *
nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len)
{
@@ -8491,6 +8549,27 @@ nm_platform_lnk_gre_cmp(const NMPlatformLnkGre *a, const NMPlatformLnkGre *b)
return 0;
}
+void
+nm_platform_lnk_geneve_hash_update(const NMPlatformLnkGeneve *obj, NMHashState *h)
+{
+ nm_hash_update_vals(h, obj->id, obj->remote, obj->dst_port, obj->ttl, obj->tos, obj->df);
+ nm_hash_update_mem(h, &obj->remote6, sizeof(obj->remote6));
+}
+
+int
+nm_platform_lnk_geneve_cmp(const NMPlatformLnkGeneve *a, const NMPlatformLnkGeneve *b)
+{
+ NM_CMP_SELF(a, b);
+ NM_CMP_FIELD(a, b, id);
+ NM_CMP_FIELD(a, b, remote);
+ NM_CMP_FIELD_MEMCMP(a, b, remote6);
+ NM_CMP_FIELD(a, b, ttl);
+ NM_CMP_FIELD(a, b, tos);
+ NM_CMP_FIELD(a, b, dst_port);
+ NM_CMP_FIELD(a, b, df);
+ return 0;
+}
+
void
nm_platform_lnk_hsr_hash_update(const NMPlatformLnkHsr *obj, NMHashState *h)
{
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index 033dc51541..1cdd160114 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -835,6 +835,16 @@ typedef struct {
bool use_carrier : 1;
} _nm_alignas(NMPlatformObject) NMPlatformLnkBond;
+typedef struct {
+ struct in6_addr remote6;
+ in_addr_t remote;
+ guint32 id;
+ gint32 ttl;
+ guint16 dst_port;
+ guint8 tos;
+ guint8 df;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkGeneve;
+
typedef struct {
int parent_ifindex;
in_addr_t local;
@@ -1858,6 +1868,15 @@ nm_platform_link_vxlan_add(NMPlatform *self,
return nm_platform_link_add(self, NM_LINK_TYPE_VXLAN, name, 0, NULL, 0, 0, props, out_link);
}
+static inline int
+nm_platform_link_geneve_add(NMPlatform *self,
+ const char *name,
+ const NMPlatformLnkGeneve *props,
+ const NMPlatformLink **out_link)
+{
+ return nm_platform_link_add(self, NM_LINK_TYPE_GENEVE, name, 0, NULL, 0, 0, props, out_link);
+}
+
static inline int
nm_platform_link_6lowpan_add(NMPlatform *self,
const char *name,
@@ -2143,6 +2162,8 @@ const NMPlatformLnkBond *
nm_platform_link_get_lnk_bond(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkBridge *
nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
+const NMPlatformLnkGeneve *
+nm_platform_link_get_lnk_geneve(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkGre *
nm_platform_link_get_lnk_gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkGre *
@@ -2482,6 +2503,7 @@ gboolean nm_platform_tc_sync(NMPlatform *self,
const char *nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len);
const char *nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize len);
const char *nm_platform_lnk_bridge_to_string(const NMPlatformLnkBridge *lnk, char *buf, gsize len);
+const char *nm_platform_lnk_geneve_to_string(const NMPlatformLnkGeneve *lnk, char *buf, gsize len);
const char *nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len);
const char *nm_platform_lnk_hsr_to_string(const NMPlatformLnkHsr *lnk, char *buf, gsize len);
const char *
@@ -2537,6 +2559,7 @@ nm_platform_mptcp_addr_to_string(const NMPlatformMptcpAddr *mptcp_addr, char *bu
int nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b);
int nm_platform_lnk_bond_cmp(const NMPlatformLnkBond *a, const NMPlatformLnkBond *b);
int nm_platform_lnk_bridge_cmp(const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b);
+int nm_platform_lnk_geneve_cmp(const NMPlatformLnkGeneve *a, const NMPlatformLnkGeneve *b);
int nm_platform_lnk_gre_cmp(const NMPlatformLnkGre *a, const NMPlatformLnkGre *b);
int nm_platform_lnk_hsr_cmp(const NMPlatformLnkHsr *a, const NMPlatformLnkHsr *b);
int nm_platform_lnk_infiniband_cmp(const NMPlatformLnkInfiniband *a,
@@ -2612,6 +2635,7 @@ void nm_platform_routing_rule_hash_update(const NMPlatformRoutingRule *obj,
NMHashState *h);
void nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h);
void nm_platform_lnk_bridge_hash_update(const NMPlatformLnkBridge *obj, NMHashState *h);
+void nm_platform_lnk_geneve_hash_update(const NMPlatformLnkGeneve *obj, NMHashState *h);
void nm_platform_lnk_gre_hash_update(const NMPlatformLnkGre *obj, NMHashState *h);
void nm_platform_lnk_hsr_hash_update(const NMPlatformLnkHsr *obj, NMHashState *h);
void nm_platform_lnk_infiniband_hash_update(const NMPlatformLnkInfiniband *obj, NMHashState *h);
diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h
index f665629645..ae538f5bad 100644
--- a/src/libnm-platform/nmp-base.h
+++ b/src/libnm-platform/nmp-base.h
@@ -166,6 +166,7 @@ typedef enum _nm_packed {
NMP_OBJECT_TYPE_TFILTER,
NMP_OBJECT_TYPE_LNK_BRIDGE,
+ NMP_OBJECT_TYPE_LNK_GENEVE,
NMP_OBJECT_TYPE_LNK_GRE,
NMP_OBJECT_TYPE_LNK_GRETAP,
NMP_OBJECT_TYPE_LNK_HSR,
@@ -194,15 +195,15 @@ typedef enum _nm_packed {
NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1,
} NMPObjectType;
-static inline guint32
+static inline guint64
nmp_object_type_to_flags(NMPObjectType obj_type)
{
- G_STATIC_ASSERT_EXPR(NMP_OBJECT_TYPE_MAX < 32);
+ G_STATIC_ASSERT_EXPR(NMP_OBJECT_TYPE_MAX < 64);
nm_assert(_NM_INT_NOT_NEGATIVE(obj_type));
nm_assert(obj_type < NMP_OBJECT_TYPE_MAX);
- return ((guint32) 1u) << obj_type;
+ return ((guint64) 1u) << obj_type;
}
/*****************************************************************************/
diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c
index 56533c99c5..d73eb641f1 100644
--- a/src/libnm-platform/nmp-object.c
+++ b/src/libnm-platform/nmp-object.c
@@ -3469,6 +3469,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_bridge_hash_update,
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_bridge_cmp,
},
+ [NMP_OBJECT_TYPE_LNK_GENEVE - 1] =
+ {
+ .parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
+ .obj_type = NMP_OBJECT_TYPE_LNK_GENEVE,
+ .sizeof_data = sizeof(NMPObjectLnkGeneve),
+ .sizeof_public = sizeof(NMPlatformLnkGeneve),
+ .obj_type_name = "geneve",
+ .lnk_link_type = NM_LINK_TYPE_GENEVE,
+ .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_geneve_to_string,
+ .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_geneve_hash_update,
+ .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_geneve_cmp,
+ },
[NMP_OBJECT_TYPE_LNK_GRE - 1] =
{
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h
index b526fc3bec..a960c528c7 100644
--- a/src/libnm-platform/nmp-object.h
+++ b/src/libnm-platform/nmp-object.h
@@ -250,6 +250,10 @@ typedef struct {
NMPlatformLnkBond _public;
} NMPObjectLnkBond;
+typedef struct {
+ NMPlatformLnkGeneve _public;
+} NMPObjectLnkGeneve;
+
typedef struct {
NMPlatformLnkGre _public;
} NMPObjectLnkGre;
@@ -383,6 +387,9 @@ struct _NMPObject {
NMPlatformLnkBond lnk_bond;
NMPObjectLnkBond _lnk_bond;
+ NMPlatformLnkGeneve lnk_geneve;
+ NMPObjectLnkGeneve _lnk_geneve;
+
NMPlatformLnkGre lnk_gre;
NMPObjectLnkGre _lnk_gre;
@@ -543,6 +550,7 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type)
case NMP_OBJECT_TYPE_LNK_BRIDGE:
case NMP_OBJECT_TYPE_LNK_BOND:
+ case NMP_OBJECT_TYPE_LNK_GENEVE:
case NMP_OBJECT_TYPE_LNK_GRE:
case NMP_OBJECT_TYPE_LNK_GRETAP:
case NMP_OBJECT_TYPE_LNK_HSR:
diff --git a/src/libnm-platform/tests/test-nm-platform.c b/src/libnm-platform/tests/test-nm-platform.c
index 22148f8c64..d087d66f38 100644
--- a/src/libnm-platform/tests/test-nm-platform.c
+++ b/src/libnm-platform/tests/test-nm-platform.c
@@ -19,6 +19,7 @@ G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP6Route))
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLink));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkBond));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkBridge));
+G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkGeneve));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkGre));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkInfiniband));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkIp6Tnl));
@@ -49,6 +50,7 @@ G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPXRoute)
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLink));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkBond));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkBridge));
+G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkGeneve));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkGre));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkInfiniband));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkIp6Tnl));
diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.c b/src/libnmc-setting/nm-meta-setting-base-impl.c
index 52af9ad259..d84490d238 100644
--- a/src/libnmc-setting/nm-meta-setting-base-impl.c
+++ b/src/libnmc-setting/nm-meta-setting-base-impl.c
@@ -27,6 +27,7 @@
#include "nm-setting-dummy.h"
#include "nm-setting-ethtool.h"
#include "nm-setting-generic.h"
+#include "nm-setting-geneve.h"
#include "nm-setting-gsm.h"
#include "nm-setting-hostname.h"
#include "nm-setting-hsr.h"
@@ -324,6 +325,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
.setting_name = NM_SETTING_GENERIC_SETTING_NAME,
.get_setting_gtype = nm_setting_generic_get_type,
},
+ [NM_META_SETTING_TYPE_GENEVE] =
+ {
+ .meta_type = NM_META_SETTING_TYPE_GENEVE,
+ .setting_priority = NM_SETTING_PRIORITY_HW_BASE,
+ .setting_name = NM_SETTING_GENEVE_SETTING_NAME,
+ .get_setting_gtype = nm_setting_geneve_get_type,
+ },
[NM_META_SETTING_TYPE_GSM] =
{
.meta_type = NM_META_SETTING_TYPE_GSM,
@@ -655,6 +663,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
NM_META_SETTING_TYPE_CDMA,
NM_META_SETTING_TYPE_DUMMY,
NM_META_SETTING_TYPE_GENERIC,
+ NM_META_SETTING_TYPE_GENEVE,
NM_META_SETTING_TYPE_GSM,
NM_META_SETTING_TYPE_HSR,
NM_META_SETTING_TYPE_INFINIBAND,
diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.h b/src/libnmc-setting/nm-meta-setting-base-impl.h
index d1535e5e1f..8f512d09c6 100644
--- a/src/libnmc-setting/nm-meta-setting-base-impl.h
+++ b/src/libnmc-setting/nm-meta-setting-base-impl.h
@@ -121,6 +121,7 @@ typedef enum _nm_packed {
NM_META_SETTING_TYPE_DUMMY,
NM_META_SETTING_TYPE_ETHTOOL,
NM_META_SETTING_TYPE_GENERIC,
+ NM_META_SETTING_TYPE_GENEVE,
NM_META_SETTING_TYPE_GSM,
NM_META_SETTING_TYPE_HOSTNAME,
NM_META_SETTING_TYPE_HSR,
diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c
index 5ce86e8bcb..6c9a5c0c1b 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.c
+++ b/src/libnmc-setting/nm-meta-setting-desc.c
@@ -6112,6 +6112,57 @@ static const NMMetaPropertyInfo *const property_infos_GENERIC[] = {
NULL
};
+#undef _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_GENEVE
+static const NMMetaPropertyInfo *const property_infos_GENEVE[] = {
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_ID,
+ .is_cli_option = TRUE,
+ .property_alias = "id",
+ .inf_flags = NM_META_PROPERTY_INF_FLAG_REQD,
+ .prompt = N_("GENEVE ID"),
+ .property_type = &_pt_gobject_int,
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_REMOTE,
+ .is_cli_option = TRUE,
+ .property_alias = "remote",
+ .inf_flags = NM_META_PROPERTY_INF_FLAG_REQD,
+ .prompt = N_("Remote"),
+ .property_type = &_pt_gobject_string,
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_DESTINATION_PORT,
+ .is_cli_option = TRUE,
+ .property_alias = "destination-port",
+ .prompt = N_("Destination port"),
+ .property_type = &_pt_gobject_int,
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_TOS,
+ .property_type = &_pt_gobject_int,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+ .value_infos = INT_VALUE_INFOS (
+ {
+ .value.u64 = 1,
+ .nick = "inherit",
+ },
+ ),
+ ),
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_TTL,
+ .property_type = &_pt_gobject_int,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+ .value_infos = INT_VALUE_INFOS (
+ {
+ .value.i64 = -1,
+ .nick = "inherit",
+ },
+ ),
+ ),
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_DF,
+ .property_type = &_pt_gobject_enum,
+ ),
+ NULL
+};
+
#undef _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_GSM
static const NMMetaPropertyInfo *const property_infos_GSM[] = {
@@ -9018,6 +9069,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
#define SETTING_PRETTY_NAME_DUMMY N_("Dummy settings")
#define SETTING_PRETTY_NAME_ETHTOOL N_("Ethtool settings")
#define SETTING_PRETTY_NAME_GENERIC N_("Generic settings")
+#define SETTING_PRETTY_NAME_GENEVE N_("Geneve settings")
#define SETTING_PRETTY_NAME_GSM N_("GSM mobile broadband connection")
#define SETTING_PRETTY_NAME_HOSTNAME N_("Hostname settings")
#define SETTING_PRETTY_NAME_HSR N_("HSR settings")
@@ -9156,6 +9208,14 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
NM_META_SETTING_VALID_PART_ITEM (GENERIC, TRUE),
),
),
+ SETTING_INFO (GENEVE,
+ .valid_parts = NM_META_SETTING_VALID_PARTS (
+ NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
+ NM_META_SETTING_VALID_PART_ITEM (GENEVE, TRUE),
+ NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE),
+ NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE),
+ ),
+ ),
SETTING_INFO (GSM,
.valid_parts = NM_META_SETTING_VALID_PARTS (
NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in
index e02a93e43d..97848c8a67 100644
--- a/src/libnmc-setting/settings-docs.h.in
+++ b/src/libnmc-setting/settings-docs.h.in
@@ -499,6 +499,12 @@
#define DESCRIBE_DOC_NM_SETTING_WPAN_SHORT_ADDRESS N_("Short IEEE 802.15.4 address to be used within a restricted environment.")
#define DESCRIBE_DOC_NM_SETTING_BOND_PORT_PRIO N_("The port priority for bond active port re-selection during failover. A higher number means a higher priority in selection. The primary port has the highest priority. This option is only compatible with active-backup, balance-tlb and balance-alb modes.")
#define DESCRIBE_DOC_NM_SETTING_BOND_PORT_QUEUE_ID N_("The queue ID of this bond port. The maximum value of queue ID is the number of TX queues currently active in device.")
+#define DESCRIBE_DOC_NM_SETTING_GENEVE_DESTINATION_PORT N_("Specifies the UDP destination port to communicate to the remote GENEVE tunnel endpoint.")
+#define DESCRIBE_DOC_NM_SETTING_GENEVE_DF N_("Specifies how the Don't Fragment (DF) flag should be handled in the outer IP header of GENEVE tunnel packets. \"unset\" (0) (0): Don't set the DF flag, packets may be fragmented. \"set\" (1) (1): Always set the DF flag, packets will not be fragmented. \"inherit\" (2) (2): Inherit the DF flag from the inner IP header.")
+#define DESCRIBE_DOC_NM_SETTING_GENEVE_ID N_("Specifies the GENEVE Network Identifier (or GENEVE Segment Identifier) to use.")
+#define DESCRIBE_DOC_NM_SETTING_GENEVE_REMOTE N_("Specifies the unicast destination IP address to use in outgoing packets when communicating with the remote GENEVE tunnel endpoint.")
+#define DESCRIBE_DOC_NM_SETTING_GENEVE_TOS N_("Specifies the TOS value to use in outgoing packets. The special value \"inherit\" (1) means inherit from outer packet.")
+#define DESCRIBE_DOC_NM_SETTING_GENEVE_TTL N_("Specifies the time-to-live value to use in outgoing packets. The special value \"inherit\" (-1) means inherit from outer packet, 0 means auto, 1-255 are fixed values.")
#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DHCP N_("Whether the system hostname can be determined from DHCP on this connection. When set to \"default\" (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be \"true\" (1).")
#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP N_("Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. When set to \"default\" (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be \"true\" (1).")
#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT N_("If set to \"true\" (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6). If set to \"false\" (0), the hostname can be set from this device even if it doesn't have the default route. When set to \"default\" (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be \"false\" (0).")
diff --git a/src/nmcli/connections.c b/src/nmcli/connections.c
index d93e16f03c..a3b03efe31 100644
--- a/src/nmcli/connections.c
+++ b/src/nmcli/connections.c
@@ -4,6 +4,7 @@
*/
#include "libnm-client-aux-extern/nm-default-client.h"
+#include "nmcli.h"
#include "connections.h"
@@ -1070,13 +1071,13 @@ const NmcMetaGenericInfo
"," NM_SETTING_DCB_SETTING_NAME "," NM_SETTING_TUN_SETTING_NAME \
"," NM_SETTING_IP_TUNNEL_SETTING_NAME "," NM_SETTING_MACSEC_SETTING_NAME \
"," NM_SETTING_MACVLAN_SETTING_NAME "," NM_SETTING_VXLAN_SETTING_NAME \
- "," NM_SETTING_VRF_SETTING_NAME "," NM_SETTING_WPAN_SETTING_NAME \
- "," NM_SETTING_6LOWPAN_SETTING_NAME "," NM_SETTING_WIREGUARD_SETTING_NAME \
- "," NM_SETTING_LINK_SETTING_NAME "," 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_IPVLAN_SETTING_NAME
+ "," NM_SETTING_GENEVE_SETTING_NAME "," NM_SETTING_VRF_SETTING_NAME \
+ "," NM_SETTING_WPAN_SETTING_NAME "," NM_SETTING_6LOWPAN_SETTING_NAME \
+ "," NM_SETTING_WIREGUARD_SETTING_NAME "," NM_SETTING_LINK_SETTING_NAME \
+ "," 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_IPVLAN_SETTING_NAME
/* NM_SETTING_DUMMY_SETTING_NAME NM_SETTING_WIMAX_SETTING_NAME */
const NmcMetaGenericInfo *const nmc_fields_con_active_details_groups[] = {
@@ -1306,6 +1307,9 @@ usage_connection_add(void)
" [source-port-min <0-65535>]\n"
" [source-port-max <0-65535>]\n"
" [destination-port <0-65535>]\n\n"
+ " geneve: id \n"
+ " remote \n"
+ " [destination-port <0-65535>]\n\n"
" wpan: [short-addr <0x0000-0xffff>]\n"
" [pan-id <0x0000-0xffff>]\n"
" [page ]\n"
diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
index 8733cc995b..8b98e95e24 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, ipvlan, loopback, macsec, macvlan, ovs-bridge, ovs-interface, 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, geneve, gsm, hsr, infiniband, ip-tunnel, ipvlan, loopback, macsec, macvlan, ovs-bridge, ovs-interface, ovs-port, pppoe, team, tun, veth, vlan, vpn, vrf, vxlan, wifi-p2p, wimax, wireguard, wpan" />
+
+
+
+
+
+
+
+