geneve: added GENEVE device support

Support device type geneve in libnm and nmcli.
This commit is contained in:
Rahul Rajesh 2026-02-02 19:02:29 -05:00
parent 2aaf88375e
commit 0bfb8fa89d
24 changed files with 1020 additions and 3 deletions

View file

@ -183,6 +183,7 @@
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Bridge.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Dummy.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Generic.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Geneve.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Hsr.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.IPTunnel.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Infiniband.xml"/>

View file

@ -378,6 +378,7 @@ print ("NetworkManager version " + client.get_version())]]></programlisting></in
<xi:include href="xml/nm-device-dummy.xml"/>
<xi:include href="xml/nm-device-ethernet.xml"/>
<xi:include href="xml/nm-device-generic.xml"/>
<xi:include href="xml/nm-device-geneve.xml"/>
<xi:include href="xml/nm-device-hsr.xml"/>
<xi:include href="xml/nm-device-infiniband.xml"/>
<xi:include href="xml/nm-device-ip-tunnel.xml"/>

View file

@ -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',

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/">
<!--
org.freedesktop.NetworkManager.Device.Geneve:
@short_description: GENEVE Device.
-->
<interface name="org.freedesktop.NetworkManager.Device.Geneve">
<!--
Id:
@since: 1.58
The GENEVE Virtual Network Identifier (VNI).
-->
<property name="Id" type="u" access="read"/>
<!--
Remote:
@since: 1.58
The IP (v4 or v6) address of the remote endpoint to which GENEVE packets
are sent.
-->
<property name="Remote" type="s" access="read"/>
<!--
Tos:
@since: 1.58
The value to use in the IP ToS field for GENEVE packets sent to the remote
endpoint.
-->
<property name="Tos" type="y" access="read"/>
<!--
Ttl:
@since: 1.58
The value to use in the IP TTL field for GENEVE packets sent to the remote
endpoint.
-->
<property name="Ttl" type="i" access="read"/>
<!--
Df:
@since: 1.58
The Don't Fragment (DF) flag setting for GENEVE packets. 0 means unset,
1 means set, 2 means inherit from the underlying interface.
-->
<property name="Df" type="y" access="read"/>
<!--
DstPort:
@since: 1.58
Destination port for outgoing GENEVE packets.
-->
<property name="DstPort" type="q" access="read"/>
</interface>
</node>

View file

@ -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

View file

@ -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);

View file

@ -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;);

View file

@ -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__ */

View file

@ -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 */

View file

@ -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',

View file

@ -2107,6 +2107,13 @@ 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;

View file

@ -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',

View file

@ -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"

View file

@ -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);
}

View file

@ -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:

View file

@ -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,

View file

@ -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;

View file

@ -115,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"

View file

@ -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',

View file

@ -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)

View file

@ -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 <NetworkManager.h> 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__ */

View file

@ -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,

View file

@ -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;
/**

View file

@ -115,6 +115,7 @@ DEVICE_BT_* parent="NM.DeviceBt" name="DEVICE
DEVICE_DUMMY_* parent="NM.DeviceDummy" name="DEVICE_DUMMY_(.+)"
DEVICE_ETHERNET_* parent="NM.DeviceEthernet" name="DEVICE_ETHERNET_(.+)"
DEVICE_GENERIC_* parent="NM.DeviceGeneric" name="DEVICE_GENERIC_(.+)"
DEVICE_GENEVE_* parent="NM.DeviceGeneve" name="DEVICE_GENEVE_(.+)"
DEVICE_HSR_* parent="NM.DeviceHsr" name="DEVICE_HSR_(.+)"
DEVICE_INFINIBAND_* parent="NM.DeviceInfiniband" name="DEVICE_INFINIBAND_(.+)"
DEVICE_IP_TUNNEL_* parent="NM.DeviceIPTunnel" name="DEVICE_IP_TUNNEL_(.+)"