summaryrefslogtreecommitdiffstats
path: root/src/network/netdev/netdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/netdev/netdev.c')
-rw-r--r--src/network/netdev/netdev.c221
1 files changed, 179 insertions, 42 deletions
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index 8e58a1ae12..85760c741d 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -34,6 +34,7 @@
#include "networkd-queue.h"
#include "networkd-setlink.h"
#include "networkd-sriov.h"
+#include "networkd-state-file.h"
#include "nlmon.h"
#include "path-lookup.h"
#include "siphash24.h"
@@ -241,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);
@@ -273,18 +275,17 @@ void netdev_drop(NetDev *netdev) {
netdev_detach(netdev);
}
-int netdev_attach_name(NetDev *netdev, const char *name) {
+static int netdev_attach_name_full(NetDev *netdev, const char *name, Hashmap **netdevs) {
int r;
assert(netdev);
- assert(netdev->manager);
assert(name);
- r = hashmap_ensure_put(&netdev->manager->netdevs, &string_hash_ops, name, netdev);
+ r = hashmap_ensure_put(netdevs, &string_hash_ops, name, netdev);
if (r == -ENOMEM)
return log_oom();
if (r == -EEXIST) {
- NetDev *n = hashmap_get(netdev->manager->netdevs, name);
+ NetDev *n = hashmap_get(*netdevs, name);
assert(n);
if (!streq(netdev->filename, n->filename))
@@ -299,6 +300,13 @@ int netdev_attach_name(NetDev *netdev, const char *name) {
return 0;
}
+int netdev_attach_name(NetDev *netdev, const char *name) {
+ assert(netdev);
+ assert(netdev->manager);
+
+ return netdev_attach_name_full(netdev, name, &netdev->manager->netdevs);
+}
+
static int netdev_attach(NetDev *netdev) {
int r;
@@ -345,16 +353,16 @@ void link_assign_netdev(Link *link) {
old = TAKE_PTR(link->netdev);
if (netdev_get(link->manager, link->ifname, &netdev) < 0)
- return;
+ goto not_found;
int ifindex = NETDEV_VTABLE(netdev)->get_ifindex ?
NETDEV_VTABLE(netdev)->get_ifindex(netdev, link->ifname) :
netdev->ifindex;
if (ifindex != link->ifindex)
- return;
+ goto not_found;
if (NETDEV_VTABLE(netdev)->iftype != link->iftype)
- return;
+ goto not_found;
if (!NETDEV_VTABLE(netdev)->skip_netdev_kind_check) {
const char *kind;
@@ -365,13 +373,23 @@ void link_assign_netdev(Link *link) {
kind = netdev_kind_to_string(netdev->kind);
if (!streq_ptr(kind, link->kind))
- return;
+ goto not_found;
}
link->netdev = netdev_ref(netdev);
- if (netdev != old)
- log_link_debug(link, "Found matching .netdev file: %s", netdev->filename);
+ if (netdev == old)
+ return; /* The same NetDev found. */
+
+ log_link_debug(link, "Found matching .netdev file: %s", netdev->filename);
+ link_dirty(link);
+ return;
+
+not_found:
+
+ if (old)
+ /* Previously assigned NetDev is detached from Manager? Update the state file. */
+ link_dirty(link);
}
void netdev_enter_failed(NetDev *netdev) {
@@ -395,6 +413,17 @@ int netdev_enter_ready(NetDev *netdev) {
return 0;
}
+bool netdev_needs_reconfigure(NetDev *netdev, NetDevLocalAddressType type) {
+ assert(netdev);
+ assert(type < _NETDEV_LOCAL_ADDRESS_TYPE_MAX);
+
+ if (type < 0)
+ return true;
+
+ return NETDEV_VTABLE(netdev)->needs_reconfigure &&
+ NETDEV_VTABLE(netdev)->needs_reconfigure(netdev, type);
+}
+
/* callback for netdev's created without a backing Link */
static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
int r;
@@ -605,6 +634,31 @@ finalize:
return 0;
}
+static bool netdev_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
+ assert(netdev);
+ assert(hw_addr);
+
+ if (hw_addr->length <= 0)
+ return false;
+
+ if (!NETDEV_VTABLE(netdev)->can_set_mac)
+ return true;
+
+ return NETDEV_VTABLE(netdev)->can_set_mac(netdev, hw_addr);
+}
+
+static bool netdev_can_set_mtu(NetDev *netdev, uint32_t mtu) {
+ assert(netdev);
+
+ if (mtu <= 0)
+ return false;
+
+ if (!NETDEV_VTABLE(netdev)->can_set_mtu)
+ return true;
+
+ return NETDEV_VTABLE(netdev)->can_set_mtu(netdev, mtu);
+}
+
static int netdev_create_message(NetDev *netdev, Link *link, sd_netlink_message *m) {
int r;
@@ -617,14 +671,14 @@ static int netdev_create_message(NetDev *netdev, Link *link, sd_netlink_message
if (r < 0)
return r;
- if (hw_addr.length > 0) {
+ if (netdev_can_set_mac(netdev, &hw_addr)) {
log_netdev_debug(netdev, "Using MAC address: %s", HW_ADDR_TO_STR(&hw_addr));
r = netlink_message_append_hw_addr(m, IFLA_ADDRESS, &hw_addr);
if (r < 0)
return r;
}
- if (netdev->mtu != 0) {
+ if (netdev_can_set_mtu(netdev, netdev->mtu)) {
r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu);
if (r < 0)
return r;
@@ -670,6 +724,7 @@ static int independent_netdev_create(NetDev *netdev) {
int r;
assert(netdev);
+ assert(netdev->manager);
/* create netdev */
if (NETDEV_VTABLE(netdev)->create) {
@@ -681,7 +736,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;
@@ -710,7 +765,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;
@@ -755,9 +810,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;
@@ -774,6 +826,9 @@ static int stacked_netdev_process_request(Request *req, Link *link, void *userda
assert(req);
assert(link);
+ if (!netdev_is_managed(netdev))
+ return 1; /* Already detached, due to e.g. reloading .netdev files, cancelling the request. */
+
r = netdev_is_ready_to_create(netdev, link);
if (r <= 0)
return r;
@@ -816,8 +871,8 @@ 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. */
link->stacked_netdevs_created = false;
r = link_queue_request_full(link, REQUEST_TYPE_NETDEV_STACKED,
@@ -843,6 +898,9 @@ static int independent_netdev_process_request(Request *req, Link *link, void *us
assert(!link);
+ if (!netdev_is_managed(netdev))
+ return 1; /* Already detached, due to e.g. reloading .netdev files, cancelling the request. */
+
r = netdev_is_ready_to_create(netdev, NULL);
if (r <= 0)
return r;
@@ -866,6 +924,12 @@ static int netdev_request_to_create(NetDev *netdev) {
if (netdev_is_stacked(netdev))
return 0;
+ 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;
@@ -885,21 +949,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)
@@ -924,10 +987,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);
@@ -954,7 +1015,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
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. */
@@ -970,35 +1031,111 @@ 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);
+ *ret = TAKE_PTR(netdev);
+ return 0;
+}
+
+int netdev_load(Manager *manager) {
+ _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 r; /* netdev_request_to_create() logs internally. */
+ return log_error_errno(r, "Failed to enumerate netdev files: %m");
+
+ 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);
+ }
- TAKE_PTR(netdev);
return 0;
}
-int netdev_load(Manager *manager, bool reload) {
+int netdev_reload(Manager *manager) {
+ _cleanup_hashmap_free_ Hashmap *new_netdevs = NULL;
_cleanup_strv_free_ char **files = NULL;
int r;
assert(manager);
- if (!reload)
- hashmap_clear_with_destructor(manager->netdevs, netdev_unref);
-
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)
- (void) netdev_load_one(manager, *f);
+ 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;
}