From 396dfe0a14afcf42ddbcddb938605f11cda4372d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 2 Feb 2022 08:23:59 +0900 Subject: [PATCH 1/6] network: tunnel: use "data" field to assign result --- src/network/netdev/tunnel.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index 389403f42c6..fe8bbea577a 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -809,15 +809,12 @@ int config_parse_tunnel_key( void *data, void *userdata) { + uint32_t *dest = ASSERT_PTR(data), k; union in_addr_union buffer; - Tunnel *t = userdata; - uint32_t k; int r; assert(filename); - assert(lvalue); assert(rvalue); - assert(data); r = in_addr_from_string(AF_INET, rvalue, &buffer); if (r < 0) { @@ -830,13 +827,7 @@ int config_parse_tunnel_key( } else k = be32toh(buffer.in.s_addr); - if (streq(lvalue, "Key")) - t->key = k; - else if (streq(lvalue, "InputKey")) - t->ikey = k; - else - t->okey = k; - + *dest = k; return 0; } From 59c8bef0089237a2c74f1e4fb3de1934138142d6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 2 Feb 2022 08:25:10 +0900 Subject: [PATCH 2/6] network: tunnel: reduce indentation in config_parse_ipv6_flowlabel() --- src/network/netdev/netdev-gperf.gperf | 2 +- src/network/netdev/tunnel.c | 37 ++++++++++++++------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 9cb5db93663..d03a4e57846 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -77,7 +77,7 @@ Tunnel.InputKey, config_parse_tunnel_key, Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) -Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) +Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, 0 Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent) diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index fe8bbea577a..8eb633549f9 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -843,32 +843,33 @@ int config_parse_ipv6_flowlabel( void *data, void *userdata) { - IPv6FlowLabel *ipv6_flowlabel = data; - Tunnel *t = userdata; - int k = 0; - int r; + Tunnel *t = ASSERT_PTR(userdata); + int k, r; assert(filename); - assert(lvalue); assert(rvalue); - assert(ipv6_flowlabel); if (streq(rvalue, "inherit")) { - *ipv6_flowlabel = IP6_FLOWINFO_FLOWLABEL; + t->ipv6_flowlabel = IP6_FLOWINFO_FLOWLABEL; t->flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL; - } else { - r = config_parse_int(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &k, userdata); - if (r < 0) - return r; - - if (k > 0xFFFFF) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue); - else { - *ipv6_flowlabel = htobe32(k) & IP6_FLOWINFO_FLOWLABEL; - t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL; - } + return 0; } + r = safe_atoi(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse tunnel IPv6 flowlabel, ignoring assignment: %s", rvalue); + return 0; + } + + if (k > 0xFFFFF) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid tunnel IPv6 flowlabel, ignoring assignment: %s", rvalue); + return 0; + } + + t->ipv6_flowlabel = htobe32(k) & IP6_FLOWINFO_FLOWLABEL; + t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL; return 0; } From a07e07cd9c781a078b89a987cdced499507b0f72 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 2 Feb 2022 08:25:38 +0900 Subject: [PATCH 3/6] network: tunnel: reduce indentation in config_parse_encap_limit() --- src/network/netdev/netdev-gperf.gperf | 2 +- src/network/netdev/tunnel.c | 38 ++++++++++++++------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index d03a4e57846..a993e258679 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -79,7 +79,7 @@ Tunnel.DiscoverPathMTU, config_parse_bool, Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, 0 Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) -Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) +Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, 0 Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent) Tunnel.AssignToLoopback, config_parse_bool, 0, offsetof(Tunnel, assign_to_loopback) Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote) diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index 8eb633549f9..a70121efdac 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -885,31 +885,33 @@ int config_parse_encap_limit( void *data, void *userdata) { - Tunnel *t = userdata; - int k = 0; - int r; + Tunnel *t = ASSERT_PTR(userdata); + int k, r; assert(filename); - assert(lvalue); assert(rvalue); - if (streq(rvalue, "none")) + if (streq(rvalue, "none")) { t->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; - else { - r = safe_atoi(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue); - return 0; - } - - if (k > 255 || k < 0) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid Tunnel Encapsulation value, ignoring: %d", k); - else { - t->encap_limit = k; - t->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; - } + t->encap_limit = 0; + return 0; } + r = safe_atoi(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse Tunnel Encapsulation Limit option, ignoring assignment: %s", rvalue); + return 0; + } + + if (k > 255 || k < 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid Tunnel Encapsulation value, ignoring assignment: %d", k); + return 0; + } + + t->encap_limit = k; + t->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; return 0; } From acd8abb7f9f21316fe78b637be552f4764bd85c2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 2 Feb 2022 08:54:52 +0900 Subject: [PATCH 4/6] network: tunnel: reorder setting ip6tnl attributes Just for improving readability. --- src/network/netdev/tunnel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index a70121efdac..336ff3c8294 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -587,16 +587,16 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl if (t->allow_localremote >= 0) SET_FLAG(t->flags, IP6_TNL_F_ALLOW_LOCAL_REMOTE, t->allow_localremote); + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLAGS, t->flags); + if (r < 0) + return r; + if (t->encap_limit != 0) { r = sd_netlink_message_append_u8(m, IFLA_IPTUN_ENCAP_LIMIT, t->encap_limit); if (r < 0) return r; } - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLAGS, t->flags); - if (r < 0) - return r; - switch (t->ip6tnl_mode) { case NETDEV_IP6_TNL_MODE_IP6IP6: proto = IPPROTO_IPV6; From 1ae308abb50191e9111172bf1f1a5df34bbd8f4e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 2 Feb 2022 08:56:14 +0900 Subject: [PATCH 5/6] network: tunnel: support external mode Closes #22352. --- man/systemd.netdev.xml | 9 ++ src/network/netdev/netdev-gperf.gperf | 1 + src/network/netdev/tunnel.c | 92 ++++++++++++++----- src/network/netdev/tunnel.h | 1 + .../fuzz/fuzz-netdev-parser/directives.netdev | 1 + 5 files changed, 82 insertions(+), 22 deletions(-) diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index ff0bdee51fb..0ad0864da0f 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -1149,6 +1149,15 @@ the following keys: + + External= + + Takes a boolean value. When true, then the tunnel is externally controlled, which is + also known as collect metadata mode, and most settings below like Local= + or Remote= are ignored. This implies Independent=. + Defaults to false. + + Local= diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index a993e258679..eadafbddb84 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -91,6 +91,7 @@ Tunnel.IPv6RapidDeploymentPrefix, config_parse_6rd_prefix, Tunnel.ERSPANIndex, config_parse_uint32, 0, offsetof(Tunnel, erspan_index) Tunnel.SerializeTunneledPackets, config_parse_tristate, 0, offsetof(Tunnel, gre_erspan_sequence) Tunnel.ISATAP, config_parse_tristate, 0, offsetof(Tunnel, isatap) +Tunnel.External, config_parse_bool, 0, offsetof(Tunnel, external) FooOverUDP.Protocol, config_parse_ip_protocol, 0, offsetof(FouTunnel, fou_protocol) FooOverUDP.Encapsulation, config_parse_fou_encap_type, 0, offsetof(FouTunnel, fou_encap_type) FooOverUDP.Port, config_parse_ip_port, 0, offsetof(FouTunnel, port) diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index 336ff3c8294..bb6e4f75869 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -213,6 +213,15 @@ static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_ne assert(t); + if (t->external) { + r = sd_netlink_message_append_flag(m, IFLA_IPTUN_COLLECT_METADATA); + if (r < 0) + return r; + + /* If external mode is enabled, then the following settings should not be appended. */ + return 0; + } + if (link || t->assign_to_loopback) { r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); if (r < 0) @@ -309,6 +318,15 @@ static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_ assert(t); + if (t->external) { + r = sd_netlink_message_append_flag(m, IFLA_GRE_COLLECT_METADATA); + if (r < 0) + return r; + + /* If external mode is enabled, then the following settings should not be appended. */ + return 0; + } + if (link || t->assign_to_loopback) { r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); if (r < 0) @@ -421,6 +439,15 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl assert(t); + if (t->external) { + r = sd_netlink_message_append_flag(m, IFLA_GRE_COLLECT_METADATA); + if (r < 0) + return r; + + /* If external mode is enabled, then the following settings should not be appended. */ + return 0; + } + if (link || t->assign_to_loopback) { r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); if (r < 0) @@ -553,6 +580,32 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl assert(t); + switch (t->ip6tnl_mode) { + case NETDEV_IP6_TNL_MODE_IP6IP6: + proto = IPPROTO_IPV6; + break; + case NETDEV_IP6_TNL_MODE_IPIP6: + proto = IPPROTO_IPIP; + break; + case NETDEV_IP6_TNL_MODE_ANYIP6: + default: + proto = 0; + break; + } + + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PROTO, proto); + if (r < 0) + return r; + + if (t->external) { + r = sd_netlink_message_append_flag(m, IFLA_IPTUN_COLLECT_METADATA); + if (r < 0) + return r; + + /* If external mode is enabled, then the following settings should not be appended. */ + return 0; + } + if (link || t->assign_to_loopback) { r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); if (r < 0) @@ -597,23 +650,6 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl return r; } - switch (t->ip6tnl_mode) { - case NETDEV_IP6_TNL_MODE_IP6IP6: - proto = IPPROTO_IPV6; - break; - case NETDEV_IP6_TNL_MODE_IPIP6: - proto = IPPROTO_IPIP; - break; - case NETDEV_IP6_TNL_MODE_ANYIP6: - default: - proto = 0; - break; - } - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PROTO, proto); - if (r < 0) - return r; - return 0; } @@ -640,6 +676,23 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { assert(t); + if (netdev->kind == NETDEV_KIND_IP6TNL && + t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "ip6tnl without mode configured in %s. Ignoring", filename); + + if (t->external) { + if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_VTI6)) + log_netdev_debug(netdev, "vti/vti6 tunnel do not support external mode, ignoring."); + else { + /* tunnel with external mode does not require underlying interface. */ + t->independent = true; + + /* tunnel with external mode does not require any settings checked below. */ + return 0; + } + } + if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE) && !IN_SET(t->family, AF_UNSPEC, AF_INET)) return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), @@ -660,11 +713,6 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "ip6gretap tunnel without a remote IPv6 address configured in %s. Ignoring", filename); - if (netdev->kind == NETDEV_KIND_IP6TNL && - t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) - return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), - "ip6tnl without mode configured in %s. Ignoring", filename); - if (t->fou_tunnel && t->fou_destination_port <= 0) return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "FooOverUDP missing port configured in %s. Ignoring", filename); diff --git a/src/network/netdev/tunnel.h b/src/network/netdev/tunnel.h index 4d42ff9a154..e25dfb215a0 100644 --- a/src/network/netdev/tunnel.h +++ b/src/network/netdev/tunnel.h @@ -55,6 +55,7 @@ typedef struct Tunnel { bool independent; bool fou_tunnel; bool assign_to_loopback; + bool external; /* a.k.a collect metadata mode */ uint16_t encap_src_port; uint16_t fou_destination_port; diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev index f5fa2418fee..584c1c21361 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -95,6 +95,7 @@ IPv6RapidDeploymentPrefix= ERSPANIndex= SerializeTunneledPackets= ISATAP= +External= [VXLAN] UDP6ZeroChecksumRx= ARPProxy= From 7809cab71738aa582ac30e7dbc8d1e76c303ff9e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 3 Feb 2022 11:11:37 +0900 Subject: [PATCH 6/6] test-network: add testcase for external tunnel --- test/test-network/conf/25-ip6tnl-external.netdev | 8 ++++++++ .../conf/netdev-link-local-addressing-yes.network | 1 + test/test-network/systemd-networkd-tests.py | 13 ++++++++++--- 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 test/test-network/conf/25-ip6tnl-external.netdev diff --git a/test/test-network/conf/25-ip6tnl-external.netdev b/test/test-network/conf/25-ip6tnl-external.netdev new file mode 100644 index 00000000000..68926cdd1e2 --- /dev/null +++ b/test/test-network/conf/25-ip6tnl-external.netdev @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=ip6tnl-external +Kind=ip6tnl + +[Tunnel] +Mode=ip6ip6 +External=yes diff --git a/test/test-network/conf/netdev-link-local-addressing-yes.network b/test/test-network/conf/netdev-link-local-addressing-yes.network index ea1811bbfdd..8ec0190bcb8 100644 --- a/test/test-network/conf/netdev-link-local-addressing-yes.network +++ b/test/test-network/conf/netdev-link-local-addressing-yes.network @@ -2,6 +2,7 @@ [Match] Name=bareudp99 Name=batadv99 +Name=ip6tnl-external Name=ipvlan99 Name=ipvtap99 Name=macvlan99 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 7013f73851c..ff6d18cd20e 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -916,6 +916,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'ip6gretun97', 'ip6gretun98', 'ip6gretun99', + 'ip6tnl-external', 'ip6tnl-slaac', 'ip6tnl97', 'ip6tnl98', @@ -1002,7 +1003,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ip6gre-tunnel-local-any.netdev', '25-ip6gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel.netdev', - '25-ip6tnl-tunnel-any-any.netdev', + '25-ip6tnl-tunnel-external.netdev', '25-ip6tnl-tunnel-local-any.netdev', '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network', @@ -1691,9 +1692,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network', '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', '25-veth.netdev', 'ip6tnl-slaac.network', 'ipv6-prefix.network', - '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network') + '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network', + '25-ip6tnl-external.netdev', 'netdev-link-local-addressing-yes.network') start_networkd() - self.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', 'ip6tnl-slaac:degraded', + self.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', + 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded', 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded']) output = check_output('ip -d link show ip6tnl99') @@ -1705,6 +1708,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -d link show ip6tnl97') print(output) self.assertRegex(output, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98') + output = check_output('ip -d link show ip6tnl-external') + print(output) + self.assertIn('ip6tnl-external@NONE:', output) + self.assertIn('ip6tnl external ', output) output = check_output('ip -d link show ip6tnl-slaac') print(output) self.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)