summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2022-12-21 16:03:23 +0100
committerBeniamino Galvani <bgalvani@redhat.com>2022-12-21 16:03:23 +0100
commitf2974b1c61ce57b60aaef584b0c62f7c19a64d4f (patch)
treea6edac7fbe22edfb0709d0159c437a8ca653aa10
parentaec7ae8279cc9b3de7e75140b75fe997f43700fc (diff)
parentc0b0f823ca6f041f4131f86ed83df75d40103712 (diff)
downloadNetworkManager-f2974b1c61ce57b60aaef584b0c62f7c19a64d4f.tar.gz
ip-tunnel: merge branch 'bg/vti'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/985 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1435
-rw-r--r--introspection/org.freedesktop.NetworkManager.Device.IPTunnel.xml8
-rw-r--r--src/core/devices/nm-device-ip-tunnel.c261
-rw-r--r--src/core/devices/nm-device-ip-tunnel.h1
-rw-r--r--src/core/platform/tests/test-common.c82
-rw-r--r--src/core/platform/tests/test-common.h8
-rw-r--r--src/core/platform/tests/test-link.c76
-rw-r--r--src/libnm-client-impl/libnm.ver1
-rw-r--r--src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in4
-rw-r--r--src/libnm-core-impl/nm-setting-ip-tunnel.c57
-rw-r--r--src/libnm-core-public/nm-setting-ip-tunnel.h3
-rw-r--r--src/libnm-glib-aux/nm-shared-utils.h2
-rw-r--r--src/libnm-platform/nm-linux-platform.c118
-rw-r--r--src/libnm-platform/nm-platform.c153
-rw-r--r--src/libnm-platform/nm-platform.h47
-rw-r--r--src/libnm-platform/nmp-base.h2
-rw-r--r--src/libnm-platform/nmp-object.c24
-rw-r--r--src/libnm-platform/nmp-object.h16
-rw-r--r--src/libnm-platform/tests/test-nm-platform.c4
-rw-r--r--src/libnmc-setting/nm-meta-setting-desc.c6
-rw-r--r--src/libnmc-setting/settings-docs.h.in1
-rw-r--r--src/nmcli/gen-metadata-nm-settings-nmcli.xml.in2
21 files changed, 801 insertions, 75 deletions
diff --git a/introspection/org.freedesktop.NetworkManager.Device.IPTunnel.xml b/introspection/org.freedesktop.NetworkManager.Device.IPTunnel.xml
index 475da37525..dab7f3b9b1 100644
--- a/introspection/org.freedesktop.NetworkManager.Device.IPTunnel.xml
+++ b/introspection/org.freedesktop.NetworkManager.Device.IPTunnel.xml
@@ -89,6 +89,14 @@
<property name="FlowLabel" type="u" access="read"/>
<!--
+ FwMark:
+
+ The fwmark value to assign to tunnel packets. This property applies only to
+ VTI tunnels.
+ -->
+ <property name="FwMark" type="u" access="read"/>
+
+ <!--
Flags:
Tunnel flags.
diff --git a/src/core/devices/nm-device-ip-tunnel.c b/src/core/devices/nm-device-ip-tunnel.c
index 946f4bbaaa..5e5ba63243 100644
--- a/src/core/devices/nm-device-ip-tunnel.c
+++ b/src/core/devices/nm-device-ip-tunnel.c
@@ -39,6 +39,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceIPTunnel,
PROP_OUTPUT_KEY,
PROP_ENCAPSULATION_LIMIT,
PROP_FLOW_LABEL,
+ PROP_FWMARK,
PROP_FLAGS, );
typedef struct {
@@ -53,6 +54,7 @@ typedef struct {
char *output_key;
guint8 encap_limit;
guint32 flow_label;
+ guint32 fwmark;
NMIPTunnelFlags flags;
} NMDeviceIPTunnelPrivate;
@@ -162,8 +164,10 @@ update_properties_from_ifindex(NMDevice *device, int ifindex)
guint8 encap_limit = 0;
gboolean pmtud = FALSE;
guint32 flow_label = 0;
+ guint32 fwmark = 0;
NMIPTunnelFlags flags = NM_IP_TUNNEL_FLAG_NONE;
- char *key;
+ gs_free char *input_key = NULL;
+ gs_free char *output_key = NULL;
if (ifindex <= 0) {
clear:
@@ -207,35 +211,10 @@ clear:
tos = lnk->tos;
pmtud = lnk->path_mtu_discovery;
- if (NM_FLAGS_HAS(lnk->input_flags, NM_GRE_KEY)) {
- key = g_strdup_printf("%u", lnk->input_key);
- if (g_strcmp0(priv->input_key, key)) {
- g_free(priv->input_key);
- priv->input_key = key;
- _notify(self, PROP_INPUT_KEY);
- } else
- g_free(key);
- } else {
- if (priv->input_key) {
- nm_clear_g_free(&priv->input_key);
- _notify(self, PROP_INPUT_KEY);
- }
- }
-
- if (NM_FLAGS_HAS(lnk->output_flags, NM_GRE_KEY)) {
- key = g_strdup_printf("%u", lnk->output_key);
- if (g_strcmp0(priv->output_key, key)) {
- g_free(priv->output_key);
- priv->output_key = key;
- _notify(self, PROP_OUTPUT_KEY);
- } else
- g_free(key);
- } else {
- if (priv->output_key) {
- nm_clear_g_free(&priv->output_key);
- _notify(self, PROP_OUTPUT_KEY);
- }
- }
+ if (NM_FLAGS_HAS(lnk->input_flags, NM_GRE_KEY))
+ input_key = g_strdup_printf("%u", lnk->input_key);
+ if (NM_FLAGS_HAS(lnk->output_flags, NM_GRE_KEY))
+ output_key = g_strdup_printf("%u", lnk->output_key);
} else if (priv->mode == NM_IP_TUNNEL_MODE_SIT) {
const NMPlatformLnkSit *lnk;
@@ -296,36 +275,45 @@ clear:
flags = ip6tnl_flags_plat_to_setting(lnk->flags);
if (NM_IN_SET(priv->mode, NM_IP_TUNNEL_MODE_IP6GRE, NM_IP_TUNNEL_MODE_IP6GRETAP)) {
- if (NM_FLAGS_HAS(lnk->input_flags, NM_GRE_KEY)) {
- key = g_strdup_printf("%u", lnk->input_key);
- if (g_strcmp0(priv->input_key, key)) {
- g_free(priv->input_key);
- priv->input_key = key;
- _notify(self, PROP_INPUT_KEY);
- } else
- g_free(key);
- } else {
- if (priv->input_key) {
- nm_clear_g_free(&priv->input_key);
- _notify(self, PROP_INPUT_KEY);
- }
- }
+ if (NM_FLAGS_HAS(lnk->input_flags, NM_GRE_KEY))
+ input_key = g_strdup_printf("%u", lnk->input_key);
+ if (NM_FLAGS_HAS(lnk->output_flags, NM_GRE_KEY))
+ output_key = g_strdup_printf("%u", lnk->output_key);
+ }
+ } else if (priv->mode == NM_IP_TUNNEL_MODE_VTI) {
+ const NMPlatformLnkVti *lnk;
- if (NM_FLAGS_HAS(lnk->output_flags, NM_GRE_KEY)) {
- key = g_strdup_printf("%u", lnk->output_key);
- if (g_strcmp0(priv->output_key, key)) {
- g_free(priv->output_key);
- priv->output_key = key;
- _notify(self, PROP_OUTPUT_KEY);
- } else
- g_free(key);
- } else {
- if (priv->output_key) {
- nm_clear_g_free(&priv->output_key);
- _notify(self, PROP_OUTPUT_KEY);
- }
- }
+ lnk = nm_platform_link_get_lnk_vti(nm_device_get_platform(device), ifindex, NULL);
+ if (!lnk) {
+ _LOGW(LOGD_PLATFORM, "could not read %s properties", "vti");
+ goto clear;
+ }
+
+ parent_ifindex = lnk->parent_ifindex;
+ local.addr4 = lnk->local;
+ remote.addr4 = lnk->remote;
+ fwmark = lnk->fwmark;
+ if (lnk->ikey)
+ input_key = g_strdup_printf("%u", lnk->ikey);
+ if (lnk->okey)
+ input_key = g_strdup_printf("%u", lnk->okey);
+ } else if (priv->mode == NM_IP_TUNNEL_MODE_VTI6) {
+ const NMPlatformLnkVti6 *lnk;
+
+ lnk = nm_platform_link_get_lnk_vti6(nm_device_get_platform(device), ifindex, NULL);
+ if (!lnk) {
+ _LOGW(LOGD_PLATFORM, "could not read %s properties", "vti6");
+ goto clear;
}
+
+ parent_ifindex = lnk->parent_ifindex;
+ local.addr6 = lnk->local;
+ remote.addr6 = lnk->remote;
+ fwmark = lnk->fwmark;
+ if (lnk->ikey)
+ input_key = g_strdup_printf("%u", lnk->ikey);
+ if (lnk->okey)
+ input_key = g_strdup_printf("%u", lnk->okey);
} else
g_return_if_reached();
@@ -337,7 +325,6 @@ clear:
_notify(self, PROP_REMOTE);
out:
-
if (priv->ttl != ttl) {
priv->ttl = ttl;
_notify(self, PROP_TTL);
@@ -363,6 +350,23 @@ out:
_notify(self, PROP_FLOW_LABEL);
}
+ if (!nm_streq0(priv->input_key, input_key)) {
+ g_free(priv->input_key);
+ priv->input_key = g_steal_pointer(&input_key);
+ _notify(self, PROP_INPUT_KEY);
+ }
+
+ if (!nm_streq0(priv->output_key, output_key)) {
+ g_free(priv->output_key);
+ priv->output_key = g_steal_pointer(&output_key);
+ _notify(self, PROP_OUTPUT_KEY);
+ }
+
+ if (priv->fwmark != fwmark) {
+ priv->fwmark = fwmark;
+ _notify(self, PROP_FWMARK);
+ }
+
if (priv->flags != flags) {
priv->flags = flags;
_notify(self, PROP_FLAGS);
@@ -467,11 +471,17 @@ update_connection(NMDevice *device, NMConnection *connection)
NULL);
}
+ if (nm_setting_ip_tunnel_get_fwmark(s_ip_tunnel) != priv->fwmark) {
+ g_object_set(G_OBJECT(s_ip_tunnel), NM_SETTING_IP_TUNNEL_FWMARK, priv->fwmark, NULL);
+ }
+
if (NM_IN_SET(priv->mode,
NM_IP_TUNNEL_MODE_GRE,
NM_IP_TUNNEL_MODE_GRETAP,
NM_IP_TUNNEL_MODE_IP6GRE,
- NM_IP_TUNNEL_MODE_IP6GRETAP)) {
+ NM_IP_TUNNEL_MODE_IP6GRETAP,
+ NM_IP_TUNNEL_MODE_VTI,
+ NM_IP_TUNNEL_MODE_VTI6)) {
if (g_strcmp0(nm_setting_ip_tunnel_get_input_key(s_ip_tunnel), priv->input_key)) {
g_object_set(G_OBJECT(s_ip_tunnel),
NM_SETTING_IP_TUNNEL_INPUT_KEY,
@@ -493,6 +503,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL(device);
NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE(self);
NMSettingIPTunnel *s_ip_tunnel;
+ NMIPTunnelMode mode;
const char *parent;
if (!NM_DEVICE_CLASS(nm_device_ip_tunnel_parent_class)
@@ -500,8 +511,9 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
return FALSE;
s_ip_tunnel = nm_connection_get_setting_ip_tunnel(connection);
+ mode = nm_setting_ip_tunnel_get_mode(s_ip_tunnel);
- if (nm_setting_ip_tunnel_get_mode(s_ip_tunnel) != priv->mode) {
+ if (mode != priv->mode) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"incompatible IP tunnel mode");
@@ -536,14 +548,16 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
return FALSE;
}
- if (nm_setting_ip_tunnel_get_ttl(s_ip_tunnel) != priv->ttl) {
+ if (!NM_IN_SET(mode, NM_IP_TUNNEL_MODE_VTI, NM_IP_TUNNEL_MODE_VTI6)
+ && nm_setting_ip_tunnel_get_ttl(s_ip_tunnel) != priv->ttl) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"TTL of IP tunnel mismatches");
return FALSE;
}
- if (nm_setting_ip_tunnel_get_tos(s_ip_tunnel) != priv->tos) {
+ if (!NM_IN_SET(mode, NM_IP_TUNNEL_MODE_VTI, NM_IP_TUNNEL_MODE_VTI6)
+ && nm_setting_ip_tunnel_get_tos(s_ip_tunnel) != priv->tos) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"TOS of IP tunnel mismatches");
@@ -551,11 +565,14 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
}
if (priv->addr_family == AF_INET) {
- if (nm_setting_ip_tunnel_get_path_mtu_discovery(s_ip_tunnel)
- != priv->path_mtu_discovery) {
- nm_utils_error_set_literal(error,
- NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "MTU discovery setting of IP tunnel mismatches");
+ if (!NM_IN_SET(mode, NM_IP_TUNNEL_MODE_VTI, NM_IP_TUNNEL_MODE_VTI6)
+ && nm_setting_ip_tunnel_get_path_mtu_discovery(s_ip_tunnel)
+ != priv->path_mtu_discovery) {
+ nm_utils_error_set(error,
+ NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "MTU discovery setting of IP tunnel mismatches: %d vs %d",
+ nm_setting_ip_tunnel_get_path_mtu_discovery(s_ip_tunnel),
+ priv->path_mtu_discovery);
return FALSE;
}
} else {
@@ -605,6 +622,10 @@ platform_link_to_tunnel_mode(const NMPlatformLink *link)
return NM_IP_TUNNEL_MODE_IPIP;
case NM_LINK_TYPE_SIT:
return NM_IP_TUNNEL_MODE_SIT;
+ case NM_LINK_TYPE_VTI:
+ return NM_IP_TUNNEL_MODE_VTI;
+ case NM_LINK_TYPE_VTI6:
+ return NM_IP_TUNNEL_MODE_VTI6;
default:
g_return_val_if_reached(NM_IP_TUNNEL_MODE_UNKNOWN);
}
@@ -630,7 +651,9 @@ tunnel_mode_to_link_type(NMIPTunnelMode tunnel_mode)
case NM_IP_TUNNEL_MODE_SIT:
return NM_LINK_TYPE_SIT;
case NM_IP_TUNNEL_MODE_VTI:
+ return NM_LINK_TYPE_VTI;
case NM_IP_TUNNEL_MODE_VTI6:
+ return NM_LINK_TYPE_VTI6;
case NM_IP_TUNNEL_MODE_ISATAP:
return NM_LINK_TYPE_UNKNOWN;
case NM_IP_TUNNEL_MODE_UNKNOWN:
@@ -654,6 +677,8 @@ create_and_realize(NMDevice *device,
NMPlatformLnkSit lnk_sit = {};
NMPlatformLnkIpIp lnk_ipip = {};
NMPlatformLnkIp6Tnl lnk_ip6tnl = {};
+ NMPlatformLnkVti lnk_vti = {};
+ NMPlatformLnkVti6 lnk_vti6 = {};
const char *str;
gint64 val;
NMIPTunnelMode mode;
@@ -871,6 +896,81 @@ create_and_realize(NMDevice *device,
return FALSE;
}
break;
+ case NM_IP_TUNNEL_MODE_VTI:
+ if (parent)
+ lnk_vti.parent_ifindex = nm_device_get_ifindex(parent);
+
+ str = nm_setting_ip_tunnel_get_local(s_ip_tunnel);
+ if (str)
+ inet_pton(AF_INET, str, &lnk_vti.local);
+
+ str = nm_setting_ip_tunnel_get_remote(s_ip_tunnel);
+ nm_assert(str);
+ inet_pton(AF_INET, str, &lnk_vti.remote);
+
+ lnk_vti.ikey = _nm_utils_ascii_str_to_int64(nm_setting_ip_tunnel_get_input_key(s_ip_tunnel),
+ 10,
+ 0,
+ G_MAXUINT32,
+ 0);
+ lnk_vti.okey =
+ _nm_utils_ascii_str_to_int64(nm_setting_ip_tunnel_get_output_key(s_ip_tunnel),
+ 10,
+ 0,
+ G_MAXUINT32,
+ 0);
+ lnk_vti.fwmark = nm_setting_ip_tunnel_get_fwmark(s_ip_tunnel);
+
+ r = nm_platform_link_vti_add(nm_device_get_platform(device), iface, &lnk_vti, out_plink);
+ if (r < 0) {
+ g_set_error(error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_CREATION_FAILED,
+ "Failed to create VTI interface '%s' for '%s': %s",
+ iface,
+ nm_connection_get_id(connection),
+ nm_strerror(r));
+ return FALSE;
+ }
+ break;
+ case NM_IP_TUNNEL_MODE_VTI6:
+ if (parent)
+ lnk_vti6.parent_ifindex = nm_device_get_ifindex(parent);
+
+ str = nm_setting_ip_tunnel_get_local(s_ip_tunnel);
+ if (str)
+ inet_pton(AF_INET6, str, &lnk_vti6.local);
+
+ str = nm_setting_ip_tunnel_get_remote(s_ip_tunnel);
+ nm_assert(str);
+ inet_pton(AF_INET6, str, &lnk_vti6.remote);
+
+ lnk_vti6.ikey =
+ _nm_utils_ascii_str_to_int64(nm_setting_ip_tunnel_get_input_key(s_ip_tunnel),
+ 10,
+ 0,
+ G_MAXUINT32,
+ 0);
+ lnk_vti6.okey =
+ _nm_utils_ascii_str_to_int64(nm_setting_ip_tunnel_get_output_key(s_ip_tunnel),
+ 10,
+ 0,
+ G_MAXUINT32,
+ 0);
+ lnk_vti6.fwmark = nm_setting_ip_tunnel_get_fwmark(s_ip_tunnel);
+
+ r = nm_platform_link_vti6_add(nm_device_get_platform(device), iface, &lnk_vti6, out_plink);
+ if (r < 0) {
+ g_set_error(error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_CREATION_FAILED,
+ "Failed to create VTI6 interface '%s' for '%s': %s",
+ iface,
+ nm_connection_get_id(connection),
+ nm_strerror(r));
+ return FALSE;
+ }
+ break;
default:
g_set_error(error,
NM_DEVICE_ERROR,
@@ -986,6 +1086,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
case PROP_FLAGS:
g_value_set_uint(value, priv->flags);
break;
+ case PROP_FWMARK:
+ g_value_set_uint(value, priv->fwmark);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -1021,7 +1124,8 @@ constructed(GObject *object)
NM_IP_TUNNEL_MODE_IPIP6,
NM_IP_TUNNEL_MODE_IP6IP6,
NM_IP_TUNNEL_MODE_IP6GRE,
- NM_IP_TUNNEL_MODE_IP6GRETAP))
+ NM_IP_TUNNEL_MODE_IP6GRETAP,
+ NM_IP_TUNNEL_MODE_VTI6))
priv->addr_family = AF_INET6;
else
priv->addr_family = AF_INET;
@@ -1070,6 +1174,9 @@ static const NMDBusInterfaceInfoExtended interface_info_device_ip_tunnel = {
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("FlowLabel",
"u",
NM_DEVICE_IP_TUNNEL_FLOW_LABEL),
+ NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("FwMark",
+ "u",
+ NM_DEVICE_IP_TUNNEL_FWMARK),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Flags",
"u",
NM_DEVICE_IP_TUNNEL_FLAGS), ), ),
@@ -1097,7 +1204,9 @@ nm_device_ip_tunnel_class_init(NMDeviceIPTunnelClass *klass)
NM_LINK_TYPE_IP6GRE,
NM_LINK_TYPE_IP6GRETAP,
NM_LINK_TYPE_IPIP,
- NM_LINK_TYPE_SIT);
+ NM_LINK_TYPE_SIT,
+ NM_LINK_TYPE_VTI,
+ NM_LINK_TYPE_VTI6);
device_class->act_stage1_prepare = act_stage1_prepare;
device_class->link_changed = link_changed;
@@ -1192,6 +1301,14 @@ nm_device_ip_tunnel_class_init(NMDeviceIPTunnelClass *klass)
0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_FWMARK] = g_param_spec_uint(NM_DEVICE_IP_TUNNEL_FWMARK,
+ "",
+ "",
+ 0,
+ G_MAXUINT32,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
}
@@ -1284,7 +1401,9 @@ NM_DEVICE_FACTORY_DEFINE_INTERNAL(
NM_LINK_TYPE_IPIP,
NM_LINK_TYPE_IP6TNL,
NM_LINK_TYPE_IP6GRE,
- NM_LINK_TYPE_IP6GRETAP)
+ NM_LINK_TYPE_IP6GRETAP,
+ NM_LINK_TYPE_VTI,
+ NM_LINK_TYPE_VTI6)
NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_IP_TUNNEL_SETTING_NAME),
factory_class->create_device = create_device;
factory_class->get_connection_parent = get_connection_parent;
diff --git a/src/core/devices/nm-device-ip-tunnel.h b/src/core/devices/nm-device-ip-tunnel.h
index d8f876a9fe..1d7b7381d3 100644
--- a/src/core/devices/nm-device-ip-tunnel.h
+++ b/src/core/devices/nm-device-ip-tunnel.h
@@ -31,6 +31,7 @@
#define NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT "encapsulation-limit"
#define NM_DEVICE_IP_TUNNEL_FLOW_LABEL "flow-label"
#define NM_DEVICE_IP_TUNNEL_FLAGS "flags"
+#define NM_DEVICE_IP_TUNNEL_FWMARK "fwmark"
typedef struct _NMDeviceIPTunnel NMDeviceIPTunnel;
typedef struct _NMDeviceIPTunnelClass NMDeviceIPTunnelClass;
diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c
index 7116bcf35d..7b5dd1c58f 100644
--- a/src/core/platform/tests/test-common.c
+++ b/src/core/platform/tests/test-common.c
@@ -2118,6 +2118,88 @@ nmtstp_link_vlan_add(NMPlatform *platform,
}
const NMPlatformLink *
+nmtstp_link_vti_add(NMPlatform *platform,
+ gboolean external_command,
+ const char *name,
+ const NMPlatformLnkVti *lnk)
+{
+ const NMPlatformLink *pllink = NULL;
+ gboolean success;
+ char b1[INET_ADDRSTRLEN];
+ char b2[INET_ADDRSTRLEN];
+
+ g_assert(nm_utils_ifname_valid_kernel(name, NULL));
+ external_command = nmtstp_run_command_check_external(external_command);
+ _init_platform(&platform, external_command);
+
+ if (external_command) {
+ gs_free char *dev = NULL;
+
+ if (lnk->parent_ifindex)
+ dev =
+ g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex));
+
+ success = !nmtstp_run_command(
+ "ip link add %s type vti %s local %s remote %s ikey %u okey %u fwmark 0x%x",
+ name,
+ dev ?: "",
+ nm_inet4_ntop(lnk->local, b1),
+ nm_inet4_ntop(lnk->remote, b2),
+ lnk->ikey,
+ lnk->okey,
+ lnk->fwmark);
+ if (success)
+ pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VTI, 100);
+ } else
+ success = NMTST_NM_ERR_SUCCESS(nm_platform_link_vti_add(platform, name, lnk, &pllink));
+
+ _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_VTI);
+
+ return pllink;
+}
+
+const NMPlatformLink *
+nmtstp_link_vti6_add(NMPlatform *platform,
+ gboolean external_command,
+ const char *name,
+ const NMPlatformLnkVti6 *lnk)
+{
+ const NMPlatformLink *pllink = NULL;
+ gboolean success;
+ char b1[INET6_ADDRSTRLEN];
+ char b2[INET6_ADDRSTRLEN];
+
+ g_assert(nm_utils_ifname_valid_kernel(name, NULL));
+ external_command = nmtstp_run_command_check_external(external_command);
+ _init_platform(&platform, external_command);
+
+ if (external_command) {
+ gs_free char *dev = NULL;
+
+ if (lnk->parent_ifindex)
+ dev =
+ g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex));
+
+ success = !nmtstp_run_command(
+ "ip link add %s type vti6 %s local %s remote %s ikey %u okey %u fwmark 0x%x",
+ name,
+ dev ?: "",
+ nm_inet6_ntop(&lnk->local, b1),
+ nm_inet6_ntop(&lnk->remote, b2),
+ lnk->ikey,
+ lnk->okey,
+ lnk->fwmark);
+ if (success)
+ pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VTI6, 100);
+ } else
+ success = NMTST_NM_ERR_SUCCESS(nm_platform_link_vti6_add(platform, name, lnk, &pllink));
+
+ _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_VTI6);
+
+ return pllink;
+}
+
+const NMPlatformLink *
nmtstp_link_vrf_add(NMPlatform *platform,
int external_command,
const char *name,
diff --git a/src/core/platform/tests/test-common.h b/src/core/platform/tests/test-common.h
index c18d0bde62..9319c79b8d 100644
--- a/src/core/platform/tests/test-common.h
+++ b/src/core/platform/tests/test-common.h
@@ -504,6 +504,14 @@ const NMPlatformLink *nmtstp_link_vlan_add(NMPlatform *platform,
const char *name,
int parent,
const NMPlatformLnkVlan *lnk);
+const NMPlatformLink *nmtstp_link_vti_add(NMPlatform *platform,
+ gboolean external_command,
+ const char *name,
+ const NMPlatformLnkVti *lnk);
+const NMPlatformLink *nmtstp_link_vti6_add(NMPlatform *platform,
+ gboolean external_command,
+ const char *name,
+ const NMPlatformLnkVti6 *lnk);
const NMPlatformLink *nmtstp_link_vrf_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 57b074a068..cb487c0189 100644
--- a/src/core/platform/tests/test-link.c
+++ b/src/core/platform/tests/test-link.c
@@ -1286,8 +1286,10 @@ test_software_detect(gconstpointer user_data)
const gboolean ext = test_data->external_command;
NMPlatformLnkBridge lnk_bridge = {};
NMPlatformLnkTun lnk_tun;
- NMPlatformLnkGre lnk_gre = {};
- nm_auto_close int tun_fd = -1;
+ NMPlatformLnkGre lnk_gre = {};
+ NMPlatformLnkVti lnk_vti = {};
+ NMPlatformLnkVti6 lnk_vti6 = {};
+ nm_auto_close int tun_fd = -1;
nmtstp_run_command_check("ip link add %s type dummy", PARENT_NAME);
ifindex_parent =
@@ -1615,6 +1617,58 @@ test_software_detect(gconstpointer user_data)
}
break;
}
+ case NM_LINK_TYPE_VTI:
+ {
+ gboolean gracefully_skip = FALSE;
+
+ if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip_vti0")) {
+ /* Seems that the ip_vti module is not loaded... try to load it. */
+ gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip_vti", NULL) != 0;
+ }
+
+ lnk_vti.local = nmtst_inet4_from_string("192.168.212.204");
+ lnk_vti.remote = nmtst_inet4_from_string("172.168.11.25");
+ lnk_vti.parent_ifindex = ifindex_parent;
+ lnk_vti.fwmark = 0x42;
+ lnk_vti.ikey = 12;
+ lnk_vti.okey = 13;
+
+ if (!nmtstp_link_vti_add(NULL, ext, DEVICE_NAME, &lnk_vti)) {
+ if (gracefully_skip) {
+ g_test_skip(
+ "Cannot create vti tunnel because of missing vti module (modprobe ip_vti)");
+ goto out_delete_parent;
+ }
+ g_error("Failed adding VTI tunnel");
+ }
+ break;
+ }
+ case NM_LINK_TYPE_VTI6:
+ {
+ gboolean gracefully_skip = FALSE;
+
+ if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6_vti0")) {
+ /* Seems that the ip6_vti module is not loaded... try to load it. */
+ gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_vti", NULL) != 0;
+ }
+
+ lnk_vti6.local = nmtst_inet6_from_string("fd01::1");
+ lnk_vti6.remote = nmtst_inet6_from_string("fd02::2");
+ lnk_vti6.parent_ifindex = ifindex_parent;
+ lnk_vti6.fwmark = 0x43;
+ lnk_vti6.ikey = 13;
+ lnk_vti6.okey = 14;
+
+ if (!nmtstp_link_vti6_add(NULL, ext, DEVICE_NAME, &lnk_vti6)) {
+ if (gracefully_skip) {
+ g_test_skip(
+ "Cannot create vti6 tunnel because of missing vti module (modprobe ip_vti)");
+ goto out_delete_parent;
+ }
+ g_error("Failed adding VTI6 tunnel");
+ }
+ break;
+ }
case NM_LINK_TYPE_VXLAN:
{
NMPlatformLnkVxlan lnk_vxlan = {};
@@ -1957,6 +2011,22 @@ test_software_detect(gconstpointer user_data)
g_assert_cmpint(plnk->table, ==, 9876);
break;
}
+ case NM_LINK_TYPE_VTI:
+ {
+ const NMPlatformLnkVti *plnk = &lnk->lnk_vti;
+
+ g_assert(plnk == nm_platform_link_get_lnk_vti(NM_PLATFORM_GET, ifindex, NULL));
+ g_assert(nm_platform_lnk_vti_cmp(plnk, &lnk_vti) == 0);
+ break;
+ }
+ case NM_LINK_TYPE_VTI6:
+ {
+ const NMPlatformLnkVti6 *plnk = &lnk->lnk_vti6;
+
+ g_assert(plnk == nm_platform_link_get_lnk_vti6(NM_PLATFORM_GET, ifindex, NULL));
+ g_assert(nm_platform_lnk_vti6_cmp(plnk, &lnk_vti6) == 0);
+ break;
+ }
case NM_LINK_TYPE_VXLAN:
{
const NMPlatformLnkVxlan *plnk = &lnk->lnk_vxlan;
@@ -3861,6 +3931,8 @@ _nmtstp_setup_tests(void)
test_software_detect_add("/link/software/detect/tun", NM_LINK_TYPE_TUN, 0);
test_software_detect_add("/link/software/detect/vlan/0", NM_LINK_TYPE_VLAN, 0);
test_software_detect_add("/link/software/detect/vlan/1", NM_LINK_TYPE_VLAN, 1);
+ test_software_detect_add("/link/software/detect/vti", NM_LINK_TYPE_VTI, 0);
+ test_software_detect_add("/link/software/detect/vti6", NM_LINK_TYPE_VTI6, 0);
test_software_detect_add("/link/software/detect/vrf", NM_LINK_TYPE_VRF, 0);
test_software_detect_add("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0);
test_software_detect_add("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1);
diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver
index 8c9a4ef158..2271386860 100644
--- a/src/libnm-client-impl/libnm.ver
+++ b/src/libnm-client-impl/libnm.ver
@@ -1896,6 +1896,7 @@ global:
nm_range_unref;
nm_setting_ip_config_get_dhcp_iaid;
nm_setting_ip_config_get_dhcp_iaid;
+ nm_setting_ip_tunnel_get_fwmark;
nm_setting_loopback_get_mtu;
nm_setting_loopback_get_type;
nm_setting_loopback_new;
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 6e4347b915..c0868afb22 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
@@ -1422,6 +1422,10 @@
dbus-type="u"
gprop-type="guint"
/>
+ <property name="fwmark"
+ dbus-type="u"
+ gprop-type="guint"
+ />
<property name="input-key"
dbus-type="s"
gprop-type="gchararray"
diff --git a/src/libnm-core-impl/nm-setting-ip-tunnel.c b/src/libnm-core-impl/nm-setting-ip-tunnel.c
index da464c1ae1..7fb8b01739 100644
--- a/src/libnm-core-impl/nm-setting-ip-tunnel.c
+++ b/src/libnm-core-impl/nm-setting-ip-tunnel.c
@@ -28,6 +28,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT,
PROP_OUTPUT_KEY,
PROP_ENCAPSULATION_LIMIT,
PROP_FLOW_LABEL,
+ PROP_FWMARK,
PROP_MTU,
PROP_FLAGS, );
@@ -41,6 +42,7 @@ typedef struct {
guint32 tos;
guint32 encapsulation_limit;
guint32 flow_label;
+ guint32 fwmark;
guint32 mode;
guint32 mtu;
guint32 flags;
@@ -269,6 +271,24 @@ nm_setting_ip_tunnel_get_flow_label(NMSettingIPTunnel *setting)
}
/**
+ * nm_setting_ip_tunnel_get_fwmark:
+ * @setting: the #NMSettingIPTunnel
+ *
+ * Returns the #NMSettingIPTunnel:fwmark property of the setting.
+ *
+ * Returns: the fwmark value
+ *
+ * Since: 1.42
+ **/
+guint32
+nm_setting_ip_tunnel_get_fwmark(NMSettingIPTunnel *setting)
+{
+ g_return_val_if_fail(NM_IS_SETTING_IP_TUNNEL(setting), 0);
+
+ return NM_SETTING_IP_TUNNEL_GET_PRIVATE(setting)->fwmark;
+}
+
+/**
* nm_setting_ip_tunnel_get_mtu:
* @setting: the #NMSettingIPTunnel
*
@@ -411,11 +431,13 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
NM_IP_TUNNEL_MODE_GRE,
NM_IP_TUNNEL_MODE_GRETAP,
NM_IP_TUNNEL_MODE_IP6GRE,
- NM_IP_TUNNEL_MODE_IP6GRETAP)) {
+ NM_IP_TUNNEL_MODE_IP6GRETAP,
+ NM_IP_TUNNEL_MODE_VTI,
+ NM_IP_TUNNEL_MODE_VTI6)) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
- _("tunnel keys can only be specified for GRE tunnels"));
+ _("tunnel keys can only be specified for GRE and VTI tunnels"));
return FALSE;
}
}
@@ -484,6 +506,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
return FALSE;
}
+ if (priv->fwmark && !NM_IN_SET(priv->mode, NM_IP_TUNNEL_MODE_VTI, NM_IP_TUNNEL_MODE_VTI6)) {
+ g_set_error_literal(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("can be set only on VTI tunnels"));
+ g_prefix_error(error,
+ "%s.%s: ",
+ NM_SETTING_IP_TUNNEL_SETTING_NAME,
+ NM_SETTING_IP_TUNNEL_FWMARK);
+ return FALSE;
+ }
+
if (nm_connection_get_setting_wired(connection) && !_nm_ip_tunnel_mode_is_layer2(priv->mode)) {
g_set_error(error,
NM_CONNECTION_ERROR,
@@ -728,6 +762,25 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
flow_label);
/**
+ * NMSettingIPTunnel:fwmark:
+ *
+ * The fwmark value to assign to tunnel packets. This property can be set
+ * to a non zero value only on VTI and VTI6 tunnels.
+ *
+ * Since: 1.42
+ **/
+ _nm_setting_property_define_direct_uint32(properties_override,
+ obj_properties,
+ NM_SETTING_IP_TUNNEL_FWMARK,
+ PROP_FWMARK,
+ 0,
+ G_MAXUINT32,
+ 0,
+ NM_SETTING_PARAM_INFERRABLE,
+ NMSettingIPTunnelPrivate,
+ fwmark);
+
+ /**
* NMSettingIPTunnel:mtu:
*
* If non-zero, only transmit packets of the specified size or smaller,
diff --git a/src/libnm-core-public/nm-setting-ip-tunnel.h b/src/libnm-core-public/nm-setting-ip-tunnel.h
index 7aa48281b3..bcb3eab6e2 100644
--- a/src/libnm-core-public/nm-setting-ip-tunnel.h
+++ b/src/libnm-core-public/nm-setting-ip-tunnel.h
@@ -38,6 +38,7 @@ G_BEGIN_DECLS
#define NM_SETTING_IP_TUNNEL_OUTPUT_KEY "output-key"
#define NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT "encapsulation-limit"
#define NM_SETTING_IP_TUNNEL_FLOW_LABEL "flow-label"
+#define NM_SETTING_IP_TUNNEL_FWMARK "fwmark"
#define NM_SETTING_IP_TUNNEL_MTU "mtu"
#define NM_SETTING_IP_TUNNEL_FLAGS "flags"
@@ -98,6 +99,8 @@ NM_AVAILABLE_IN_1_42
guint nm_setting_ip_tunnel_get_encapsulation_limit(NMSettingIPTunnel *setting);
NM_AVAILABLE_IN_1_42
guint nm_setting_ip_tunnel_get_flow_label(NMSettingIPTunnel *setting);
+NM_AVAILABLE_IN_1_42
+guint32 nm_setting_ip_tunnel_get_fwmark(NMSettingIPTunnel *setting);
NM_AVAILABLE_IN_1_2
guint nm_setting_ip_tunnel_get_mtu(NMSettingIPTunnel *setting);
NM_AVAILABLE_IN_1_12
diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h
index 044f34abf0..7ad2874244 100644
--- a/src/libnm-glib-aux/nm-shared-utils.h
+++ b/src/libnm-glib-aux/nm-shared-utils.h
@@ -139,6 +139,8 @@ typedef enum {
NM_LINK_TYPE_VETH,
NM_LINK_TYPE_VLAN,
NM_LINK_TYPE_VRF,
+ NM_LINK_TYPE_VTI,
+ NM_LINK_TYPE_VTI6,
NM_LINK_TYPE_VXLAN,
NM_LINK_TYPE_WIREGUARD,
#define _NM_LINK_TYPE_SW_LAST NM_LINK_TYPE_WIREGUARD
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index d452e262fd..2f7178e381 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -808,6 +808,8 @@ static const LinkDesc link_descs[] = {
[NM_LINK_TYPE_VETH] = {"veth", "veth", NULL},
[NM_LINK_TYPE_VLAN] = {"vlan", "vlan", "vlan"},
[NM_LINK_TYPE_VRF] = {"vrf", "vrf", "vrf"},
+ [NM_LINK_TYPE_VTI] = {"vti", "vti", NULL},
+ [NM_LINK_TYPE_VTI6] = {"vti6", "vti6", NULL},
[NM_LINK_TYPE_VXLAN] = {"vxlan", "vxlan", "vxlan"},
[NM_LINK_TYPE_WIREGUARD] = {"wireguard", "wireguard", "wireguard"},
@@ -850,6 +852,8 @@ _link_type_from_rtnl_type(const char *name)
NM_LINK_TYPE_VETH, /* "veth" */
NM_LINK_TYPE_VLAN, /* "vlan" */
NM_LINK_TYPE_VRF, /* "vrf" */
+ NM_LINK_TYPE_VTI, /* "vti" */
+ NM_LINK_TYPE_VTI6, /* "vti6" */
NM_LINK_TYPE_VXLAN, /* "vxlan" */
NM_LINK_TYPE_WIMAX, /* "wimax" */
NM_LINK_TYPE_WIREGUARD, /* "wireguard" */
@@ -2405,6 +2409,76 @@ _parse_lnk_vxlan(const char *kind, struct nlattr *info_data)
}
static NMPObject *
+_parse_lnk_vti(const char *kind, struct nlattr *info_data)
+{
+ static const struct nla_policy policy[] = {
+ [IFLA_VTI_LINK] = {.type = NLA_U32},
+ [IFLA_VTI_LOCAL] = {.type = NLA_U32},
+ [IFLA_VTI_REMOTE] = {.type = NLA_U32},
+ [IFLA_VTI_IKEY] = {.type = NLA_U32},
+ [IFLA_VTI_OKEY] = {.type = NLA_U32},
+ [IFLA_VTI_FWMARK] = {.type = NLA_U32},
+ };
+ struct nlattr *tb[G_N_ELEMENTS(policy)];
+ NMPObject *obj;
+ NMPlatformLnkVti *props;
+
+ if (!info_data || !nm_streq0(kind, "vti"))
+ return NULL;
+
+ if (nla_parse_nested_arr(tb, info_data, policy) < 0)
+ return NULL;
+
+ obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VTI, NULL);
+ props = &obj->lnk_vti;
+
+ props->parent_ifindex = tb[IFLA_VTI_LINK] ? nla_get_u32(tb[IFLA_VTI_LINK]) : 0;
+ props->local = tb[IFLA_VTI_LOCAL] ? nla_get_u32(tb[IFLA_VTI_LOCAL]) : 0;
+ props->remote = tb[IFLA_VTI_REMOTE] ? nla_get_u32(tb[IFLA_VTI_REMOTE]) : 0;
+ props->ikey = tb[IFLA_VTI_IKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_IKEY])) : 0;
+ props->okey = tb[IFLA_VTI_OKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_OKEY])) : 0;
+ props->fwmark = tb[IFLA_VTI_FWMARK] ? nla_get_u32(tb[IFLA_VTI_FWMARK]) : 0;
+
+ return obj;
+}
+
+static NMPObject *
+_parse_lnk_vti6(const char *kind, struct nlattr *info_data)
+{
+ static const struct nla_policy policy[] = {
+ [IFLA_VTI_LINK] = {.type = NLA_U32},
+ [IFLA_VTI_LOCAL] = {.minlen = sizeof(struct in6_addr)},
+ [IFLA_VTI_REMOTE] = {.minlen = sizeof(struct in6_addr)},
+ [IFLA_VTI_IKEY] = {.type = NLA_U32},
+ [IFLA_VTI_OKEY] = {.type = NLA_U32},
+ [IFLA_VTI_FWMARK] = {.type = NLA_U32},
+ };
+ struct nlattr *tb[G_N_ELEMENTS(policy)];
+ NMPObject *obj;
+ NMPlatformLnkVti6 *props;
+
+ if (!info_data || !nm_streq0(kind, "vti6"))
+ return NULL;
+
+ if (nla_parse_nested_arr(tb, info_data, policy) < 0)
+ return NULL;
+
+ obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VTI6, NULL);
+ props = &obj->lnk_vti6;
+
+ props->parent_ifindex = tb[IFLA_VTI_LINK] ? nla_get_u32(tb[IFLA_VTI_LINK]) : 0;
+ if (tb[IFLA_VTI_LOCAL])
+ props->local = *nla_data_as(struct in6_addr, tb[IFLA_VTI_LOCAL]);
+ if (tb[IFLA_VTI_REMOTE])
+ props->remote = *nla_data_as(struct in6_addr, tb[IFLA_VTI_REMOTE]);
+ props->ikey = tb[IFLA_VTI_IKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_IKEY])) : 0;
+ props->okey = tb[IFLA_VTI_OKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_OKEY])) : 0;
+ props->fwmark = tb[IFLA_VTI_FWMARK] ? nla_get_u32(tb[IFLA_VTI_FWMARK]) : 0;
+
+ return obj;
+}
+
+static NMPObject *
_parse_lnk_vrf(const char *kind, struct nlattr *info_data)
{
static const struct nla_policy policy[] = {
@@ -3394,6 +3468,12 @@ _new_from_nl_link(NMPlatform *platform,
case NM_LINK_TYPE_VRF:
lnk_data = _parse_lnk_vrf(nl_info_kind, nl_info_data);
break;
+ case NM_LINK_TYPE_VTI:
+ lnk_data = _parse_lnk_vti(nl_info_kind, nl_info_data);
+ break;
+ case NM_LINK_TYPE_VTI6:
+ lnk_data = _parse_lnk_vti6(nl_info_kind, nl_info_data);
+ break;
case NM_LINK_TYPE_VXLAN:
lnk_data = _parse_lnk_vxlan(nl_info_kind, nl_info_data);
break;
@@ -4920,6 +5000,44 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo
NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, props->no_promisc ? MACVLAN_FLAG_NOPROMISC : 0);
break;
}
+ case NM_LINK_TYPE_VTI:
+ {
+ const NMPlatformLnkVti *props = extra_data;
+
+ nm_assert(props);
+
+ if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+ goto nla_put_failure;
+
+ if (props->parent_ifindex > 0)
+ NLA_PUT_U32(msg, IFLA_VTI_LINK, props->parent_ifindex);
+ NLA_PUT_U32(msg, IFLA_VTI_LOCAL, props->local);
+ NLA_PUT_U32(msg, IFLA_VTI_REMOTE, props->remote);
+ NLA_PUT_U32(msg, IFLA_VTI_IKEY, htonl(props->ikey));
+ NLA_PUT_U32(msg, IFLA_VTI_OKEY, htonl(props->okey));
+ NLA_PUT_U32(msg, IFLA_VTI_FWMARK, props->fwmark);
+ break;
+ }
+ case NM_LINK_TYPE_VTI6:
+ {
+ const NMPlatformLnkVti6 *props = extra_data;
+
+ nm_assert(props);
+
+ if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+ goto nla_put_failure;
+
+ if (props->parent_ifindex > 0)
+ NLA_PUT_U32(msg, IFLA_VTI_LINK, props->parent_ifindex);
+ if (!IN6_IS_ADDR_UNSPECIFIED(&props->local))
+ NLA_PUT(msg, IFLA_VTI_LOCAL, sizeof(props->local), &props->local);
+ if (!IN6_IS_ADDR_UNSPECIFIED(&props->remote))
+ NLA_PUT(msg, IFLA_VTI_REMOTE, sizeof(props->remote), &props->remote);
+ NLA_PUT_U32(msg, IFLA_VTI_IKEY, htonl(props->ikey));
+ NLA_PUT_U32(msg, IFLA_VTI_OKEY, htonl(props->okey));
+ NLA_PUT_U32(msg, IFLA_VTI_FWMARK, props->fwmark);
+ break;
+ }
default:
nm_assert(!extra_data);
break;
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index bc60f19f89..f201f2f9c7 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -1370,6 +1370,18 @@ nm_platform_link_add(NMPlatform *self,
buf_p,
buf_len);
break;
+ case NM_LINK_TYPE_VTI:
+ nm_strbuf_append_str(&buf_p, &buf_len, ", ");
+ nm_platform_lnk_vti_to_string((const NMPlatformLnkVti *) extra_data,
+ buf_p,
+ buf_len);
+ break;
+ case NM_LINK_TYPE_VTI6:
+ nm_strbuf_append_str(&buf_p, &buf_len, ", ");
+ nm_platform_lnk_vti6_to_string((const NMPlatformLnkVti6 *) extra_data,
+ buf_p,
+ buf_len);
+ break;
case NM_LINK_TYPE_BOND:
nm_strbuf_append_str(&buf_p, &buf_len, ", ");
nm_platform_lnk_bond_to_string((const NMPlatformLnkBond *) extra_data,
@@ -2418,6 +2430,18 @@ nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink
return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VRF, out_link);
}
+const NMPlatformLnkVti *
+nm_platform_link_get_lnk_vti(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
+{
+ return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VTI, out_link);
+}
+
+const NMPlatformLnkVti6 *
+nm_platform_link_get_lnk_vti6(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
+{
+ return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VTI6, out_link);
+}
+
const NMPlatformLnkVxlan *
nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
{
@@ -6497,6 +6521,85 @@ nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize le
}
const char *
+nm_platform_lnk_vti_to_string(const NMPlatformLnkVti *lnk, char *buf, gsize len)
+{
+ char str_local[30 + NM_INET_ADDRSTRLEN];
+ char str_local1[NM_INET_ADDRSTRLEN];
+ char str_remote[30 + NM_INET_ADDRSTRLEN];
+ char str_remote1[NM_INET_ADDRSTRLEN];
+ char str_ikey[30];
+ char str_okey[30];
+ char str_fwmark[30];
+ char str_parent_ifindex[30];
+
+ if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
+ return buf;
+
+ g_snprintf(
+ buf,
+ len,
+ "vti"
+ "%s" /* remote */
+ "%s" /* local */
+ "%s" /* parent_ifindex */
+ "%s" /* ikey */
+ "%s" /* okey */
+ "%s" /* fwmark */
+ "",
+ lnk->remote
+ ? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1))
+ : "",
+ lnk->local ? nm_sprintf_buf(str_local, " local %s", nm_inet4_ntop(lnk->local, str_local1))
+ : "",
+ lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex)
+ : "",
+ lnk->ikey ? nm_sprintf_buf(str_ikey, " ikey %u", lnk->ikey) : "",
+ lnk->okey ? nm_sprintf_buf(str_okey, " okey %u", lnk->okey) : "",
+ lnk->fwmark ? nm_sprintf_buf(str_fwmark, " fwmark 0x%x", lnk->fwmark) : "");
+ return buf;
+}
+
+const char *
+nm_platform_lnk_vti6_to_string(const NMPlatformLnkVti6 *lnk, char *buf, gsize len)
+{
+ char str_local[30 + NM_INET_ADDRSTRLEN];
+ char str_local1[NM_INET_ADDRSTRLEN];
+ char str_remote[30 + NM_INET_ADDRSTRLEN];
+ char str_remote1[NM_INET_ADDRSTRLEN];
+ char str_ikey[30];
+ char str_okey[30];
+ char str_fwmark[30];
+ char str_parent_ifindex[30];
+
+ if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
+ return buf;
+
+ g_snprintf(
+ buf,
+ len,
+ "vti6"
+ "%s" /* remote */
+ "%s" /* local */
+ "%s" /* parent_ifindex */
+ "%s" /* ikey */
+ "%s" /* okey */
+ "%s" /* fwmark */
+ "",
+ IN6_IS_ADDR_UNSPECIFIED(&lnk->remote)
+ ? ""
+ : nm_sprintf_buf(str_remote, " remote %s", nm_inet6_ntop(&lnk->remote, str_remote1)),
+ IN6_IS_ADDR_UNSPECIFIED(&lnk->local)
+ ? ""
+ : nm_sprintf_buf(str_local, " local %s", nm_inet6_ntop(&lnk->local, str_local1)),
+ lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex)
+ : "",
+ lnk->ikey ? nm_sprintf_buf(str_ikey, " ikey %u", lnk->ikey) : "",
+ lnk->okey ? nm_sprintf_buf(str_okey, " okey %u", lnk->okey) : "",
+ lnk->fwmark ? nm_sprintf_buf(str_fwmark, " fwmark 0x%x", lnk->fwmark) : "");
+ return buf;
+}
+
+const char *
nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len)
{
char *b;
@@ -8186,6 +8289,56 @@ nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b)
}
void
+nm_platform_lnk_vti_hash_update(const NMPlatformLnkVti *obj, NMHashState *h)
+{
+ nm_hash_update_vals(h,
+ obj->local,
+ obj->remote,
+ obj->parent_ifindex,
+ obj->ikey,
+ obj->okey,
+ obj->fwmark);
+}
+
+int
+nm_platform_lnk_vti_cmp(const NMPlatformLnkVti *a, const NMPlatformLnkVti *b)
+{
+ NM_CMP_SELF(a, b);
+ NM_CMP_FIELD(a, b, parent_ifindex);
+ NM_CMP_FIELD(a, b, local);
+ NM_CMP_FIELD(a, b, remote);
+ NM_CMP_FIELD(a, b, ikey);
+ NM_CMP_FIELD(a, b, okey);
+ NM_CMP_FIELD(a, b, fwmark);
+ return 0;
+}
+
+void
+nm_platform_lnk_vti6_hash_update(const NMPlatformLnkVti6 *obj, NMHashState *h)
+{
+ nm_hash_update_vals(h,
+ obj->local,
+ obj->remote,
+ obj->parent_ifindex,
+ obj->ikey,
+ obj->okey,
+ obj->fwmark);
+}
+
+int
+nm_platform_lnk_vti6_cmp(const NMPlatformLnkVti6 *a, const NMPlatformLnkVti6 *b)
+{
+ NM_CMP_SELF(a, b);
+ NM_CMP_FIELD(a, b, parent_ifindex);
+ NM_CMP_FIELD_MEMCMP(a, b, local);
+ NM_CMP_FIELD_MEMCMP(a, b, remote);
+ NM_CMP_FIELD(a, b, ikey);
+ NM_CMP_FIELD(a, b, okey);
+ NM_CMP_FIELD(a, b, fwmark);
+ return 0;
+}
+
+void
nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h)
{
nm_hash_update_vals(h,
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index edde7ddb40..71ac6ccc3b 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -814,6 +814,25 @@ typedef struct {
} _nm_alignas(NMPlatformObject) NMPlatformLnkIpIp;
typedef struct {
+ int parent_ifindex;
+ in_addr_t local;
+ in_addr_t remote;
+ guint32 ikey;
+ guint32 okey;
+ guint32 fwmark;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkVti;
+
+typedef struct {
+ int parent_ifindex;
+ struct in6_addr local;
+ struct in6_addr remote;
+ guint32 ikey;
+ guint32 okey;
+ guint32 fwmark;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkVti6;
+
+typedef struct {
+ int parent_ifindex;
guint64 sci; /* host byte order */
guint64 cipher_suite;
guint32 window;
@@ -1678,6 +1697,24 @@ nm_platform_link_vrf_add(NMPlatform *self,
}
static inline int
+nm_platform_link_vti_add(NMPlatform *self,
+ const char *name,
+ const NMPlatformLnkVti *props,
+ const NMPlatformLink **out_link)
+{
+ return nm_platform_link_add(self, NM_LINK_TYPE_VTI, name, 0, NULL, 0, 0, props, out_link);
+}
+
+static inline int
+nm_platform_link_vti6_add(NMPlatform *self,
+ const char *name,
+ const NMPlatformLnkVti6 *props,
+ const NMPlatformLink **out_link)
+{
+ return nm_platform_link_add(self, NM_LINK_TYPE_VTI6, name, 0, NULL, 0, 0, props, out_link);
+}
+
+static inline int
nm_platform_link_vxlan_add(NMPlatform *self,
const char *name,
const NMPlatformLnkVxlan *props,
@@ -1966,6 +2003,10 @@ const NMPlatformLnkVlan *
nm_platform_link_get_lnk_vlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkVrf *
nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
+const NMPlatformLnkVti *
+nm_platform_link_get_lnk_vti(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
+const NMPlatformLnkVti6 *
+nm_platform_link_get_lnk_vti6(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkVxlan *
nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkWireGuard *
@@ -2254,6 +2295,8 @@ const char *nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf
const char *nm_platform_lnk_tun_to_string(const NMPlatformLnkTun *lnk, char *buf, gsize len);
const char *nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len);
const char *nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len);
+const char *nm_platform_lnk_vti_to_string(const NMPlatformLnkVti *lnk, char *buf, gsize len);
+const char *nm_platform_lnk_vti6_to_string(const NMPlatformLnkVti6 *lnk, char *buf, gsize len);
const char *nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize len);
const char *
nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf, gsize len);
@@ -2304,6 +2347,8 @@ int nm_platform_lnk_sit_cmp(const NMPlatformLnkSit *a, const NMPlatformLnkSit *b
int nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b);
int nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b);
int nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b);
+int nm_platform_lnk_vti_cmp(const NMPlatformLnkVti *a, const NMPlatformLnkVti *b);
+int nm_platform_lnk_vti6_cmp(const NMPlatformLnkVti6 *a, const NMPlatformLnkVti6 *b);
int nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b);
int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b);
@@ -2366,6 +2411,8 @@ void nm_platform_lnk_sit_hash_update(const NMPlatformLnkSit *obj, NMHashState *h
void nm_platform_lnk_tun_hash_update(const NMPlatformLnkTun *obj, NMHashState *h);
void nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h);
void nm_platform_lnk_vrf_hash_update(const NMPlatformLnkVrf *obj, NMHashState *h);
+void nm_platform_lnk_vti_hash_update(const NMPlatformLnkVti *obj, NMHashState *h);
+void nm_platform_lnk_vti6_hash_update(const NMPlatformLnkVti6 *obj, NMHashState *h);
void nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h);
void nm_platform_lnk_wireguard_hash_update(const NMPlatformLnkWireGuard *obj, NMHashState *h);
diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h
index 9c1c4290f3..80d254b261 100644
--- a/src/libnm-platform/nmp-base.h
+++ b/src/libnm-platform/nmp-base.h
@@ -161,6 +161,8 @@ typedef enum _nm_packed {
NMP_OBJECT_TYPE_LNK_TUN,
NMP_OBJECT_TYPE_LNK_VLAN,
NMP_OBJECT_TYPE_LNK_VRF,
+ NMP_OBJECT_TYPE_LNK_VTI,
+ NMP_OBJECT_TYPE_LNK_VTI6,
NMP_OBJECT_TYPE_LNK_VXLAN,
NMP_OBJECT_TYPE_LNK_WIREGUARD,
NMP_OBJECT_TYPE_LNK_BOND,
diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c
index c95dd6ed34..f978e0e581 100644
--- a/src/libnm-platform/nmp-object.c
+++ b/src/libnm-platform/nmp-object.c
@@ -3601,6 +3601,30 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vrf_hash_update,
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vrf_cmp,
},
+ [NMP_OBJECT_TYPE_LNK_VTI - 1] =
+ {
+ .parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
+ .obj_type = NMP_OBJECT_TYPE_LNK_VTI,
+ .sizeof_data = sizeof(NMPObjectLnkVti),
+ .sizeof_public = sizeof(NMPlatformLnkVti),
+ .obj_type_name = "vti",
+ .lnk_link_type = NM_LINK_TYPE_VTI,
+ .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vti_to_string,
+ .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vti_hash_update,
+ .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vti_cmp,
+ },
+ [NMP_OBJECT_TYPE_LNK_VTI6 - 1] =
+ {
+ .parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
+ .obj_type = NMP_OBJECT_TYPE_LNK_VTI6,
+ .sizeof_data = sizeof(NMPObjectLnkVti6),
+ .sizeof_public = sizeof(NMPlatformLnkVti6),
+ .obj_type_name = "vti6",
+ .lnk_link_type = NM_LINK_TYPE_VTI6,
+ .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vti6_to_string,
+ .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vti6_hash_update,
+ .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vti6_cmp,
+ },
[NMP_OBJECT_TYPE_LNK_VXLAN - 1] =
{
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h
index 4901ebcba3..dcf431255e 100644
--- a/src/libnm-platform/nmp-object.h
+++ b/src/libnm-platform/nmp-object.h
@@ -297,6 +297,14 @@ typedef struct {
} NMPObjectLnkVrf;
typedef struct {
+ NMPlatformLnkVti _public;
+} NMPObjectLnkVti;
+
+typedef struct {
+ NMPlatformLnkVti6 _public;
+} NMPObjectLnkVti6;
+
+typedef struct {
NMPlatformLnkVxlan _public;
} NMPObjectLnkVxlan;
@@ -396,6 +404,12 @@ struct _NMPObject {
NMPlatformLnkVrf lnk_vrf;
NMPObjectLnkVrf _lnk_vrf;
+ NMPlatformLnkVti lnk_vti;
+ NMPObjectLnkVti _lnk_vti;
+
+ NMPlatformLnkVti6 lnk_vti6;
+ NMPObjectLnkVti6 _lnk_vti6;
+
NMPlatformLnkVxlan lnk_vxlan;
NMPObjectLnkVxlan _lnk_vxlan;
@@ -528,6 +542,8 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type)
case NMP_OBJECT_TYPE_LNK_TUN:
case NMP_OBJECT_TYPE_LNK_VLAN:
case NMP_OBJECT_TYPE_LNK_VRF:
+ case NMP_OBJECT_TYPE_LNK_VTI:
+ case NMP_OBJECT_TYPE_LNK_VTI6:
case NMP_OBJECT_TYPE_LNK_VXLAN:
case NMP_OBJECT_TYPE_LNK_WIREGUARD:
diff --git a/src/libnm-platform/tests/test-nm-platform.c b/src/libnm-platform/tests/test-nm-platform.c
index 31c16e4def..90a2904599 100644
--- a/src/libnm-platform/tests/test-nm-platform.c
+++ b/src/libnm-platform/tests/test-nm-platform.c
@@ -29,6 +29,8 @@ G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkSit));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkTun));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVlan));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVrf));
+G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVti));
+G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVti6));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVxlan));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkWireGuard));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectQdisc));
@@ -57,6 +59,8 @@ G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkSit));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkTun));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVlan));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVrf));
+G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVti));
+G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVti6));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVxlan));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkWireGuard));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformObjWithIfindex));
diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c
index d5a92d9821..7173ea85d1 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.c
+++ b/src/libnmc-setting/nm-meta-setting-desc.c
@@ -6592,6 +6592,12 @@ static const NMMetaPropertyInfo *const property_infos_IP_TUNNEL[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_FLOW_LABEL,
.property_type = &_pt_gobject_int,
),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_FWMARK,
+ .property_type = &_pt_gobject_int,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+ .base = 16,
+ ),
+ ),
PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_MTU,
.property_type = &_pt_gobject_mtu,
),
diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in
index 6905a1793d..bfc5793680 100644
--- a/src/libnmc-setting/settings-docs.h.in
+++ b/src/libnmc-setting/settings-docs.h.in
@@ -215,6 +215,7 @@
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT N_("How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels.")
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FLAGS N_("Tunnel flags. Currently, the following values are supported: NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT (0x1), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS (0x2), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL (0x4), NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV (0x8), NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY (0x10), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK (0x20). They are valid only for IPv6 tunnels.")
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FLOW_LABEL N_("The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels.")
+#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FWMARK N_("The fwmark value to assign to tunnel packets. This property can be set to a non zero value only on VTI and VTI6 tunnels.")
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_INPUT_KEY N_("The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used.")
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_LOCAL N_("The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address.")
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MODE N_("The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2).")
diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
index 8b1ee61ebf..edbd6ed6c3 100644
--- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
+++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
@@ -643,6 +643,8 @@
description="How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels." />
<property name="flow-label"
description="The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels." />
+ <property name="fwmark"
+ description="The fwmark value to assign to tunnel packets. This property can be set to a non zero value only on VTI and VTI6 tunnels." />
<property name="mtu"
description="If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments." />
<property name="flags"