]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/netdev: replace old NetDev object with newer one on reload
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 27 Oct 2024 07:38:24 +0000 (16:38 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 30 Oct 2024 20:30:40 +0000 (05:30 +0900)
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
src/network/netdev/netdev.h
src/network/networkd-manager.c

index 0a311544b27203fa37c245f91276cb2c2692d3ea..1e046397d502e208faf3cdfceb78e9dcb94e63b7 100644 (file)
@@ -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,
index a91ef5065448eb46a2eff5e85db53d73b76aeff7..106f47acadad0c20993487d880d2d3d9d65bdb6e 100644 (file)
@@ -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);
index ca2b008a34910af0c9647a2e1330d83cf58d9a75..6639d59b32179b8d48aa3ca5750b9dadb6d1460f 100644 (file)
@@ -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;