From 5388e103eaf2a31c0020ff8b8d9442426aee40ab Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jun 2021 05:24:35 +0900 Subject: [PATCH 1/2] network: check the size of hardware address before setting MAC address Also, skip to set MAC address when the current address equals to the requrested one. --- src/network/networkd-setlink.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 478591b5bb5..ec9eac47520 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -680,6 +680,16 @@ int link_request_to_set_mac(Link *link) { if (!link->network->mac) return 0; + if (link->hw_addr.length != sizeof(struct ether_addr)) { + /* Note that for now we only support changing hardware addresses on Ethernet. */ + log_link_debug(link, "Size of the hardware address (%zu) does not match the size of MAC address (%zu), ignoring.", + link->hw_addr.length, sizeof(struct ether_addr)); + return 0; + } + + if (ether_addr_equal(&link->hw_addr.ether, link->network->mac)) + return 0; + return link_request_set_link(link, SET_LINK_MAC, link_set_mac_handler, NULL); } From d05c332c8d9cbbde6504b6635dc3598a60ab189f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jun 2021 05:12:03 +0900 Subject: [PATCH 2/2] network: try to bring down before setting MAC address Most real network devices refuse to set MAC address when its operstate is not down. So, setting MAC address once failed, then let's bring down the interface and retry to set. Closes #6696. --- src/network/networkd-link.c | 2 +- src/network/networkd-setlink.c | 50 ++++++++++++++++++++++++++++++++-- src/network/networkd-setlink.h | 2 +- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 911fb9b302c..d9b94ebd800 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1052,7 +1052,7 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = link_request_to_set_mac(link); + r = link_request_to_set_mac(link, /* allow_retry = */ true); if (r < 0) return r; diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index ec9eac47520..43519beab74 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -147,6 +147,38 @@ static int link_set_mac_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l return set_link_handler_internal(rtnl, m, link, SET_LINK_MAC, true, get_link_default_handler); } +static int link_set_mac_allow_retry_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + assert(link->set_link_messages > 0); + + 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 == -EBUSY) { + /* Most real network devices refuse to set its hardware address with -EBUSY when its + * operstate is not down. See, eth_prepare_mac_addr_change() in net/ethernet/eth.c + * of kernel. */ + + log_link_message_debug_errno(link, m, r, "Failed to set MAC address, retrying again: %m"); + + r = link_request_to_set_mac(link, /* allow_retry = */ false); + if (r < 0) + link_enter_failed(link); + + return 0; + } + + /* set_link_mac_handler() also decrement set_link_messages, so once increment the value. */ + link->set_link_messages++; + return link_set_mac_handler(rtnl, m, link); +} + static int link_set_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { return set_link_handler_internal(rtnl, m, link, SET_LINK_MASTER, false, get_link_master_handler); } @@ -483,6 +515,18 @@ static bool link_is_ready_to_call_set_link(Request *req) { } } break; + case SET_LINK_MAC: + if (req->netlink_handler == link_set_mac_handler) { + /* This is the second trial to set MTU. On the first attempt + * req->netlink_handler points to link_set_mac_allow_retry_handler(). + * The first trial failed as the interface was up. */ + r = link_down(link); + if (r < 0) { + link_enter_failed(link); + return false; + } + } + break; case SET_LINK_MASTER: { uint32_t m = 0; @@ -673,7 +717,7 @@ int link_request_to_set_group(Link *link) { return link_request_set_link(link, SET_LINK_GROUP, link_set_group_handler, NULL); } -int link_request_to_set_mac(Link *link) { +int link_request_to_set_mac(Link *link, bool allow_retry) { assert(link); assert(link->network); @@ -690,7 +734,9 @@ int link_request_to_set_mac(Link *link) { if (ether_addr_equal(&link->hw_addr.ether, link->network->mac)) return 0; - return link_request_set_link(link, SET_LINK_MAC, link_set_mac_handler, NULL); + return link_request_set_link(link, SET_LINK_MAC, + allow_retry ? link_set_mac_allow_retry_handler : link_set_mac_handler, + NULL); } int link_request_to_set_master(Link *link) { diff --git a/src/network/networkd-setlink.h b/src/network/networkd-setlink.h index 0dc7415257f..87aa26e46f6 100644 --- a/src/network/networkd-setlink.h +++ b/src/network/networkd-setlink.h @@ -28,7 +28,7 @@ int link_request_to_set_bridge_vlan(Link *link); int link_request_to_set_can(Link *link); int link_request_to_set_flags(Link *link); int link_request_to_set_group(Link *link); -int link_request_to_set_mac(Link *link); +int link_request_to_set_mac(Link *link, bool allow_retry); int link_request_to_set_master(Link *link); int link_request_to_set_mtu(Link *link, uint32_t mtu);