]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: use request queue to set MTU
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 20 May 2021 19:51:07 +0000 (04:51 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 7 Jun 2021 21:33:27 +0000 (06:33 +0900)
src/network/meson.build
src/network/networkd-dhcp4.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-queue.c
src/network/networkd-queue.h
src/network/networkd-setlink.c [new file with mode: 0644]
src/network/networkd-setlink.h [new file with mode: 0644]

index ad85ad3a63cc4c05741fb4d76201e795e3fbd664..a3482b4b964446771df35e3c81473bd6112a9ce0 100644 (file)
@@ -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
index 934ee05002d3ea034093c432ca61c1cc2b5cac12..4a11b916775ac2dbae70eaca9569be6b3ab37949 100644 (file)
@@ -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);
                 }
index 43c1b514ed4bd3be131c1ec0cf88f0a34dc9087b..fb2b9c2a3f31a4011fa8f61ca00b7065c352515d 100644 (file)
@@ -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;
index 0eca653e9e4ae393a3d06ea6966a1b212be46007..7da331139316a8e1a8031489c6cad203ba200466 100644 (file)
@@ -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);
index 01636468c6a5b16b9e02aa5babb0a5ad68899e8e..f05fb2221e902c474ada4e9ca5c32b51a2342867 100644 (file)
@@ -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;
                         }
index 9fe954aa83411bf7c4e649407a82595acc225e21..5d7c85b8a4527a92074a04110f448613cbdea4d7 100644 (file)
@@ -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 (file)
index 0000000..32866ee
--- /dev/null
@@ -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 (file)
index 0000000..e2d3288
--- /dev/null
@@ -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);