diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2022-02-16 21:24:26 +0100 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2022-02-17 10:55:43 +0100 |
commit | e1658632aefc963c4a651de433cceb3a9512afd6 (patch) | |
tree | 7047cc6b0aab0acbebc84612c7dc1fda537bfa8f | |
parent | network: use udev_available() where applicable (diff) | |
download | systemd-e1658632aefc963c4a651de433cceb3a9512afd6.tar.xz systemd-e1658632aefc963c4a651de433cceb3a9512afd6.zip |
network: call ethtool after link is initialized by udevd
Fixes #22538.
-rw-r--r-- | src/network/networkd-link.c | 125 |
1 files changed, 87 insertions, 38 deletions
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 5706174f5b..6fdf800782 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2022,6 +2022,85 @@ static int link_update_master(Link *link, sd_netlink_message *message) { return 0; } +static int link_update_driver(Link *link, sd_netlink_message *message) { + int r; + + assert(link); + assert(link->manager); + assert(message); + + /* Driver is already read. Assuming the driver is never changed. */ + if (link->driver) + return 0; + + /* When udevd is running, read the driver after the interface is initialized by udevd. + * Otherwise, ethtool may not work correctly. See issue #22538. + * When udevd is not running, read the value when the interface is detected. */ + if (link->state != (udev_available() ? LINK_STATE_INITIALIZED : LINK_STATE_PENDING)) + return 0; + + r = ethtool_get_driver(&link->manager->ethtool_fd, link->ifname, &link->driver); + if (r < 0) { + log_link_debug_errno(link, r, "Failed to get driver, continuing without: %m"); + return 0; + } + + log_link_debug(link, "Found driver: %s", strna(link->driver)); + + if (streq_ptr(link->driver, "dsa")) { + uint32_t dsa_master_ifindex = 0; + + r = sd_netlink_message_read_u32(message, IFLA_LINK, &dsa_master_ifindex); + if (r < 0 && r != -ENODATA) + return log_link_debug_errno(link, r, "rtnl: failed to read ifindex of the DSA master interface: %m"); + + if (dsa_master_ifindex > INT_MAX) { + log_link_debug(link, "rtnl: received too large DSA master ifindex (%"PRIu32" > INT_MAX), ignoring.", + dsa_master_ifindex); + dsa_master_ifindex = 0; + } + + link->dsa_master_ifindex = (int) dsa_master_ifindex; + } + + return 0; +} + +static int link_update_permanent_hardware_address(Link *link, sd_netlink_message *message) { + int r; + + assert(link); + assert(link->manager); + assert(message); + + if (link->permanent_hw_addr.length > 0) + return 0; + + /* When udevd is running, read the permanent hardware address after the interface is + * initialized by udevd. Otherwise, ethtool may not work correctly. See issue #22538. + * When udevd is not running, read the value when the interface is detected. */ + if (link->state != (udev_available() ? LINK_STATE_INITIALIZED : LINK_STATE_PENDING)) + return 0; + + r = netlink_message_read_hw_addr(message, IFLA_PERM_ADDRESS, &link->permanent_hw_addr); + if (r < 0) { + if (r != -ENODATA) + return log_link_debug_errno(link, r, "Failed to read IFLA_PERM_ADDRESS attribute: %m"); + + if (netlink_message_read_hw_addr(message, IFLA_ADDRESS, NULL) >= 0) { + /* Fallback to ethtool, if the link has a hardware address. */ + r = ethtool_get_permanent_hw_addr(&link->manager->ethtool_fd, link->ifname, &link->permanent_hw_addr); + if (r < 0) + log_link_debug_errno(link, r, "Permanent hardware address not found, continuing without: %m"); + } + } + + if (link->permanent_hw_addr.length > 0) + log_link_debug(link, "Saved permanent hardware address: %s", HW_ADDR_TO_STR(&link->permanent_hw_addr)); + + return 0; +} + static int link_update_hardware_address(Link *link, sd_netlink_message *message) { struct hw_addr_data addr; int r; @@ -2309,6 +2388,14 @@ static int link_update(Link *link, sd_netlink_message *message) { if (r < 0) return r; + r = link_update_driver(link, message); + if (r < 0) + return r; + + r = link_update_permanent_hardware_address(link, message); + if (r < 0) + return r; + r = link_update_hardware_address(link, message); if (r < 0) return r; @@ -2421,44 +2508,6 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { log_link_debug(link, "Saved new link: ifindex=%i, iftype=%s(%u), kind=%s", link->ifindex, strna(arphrd_to_name(link->iftype)), link->iftype, strna(link->kind)); - r = netlink_message_read_hw_addr(message, IFLA_PERM_ADDRESS, &link->permanent_hw_addr); - if (r < 0) { - if (r != -ENODATA) - log_link_debug_errno(link, r, "Failed to read IFLA_PERM_ADDRESS attribute, ignoring: %m"); - - if (netlink_message_read_hw_addr(message, IFLA_ADDRESS, NULL) >= 0) { - /* Fallback to ethtool, if the link has a hardware address. */ - r = ethtool_get_permanent_hw_addr(&manager->ethtool_fd, link->ifname, &link->permanent_hw_addr); - if (r < 0) - log_link_debug_errno(link, r, "Permanent hardware address not found, continuing without: %m"); - } - } - if (link->permanent_hw_addr.length > 0) - log_link_debug(link, "Saved permanent hardware address: %s", HW_ADDR_TO_STR(&link->permanent_hw_addr)); - - r = ethtool_get_driver(&manager->ethtool_fd, link->ifname, &link->driver); - if (r < 0) - log_link_debug_errno(link, r, "Failed to get driver, continuing without: %m"); - else - log_link_debug(link, "Found driver: %s", strna(link->driver)); - - if (streq_ptr(link->driver, "dsa")) { - uint32_t dsa_master_ifindex; - - r = sd_netlink_message_read_u32(message, IFLA_LINK, &dsa_master_ifindex); - if (r < 0) { - dsa_master_ifindex = 0; - if (r != -ENODATA) - log_link_warning_errno(link, r, "rtnl: failed to read ifindex of the DSA master interface, ignoring: %m"); - } else if (dsa_master_ifindex > INT_MAX) { - dsa_master_ifindex = 0; - log_link_warning(link, "rtnl: received too large DSA master ifindex (%"PRIu32" > INT_MAX), ignoring.", - dsa_master_ifindex); - } - - link->dsa_master_ifindex = (int) dsa_master_ifindex; - } - *ret = TAKE_PTR(link); return 0; } |