From 933d88f756fe69dd6b8ffe6e480d298edd7ba982 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 27 Oct 2024 16:37:17 +0900 Subject: [PATCH 1/5] network/netdev: move calls of netdev_attach() and netdev_request_to_create() to netdev_load() No functional change, preparation for later commits. --- src/network/fuzz-netdev-parser.c | 3 ++- src/network/netdev/netdev.c | 41 ++++++++++++++++---------------- src/network/netdev/netdev.h | 2 +- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/network/fuzz-netdev-parser.c b/src/network/fuzz-netdev-parser.c index f0988bd4cc..7e29ba9b8e 100644 --- a/src/network/fuzz-netdev-parser.c +++ b/src/network/fuzz-netdev-parser.c @@ -10,6 +10,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_(manager_freep) Manager *manager = NULL; _cleanup_fclose_ FILE *f = NULL; _cleanup_(unlink_tempfilep) char netdev_config[] = "/tmp/fuzz-networkd.XXXXXX"; + _cleanup_(netdev_unrefp) NetDev *netdev = NULL; if (outside_size_range(size, 0, 65536)) return 0; @@ -22,6 +23,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { fflush(f); assert_se(manager_new(&manager, /* test_mode = */ true) >= 0); - (void) netdev_load_one(manager, netdev_config); + (void) netdev_load_one(manager, netdev_config, &netdev); return 0; } diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 80538a60ec..0cfe6ebb9e 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -940,21 +940,20 @@ static int netdev_request_to_create(NetDev *netdev) { return 0; } -int netdev_load_one(Manager *manager, const char *filename) { +int netdev_load_one(Manager *manager, const char *filename, NetDev **ret) { _cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL; const char *dropin_dirname; int r; assert(manager); assert(filename); + assert(ret); r = null_or_empty_path(filename); if (r < 0) return log_warning_errno(r, "Failed to check if \"%s\" is empty: %m", filename); - if (r > 0) { - log_debug("Skipping empty file: %s", filename); - return 0; - } + if (r > 0) + return log_debug_errno(SYNTHETIC_ERRNO(ENOENT), "Skipping empty file: %s", filename); netdev_raw = new(NetDev, 1); if (!netdev_raw) @@ -979,10 +978,8 @@ int netdev_load_one(Manager *manager, const char *filename) { return r; /* config_parse_many() logs internally. */ /* skip out early if configuration does not match the environment */ - if (!condition_test_list(netdev_raw->conditions, environ, NULL, NULL, NULL)) { - log_debug("%s: Conditions in the file do not match the system environment, skipping.", filename); - return 0; - } + if (!condition_test_list(netdev_raw->conditions, environ, NULL, NULL, NULL)) + return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "%s: Conditions in the file do not match the system environment, skipping.", filename); if (netdev_raw->kind == _NETDEV_KIND_INVALID) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "NetDev has no Kind= configured in \"%s\", ignoring.", filename); @@ -1025,17 +1022,9 @@ int netdev_load_one(Manager *manager, const char *filename) { if (!netdev->filename) return log_oom(); - r = netdev_attach(netdev); - if (r < 0) - return r; - log_syntax(/* unit = */ NULL, LOG_DEBUG, filename, /* config_line = */ 0, /* error = */ 0, "Successfully loaded."); - r = netdev_request_to_create(netdev); - if (r < 0) - return r; /* netdev_request_to_create() logs internally. */ - - TAKE_PTR(netdev); + *ret = TAKE_PTR(netdev); return 0; } @@ -1049,8 +1038,20 @@ int netdev_load(Manager *manager) { if (r < 0) return log_error_errno(r, "Failed to enumerate netdev files: %m"); - STRV_FOREACH(f, files) - (void) netdev_load_one(manager, *f); + STRV_FOREACH(f, files) { + _cleanup_(netdev_unrefp) NetDev *netdev = NULL; + + if (netdev_load_one(manager, *f, &netdev) < 0) + continue; + + if (netdev_attach(netdev) < 0) + continue; + + if (netdev_request_to_create(netdev) < 0) + continue; + + TAKE_PTR(netdev); + } return 0; } diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index 786fe9e9f6..a91ef50654 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -218,7 +218,7 @@ void netdev_detach(NetDev *netdev); int netdev_set_ifindex_internal(NetDev *netdev, int ifindex); int netdev_load(Manager *manager); -int netdev_load_one(Manager *manager, const char *filename); +int netdev_load_one(Manager *manager, const char *filename, NetDev **ret); void netdev_drop(NetDev *netdev); void netdev_enter_failed(NetDev *netdev); int netdev_enter_ready(NetDev *netdev); From 890bd7225a68cc9ff9207442b37285f8b009eb47 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 28 Oct 2024 23:42:38 +0900 Subject: [PATCH 2/5] network/netdev: reconfigure netdev if possible Some netdev configs can be modified after the interface is created. Let's allow to reconfigure existing interfaces. --- src/network/netdev/netdev.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 0cfe6ebb9e..0a311544b2 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -724,7 +724,7 @@ static int independent_netdev_create(NetDev *netdev) { return 0; } - r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0); + r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, netdev->ifindex); if (r < 0) return r; @@ -753,7 +753,7 @@ static int stacked_netdev_create(NetDev *netdev, Link *link, Request *req) { assert(link); assert(req); - r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0); + r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, netdev->ifindex); if (r < 0) return r; @@ -798,9 +798,6 @@ static bool link_is_ready_to_create_stacked_netdev(Link *link) { static int netdev_is_ready_to_create(NetDev *netdev, Link *link) { assert(netdev); - if (netdev->state != NETDEV_STATE_LOADING) - return false; - if (link && !link_is_ready_to_create_stacked_netdev(link)) return false; @@ -862,9 +859,6 @@ int link_request_stacked_netdev(Link *link, NetDev *netdev) { if (!netdev_is_stacked(netdev)) return -EINVAL; - if (!IN_SET(netdev->state, NETDEV_STATE_LOADING, NETDEV_STATE_FAILED) || netdev->ifindex > 0) - return 0; /* Already created. */ - if (!netdev_is_managed(netdev)) return 0; /* Already detached, due to e.g. reloading .netdev files. */ @@ -921,6 +915,9 @@ static int netdev_request_to_create(NetDev *netdev) { if (!netdev_is_managed(netdev)) return 0; /* Already detached, due to e.g. reloading .netdev files. */ + if (netdev->state != NETDEV_STATE_LOADING) + return 0; /* Already configured (at least tried previously). Not necessary to reconfigure. */ + r = netdev_is_ready_to_create(netdev, NULL); if (r < 0) return r; From b3ae4e8622c04f5653a476d40cdef243827eedd5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 27 Oct 2024 16:38:24 +0900 Subject: [PATCH 3/5] network/netdev: replace old NetDev object with newer one on reload Then, when a .netdev file of a stacked netdev is modified, the netdev can be reconfigured with the updated setting by something like the following way: ``` ip link del vlan99 networkctl reload ``` Note, removing the vlan interface in the above example may not be necessary, e.g. when only VLAN flags, egress mapping, or ingress mapping are updated. But, it is necessary when VLAN ID is updated. Closes #9627. Closes #27177. Closes #34907. Replaces #22557. --- src/network/netdev/netdev.c | 78 +++++++++++++++++++++++++++++++++- src/network/netdev/netdev.h | 2 + src/network/networkd-manager.c | 2 +- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 0a311544b2..1e046397d5 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -242,6 +242,7 @@ static NetDev* netdev_free(NetDev *netdev) { condition_free_list(netdev->conditions); free(netdev->filename); strv_free(netdev->dropins); + hashmap_free(netdev->stats_by_path); free(netdev->description); free(netdev->ifname); @@ -1003,7 +1004,7 @@ int netdev_load_one(Manager *manager, const char *filename, NetDev **ret) { config_item_perf_lookup, network_netdev_gperf_lookup, CONFIG_PARSE_WARN, netdev, - NULL, + &netdev->stats_by_path, &netdev->dropins); if (r < 0) return r; /* config_parse_many() logs internally. */ @@ -1053,6 +1054,81 @@ int netdev_load(Manager *manager) { return 0; } +int netdev_reload(Manager *manager) { + _cleanup_hashmap_free_ Hashmap *new_netdevs = NULL; + _cleanup_strv_free_ char **files = NULL; + int r; + + assert(manager); + + r = conf_files_list_strv(&files, ".netdev", NULL, 0, NETWORK_DIRS); + if (r < 0) + return log_error_errno(r, "Failed to enumerate netdev files: %m"); + + STRV_FOREACH(f, files) { + _cleanup_(netdev_unrefp) NetDev *netdev = NULL; + NetDev *old; + + if (netdev_load_one(manager, *f, &netdev) < 0) + continue; + + if (netdev_get(manager, netdev->ifname, &old) < 0) { + log_netdev_debug(netdev, "Found new .netdev file: %s", netdev->filename); + + if (netdev_attach_name_full(netdev, netdev->ifname, &new_netdevs) >= 0) + TAKE_PTR(netdev); + + continue; + } + + if (!stats_by_path_equal(netdev->stats_by_path, old->stats_by_path)) { + log_netdev_debug(netdev, "Found updated .netdev file: %s", netdev->filename); + + /* Copy ifindex. */ + netdev->ifindex = old->ifindex; + + if (netdev_attach_name_full(netdev, netdev->ifname, &new_netdevs) >= 0) + TAKE_PTR(netdev); + + continue; + } + + /* Keep the original object, and drop the new one. */ + if (netdev_attach_name_full(old, old->ifname, &new_netdevs) >= 0) + netdev_ref(old); + } + + /* Detach old NetDev objects from Manager. + * Note, the same object may be registered with multiple names, and netdev_detach() may drop multiple + * entries. Hence, hashmap_free_with_destructor() cannot be used. */ + for (NetDev *n; (n = hashmap_first(manager->netdevs)); ) + netdev_detach(n); + + /* Attach new NetDev objects to Manager. */ + for (;;) { + _cleanup_(netdev_unrefp) NetDev *netdev = hashmap_steal_first(new_netdevs); + if (!netdev) + break; + + netdev->manager = manager; + if (netdev_attach(netdev) < 0) + continue; + + /* Create a new netdev or update existing netdev, */ + if (netdev_request_to_create(netdev) < 0) + continue; + + TAKE_PTR(netdev); + } + + /* Reassign NetDev objects to Link object. */ + Link *link; + HASHMAP_FOREACH(link, manager->links_by_index) + link_assign_netdev(link); + + return 0; +} + int config_parse_netdev_kind( const char *unit, const char *filename, diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index a91ef50654..106f47acad 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -118,6 +118,7 @@ typedef struct NetDev { char *filename; char **dropins; + Hashmap *stats_by_path; LIST_HEAD(Condition, conditions); @@ -218,6 +219,7 @@ void netdev_detach(NetDev *netdev); int netdev_set_ifindex_internal(NetDev *netdev, int ifindex); int netdev_load(Manager *manager); +int netdev_reload(Manager *manager); int netdev_load_one(Manager *manager, const char *filename, NetDev **ret); void netdev_drop(NetDev *netdev); void netdev_enter_failed(NetDev *netdev); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index ca2b008a34..6639d59b32 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -1188,7 +1188,7 @@ int manager_reload(Manager *m, sd_bus_message *message) { (void) notify_reloading(); - r = netdev_load(m); + r = netdev_reload(m); if (r < 0) goto finish; From 0de5562413087f9b7cfcd0a2ca4bc56ecacd8a1b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 27 Oct 2024 14:23:08 +0900 Subject: [PATCH 4/5] test-network: test for reload of .netdev file of stacked netdev For issue #9627, #27177, and #34907. --- test/test-network/systemd-networkd-tests.py | 224 +++++++++++++++++++- 1 file changed, 214 insertions(+), 10 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 79d593bb7e..82339583be 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -358,6 +358,10 @@ def remove_network_unit(*units): if has_link: udevadm_reload() +def touch_network_unit(*units): + for unit in units: + touch(os.path.join(network_unit_dir, unit)) + def clear_network_units(): has_link = False if os.path.exists(network_unit_dir): @@ -1711,11 +1715,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10') self.check_link_attr('bond97', 'bonding', 'peer_notif_delay', '300000') - def test_vlan(self): - copy_network_unit('21-vlan.netdev', '11-dummy.netdev', - '21-vlan.network', '21-vlan-test1.network') - start_networkd() - + def check_vlan(self, id, flags): self.wait_online('test1:degraded', 'vlan99:routable') self.networkctl_check_unit('vlan99', '21-vlan', '21-vlan') self.networkctl_check_unit('test1', '11-dummy', '21-vlan-test1') @@ -1727,11 +1727,17 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -d link show vlan99') print(output) self.assertIn(' mtu 2000 ', output) - self.assertIn('REORDER_HDR', output) - self.assertIn('LOOSE_BINDING', output) - self.assertIn('GVRP', output) - self.assertIn('MVRP', output) - self.assertIn(' id 99 ', output) + if flags: + self.assertIn('REORDER_HDR', output) + self.assertIn('LOOSE_BINDING', output) + self.assertIn('GVRP', output) + self.assertIn('MVRP', output) + else: + self.assertNotIn('REORDER_HDR', output) + self.assertNotIn('LOOSE_BINDING', output) + self.assertNotIn('GVRP', output) + self.assertNotIn('MVRP', output) + self.assertIn(f' id {id} ', output) self.assertIn('ingress-qos-map { 4:100 7:13 }', output) self.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output) @@ -1744,6 +1750,32 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99') + def test_vlan(self): + copy_network_unit('21-vlan.netdev', '11-dummy.netdev', + '21-vlan.network', '21-vlan-test1.network') + start_networkd() + self.check_vlan(id=99, flags=True) + + # Test for reloading .netdev file. See issue #34907. + with open(os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8') as f: + f.write('[VLAN]\nId=42\n') + + # VLAN ID cannot be changed, so we need to remove the existing netdev. + check_output("ip link del vlan99") + networkctl_reload() + self.check_vlan(id=42, flags=True) + + with open(os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8') as f: + f.write('[VLAN]\n' + 'GVRP=no\n' + 'MVRP=no\n' + 'LooseBinding=no\n' + 'ReorderHeader=no\n') + + # flags can be changed, hence it is not necessary to remove the existing netdev. + networkctl_reload() + self.check_vlan(id=42, flags=False) + def test_vlan_on_bond(self): # For issue #24377 (https://github.com/systemd/systemd/issues/24377), # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020). @@ -1787,6 +1819,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'macvtap mode ' + mode + ' ') + touch_network_unit('21-macvtap.netdev') + networkctl_reload() + self.wait_online('macvtap99:degraded', + 'test1:carrier' if mode == 'passthru' else 'test1:degraded') + @expectedFailureIfModuleIsNotAvailable('macvlan') def test_macvlan(self): first = True @@ -1837,6 +1874,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): if ' bclim ' in output: # This is new in kernel and iproute2 v6.4 self.assertIn(' bclim 2147483647 ', output) + touch_network_unit('21-macvlan.netdev') + networkctl_reload() + self.wait_online('macvlan99:degraded', + 'test1:carrier' if mode == 'passthru' else 'test1:degraded') + @expectedFailureIfModuleIsNotAvailable('ipvlan') def test_ipvlan(self): first = True @@ -1862,6 +1904,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'ipvlan *mode ' + mode.lower() + ' ' + flag) + touch_network_unit('25-ipvlan.netdev') + networkctl_reload() + self.wait_online('ipvlan99:degraded', 'test1:degraded') + @expectedFailureIfModuleIsNotAvailable('ipvtap') def test_ipvtap(self): first = True @@ -1887,6 +1933,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'ipvtap *mode ' + mode.lower() + ' ' + flag) + touch_network_unit('25-ipvtap.netdev') + networkctl_reload() + self.wait_online('ipvtap99:degraded', 'test1:degraded') + def test_veth(self): copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network', '25-veth-mtu.netdev') @@ -2197,6 +2247,19 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'ipip (ipip )?remote any local any dev dummy98') + touch_network_unit( + '25-ipip-tunnel.netdev', + '25-ipip-tunnel-local-any.netdev', + '25-ipip-tunnel-remote-any.netdev', + '25-ipip-tunnel-any-any.netdev') + networkctl_reload() + self.wait_online( + 'ipiptun99:routable', + 'ipiptun98:routable', + 'ipiptun97:routable', + 'ipiptun96:routable', + 'dummy98:degraded') + def test_gre_tunnel(self): copy_network_unit('12-dummy.netdev', '25-gretun.network', '25-gre-tunnel.netdev', '25-tunnel.network', @@ -2240,6 +2303,19 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertNotRegex(output, 'iseq') self.assertNotRegex(output, 'oseq') + touch_network_unit( + '25-gre-tunnel.netdev', + '25-gre-tunnel-local-any.netdev', + '25-gre-tunnel-remote-any.netdev', + '25-gre-tunnel-any-any.netdev') + networkctl_reload() + self.wait_online( + 'gretun99:routable', + 'gretun98:routable', + 'gretun97:routable', + 'gretun96:routable', + 'dummy98:degraded') + def test_ip6gre_tunnel(self): copy_network_unit('12-dummy.netdev', '25-ip6gretun.network', '25-ip6gre-tunnel.netdev', '25-tunnel.network', @@ -2265,6 +2341,19 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'ip6gre remote any local any dev dummy98') + touch_network_unit( + '25-ip6gre-tunnel.netdev', + '25-ip6gre-tunnel-local-any.netdev', + '25-ip6gre-tunnel-remote-any.netdev', + '25-ip6gre-tunnel-any-any.netdev') + networkctl_reload() + self.wait_links( + 'dummy98', + 'ip6gretun99', + 'ip6gretun98', + 'ip6gretun97', + 'ip6gretun96') + def test_gretap_tunnel(self): copy_network_unit('12-dummy.netdev', '25-gretap.network', '25-gretap-tunnel.netdev', '25-tunnel.network', @@ -2292,6 +2381,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'iseq') self.assertRegex(output, 'oseq') + touch_network_unit( + '25-gretap-tunnel.netdev', + '25-gretap-tunnel-local-any.netdev') + networkctl_reload() + self.wait_online( + 'gretap99:routable', + 'gretap98:routable', + 'dummy98:degraded') + def test_ip6gretap_tunnel(self): copy_network_unit('12-dummy.netdev', '25-ip6gretap.network', '25-ip6gretap-tunnel.netdev', '25-tunnel.network', @@ -2309,6 +2407,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98') + touch_network_unit( + '25-ip6gretap-tunnel.netdev', + '25-ip6gretap-tunnel-local-any.netdev') + networkctl_reload() + self.wait_online( + 'ip6gretap99:routable', + 'ip6gretap98:routable', + 'dummy98:degraded') + def test_vti_tunnel(self): copy_network_unit('12-dummy.netdev', '25-vti.network', '25-vti-tunnel.netdev', '25-tunnel.network', @@ -2336,6 +2443,19 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'vti remote any local any dev dummy98') + touch_network_unit( + '25-vti-tunnel.netdev', + '25-vti-tunnel-local-any.netdev', + '25-vti-tunnel-remote-any.netdev', + '25-vti-tunnel-any-any.netdev') + networkctl_reload() + self.wait_online( + 'vtitun99:routable', + 'vtitun98:routable', + 'vtitun97:routable', + 'vtitun96:routable', + 'dummy98:degraded') + def test_vti6_tunnel(self): copy_network_unit('12-dummy.netdev', '25-vti6.network', '25-vti6-tunnel.netdev', '25-tunnel.network', @@ -2358,6 +2478,17 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98') + touch_network_unit( + '25-vti6-tunnel.netdev', + '25-vti6-tunnel-local-any.netdev', + '25-vti6-tunnel-remote-any.netdev') + networkctl_reload() + self.wait_online( + 'vti6tun99:routable', + 'vti6tun98:routable', + 'vti6tun97:routable', + 'dummy98:degraded') + def test_ip6tnl_tunnel(self): copy_network_unit('12-dummy.netdev', '25-ip6tnl.network', '25-ip6tnl-tunnel.netdev', '25-tunnel.network', @@ -2404,6 +2535,23 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertIn('default dev ip6tnl-slaac proto static', output) + touch_network_unit( + '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') + networkctl_reload() + self.wait_online( + 'ip6tnl99:routable', + 'ip6tnl98:routable', + 'ip6tnl97:routable', + 'ip6tnl-slaac:degraded', + 'ip6tnl-external:degraded', + 'dummy98:degraded', + 'veth99:routable', + 'veth-peer:degraded') + def test_sit_tunnel(self): copy_network_unit('12-dummy.netdev', '25-sit.network', '25-sit-tunnel.netdev', '25-tunnel.network', @@ -2431,6 +2579,19 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, "sit (ip6ip )?remote any local any dev dummy98") + touch_network_unit( + '25-sit-tunnel.netdev', + '25-sit-tunnel-local-any.netdev', + '25-sit-tunnel-remote-any.netdev', + '25-sit-tunnel-any-any.netdev') + networkctl_reload() + self.wait_online( + 'sittun99:routable', + 'sittun98:routable', + 'sittun97:routable', + 'sittun96:routable', + 'dummy98:degraded') + def test_isatap_tunnel(self): copy_network_unit('12-dummy.netdev', '25-isatap.network', '25-isatap-tunnel.netdev', '25-tunnel.network') @@ -2443,6 +2604,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, "isatap ") + touch_network_unit('25-isatap-tunnel.netdev') + networkctl_reload() + self.wait_online('isataptun99:routable', 'dummy98:degraded') + def test_6rd_tunnel(self): copy_network_unit('12-dummy.netdev', '25-6rd.network', '25-6rd-tunnel.netdev', '25-tunnel.network') @@ -2455,6 +2620,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, '6rd-prefix 2602::/24') + touch_network_unit('25-6rd-tunnel.netdev') + networkctl_reload() + self.wait_online('sittun99:routable', 'dummy98:degraded') + @expectedFailureIfERSPANv0IsNotSupported() def test_erspan_tunnel_v0(self): copy_network_unit('12-dummy.netdev', '25-erspan.network', @@ -2487,6 +2656,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertIn('ikey 0.0.0.102', output) self.assertIn('iseq', output) + touch_network_unit( + '25-erspan0-tunnel.netdev', + '25-erspan0-tunnel-local-any.netdev') + networkctl_reload() + self.wait_online( + 'erspan99:routable', + 'erspan98:routable', + 'dummy98:degraded') + def test_erspan_tunnel_v1(self): copy_network_unit('12-dummy.netdev', '25-erspan.network', '25-erspan1-tunnel.netdev', '25-tunnel.network', @@ -2520,6 +2698,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertIn('iseq', output) self.assertIn('oseq', output) + touch_network_unit( + '25-erspan1-tunnel.netdev', + '25-erspan1-tunnel-local-any.netdev') + networkctl_reload() + self.wait_online( + 'erspan99:routable', + 'erspan98:routable', + 'dummy98:degraded') + @expectedFailureIfERSPANv2IsNotSupported() def test_erspan_tunnel_v2(self): copy_network_unit('12-dummy.netdev', '25-erspan.network', @@ -2554,6 +2741,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertIn('iseq', output) self.assertIn('oseq', output) + touch_network_unit( + '25-erspan2-tunnel.netdev', + '25-erspan2-tunnel-local-any.netdev') + networkctl_reload() + self.wait_online( + 'erspan99:routable', + 'erspan98:routable', + 'dummy98:degraded') + def test_tunnel_independent(self): copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() @@ -2590,6 +2786,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertIn('xfrm99@lo:', output) self.assertIn('xfrm if_id 0x99 ', output) + touch_network_unit('25-xfrm.netdev') + networkctl_reload() + self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded') + @expectedFailureIfModuleIsNotAvailable('fou') def test_fou(self): # The following redundant check is necessary for CentOS CI. @@ -2718,6 +2918,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'RXSC: 8c16456c83a90002, state on') self.assertRegex(output, '0: PN [0-9]*, state off, key 02030400000000000000000000000000') + touch_network_unit('25-macsec.netdev') + networkctl_reload() + self.wait_online('dummy98:degraded', 'macsec99:routable') + def test_nlmon(self): copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network') start_networkd() From 2e612ce1b7f9d1faee44c4f8787fda1ea929dc73 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 29 Oct 2024 01:45:08 +0900 Subject: [PATCH 5/5] man: update documentation for 'networkctl reload' --- man/networkctl.xml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/man/networkctl.xml b/man/networkctl.xml index 9e2a65b879..22f9744c95 100644 --- a/man/networkctl.xml +++ b/man/networkctl.xml @@ -413,14 +413,23 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR) reload - Reload .netdev and .network files. - If a new .netdev file is found, then the corresponding netdev is created. - Note that even if an existing .netdev is modified or removed, - systemd-networkd does not update or remove the netdev. - If a new, modified or removed .network file is found, then all interfaces - which match the file are reconfigured. + + Reload .netdev and .network files. - + If a new or modified .netdev file is found, then the corresponding + netdev is created or updated, respectively. Note, if the corresponding interface already exists, + then some of new settings may not be applied. E.g., VLAN ID cannot be changed after the interface + was created, so changing [VLAN] Id= will not take effect if the matching VLAN + interface already exists. To apply such settings, the interfaces need to be removed manually before + reload. Also note that even if a .netdev file is removed, + systemd-networkd does not remove the existing netdev corresponding to the file. + + + If a new, modified, or removed .network file is found, then all + interfaces that matched the file are reconfigured. + + +