diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2022-12-21 16:03:23 +0100 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2022-12-21 16:03:23 +0100 |
commit | f2974b1c61ce57b60aaef584b0c62f7c19a64d4f (patch) | |
tree | a6edac7fbe22edfb0709d0159c437a8ca653aa10 | |
parent | aec7ae8279cc9b3de7e75140b75fe997f43700fc (diff) | |
parent | c0b0f823ca6f041f4131f86ed83df75d40103712 (diff) | |
download | NetworkManager-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
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" |