diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 97436f016a..65812aca51 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -1654,12 +1654,61 @@ Ports=eth2 Mode= - An ip6tnl tunnel can be in one of three - modes - ip6ip6 for IPv6 over IPv6, - ipip6 for IPv4 over IPv6 or - any for either. - + Specifies the tunnel mode. Acceptable values depend on the tunnel kind. + + + Supported tunnel modes + + + + + + + + Kind + Mode + Description + + + + + ipip + ipip + IPv4 over IPv4 (default) + + + any + both IPv4 and IPv6 over IPv4 + + + sit + ipip + IPv4 over IPv4 + + + ip6ip + IPv6 over IPv4 (default) + + + any + both IPv4 and IPv6 over IPv4 + + + ip6tnl + ipip6 + IPv4 over IPv6 + + + ip6ip6 + IPv6 over IPv6 + + + any + both IPv4 and IPv6 over IPv6 (default) + + + +
diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 379364df63..230fa355b9 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -84,7 +84,7 @@ Tunnel.InputKey, config_parse_tunnel_key, Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) Tunnel.DiscoverPathMTU, config_parse_tristate, 0, offsetof(Tunnel, pmtudisc) Tunnel.IgnoreDontFragment, config_parse_bool, 0, offsetof(Tunnel, ignore_df) -Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) +Tunnel.Mode, config_parse_tunnel_mode, 0, offsetof(Tunnel, 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, 0 diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index 26a97271f0..29f353d849 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -23,16 +23,26 @@ #define IP6_FLOWINFO_FLOWLABEL htobe32(0x000FFFFF) #define IP6_TNL_F_ALLOW_LOCAL_REMOTE 0x40 -static const char* const ip6tnl_mode_table[_NETDEV_IP6_TNL_MODE_MAX] = { - [NETDEV_IP6_TNL_MODE_IP6IP6] = "ip6ip6", - [NETDEV_IP6_TNL_MODE_IPIP6] = "ipip6", - [NETDEV_IP6_TNL_MODE_ANYIP6] = "any", +#define HASH_KEY SD_ID128_MAKE(74,c4,de,12,f3,d9,41,34,bb,3d,c1,a4,42,93,50,87) + +static const uint8_t tunnel_mode_to_proto[_TUNNEL_MODE_MAX] = { + [TUNNEL_MODE_ANY] = 0, + [TUNNEL_MODE_IPIP] = IPPROTO_IPIP, + [TUNNEL_MODE_IP6IP] = IPPROTO_IPV6, + [TUNNEL_MODE_IPIP6] = IPPROTO_IPIP, + [TUNNEL_MODE_IP6IP6] = IPPROTO_IPV6, }; -DEFINE_STRING_TABLE_LOOKUP(ip6tnl_mode, Ip6TnlMode); -DEFINE_CONFIG_PARSE_ENUM(config_parse_ip6tnl_mode, ip6tnl_mode, Ip6TnlMode); +static const char* const tunnel_mode_table[_TUNNEL_MODE_MAX] = { + [TUNNEL_MODE_ANY] = "any", + [TUNNEL_MODE_IPIP] = "ipip", + [TUNNEL_MODE_IP6IP] = "ip6ip", + [TUNNEL_MODE_IPIP6] = "ipip6", + [TUNNEL_MODE_IP6IP6] = "ip6ip6", +}; -#define HASH_KEY SD_ID128_MAKE(74,c4,de,12,f3,d9,41,34,bb,3d,c1,a4,42,93,50,87) +DEFINE_STRING_TABLE_LOOKUP(tunnel_mode, TunnelMode); +DEFINE_CONFIG_PARSE_ENUM(config_parse_tunnel_mode, tunnel_mode, TunnelMode); static int dhcp4_pd_create_6rd_tunnel_name(Link *link) { _cleanup_free_ char *ifname_alloc = NULL; @@ -195,6 +205,12 @@ static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_ne Tunnel *t = ASSERT_PTR(netdev)->kind == NETDEV_KIND_IPIP ? IPIP(netdev) : SIT(netdev); int r; + if (t->mode >= 0) { + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PROTO, tunnel_mode_to_proto[t->mode]); + if (r < 0) + return r; + } + if (t->external) { r = sd_netlink_message_append_flag(m, IFLA_IPTUN_COLLECT_METADATA); if (r < 0) @@ -560,26 +576,15 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl assert(m); union in_addr_union local; - uint8_t proto; Tunnel *t = IP6TNL(netdev); int 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; + if (t->mode >= 0) { + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PROTO, tunnel_mode_to_proto[t->mode]); + if (r < 0) + return r; } - 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) @@ -653,11 +658,6 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { Tunnel *t = ASSERT_PTR(TUNNEL(netdev)); - 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."); @@ -704,6 +704,34 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { "IgnoreDontFragment= cannot be enabled when DiscoverPathMTU= is enabled"); if (t->pmtudisc < 0) t->pmtudisc = !t->ignore_df; + + if (t->mode >= 0) + switch (netdev->kind) { + case NETDEV_KIND_IPIP: + if (!IN_SET(t->mode, TUNNEL_MODE_ANY, TUNNEL_MODE_IPIP)) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "Specified unsupported tunnel mode %s, ignoring.", + tunnel_mode_to_string(t->mode)); + break; + case NETDEV_KIND_SIT: + if (!IN_SET(t->mode, TUNNEL_MODE_ANY, TUNNEL_MODE_IPIP, TUNNEL_MODE_IP6IP)) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "Specified unsupported tunnel mode %s, ignoring.", + tunnel_mode_to_string(t->mode)); + break; + case NETDEV_KIND_IP6TNL: + if (!IN_SET(t->mode, TUNNEL_MODE_ANY, TUNNEL_MODE_IPIP6, TUNNEL_MODE_IP6IP6)) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "Specified unsupported tunnel mode %s, ignoring.", + tunnel_mode_to_string(t->mode)); + break; + default: + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s tunnel does not support Mode= setting, ignoring: %s", + netdev_kind_to_string(netdev->kind), + tunnel_mode_to_string(t->mode)); + } + return 0; } @@ -1109,7 +1137,7 @@ static void netdev_tunnel_init(NetDev *netdev) { t->isatap = -1; t->gre_erspan_sequence = -1; t->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; - t->ip6tnl_mode = _NETDEV_IP6_TNL_MODE_INVALID; + t->mode = _TUNNEL_MODE_INVALID; t->ipv6_flowlabel = _NETDEV_IPV6_FLOWLABEL_INVALID; t->allow_localremote = -1; t->erspan_version = 1; diff --git a/src/network/netdev/tunnel.h b/src/network/netdev/tunnel.h index 26aab55213..0f3af1efde 100644 --- a/src/network/netdev/tunnel.h +++ b/src/network/netdev/tunnel.h @@ -6,13 +6,16 @@ #include "netdev.h" #include "networkd-link.h" -typedef enum Ip6TnlMode { - NETDEV_IP6_TNL_MODE_IP6IP6, - NETDEV_IP6_TNL_MODE_IPIP6, - NETDEV_IP6_TNL_MODE_ANYIP6, - _NETDEV_IP6_TNL_MODE_MAX, - _NETDEV_IP6_TNL_MODE_INVALID = -EINVAL, -} Ip6TnlMode; +/* For IFLA_IPTUN_PROTO attribute */ +typedef enum TunnelMode { + TUNNEL_MODE_ANY, /* 0, "any" */ + TUNNEL_MODE_IPIP, /* IPPROTO_IPIP, "ipip", for ipip and sit */ + TUNNEL_MODE_IP6IP, /* IPPROTO_IPV6, "ip6ip", for sit */ + TUNNEL_MODE_IPIP6, /* IPPROTO_IPIP, "ipip6", for ip6tnl */ + TUNNEL_MODE_IP6IP6, /* IPPROTO_IPV6, "ip6ip6", for ip6tnl */ + _TUNNEL_MODE_MAX, + _TUNNEL_MODE_INVALID = -EINVAL, +} TunnelMode; typedef enum IPv6FlowLabel { NETDEV_IPV6_FLOWLABEL_INHERIT = 0xFFFFF + 1, @@ -48,7 +51,7 @@ typedef struct Tunnel { union in_addr_union local; union in_addr_union remote; - Ip6TnlMode ip6tnl_mode; + TunnelMode mode; FooOverUDPEncapType fou_encap_type; int pmtudisc; @@ -119,10 +122,10 @@ extern const NetDevVTable ip6gretap_vtable; extern const NetDevVTable ip6tnl_vtable; extern const NetDevVTable erspan_vtable; -const char* ip6tnl_mode_to_string(Ip6TnlMode d) _const_; -Ip6TnlMode ip6tnl_mode_from_string(const char *d) _pure_; +const char* tunnel_mode_to_string(TunnelMode d) _const_; +TunnelMode tunnel_mode_from_string(const char *d) _pure_; -CONFIG_PARSER_PROTOTYPE(config_parse_ip6tnl_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_mode); CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_local_address); CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_remote_address); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_flowlabel); diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c index d29331b9f2..8018c34b5a 100644 --- a/src/network/test-network-tables.c +++ b/src/network/test-network-tables.c @@ -34,7 +34,7 @@ int main(int argc, char **argv) { test_table_sparse(DHCP6MessageType, dhcp6_message_type, DHCP6_MESSAGE_TYPE); /* enum starts from 1 */ test_table(UseDomains, use_domains, USE_DOMAINS); test_table(Duplex, duplex, DUP); - test_table(Ip6TnlMode, ip6tnl_mode, NETDEV_IP6_TNL_MODE); + test_table(TunnelMode, tunnel_mode, TUNNEL_MODE); test_table(IPv6PrivacyExtensions, ipv6_privacy_extensions, IPV6_PRIVACY_EXTENSIONS); test_table(IPVlanFlags, ipvlan_flags, NETDEV_IPVLAN_FLAGS); test_table(LinkOperationalState, link_operstate, LINK_OPERSTATE); diff --git a/test/test-network/conf/25-ip6tnl-tunnel-external.netdev b/test/test-network/conf/25-ip6tnl-tunnel-external.netdev index 68926cdd1e..73f44c8c47 100644 --- a/test/test-network/conf/25-ip6tnl-tunnel-external.netdev +++ b/test/test-network/conf/25-ip6tnl-tunnel-external.netdev @@ -4,5 +4,4 @@ Name=ip6tnl-external Kind=ip6tnl [Tunnel] -Mode=ip6ip6 External=yes diff --git a/test/test-network/conf/25-ip6tnl-tunnel-local-any.netdev b/test/test-network/conf/25-ip6tnl-tunnel-local-any.netdev index 3521d33104..7d3d5fe815 100644 --- a/test/test-network/conf/25-ip6tnl-tunnel-local-any.netdev +++ b/test/test-network/conf/25-ip6tnl-tunnel-local-any.netdev @@ -4,6 +4,5 @@ Name=ip6tnl98 Kind=ip6tnl [Tunnel] -Mode=ip6ip6 Local=any Remote=2001:473:fece:cafe::5179 diff --git a/test/test-network/conf/25-ip6tnl-tunnel-local-slaac.netdev b/test/test-network/conf/25-ip6tnl-tunnel-local-slaac.netdev index b07dc4a130..ebab11f472 100644 --- a/test/test-network/conf/25-ip6tnl-tunnel-local-slaac.netdev +++ b/test/test-network/conf/25-ip6tnl-tunnel-local-slaac.netdev @@ -4,6 +4,5 @@ Name=ip6tnl-slaac Kind=ip6tnl [Tunnel] -Mode=ip6ip6 Local=slaac Remote=2001:473:fece:cafe::5179 diff --git a/test/test-network/conf/25-ip6tnl-tunnel-remote-any.netdev b/test/test-network/conf/25-ip6tnl-tunnel-remote-any.netdev index df22dd829b..1378a92fdc 100644 --- a/test/test-network/conf/25-ip6tnl-tunnel-remote-any.netdev +++ b/test/test-network/conf/25-ip6tnl-tunnel-remote-any.netdev @@ -4,6 +4,5 @@ Name=ip6tnl97 Kind=ip6tnl [Tunnel] -Mode=ip6ip6 Local=2a00:ffde:4567:edde::4987 Remote=any diff --git a/test/test-network/conf/25-ip6tnl-tunnel.netdev b/test/test-network/conf/25-ip6tnl-tunnel.netdev index 486bfbd142..3e776b7679 100644 --- a/test/test-network/conf/25-ip6tnl-tunnel.netdev +++ b/test/test-network/conf/25-ip6tnl-tunnel.netdev @@ -4,6 +4,5 @@ Name=ip6tnl99 Kind=ip6tnl [Tunnel] -Mode=ip6ip6 Local=2a00:ffde:4567:edde::4987 Remote=2001:473:fece:cafe::5179 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 4a41885902..99a91bed76 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -2460,27 +2460,38 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): networkctl_reload() self.wait_online('geneve99:degraded') - def test_ipip_tunnel(self): + def _test_ipip_tunnel(self, mode): copy_network_unit('12-dummy.netdev', '25-ipip.network', '25-ipip-tunnel.netdev', '25-tunnel.network', '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network', '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network') + + if mode: + for netdev in ['25-ipip-tunnel.netdev', + '25-ipip-tunnel-local-any.netdev', + '25-ipip-tunnel-remote-any.netdev', + '25-ipip-tunnel-any-any.netdev']: + with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f: + f.write(f'[Tunnel]\nMode={mode}\n') + else: + mode = 'ipip' # kernel default + start_networkd() self.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded') output = check_output('ip -d link show ipiptun99') print(output) - self.assertRegex(output, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98') + self.assertIn(f'ipip {mode} remote 192.169.224.239 local 192.168.223.238 dev dummy98', output) output = check_output('ip -d link show ipiptun98') print(output) - self.assertRegex(output, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98') + self.assertIn(f'ipip {mode} remote 192.169.224.239 local any dev dummy98', output) output = check_output('ip -d link show ipiptun97') print(output) - self.assertRegex(output, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98') + self.assertIn(f'ipip {mode} remote any local 192.168.223.238 dev dummy98', output) output = check_output('ip -d link show ipiptun96') print(output) - self.assertRegex(output, 'ipip (ipip )?remote any local any dev dummy98') + self.assertIn(f'ipip {mode} remote any local any dev dummy98', output) touch_network_unit( '25-ipip-tunnel.netdev', @@ -2495,6 +2506,18 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'ipiptun96:routable', 'dummy98:degraded') + def test_ipip_tunnel(self): + first = True + for mode in [None, 'ipip', 'any']: + if first: + first = False + else: + self.tearDown() + + print(f'### test_ipip_tunnel(mode={mode})') + with self.subTest(mode=mode): + self._test_ipip_tunnel(mode) + def test_gre_tunnel(self): copy_network_unit('12-dummy.netdev', '25-gretun.network', '25-gre-tunnel.netdev', '25-tunnel.network', @@ -2724,7 +2747,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'vti6tun97:routable', 'dummy98:degraded') - def test_ip6tnl_tunnel(self): + def _test_ip6tnl_tunnel(self, mode): copy_network_unit('12-dummy.netdev', '25-ip6tnl.network', '25-ip6tnl-tunnel.netdev', '25-tunnel.network', '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network', @@ -2732,6 +2755,18 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network', '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network', '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network') + + if mode: + for netdev in ['25-ip6tnl-tunnel.netdev', + '25-ip6tnl-tunnel-local-any.netdev', + '25-ip6tnl-tunnel-remote-any.netdev', + '25-ip6tnl-tunnel-local-slaac.netdev', + '25-ip6tnl-tunnel-external.netdev']: + with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f: + f.write(f'[Tunnel]\nMode={mode}\n') + else: + mode = 'any' # kernel default + start_networkd() self.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded', @@ -2747,20 +2782,20 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -d link show ip6tnl99') print(output) - self.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output) + self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output) output = check_output('ip -d link show ip6tnl98') print(output) - self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98') + self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local any dev dummy98', output) 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') + self.assertIn(f'ip6tnl {mode} remote any local 2a00:ffde:4567:edde::4987 dev dummy98', output) 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) + self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output) output = check_output('ip -6 address show veth99') print(output) @@ -2787,12 +2822,35 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'veth99:routable', 'veth-peer:degraded') - def test_sit_tunnel(self): + def test_ip6tnl_tunnel(self): + first = True + for mode in [None, 'ipip6', 'ip6ip6', 'any']: + if first: + first = False + else: + self.tearDown() + + print(f'### test_ip6tnl_tunnel(mode={mode})') + with self.subTest(mode=mode): + self._test_ip6tnl_tunnel(mode) + + def _test_sit_tunnel(self, mode): copy_network_unit('12-dummy.netdev', '25-sit.network', '25-sit-tunnel.netdev', '25-tunnel.network', '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network', '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network') + + if mode: + for netdev in ['25-sit-tunnel.netdev', + '25-sit-tunnel-local-any.netdev', + '25-sit-tunnel-remote-any.netdev', + '25-sit-tunnel-any-any.netdev']: + with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f: + f.write(f'[Tunnel]\nMode={mode}\n') + else: + mode = 'ip6ip' # kernel default + start_networkd() self.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded') self.networkctl_check_unit('sittun99', '25-sit-tunnel', '25-tunnel') @@ -2803,16 +2861,16 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -d link show sittun99') print(output) - self.assertRegex(output, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98") + self.assertIn(f'sit {mode} remote 10.65.223.239 local 10.65.223.238 dev dummy98', output) output = check_output('ip -d link show sittun98') print(output) - self.assertRegex(output, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98") + self.assertIn(f'sit {mode} remote 10.65.223.239 local any dev dummy98', output) output = check_output('ip -d link show sittun97') print(output) - self.assertRegex(output, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98") + self.assertIn(f'sit {mode} remote any local 10.65.223.238 dev dummy98', output) output = check_output('ip -d link show sittun96') print(output) - self.assertRegex(output, "sit (ip6ip )?remote any local any dev dummy98") + self.assertIn(f'sit {mode} remote any local any dev dummy98', output) touch_network_unit( '25-sit-tunnel.netdev', @@ -2827,6 +2885,18 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'sittun96:routable', 'dummy98:degraded') + def test_sit_tunnel(self): + first = True + for mode in [None, 'ipip', 'ip6ip', 'any']: + if first: + first = False + else: + self.tearDown() + + print(f'### test_sit_tunnel(mode={mode})') + with self.subTest(mode=mode): + self._test_sit_tunnel(mode) + def test_isatap_tunnel(self): copy_network_unit('12-dummy.netdev', '25-isatap.network', '25-isatap-tunnel.netdev', '25-tunnel.network')