]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/netdev/netdev.c
tree-wide: make sure net/if.h is included before any linux/ header
[thirdparty/systemd.git] / src / network / netdev / netdev.c
index 8f68a50297feb7483ea8d915fb8a848af8e67c46..229520dfb2b667bd0a8710a74ce1e90bed9c10e1 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <netinet/in.h>
 #include <linux/if_arp.h>
@@ -31,6 +32,7 @@
 #include "networkd-manager.h"
 #include "networkd-queue.h"
 #include "networkd-setlink.h"
+#include "networkd-sriov.h"
 #include "nlmon.h"
 #include "path-lookup.h"
 #include "siphash24.h"
@@ -128,7 +130,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
         [NETDEV_KIND_VXCAN]     = "vxcan",
         [NETDEV_KIND_VXLAN]     = "vxlan",
         [NETDEV_KIND_WIREGUARD] = "wireguard",
-        [NETDEV_KIND_WLAN]      = "virtual-wlan",
+        [NETDEV_KIND_WLAN]      = "wlan",
         [NETDEV_KIND_XFRM]      = "xfrm",
 };
 
@@ -144,7 +146,7 @@ bool netdev_is_managed(NetDev *netdev) {
 static bool netdev_is_stacked_and_independent(NetDev *netdev) {
         assert(netdev);
 
-        if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
+        if (netdev_get_create_type(netdev) != NETDEV_CREATE_STACKED)
                 return false;
 
         switch (netdev->kind) {
@@ -180,7 +182,7 @@ static bool netdev_is_stacked_and_independent(NetDev *netdev) {
 static bool netdev_is_stacked(NetDev *netdev) {
         assert(netdev);
 
-        if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
+        if (netdev_get_create_type(netdev) != NETDEV_CREATE_STACKED)
                 return false;
 
         if (netdev_is_stacked_and_independent(netdev))
@@ -197,14 +199,6 @@ static void netdev_detach_from_manager(NetDev *netdev) {
 static NetDev *netdev_free(NetDev *netdev) {
         assert(netdev);
 
-        netdev_detach_from_manager(netdev);
-
-        free(netdev->filename);
-
-        free(netdev->description);
-        free(netdev->ifname);
-        condition_free_list(netdev->conditions);
-
         /* Invoke the per-kind done() destructor, but only if the state field is initialized. We conditionalize that
          * because we parse .netdev files twice: once to determine the kind (with a short, minimal NetDev structure
          * allocation, with no room for per-kind fields), and once to read the kind's properties (with a full,
@@ -217,6 +211,13 @@ static NetDev *netdev_free(NetDev *netdev) {
             NETDEV_VTABLE(netdev)->done)
                 NETDEV_VTABLE(netdev)->done(netdev);
 
+        netdev_detach_from_manager(netdev);
+
+        condition_free_list(netdev->conditions);
+        free(netdev->filename);
+        free(netdev->description);
+        free(netdev->ifname);
+
         return mfree(netdev);
 }
 
@@ -236,6 +237,9 @@ void netdev_drop(NetDev *netdev) {
                 return;
         }
 
+        if (NETDEV_VTABLE(netdev) && NETDEV_VTABLE(netdev)->drop)
+                NETDEV_VTABLE(netdev)->drop(netdev);
+
         netdev->state = NETDEV_STATE_LINGER;
 
         log_netdev_debug(netdev, "netdev removed");
@@ -253,10 +257,8 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret) {
         assert(ret);
 
         netdev = hashmap_get(manager->netdevs, name);
-        if (!netdev) {
-                *ret = NULL;
+        if (!netdev)
                 return -ENOENT;
-        }
 
         *ret = netdev;
 
@@ -279,7 +281,7 @@ static int netdev_enter_ready(NetDev *netdev) {
         log_netdev_info(netdev, "netdev ready");
 
         if (NETDEV_VTABLE(netdev)->post_create)
-                NETDEV_VTABLE(netdev)->post_create(netdev, NULL, NULL);
+                NETDEV_VTABLE(netdev)->post_create(netdev, NULL);
 
         return 0;
 }
@@ -544,17 +546,14 @@ static int netdev_create_message(NetDev *netdev, Link *link, sd_netlink_message
         return 0;
 }
 
-static int netdev_create(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) {
+static int independent_netdev_create(NetDev *netdev) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
         assert(netdev);
-        assert(!link || callback);
 
         /* create netdev */
         if (NETDEV_VTABLE(netdev)->create) {
-                assert(!link);
-
                 r = NETDEV_VTABLE(netdev)->create(netdev);
                 if (r < 0)
                         return r;
@@ -565,66 +564,73 @@ static int netdev_create(NetDev *netdev, Link *link, link_netlink_message_handle
 
         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
         if (r < 0)
-                return log_netdev_error_errno(netdev, r, "Could not allocate netlink message: %m");
+                return r;
 
-        r = netdev_create_message(netdev, link, m);
+        r = netdev_create_message(netdev, NULL, m);
         if (r < 0)
-                return log_netdev_error_errno(netdev, r, "Could not create netlink message: %m");
-
-        if (link) {
-                r = netlink_call_async(netdev->manager->rtnl, NULL, m, callback,
-                                       link_netlink_destroy_callback, link);
-                if (r < 0)
-                        return log_netdev_error_errno(netdev, r, "Could not send netlink message: %m");
+                return r;
 
-                link_ref(link);
-        } else {
-                r = netlink_call_async(netdev->manager->rtnl, NULL, m, netdev_create_handler,
-                                       netdev_destroy_callback, netdev);
-                if (r < 0)
-                        return log_netdev_error_errno(netdev, r, "Could not send netlink message: %m");
+        r = netlink_call_async(netdev->manager->rtnl, NULL, m, netdev_create_handler,
+                               netdev_destroy_callback, netdev);
+        if (r < 0)
+                return r;
 
-                netdev_ref(netdev);
-        }
+        netdev_ref(netdev);
 
         netdev->state = NETDEV_STATE_CREATING;
-
         log_netdev_debug(netdev, "Creating");
         return 0;
 }
 
-static int netdev_create_after_configured(NetDev *netdev, Link *link) {
+static int stacked_netdev_create(NetDev *netdev, Link *link, Request *req) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+        int r;
+
         assert(netdev);
+        assert(netdev->manager);
         assert(link);
-        assert(NETDEV_VTABLE(netdev)->create_after_configured);
+        assert(req);
+
+        r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
+        if (r < 0)
+                return r;
 
-        return NETDEV_VTABLE(netdev)->create_after_configured(netdev, link);
+        r = netdev_create_message(netdev, link, m);
+        if (r < 0)
+                return r;
+
+        r = request_call_netlink_async(netdev->manager->rtnl, m, req);
+        if (r < 0)
+                return r;
+
+        netdev->state = NETDEV_STATE_CREATING;
+        log_netdev_debug(netdev, "Creating");
+        return 0;
 }
 
-int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) {
-        int r;
+static bool link_is_ready_to_create_stacked_netdev_one(Link *link, bool allow_unmanaged) {
+        assert(link);
 
-        assert(netdev);
-        assert(netdev->manager);
-        assert(netdev->manager->rtnl);
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED))
+                return false;
 
-        switch (netdev_get_create_type(netdev)) {
-        case NETDEV_CREATE_STACKED:
-                r = netdev_create(netdev, link, callback);
-                if (r < 0)
-                        return r;
+        if (!link->network)
+                return allow_unmanaged;
 
-                break;
-        case NETDEV_CREATE_AFTER_CONFIGURED:
-                r = netdev_create_after_configured(netdev, link);
-                if (r < 0)
-                        return r;
-                break;
-        default:
-                assert_not_reached();
-        }
+        if (link->set_link_messages > 0)
+                return false;
 
-        return 0;
+        /* If stacked netdevs are created before the underlying interface being activated, then
+         * the activation policy for the netdevs are ignored. See issue #22593. */
+        if (!link->activated)
+                return false;
+
+        return true;
+}
+
+static bool link_is_ready_to_create_stacked_netdev(Link *link) {
+        return check_ready_for_all_sr_iov_ports(link, /* allow_unmanaged = */ false,
+                                                link_is_ready_to_create_stacked_netdev_one);
 }
 
 static int netdev_is_ready_to_create(NetDev *netdev, Link *link) {
@@ -633,17 +639,8 @@ static int netdev_is_ready_to_create(NetDev *netdev, Link *link) {
         if (netdev->state != NETDEV_STATE_LOADING)
                 return false;
 
-        if (link) {
-                if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
-                        return false;
-
-                if (netdev_get_create_type(netdev) == NETDEV_CREATE_AFTER_CONFIGURED &&
-                    link->state != LINK_STATE_CONFIGURED)
-                        return false;
-
-                if (link->set_link_messages > 0)
-                        return false;
-        }
+        if (link && !link_is_ready_to_create_stacked_netdev(link))
+                return false;
 
         if (NETDEV_VTABLE(netdev)->is_ready_to_create)
                 return NETDEV_VTABLE(netdev)->is_ready_to_create(netdev, link);
@@ -651,35 +648,30 @@ static int netdev_is_ready_to_create(NetDev *netdev, Link *link) {
         return true;
 }
 
-int request_process_stacked_netdev(Request *req) {
+static int stacked_netdev_process_request(Request *req, Link *link, void *userdata) {
+        NetDev *netdev = ASSERT_PTR(userdata);
         int r;
 
         assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_NETDEV_STACKED);
-        assert(req->netdev);
-        assert(req->netlink_handler);
+        assert(link);
 
-        r = netdev_is_ready_to_create(req->netdev, req->link);
+        r = netdev_is_ready_to_create(netdev, link);
         if (r <= 0)
                 return r;
 
-        r = netdev_join(req->netdev, req->link, req->netlink_handler);
+        r = stacked_netdev_create(netdev, link, req);
         if (r < 0)
-                return log_link_error_errno(req->link, r, "Failed to create stacked netdev '%s': %m", req->netdev->ifname);
+                return log_netdev_warning_errno(netdev, r, "Failed to create netdev: %m");
 
         return 1;
 }
 
-static int link_create_stacked_netdev_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int create_stacked_netdev_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) {
         int r;
 
         assert(m);
         assert(link);
 
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 0;
-
         r = sd_netlink_message_get_errno(m);
         if (r < 0 && r != -EEXIST) {
                 log_link_message_warning_errno(link, m, r, "Could not create stacked netdev");
@@ -687,18 +679,6 @@ static int link_create_stacked_netdev_handler_internal(sd_netlink *rtnl, sd_netl
                 return 0;
         }
 
-        return 1;
-}
-
-static int link_create_stacked_netdev_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        assert(link);
-        assert(link->create_stacked_netdev_messages > 0);
-
-        link->create_stacked_netdev_messages--;
-
-        if (link_create_stacked_netdev_handler_internal(rtnl, m, link) <= 0)
-                return 0;
-
         if (link->create_stacked_netdev_messages == 0) {
                 link->stacked_netdevs_created = true;
                 log_link_debug(link, "Stacked netdevs created.");
@@ -708,23 +688,6 @@ static int link_create_stacked_netdev_handler(sd_netlink *rtnl, sd_netlink_messa
         return 0;
 }
 
-static int link_create_stacked_netdev_after_configured_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        assert(link);
-        assert(link->create_stacked_netdev_after_configured_messages > 0);
-
-        link->create_stacked_netdev_after_configured_messages--;
-
-        if (link_create_stacked_netdev_handler_internal(rtnl, m, link) <= 0)
-                return 0;
-
-        if (link->create_stacked_netdev_after_configured_messages == 0) {
-                link->stacked_netdevs_after_configured_created = true;
-                log_link_debug(link, "Stacked netdevs created.");
-        }
-
-        return 0;
-}
-
 int link_request_stacked_netdev(Link *link, NetDev *netdev) {
         int r;
 
@@ -737,63 +700,70 @@ int link_request_stacked_netdev(Link *link, NetDev *netdev) {
         if (!IN_SET(netdev->state, NETDEV_STATE_LOADING, NETDEV_STATE_FAILED) || netdev->ifindex > 0)
                 return 0; /* Already created. */
 
-        if (netdev_get_create_type(netdev) == NETDEV_CREATE_STACKED) {
-                link->stacked_netdevs_created = false;
-                r = link_queue_request(link, REQUEST_TYPE_NETDEV_STACKED, netdev_ref(netdev), true,
-                                       &link->create_stacked_netdev_messages,
-                                       link_create_stacked_netdev_handler,
-                                       NULL);
-        } else {
-                link->stacked_netdevs_after_configured_created = false;
-                r = link_queue_request(link, REQUEST_TYPE_NETDEV_STACKED, netdev_ref(netdev), true,
-                                       &link->create_stacked_netdev_after_configured_messages,
-                                       link_create_stacked_netdev_after_configured_handler,
-                                       NULL);
-        }
+        link->stacked_netdevs_created = false;
+        r = link_queue_request_full(link, REQUEST_TYPE_NETDEV_STACKED,
+                                    netdev, (mfree_func_t) netdev_unref,
+                                    trivial_hash_func, trivial_compare_func,
+                                    stacked_netdev_process_request,
+                                    &link->create_stacked_netdev_messages,
+                                    create_stacked_netdev_handler, NULL);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to request stacked netdev '%s': %m",
                                             netdev->ifname);
+        if (r == 0)
+                return 0;
 
+        netdev_ref(netdev);
         log_link_debug(link, "Requested stacked netdev '%s'", netdev->ifname);
-        return 0;
+        return 1;
 }
 
-int request_process_independent_netdev(Request *req) {
+static int independent_netdev_process_request(Request *req, Link *link, void *userdata) {
+        NetDev *netdev = ASSERT_PTR(userdata);
         int r;
 
-        assert(req);
-        assert(req->type == REQUEST_TYPE_NETDEV_INDEPENDENT);
-        assert(req->netdev);
+        assert(!link);
 
-        r = netdev_is_ready_to_create(req->netdev, NULL);
+        r = netdev_is_ready_to_create(netdev, NULL);
         if (r <= 0)
                 return r;
 
-        r = netdev_create(req->netdev, NULL, NULL);
+        r = independent_netdev_create(netdev);
         if (r < 0)
-                return r;
+                return log_netdev_warning_errno(netdev, r, "Failed to create netdev: %m");
 
         return 1;
 }
 
-static int netdev_request(NetDev *netdev) {
+static int netdev_request_to_create(NetDev *netdev) {
         int r;
 
         assert(netdev);
+        assert(netdev->manager);
 
-        if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_MASTER, NETDEV_CREATE_INDEPENDENT) &&
-            !netdev_is_stacked_and_independent(netdev))
+        if (netdev->manager->test_mode)
+                return 0;
+
+        if (netdev_is_stacked(netdev))
                 return 0;
 
         r = netdev_is_ready_to_create(netdev, NULL);
         if (r < 0)
                 return r;
-        if (r > 0)
+        if (r > 0) {
                 /* If the netdev has no dependency, then create it now. */
-                return netdev_create(netdev, NULL, NULL);
+                r = independent_netdev_create(netdev);
+                if (r < 0)
+                        return log_netdev_warning_errno(netdev, r, "Failed to create netdev: %m");
+
+        } else {
+                /* Otherwise, wait for the dependencies being resolved. */
+                r = netdev_queue_request(netdev, independent_netdev_process_request, NULL);
+                if (r < 0)
+                        return log_netdev_warning_errno(netdev, r, "Failed to request to create netdev: %m");
+        }
 
-        /* Otherwise, wait for the dependencies being resolved. */
-        return netdev_queue_request(netdev, NULL);
+        return 0;
 }
 
 int netdev_load_one(Manager *manager, const char *filename) {
@@ -805,10 +775,8 @@ int netdev_load_one(Manager *manager, const char *filename) {
         assert(filename);
 
         r = null_or_empty_path(filename);
-        if (r == -ENOENT)
-                return 0;
         if (r < 0)
-                return r;
+                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;
@@ -826,14 +794,15 @@ int netdev_load_one(Manager *manager, const char *filename) {
 
         dropin_dirname = strjoina(basename(filename), ".d");
         r = config_parse_many(
-                        STRV_MAKE_CONST(filename), NETWORK_DIRS, dropin_dirname,
+                        STRV_MAKE_CONST(filename), NETWORK_DIRS, dropin_dirname, /* root = */ NULL,
                         NETDEV_COMMON_SECTIONS NETDEV_OTHER_SECTIONS,
                         config_item_perf_lookup, network_netdev_gperf_lookup,
                         CONFIG_PARSE_WARN,
                         netdev_raw,
+                        NULL,
                         NULL);
         if (r < 0)
-                return r;
+                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)) {
@@ -841,15 +810,11 @@ int netdev_load_one(Manager *manager, const char *filename) {
                 return 0;
         }
 
-        if (netdev_raw->kind == _NETDEV_KIND_INVALID) {
-                log_warning("NetDev has no Kind= configured in %s. Ignoring", filename);
-                return 0;
-        }
+        if (netdev_raw->kind == _NETDEV_KIND_INVALID)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "NetDev has no Kind= configured in \"%s\", ignoring.", filename);
 
-        if (!netdev_raw->ifname) {
-                log_warning("NetDev without Name= configured in %s. Ignoring", filename);
-                return 0;
-        }
+        if (!netdev_raw->ifname)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "NetDev without Name= configured in \"%s\", ignoring.", filename);
 
         netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size);
         if (!netdev)
@@ -865,19 +830,19 @@ int netdev_load_one(Manager *manager, const char *filename) {
                 NETDEV_VTABLE(netdev)->init(netdev);
 
         r = config_parse_many(
-                        STRV_MAKE_CONST(filename), NETWORK_DIRS, dropin_dirname,
+                        STRV_MAKE_CONST(filename), NETWORK_DIRS, dropin_dirname, /* root = */ NULL,
                         NETDEV_VTABLE(netdev)->sections,
                         config_item_perf_lookup, network_netdev_gperf_lookup,
                         CONFIG_PARSE_WARN,
-                        netdev, NULL);
+                        netdev, NULL, NULL);
         if (r < 0)
-                return r;
+                return r; /* config_parse_many() logs internally. */
 
         /* verify configuration */
         if (NETDEV_VTABLE(netdev)->config_verify) {
                 r = NETDEV_VTABLE(netdev)->config_verify(netdev, filename);
                 if (r < 0)
-                        return 0;
+                        return r; /* config_verify() logs internally. */
         }
 
         netdev->filename = strdup(filename);
@@ -893,22 +858,21 @@ int netdev_load_one(Manager *manager, const char *filename) {
                 assert(n);
                 if (!streq(netdev->filename, n->filename))
                         log_netdev_warning_errno(netdev, r,
-                                                 "Device was already configured by file %s, ignoring %s.",
+                                                 "Device was already configured by \"%s\", ignoring %s.",
                                                  n->filename, netdev->filename);
 
                 /* Clear ifname before netdev_free() is called. Otherwise, the NetDev object 'n' is
                  * removed from the hashmap 'manager->netdevs'. */
                 netdev->ifname = mfree(netdev->ifname);
-                return 0;
+                return -EEXIST;
         }
-        if (r < 0)
-                return r;
+        assert(r > 0);
 
-        log_netdev_debug(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
+        log_netdev_debug(netdev, "loaded \"%s\"", netdev_kind_to_string(netdev->kind));
 
-        r = netdev_request(netdev);
+        r = netdev_request_to_create(netdev);
         if (r < 0)
-                return log_netdev_warning_errno(netdev, r, "Failed to request to create: %m");
+                return r; /* netdev_request_to_create() logs internally. */
 
         TAKE_PTR(netdev);
         return 0;
@@ -916,7 +880,6 @@ int netdev_load_one(Manager *manager, const char *filename) {
 
 int netdev_load(Manager *manager, bool reload) {
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         int r;
 
         assert(manager);
@@ -928,11 +891,8 @@ int netdev_load(Manager *manager, bool reload) {
         if (r < 0)
                 return log_error_errno(r, "Failed to enumerate netdev files: %m");
 
-        STRV_FOREACH(f, files) {
-                r = netdev_load_one(manager, *f);
-                if (r < 0)
-                        log_error_errno(r, "Failed to load %s, ignoring: %m", *f);
-        }
+        STRV_FOREACH(f, files)
+                (void) netdev_load_one(manager, *f);
 
         return 0;
 }
@@ -949,11 +909,10 @@ int config_parse_netdev_kind(
                 void *data,
                 void *userdata) {
 
-        NetDevKind k, *kind = data;
+        NetDevKind k, *kind = ASSERT_PTR(data);
 
         assert(filename);
         assert(rvalue);
-        assert(data);
 
         k = netdev_kind_from_string(rvalue);
         if (k < 0) {
@@ -985,10 +944,9 @@ int config_parse_netdev_hw_addr(
                 void *data,
                 void *userdata) {
 
-        struct hw_addr_data *hw_addr = data;
+        struct hw_addr_data *hw_addr = ASSERT_PTR(data);
 
         assert(rvalue);
-        assert(data);
 
         if (streq(rvalue, "none")) {
                 *hw_addr = HW_ADDR_NONE;