/* 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>
#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"
[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",
};
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) {
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))
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,
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);
}
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");
assert(ret);
netdev = hashmap_get(manager->netdevs, name);
- if (!netdev) {
- *ret = NULL;
+ if (!netdev)
return -ENOENT;
- }
*ret = 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;
}
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;
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) {
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);
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");
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.");
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;
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) {
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;
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)) {
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)
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);
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;
int netdev_load(Manager *manager, bool reload) {
_cleanup_strv_free_ char **files = NULL;
- char **f;
int r;
assert(manager);
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;
}
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) {
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;