network: use request queue to set MTU

This commit is contained in:
Yu Watanabe
2021-06-08 06:33:27 +09:00
parent fa28381202
commit 0fa8ee6c77
8 changed files with 305 additions and 130 deletions
+2
View 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
+3 -2
View 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);
}
+19 -113
View 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;
+3 -2
View 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);
+23 -13
View 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;
}
+5
View 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;
+231
View File
@@ -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);
}
+19
View File
@@ -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);