summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/network/meson.build2
-rw-r--r--src/network/networkd-dhcp4.c5
-rw-r--r--src/network/networkd-link.c132
-rw-r--r--src/network/networkd-link.h5
-rw-r--r--src/network/networkd-queue.c36
-rw-r--r--src/network/networkd-queue.h5
-rw-r--r--src/network/networkd-setlink.c231
-rw-r--r--src/network/networkd-setlink.h19
8 files changed, 305 insertions, 130 deletions
diff --git a/src/network/meson.build b/src/network/meson.build
index ad85ad3a63..a3482b4b96 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -115,6 +115,8 @@ sources = files('''
networkd-route.h
networkd-routing-policy-rule.c
networkd-routing-policy-rule.h
+ networkd-setlink.c
+ networkd-setlink.h
networkd-speed-meter.c
networkd-speed-meter.h
networkd-sriov.c
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 934ee05002..4a11b91677 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -20,6 +20,7 @@
#include "networkd-nexthop.h"
#include "networkd-queue.h"
#include "networkd-route.h"
+#include "networkd-setlink.h"
#include "networkd-state-file.h"
#include "string-table.h"
#include "strv.h"
@@ -685,7 +686,7 @@ static int dhcp_reset_mtu(Link *link) {
if (link->original_mtu == mtu)
return 0;
- r = link_set_mtu(link, link->original_mtu);
+ r = link_request_to_set_mtu(link, link->original_mtu);
if (r < 0)
return log_link_error_errno(link, r, "DHCP error: could not reset MTU: %m");
@@ -1129,7 +1130,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
r = sd_dhcp_lease_get_mtu(lease, &mtu);
if (r >= 0) {
- r = link_set_mtu(link, mtu);
+ r = link_request_to_set_mtu(link, mtu);
if (r < 0)
log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
}
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 43c1b514ed..fb2b9c2a3f 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -45,6 +45,7 @@
#include "networkd-queue.h"
#include "networkd-radv.h"
#include "networkd-routing-policy-rule.h"
+#include "networkd-setlink.h"
#include "networkd-sriov.h"
#include "networkd-state-file.h"
#include "networkd-sysctl.h"
@@ -151,6 +152,9 @@ bool link_is_ready_to_configure(Link *link, bool allow_unmanaged) {
if (!link_has_carrier(link) && !link->network->configure_without_carrier)
return false;
+ if (link->set_link_messages > 0)
+ return false;
+
return true;
}
@@ -970,118 +974,6 @@ static int link_set_nomaster(Link *link) {
return 0;
}
-static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- int r;
-
- assert(m);
- assert(link);
- assert(link->ifname);
-
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 0;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0) {
- log_link_message_warning_errno(link, m, r, "Could not set MTU, ignoring");
- return 0;
- }
-
- log_link_debug(link, "Setting MTU done.");
-
- /* The kernel resets ipv6 mtu after changing device mtu;
- * we must set this here, after we've set device mtu */
- r = link_set_ipv6_mtu(link);
- if (r < 0)
- log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
-
- return 0;
-}
-
-int link_set_mtu(Link *link, uint32_t mtu) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
- int r;
-
- assert(link);
- assert(link->manager);
- assert(link->manager->rtnl);
-
- if (mtu == 0)
- return 0;
-
- if (link->mtu == mtu)
- return 0;
-
- log_link_debug(link, "Setting MTU: %" PRIu32, mtu);
-
- r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
-
- /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes
- * on the interface. Bump up MTU bytes to IPV6_MTU_MIN. */
- if (link_ipv6_enabled(link) && mtu < IPV6_MIN_MTU) {
-
- log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as "
- "IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes");
-
- mtu = IPV6_MIN_MTU;
- }
-
- r = sd_netlink_message_append_u32(req, IFLA_MTU, mtu);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append MTU: %m");
-
- r = netlink_call_async(link->manager->rtnl, NULL, req, set_mtu_handler,
- link_netlink_destroy_callback, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
-
- link_ref(link);
-
- return 0;
-}
-
-static bool link_reduces_vlan_mtu(Link *link) {
- /* See netif_reduces_vlan_mtu() in kernel. */
- return streq_ptr(link->kind, "macsec");
-}
-
-static uint32_t link_get_requested_mtu_by_stacked_netdevs(Link *link) {
- uint32_t mtu = 0;
- NetDev *dev;
-
- HASHMAP_FOREACH(dev, link->network->stacked_netdevs)
- if (dev->kind == NETDEV_KIND_VLAN && dev->mtu > 0)
- /* See vlan_dev_change_mtu() in kernel. */
- mtu = MAX(mtu, link_reduces_vlan_mtu(link) ? dev->mtu + 4 : dev->mtu);
-
- else if (dev->kind == NETDEV_KIND_MACVLAN && dev->mtu > mtu)
- /* See macvlan_change_mtu() in kernel. */
- mtu = dev->mtu;
-
- return mtu;
-}
-
-static int link_configure_mtu(Link *link) {
- uint32_t mtu;
-
- assert(link);
- assert(link->network);
-
- if (link->network->mtu > 0)
- return link_set_mtu(link, link->network->mtu);
-
- mtu = link_get_requested_mtu_by_stacked_netdevs(link);
- if (link->mtu >= mtu)
- return 0;
-
- log_link_notice(link, "Bumping MTU bytes from %"PRIu32" to %"PRIu32" because of stacked device. "
- "If it is not desired, then please explicitly specify MTUBytes= setting.",
- link->mtu, mtu);
-
- return link_set_mtu(link, mtu);
-}
-
static int set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
@@ -1892,14 +1784,28 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
return 1;
}
-static int link_enter_join_netdev(Link *link) {
+int link_enter_join_netdev(Link *link) {
NetDev *netdev;
+ Request req;
int r;
assert(link);
assert(link->network);
assert(link->state == LINK_STATE_INITIALIZED);
+ req = (Request) {
+ .link = link,
+ .type = REQUEST_TYPE_SET_LINK,
+ .set_link_operation = SET_LINK_MTU,
+ };
+
+ if (ordered_set_contains(link->manager->request_queue, &req)) {
+ link->entering_to_join_netdev = true;
+ return 0;
+ }
+
+ link->entering_to_join_netdev = false;
+
link_set_state(link, LINK_STATE_CONFIGURING);
link->enslaving = 0;
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 0eca653e9e..7da3311393 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -95,6 +95,7 @@ typedef struct Link {
unsigned tc_messages;
unsigned sr_iov_messages;
unsigned enslaving;
+ unsigned set_link_messages;
Set *addresses;
Set *addresses_foreign;
@@ -136,6 +137,7 @@ typedef struct Link {
bool sr_iov_configured:1;
bool can_configured:1;
bool activated:1;
+ bool entering_to_join_netdev:1;
sd_dhcp_server *dhcp_server;
@@ -218,6 +220,7 @@ int link_up(Link *link);
int link_down(Link *link, link_netlink_message_handler_t callback);
int link_activate(Link *link);
+int link_enter_join_netdev(Link *link);
void link_enter_failed(Link *link);
void link_set_state(Link *link, LinkState state);
@@ -232,8 +235,6 @@ bool link_ipv6_enabled(Link *link);
bool link_ipv6ll_enabled(Link *link);
int link_ipv6ll_gained(Link *link, const struct in6_addr *address);
-int link_set_mtu(Link *link, uint32_t mtu);
-
bool link_ipv4ll_enabled(Link *link);
int link_stop_engines(Link *link, bool may_keep_dhcp);
diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c
index 01636468c6..f05fb2221e 100644
--- a/src/network/networkd-queue.c
+++ b/src/network/networkd-queue.c
@@ -44,6 +44,8 @@ static void request_free_object(RequestType type, void *object) {
case REQUEST_TYPE_ROUTING_POLICY_RULE:
routing_policy_rule_free(object);
break;
+ case REQUEST_TYPE_SET_LINK:
+ break;
default:
assert_not_reached("invalid request type.");
}
@@ -113,6 +115,9 @@ static void request_hash_func(const Request *req, struct siphash *state) {
case REQUEST_TYPE_ROUTING_POLICY_RULE:
routing_policy_rule_hash_func(req->rule, state);
break;
+ case REQUEST_TYPE_SET_LINK:
+ siphash24_compress(&req->set_link_operation, sizeof(req->set_link_operation), state);
+ break;
default:
assert_not_reached("invalid request type.");
}
@@ -153,6 +158,8 @@ static int request_compare_func(const struct Request *a, const struct Request *b
return route_compare_func(a->route, b->route);
case REQUEST_TYPE_ROUTING_POLICY_RULE:
return routing_policy_rule_compare_func(a->rule, b->rule);
+ case REQUEST_TYPE_SET_LINK:
+ return CMP(a->set_link_operation, b->set_link_operation);
default:
assert_not_reached("invalid request type.");
}
@@ -175,15 +182,14 @@ int link_queue_request(
Request **ret) {
_cleanup_(request_freep) Request *req = NULL;
+ Request *existing;
int r;
assert(link);
assert(link->manager);
assert(type >= 0 && type < _REQUEST_TYPE_MAX);
- if (type != REQUEST_TYPE_DHCP_SERVER) {
- assert(object);
- assert(netlink_handler);
- }
+ assert(IN_SET(type, REQUEST_TYPE_DHCP_SERVER, REQUEST_TYPE_SET_LINK) || object);
+ assert(type == REQUEST_TYPE_DHCP_SERVER || netlink_handler);
req = new(Request, 1);
if (!req) {
@@ -201,19 +207,20 @@ int link_queue_request(
.netlink_handler = netlink_handler,
};
- r = ordered_set_ensure_put(&link->manager->request_queue, &request_hash_ops, req);
- if (r < 0) {
- /* To prevent from removing duplicated request. */
+ existing = ordered_set_get(link->manager->request_queue, req);
+ if (existing) {
+ /* To prevent from removing the existing request. */
req->link = link_unref(req->link);
- if (r == -EEXIST) {
- if (ret)
- *ret = NULL;
- return 0;
- }
- return r;
+ if (ret)
+ *ret = existing;
+ return 0;
}
+ r = ordered_set_ensure_put(&link->manager->request_queue, &request_hash_ops, req);
+ if (r < 0)
+ return r;
+
if (req->message_counter)
(*req->message_counter)++;
@@ -266,6 +273,9 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
case REQUEST_TYPE_ROUTING_POLICY_RULE:
r = request_process_routing_policy_rule(req);
break;
+ case REQUEST_TYPE_SET_LINK:
+ r = request_process_set_link(req);
+ break;
default:
return -EINVAL;
}
diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h
index 9fe954aa83..5d7c85b8a4 100644
--- a/src/network/networkd-queue.h
+++ b/src/network/networkd-queue.h
@@ -4,6 +4,7 @@
#include "sd-event.h"
#include "networkd-link.h"
+#include "networkd-setlink.h"
typedef struct Address Address;
typedef struct AddressLabel AddressLabel;
@@ -30,10 +31,13 @@ typedef enum RequestType {
REQUEST_TYPE_NEXTHOP,
REQUEST_TYPE_ROUTE,
REQUEST_TYPE_ROUTING_POLICY_RULE,
+ REQUEST_TYPE_SET_LINK,
_REQUEST_TYPE_MAX,
_REQUEST_TYPE_INVALID = -EINVAL,
} RequestType;
+assert_cc(sizeof(SetLinkOperation) <= sizeof(void*));
+
typedef struct Request {
Link *link;
RequestType type;
@@ -48,6 +52,7 @@ typedef struct Request {
NextHop *nexthop;
Route *route;
RoutingPolicyRule *rule;
+ SetLinkOperation set_link_operation;
void *object;
};
void *userdata;
diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c
new file mode 100644
index 0000000000..32866ee5de
--- /dev/null
+++ b/src/network/networkd-setlink.c
@@ -0,0 +1,231 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "missing_network.h"
+#include "netlink-util.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "networkd-queue.h"
+#include "string-table.h"
+
+static const char *const set_link_operation_table[_SET_LINK_OPERATION_MAX] = {
+ [SET_LINK_MTU] = "MTU",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(set_link_operation, SetLinkOperation);
+
+static int set_link_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, SetLinkOperation op, bool ignore) {
+ int r;
+
+ assert(m);
+ assert(link);
+ assert(link->set_link_messages > 0);
+ assert(op >= 0 && op < _SET_LINK_OPERATION_MAX);
+
+ link->set_link_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 0;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0) {
+ const char *error_msg;
+
+ error_msg = strjoina("Failed to set ", set_link_operation_to_string(op), ignore ? ", ignoring" : "");
+ log_link_message_warning_errno(link, m, r, error_msg);
+
+ if (!ignore)
+ link_enter_failed(link);
+ return 0;
+ }
+
+ log_link_debug(link, "%s set.", set_link_operation_to_string(op));
+ return 1;
+}
+
+static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ r = set_link_handler_internal(rtnl, m, link, SET_LINK_MTU, true);
+ if (r <= 0)
+ return r;
+
+ /* The kernel resets ipv6 mtu after changing device mtu;
+ * we must set this here, after we've set device mtu */
+ r = link_set_ipv6_mtu(link);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m");
+
+ if (link->entering_to_join_netdev) {
+ r = link_enter_join_netdev(link);
+ if (r < 0)
+ link_enter_failed(link);
+ }
+
+ return 0;
+}
+
+static int link_configure(
+ Link *link,
+ SetLinkOperation op,
+ void *userdata,
+ link_netlink_message_handler_t callback) {
+
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+ assert(link->network);
+ assert(op >= 0 && op < _SET_LINK_OPERATION_MAX);
+ assert(callback);
+
+ log_link_debug(link, "Setting %s", set_link_operation_to_string(op));
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
+
+ switch (op) {
+ case SET_LINK_MTU:
+ r = sd_netlink_message_append_u32(req, IFLA_MTU, PTR_TO_UINT32(userdata));
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_MTU attribute: %m");
+ break;
+ default:
+ assert_not_reached("Invalid set link operation");
+ }
+
+ r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
+ link_netlink_destroy_callback, link);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not send RTM_SETLINK message: %m");
+
+ link_ref(link);
+ return 0;
+}
+
+static bool link_is_ready_to_call_set_link(Request *req) {
+ Link *link;
+
+ assert(req);
+
+ link = req->link;
+
+ if (!IN_SET(link->state, LINK_STATE_INITIALIZED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return false;
+
+ return true;
+}
+
+int request_process_set_link(Request *req) {
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_SET_LINK);
+ assert(req->set_link_operation >= 0 && req->set_link_operation < _SET_LINK_OPERATION_MAX);
+ assert(req->netlink_handler);
+
+ if (!link_is_ready_to_call_set_link(req))
+ return 0;
+
+ r = link_configure(req->link, req->set_link_operation, req->userdata, req->netlink_handler);
+ if (r < 0)
+ return log_link_error_errno(req->link, r, "Failed to set %s: %m",
+ set_link_operation_to_string(req->set_link_operation));
+
+ return 1;
+}
+
+static int link_request_set_link(
+ Link *link,
+ SetLinkOperation op,
+ link_netlink_message_handler_t netlink_handler,
+ Request **ret) {
+
+ Request *req;
+ int r;
+
+ assert(link);
+ assert(op >= 0 && op < _SET_LINK_OPERATION_MAX);
+ assert(netlink_handler);
+
+ r = link_queue_request(link, REQUEST_TYPE_SET_LINK, INT_TO_PTR(op), false,
+ &link->set_link_messages, netlink_handler, &req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to request to set %s: %m",
+ set_link_operation_to_string(op));
+
+ log_link_debug(link, "Requested to set %s", set_link_operation_to_string(op));
+
+ if (ret)
+ *ret = req;
+ return 0;
+}
+
+int link_request_to_set_mtu(Link *link, uint32_t mtu) {
+ Request *req = NULL; /* avoid false maybe-uninitialized warning */
+ int r;
+
+ assert(link);
+
+ /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes on the interface. Bump up
+ * MTU bytes to IPV6_MTU_MIN. */
+ if (mtu < IPV6_MIN_MTU && link_ipv6_enabled(link)) {
+ log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as IPv6 is enabled "
+ "and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes");
+ mtu = IPV6_MIN_MTU;
+ }
+
+ if (link->mtu == mtu)
+ return 0;
+
+ r = link_request_set_link(link, SET_LINK_MTU, link_set_mtu_handler, &req);
+ if (r < 0)
+ return r;
+
+ req->userdata = UINT32_TO_PTR(mtu);
+ return 0;
+}
+
+static bool link_reduces_vlan_mtu(Link *link) {
+ /* See netif_reduces_vlan_mtu() in kernel. */
+ return streq_ptr(link->kind, "macsec");
+}
+
+static uint32_t link_get_requested_mtu_by_stacked_netdevs(Link *link) {
+ uint32_t mtu = 0;
+ NetDev *dev;
+
+ HASHMAP_FOREACH(dev, link->network->stacked_netdevs)
+ if (dev->kind == NETDEV_KIND_VLAN && dev->mtu > 0)
+ /* See vlan_dev_change_mtu() in kernel. */
+ mtu = MAX(mtu, link_reduces_vlan_mtu(link) ? dev->mtu + 4 : dev->mtu);
+
+ else if (dev->kind == NETDEV_KIND_MACVLAN && dev->mtu > mtu)
+ /* See macvlan_change_mtu() in kernel. */
+ mtu = dev->mtu;
+
+ return mtu;
+}
+
+int link_configure_mtu(Link *link) {
+ uint32_t mtu;
+
+ assert(link);
+ assert(link->network);
+
+ if (link->network->mtu > 0)
+ return link_request_to_set_mtu(link, link->network->mtu);
+
+ mtu = link_get_requested_mtu_by_stacked_netdevs(link);
+ if (link->mtu >= mtu)
+ return 0;
+
+ log_link_notice(link, "Bumping MTU bytes from %"PRIu32" to %"PRIu32" because of stacked device. "
+ "If it is not desired, then please explicitly specify MTUBytes= setting.",
+ link->mtu, mtu);
+
+ return link_request_to_set_mtu(link, mtu);
+}
diff --git a/src/network/networkd-setlink.h b/src/network/networkd-setlink.h
new file mode 100644
index 0000000000..e2d32886db
--- /dev/null
+++ b/src/network/networkd-setlink.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+
+typedef struct Link Link;
+typedef struct Request Request;
+
+typedef enum SetLinkOperation {
+ SET_LINK_MTU, /* Setting MTU. */
+ _SET_LINK_OPERATION_MAX,
+ _SET_LINK_OPERATION_INVALID = -EINVAL,
+} SetLinkOperation;
+
+int link_request_to_set_mtu(Link *link, uint32_t mtu);
+
+int link_configure_mtu(Link *link);
+
+int request_process_set_link(Request *req);