diff options
-rw-r--r-- | src/network/meson.build | 2 | ||||
-rw-r--r-- | src/network/networkd-dhcp4.c | 5 | ||||
-rw-r--r-- | src/network/networkd-link.c | 132 | ||||
-rw-r--r-- | src/network/networkd-link.h | 5 | ||||
-rw-r--r-- | src/network/networkd-queue.c | 36 | ||||
-rw-r--r-- | src/network/networkd-queue.h | 5 | ||||
-rw-r--r-- | src/network/networkd-setlink.c | 231 | ||||
-rw-r--r-- | src/network/networkd-setlink.h | 19 |
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); |