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.
This commit is contained in:
Yu Watanabe 2021-06-13 05:12:03 +09:00
parent 5388e103ea
commit d05c332c8d
3 changed files with 50 additions and 4 deletions

View file

@ -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;

View file

@ -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) {

View file

@ -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);