diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index dd0535a06ee..4279326bc27 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1235,6 +1235,14 @@
+
+ TTLPropagate=
+
+ Takes a boolean. When true enables TTL propagation at Label Switched Path (LSP) egress.
+ When unset, the kernel's default will be used.
+
+
+
MTUBytes=
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 9147003b3be..0a6bd330025 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -622,23 +622,23 @@ static const NLType rtnl_route_types[] = {
[RTA_PRIORITY] = { .type = NETLINK_TYPE_U32 },
[RTA_PREFSRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
[RTA_METRICS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_metrics_type_system},
-/* [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
-*/
+ [RTA_MULTIPATH] = { .size = sizeof(struct rtnexthop) },
[RTA_FLOW] = { .type = NETLINK_TYPE_U32 }, /* 6? */
-/*
- RTA_CACHEINFO,
- RTA_TABLE,
- RTA_MARK,
- RTA_MFC_STATS,
- RTA_VIA,
- RTA_NEWDST,
-*/
+ [RTA_CACHEINFO] = { .size = sizeof(struct rta_cacheinfo) },
+ [RTA_TABLE] = { .type = NETLINK_TYPE_U32 },
+ [RTA_MARK] = { .type = NETLINK_TYPE_U32 },
+ [RTA_MFC_STATS] = { .type = NETLINK_TYPE_U64 },
+ [RTA_VIA] = { .type = NETLINK_TYPE_U32 },
+ [RTA_NEWDST] = { .type = NETLINK_TYPE_U32 },
[RTA_PREF] = { .type = NETLINK_TYPE_U8 },
-/*
- RTA_ENCAP_TYPE,
- RTA_ENCAP,
- */
[RTA_EXPIRES] = { .type = NETLINK_TYPE_U32 },
+ [RTA_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
+ [RTA_ENCAP] = { .type = NETLINK_TYPE_NESTED }, /* Multiple type systems i.e. LWTUNNEL_ENCAP_MPLS/LWTUNNEL_ENCAP_IP/LWTUNNEL_ENCAP_ILA etc... */
+ [RTA_UID] = { .type = NETLINK_TYPE_U32 },
+ [RTA_TTL_PROPAGATE] = { .type = NETLINK_TYPE_U8 },
+ [RTA_IP_PROTO] = { .type = NETLINK_TYPE_U8 },
+ [RTA_SPORT] = { .type = NETLINK_TYPE_U16 },
+ [RTA_DPORT] = { .type = NETLINK_TYPE_U16 },
};
static const NLTypeSystem rtnl_route_type_system = {
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 825fbf603a0..4d2f41fe400 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -130,6 +130,7 @@ Route.InitialCongestionWindow, config_parse_tcp_window,
Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window, 0, 0
Route.QuickAck, config_parse_quickack, 0, 0
Route.FastOpenNoCookie, config_parse_fast_open_no_cookie, 0, 0
+Route.TTLPropagate, config_parse_route_ttl_propagate, 0, 0
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index 24ef5b46bc7..b21e7dfd86f 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -61,6 +61,7 @@ int route_new(Route **ret) {
.quickack = -1,
.fast_open_no_cookie = -1,
.gateway_onlink = -1,
+ .ttl_propagate = -1,
};
*ret = TAKE_PTR(route);
@@ -617,6 +618,12 @@ int route_configure(
return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
}
+ if (route->ttl_propagate >= 0) {
+ r = sd_netlink_message_append_u8(req, RTA_TTL_PROPAGATE, route->ttl_propagate);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append RTA_TTL_PROPAGATE attribute: %m");
+ }
+
r = sd_netlink_message_open_container(req, RTA_METRICS);
if (r < 0)
return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
@@ -1300,6 +1307,45 @@ int config_parse_route_mtu(
return 0;
}
+int config_parse_route_ttl_propagate(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+ int r, k;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = route_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ k = parse_boolean(rvalue);
+ if (k < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, k,
+ "Failed to parse TTLPropagate= value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ n->ttl_propagate = k;
+
+ TAKE_PTR(n);
+ return 0;
+}
+
int route_section_verify(Route *route, Network *network) {
if (section_is_invalid(route->section))
return -EINVAL;
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index 4643f778547..3c0ac896aea 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -18,6 +18,7 @@ struct Route {
int family;
int quickack;
int fast_open_no_cookie;
+ int ttl_propagate;
unsigned char dst_prefixlen;
unsigned char src_prefixlen;
@@ -77,4 +78,5 @@ CONFIG_PARSER_PROTOTYPE(config_parse_route_type);
CONFIG_PARSER_PROTOTYPE(config_parse_tcp_window);
CONFIG_PARSER_PROTOTYPE(config_parse_quickack);
CONFIG_PARSER_PROTOTYPE(config_parse_fast_open_no_cookie);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_ttl_propagate);
CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu);
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index 5861c41e757..c905be0bcca 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -83,6 +83,7 @@ QuickAck=
FastOpenNoCookie=
Source=
Metric=
+TTLPropagate=
[Network]
IPv6DuplicateAddressDetection=
IPMasquerade=