diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 911fb9b302..d9b94ebd80 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 ec9eac4752..43519beab7 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 0dc7415257..87aa26e46f 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);