mirror of
https://github.com/systemd/systemd.git
synced 2024-11-27 04:03:36 +08:00
Merge pull request #32195 from yuwata/network-ndisc-mtu
network: several cleanups for IPv6 MTU
This commit is contained in:
commit
d0ea800943
@ -1881,7 +1881,7 @@ static int link_admin_state_up(Link *link) {
|
||||
|
||||
/* We set the ipv6 mtu after the device mtu, but the kernel resets
|
||||
* ipv6 mtu on NETDEV_UP, so we need to reset it. */
|
||||
r = link_set_ipv6_mtu(link);
|
||||
r = link_set_ipv6_mtu(link, LOG_INFO);
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
|
||||
|
||||
@ -2434,6 +2434,13 @@ static int link_update_mtu(Link *link, sd_netlink_message *message) {
|
||||
|
||||
link->mtu = mtu;
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
|
||||
/* The kernel resets IPv6 MTU after changing device MTU. So, we need to re-set IPv6 MTU again. */
|
||||
r = link_set_ipv6_mtu(link, LOG_INFO);
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m");
|
||||
}
|
||||
|
||||
if (link->dhcp_client) {
|
||||
r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
|
||||
if (r < 0)
|
||||
|
@ -167,6 +167,7 @@ typedef struct Link {
|
||||
Set *ndisc_captive_portals;
|
||||
Set *ndisc_pref64;
|
||||
Set *ndisc_redirects;
|
||||
uint32_t ndisc_mtu;
|
||||
unsigned ndisc_messages;
|
||||
bool ndisc_configured:1;
|
||||
|
||||
|
@ -1048,6 +1048,37 @@ static int ndisc_router_process_hop_limit(Link *link, sd_ndisc_router *rt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_router_process_mtu(Link *link, sd_ndisc_router *rt) {
|
||||
uint32_t mtu;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
assert(rt);
|
||||
|
||||
if (!link->network->ndisc_use_mtu)
|
||||
return 0;
|
||||
|
||||
/* Ignore the MTU option if the lifetime is zero. */
|
||||
r = sd_ndisc_router_get_lifetime(rt, NULL);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_router_get_mtu(rt, &mtu);
|
||||
if (r == -ENODATA)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to get MTU from RA: %m");
|
||||
|
||||
link->ndisc_mtu = mtu;
|
||||
|
||||
r = link_set_ipv6_mtu(link, LOG_DEBUG);
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Failed to apply IPv6 MTU (%"PRIu32"), ignoring: %m", mtu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
|
||||
usec_t lifetime_valid_usec, lifetime_preferred_usec;
|
||||
struct in6_addr prefix;
|
||||
@ -2122,6 +2153,10 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ndisc_router_process_mtu(link, rt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ndisc_router_process_options(link, rt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -2456,6 +2491,7 @@ void ndisc_flush(Link *link) {
|
||||
link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
|
||||
link->ndisc_pref64 = set_free(link->ndisc_pref64);
|
||||
link->ndisc_redirects = set_free(link->ndisc_redirects);
|
||||
link->ndisc_mtu = 0;
|
||||
}
|
||||
|
||||
static const char* const ndisc_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
|
||||
|
@ -173,19 +173,7 @@ static int link_unset_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Re
|
||||
}
|
||||
|
||||
static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) {
|
||||
int r;
|
||||
|
||||
r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler);
|
||||
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");
|
||||
|
||||
return 0;
|
||||
return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler);
|
||||
}
|
||||
|
||||
static int link_configure_fill_message(
|
||||
@ -453,6 +441,43 @@ static bool netdev_is_ready(NetDev *netdev) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t link_adjust_mtu(Link *link, uint32_t mtu) {
|
||||
const char *origin;
|
||||
uint32_t min_mtu;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
|
||||
min_mtu = link->min_mtu;
|
||||
origin = "the minimum MTU of the interface";
|
||||
if (link_ipv6_enabled(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 (min_mtu < IPV6_MIN_MTU) {
|
||||
min_mtu = IPV6_MIN_MTU;
|
||||
origin = "the minimum IPv6 MTU";
|
||||
}
|
||||
if (min_mtu < link->network->ipv6_mtu) {
|
||||
min_mtu = link->network->ipv6_mtu;
|
||||
origin = "the requested IPv6 MTU in IPv6MTUBytes=";
|
||||
}
|
||||
}
|
||||
|
||||
if (mtu < min_mtu) {
|
||||
log_link_warning(link, "Bumping the requested MTU %"PRIu32" to %s (%"PRIu32")",
|
||||
mtu, origin, min_mtu);
|
||||
mtu = min_mtu;
|
||||
}
|
||||
|
||||
if (mtu > link->max_mtu) {
|
||||
log_link_warning(link, "Reducing the requested MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
|
||||
mtu, link->max_mtu);
|
||||
mtu = link->max_mtu;
|
||||
}
|
||||
|
||||
return mtu;
|
||||
}
|
||||
|
||||
static int link_is_ready_to_set_link(Link *link, Request *req) {
|
||||
int r;
|
||||
|
||||
@ -570,13 +595,24 @@ static int link_is_ready_to_set_link(Link *link, Request *req) {
|
||||
}))
|
||||
return false;
|
||||
|
||||
/* Changing FD mode may affect MTU. */
|
||||
/* Changing FD mode may affect MTU.
|
||||
* See https://docs.kernel.org/networking/can.html#can-fd-flexible-data-rate-driver-support
|
||||
* MTU = 16 (CAN_MTU) => Classical CAN device
|
||||
* MTU = 72 (CANFD_MTU) => CAN FD capable device */
|
||||
if (ordered_set_contains(link->manager->request_queue,
|
||||
&(const Request) {
|
||||
.link = link,
|
||||
.type = REQUEST_TYPE_SET_LINK_CAN,
|
||||
}))
|
||||
return false;
|
||||
|
||||
/* Now, it is ready to set MTU, but before setting, adjust requested MTU. */
|
||||
uint32_t mtu = link_adjust_mtu(link, PTR_TO_UINT32(req->userdata));
|
||||
if (mtu == link->mtu)
|
||||
return -EALREADY; /* Not necessary to set the same value. */
|
||||
|
||||
req->userdata = UINT32_TO_PTR(mtu);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
@ -865,51 +901,12 @@ int link_request_to_set_master(Link *link) {
|
||||
}
|
||||
|
||||
int link_request_to_set_mtu(Link *link, uint32_t mtu) {
|
||||
const char *origin;
|
||||
uint32_t min_mtu, max_mtu;
|
||||
Request *req;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
|
||||
min_mtu = link->min_mtu;
|
||||
origin = "the minimum MTU of the interface";
|
||||
if (link_ipv6_enabled(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 (min_mtu < IPV6_MIN_MTU) {
|
||||
min_mtu = IPV6_MIN_MTU;
|
||||
origin = "the minimum IPv6 MTU";
|
||||
}
|
||||
if (min_mtu < link->network->ipv6_mtu) {
|
||||
min_mtu = link->network->ipv6_mtu;
|
||||
origin = "the requested IPv6 MTU in IPv6MTUBytes=";
|
||||
}
|
||||
}
|
||||
|
||||
if (mtu < min_mtu) {
|
||||
log_link_warning(link, "Bumping the requested MTU %"PRIu32" to %s (%"PRIu32")",
|
||||
mtu, origin, min_mtu);
|
||||
mtu = min_mtu;
|
||||
}
|
||||
|
||||
max_mtu = link->max_mtu;
|
||||
if (link->iftype == ARPHRD_CAN)
|
||||
/* The maximum MTU may be changed when FD mode is changed.
|
||||
* See https://docs.kernel.org/networking/can.html#can-fd-flexible-data-rate-driver-support
|
||||
* MTU = 16 (CAN_MTU) => Classical CAN device
|
||||
* MTU = 72 (CANFD_MTU) => CAN FD capable device
|
||||
* So, even if the current maximum is 16, we should not reduce the requested value now. */
|
||||
max_mtu = MAX(max_mtu, 72u);
|
||||
|
||||
if (mtu > max_mtu) {
|
||||
log_link_warning(link, "Reducing the requested MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
|
||||
mtu, max_mtu);
|
||||
mtu = max_mtu;
|
||||
}
|
||||
|
||||
if (link->mtu == mtu)
|
||||
if (mtu == 0)
|
||||
return 0;
|
||||
|
||||
r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_MTU,
|
||||
|
@ -250,22 +250,28 @@ static int link_set_ipv6_proxy_ndp(Link *link) {
|
||||
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v);
|
||||
}
|
||||
|
||||
int link_set_ipv6_mtu(Link *link) {
|
||||
uint32_t mtu;
|
||||
int link_set_ipv6_mtu(Link *link, int log_level) {
|
||||
uint32_t mtu = 0;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (!link_is_configured_for_family(link, AF_INET6))
|
||||
return 0;
|
||||
|
||||
if (link->network->ipv6_mtu == 0)
|
||||
assert(link->network);
|
||||
|
||||
if (link->network->ndisc_use_mtu)
|
||||
mtu = link->ndisc_mtu;
|
||||
if (mtu == 0)
|
||||
mtu = link->network->ipv6_mtu;
|
||||
if (mtu == 0)
|
||||
return 0;
|
||||
|
||||
mtu = link->network->ipv6_mtu;
|
||||
if (mtu > link->max_mtu) {
|
||||
log_link_warning(link, "Reducing requested IPv6 MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
|
||||
mtu, link->max_mtu);
|
||||
mtu = link->max_mtu;
|
||||
if (mtu > link->mtu) {
|
||||
log_link_full(link, log_level,
|
||||
"Reducing requested IPv6 MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
|
||||
mtu, link->mtu);
|
||||
mtu = link->mtu;
|
||||
}
|
||||
|
||||
return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu);
|
||||
@ -355,7 +361,7 @@ int link_set_sysctl(Link *link) {
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m");
|
||||
|
||||
r = link_set_ipv6_mtu(link);
|
||||
r = link_set_ipv6_mtu(link, LOG_INFO);
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
|
||||
|
||||
|
@ -31,7 +31,7 @@ void manager_set_sysctl(Manager *manager);
|
||||
|
||||
int link_get_ip_forwarding(Link *link, int family);
|
||||
int link_set_sysctl(Link *link);
|
||||
int link_set_ipv6_mtu(Link *link);
|
||||
int link_set_ipv6_mtu(Link *link, int log_level);
|
||||
|
||||
const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
|
||||
IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
|
||||
|
6
test/test-network/conf/25-veth-peer-no-address.network
Normal file
6
test/test-network/conf/25-veth-peer-no-address.network
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=veth-peer
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=no
|
@ -5572,6 +5572,48 @@ class NetworkdRATests(unittest.TestCase, Utilities):
|
||||
self.wait_route_dropped('veth99', 'proto redirect', ipv='-6', timeout_sec=10)
|
||||
self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10)
|
||||
|
||||
def check_ndisc_mtu(self, mtu):
|
||||
for _ in range(20):
|
||||
output = read_ipv6_sysctl_attr('veth99', 'mtu')
|
||||
if output == f'{mtu}':
|
||||
break
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
self.fail(f'IPv6 MTU does not matches: value={output}, expected={mtu}')
|
||||
|
||||
def test_ndisc_mtu(self):
|
||||
if not os.path.exists(test_ndisc_send):
|
||||
self.skipTest(f"{test_ndisc_send} does not exist.")
|
||||
|
||||
copy_network_unit('25-veth.netdev',
|
||||
'25-veth-peer-no-address.network',
|
||||
'25-ipv6-prefix-veth-token-static.network')
|
||||
start_networkd()
|
||||
self.wait_online('veth-peer:degraded')
|
||||
|
||||
for _ in range(20):
|
||||
output = read_networkd_log()
|
||||
if 'veth99: NDISC: Started IPv6 Router Solicitation client' in output:
|
||||
break
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
self.fail('sd-ndisc does not started on veth99.')
|
||||
|
||||
check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400')
|
||||
self.check_ndisc_mtu(1400)
|
||||
|
||||
check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410')
|
||||
self.check_ndisc_mtu(1410)
|
||||
|
||||
check_output('ip link set dev veth99 mtu 1600')
|
||||
self.check_ndisc_mtu(1410)
|
||||
|
||||
check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700')
|
||||
self.check_ndisc_mtu(1600)
|
||||
|
||||
check_output('ip link set dev veth99 mtu 1800')
|
||||
self.check_ndisc_mtu(1700)
|
||||
|
||||
def test_ipv6_token_prefixstable(self):
|
||||
copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
|
||||
start_networkd()
|
||||
|
Loading…
Reference in New Issue
Block a user