]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
interfaces: use ethtool link mode bits for accurate MAU type selection (#771)
authorCiro Iriarte <ciro.iriarte@gmail.com>
Mon, 9 Mar 2026 20:52:54 +0000 (17:52 -0300)
committerGitHub <noreply@github.com>
Mon, 9 Mar 2026 20:52:54 +0000 (21:52 +0100)
* interfaces: use ethtool link mode bits for accurate MAU type selection

For speeds >= 10G, consult the ethtool supported link mode bitmask
to determine the correct MAU type instead of relying solely on
speed + port type. This fixes incorrect MAU type announcements
(e.g., 100G-SR4 transceivers being reported as 100G-LR4).

Fall back to the existing port-type approximation when no link mode
bit matches (e.g., legacy kernels without GLINKSETTINGS).

Add missing ethtool link mode bit definitions (bits 52-89) and speed
defines for 200G/400G to the local ethtool header.

Fixes: #477
* interfaces: move mau variable to function scope

Address review feedback: declare mau at the top of iflinux_macphy()
instead of inside each case block.

* interfaces: simplify mau assignment with combined if-assign pattern

include/linux/ethtool.h
src/daemon/interfaces-linux.c

index 6bfbb85f94022eba08af5cfcc5b4e2491285464c..5b5e6efbe2bdcdee19651aeb6f2a4a796b4662a7 100644 (file)
@@ -1456,6 +1456,48 @@ enum ethtool_link_mode_bit_indices {
        ETHTOOL_LINK_MODE_FEC_RS_BIT    = 50,
        ETHTOOL_LINK_MODE_FEC_BASER_BIT = 51,
 
+       ETHTOOL_LINK_MODE_50000baseKR_Full_BIT       = 52,
+       ETHTOOL_LINK_MODE_50000baseSR_Full_BIT       = 53,
+       ETHTOOL_LINK_MODE_50000baseCR_Full_BIT       = 54,
+       ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT = 55,
+       ETHTOOL_LINK_MODE_50000baseDR_Full_BIT       = 56,
+
+       ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT         = 57,
+       ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT         = 58,
+       ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT         = 59,
+       ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT = 60,
+       ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT         = 61,
+
+       ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT         = 62,
+       ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT         = 63,
+       ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT = 64,
+       ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT         = 65,
+       ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT         = 66,
+
+       ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT         = 69,
+       ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT         = 70,
+       ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71,
+       ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT         = 72,
+       ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT         = 73,
+
+       ETHTOOL_LINK_MODE_100000baseKR_Full_BIT          = 75,
+       ETHTOOL_LINK_MODE_100000baseSR_Full_BIT          = 76,
+       ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT    = 77,
+       ETHTOOL_LINK_MODE_100000baseCR_Full_BIT          = 78,
+       ETHTOOL_LINK_MODE_100000baseDR_Full_BIT          = 79,
+
+       ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT         = 80,
+       ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT         = 81,
+       ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT = 82,
+       ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT         = 83,
+       ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT         = 84,
+
+       ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT         = 85,
+       ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT         = 86,
+       ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT = 87,
+       ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT         = 88,
+       ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT         = 89,
+
        /* Last allowed bit for __ETHTOOL_LINK_MODE_LEGACY_MASK is bit
         * 31. Please do NOT define any SUPPORTED_* or ADVERTISED_*
         * macro for bits > 31. The only way to use indices > 31 is to
@@ -1463,7 +1505,7 @@ enum ethtool_link_mode_bit_indices {
         */
 
        __ETHTOOL_LINK_MODE_LAST
-         = ETHTOOL_LINK_MODE_FEC_BASER_BIT,
+         = ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT,
 };
 
 #define __ETHTOOL_LINK_MODE_LEGACY_MASK(base_name)     \
@@ -1571,6 +1613,8 @@ enum ethtool_link_mode_bit_indices {
 #define SPEED_50000            50000
 #define SPEED_56000            56000
 #define SPEED_100000           100000
+#define SPEED_200000           200000
+#define SPEED_400000           400000
 
 #define SPEED_UNKNOWN          -1
 
index d4f8634efe9711f8f6f2a73956272fa15d145bea..c8ada99f08de9671134296687727300dd4c7497a 100644 (file)
@@ -519,13 +519,114 @@ end:
        return rc;
 }
 
+struct ethtool_to_mau_type {
+       int ethtool_bit;
+       int mau_type;
+};
+
+static int
+iflinux_ethtool_to_mau_type(const struct ethtool_to_mau_type *map,
+    const uint32_t *supported)
+{
+       for (int i = 0; map[i].ethtool_bit >= 0; i++) {
+               if (iflinux_ethtool_link_mode_test_bit(
+                       map[i].ethtool_bit, supported))
+                       return map[i].mau_type;
+       }
+       return 0;
+}
+
+static const struct ethtool_to_mau_type ethtool_10g_to_mau[] = {
+       { ETHTOOL_LINK_MODE_10000baseT_Full_BIT, LLDP_DOT3_MAU_10GBASET },
+       { ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, LLDP_DOT3_MAU_10GBASEKX4 },
+       { ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, LLDP_DOT3_MAU_10GBASEKR },
+       { ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, LLDP_DOT3_MAU_10GIGBASECX4 },
+       { ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, LLDP_DOT3_MAU_10GIGBASESR },
+       { ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, LLDP_DOT3_MAU_10GIGBASELR },
+       { ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, LLDP_DOT3_MAU_10GBASELRM },
+       { ETHTOOL_LINK_MODE_10000baseER_Full_BIT, LLDP_DOT3_MAU_10GIGBASEER },
+       { -1, 0 }
+};
+
+static const struct ethtool_to_mau_type ethtool_25g_to_mau[] = {
+       { ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, LLDP_DOT3_MAU_25GBASECR },
+       { ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, LLDP_DOT3_MAU_25GBASEKR },
+       { ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, LLDP_DOT3_MAU_25GBASESR },
+       { -1, 0 }
+};
+
+static const struct ethtool_to_mau_type ethtool_40g_to_mau[] = {
+       { ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, LLDP_DOT3_MAU_40GBASEKR4 },
+       { ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, LLDP_DOT3_MAU_40GBASECR4 },
+       { ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, LLDP_DOT3_MAU_40GBASESR4 },
+       { ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, LLDP_DOT3_MAU_40GBASELR4 },
+       { -1, 0 }
+};
+
+static const struct ethtool_to_mau_type ethtool_50g_to_mau[] = {
+       { ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, LLDP_DOT3_MAU_50GBASECR },
+       { ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, LLDP_DOT3_MAU_50GBASEKR },
+       { ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, LLDP_DOT3_MAU_50GBASESR },
+       { ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, LLDP_DOT3_MAU_50GBASEKR },
+       { ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, LLDP_DOT3_MAU_50GBASESR },
+       { ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, LLDP_DOT3_MAU_50GBASECR },
+       { ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, LLDP_DOT3_MAU_50GBASELR },
+       { -1, 0 }
+};
+
+static const struct ethtool_to_mau_type ethtool_100g_to_mau[] = {
+       { ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, LLDP_DOT3_MAU_100GBASEKR4 },
+       { ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, LLDP_DOT3_MAU_100GBASESR4 },
+       { ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, LLDP_DOT3_MAU_100GBASECR4 },
+       { ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, LLDP_DOT3_MAU_100GBASELR4 },
+       { ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, LLDP_DOT3_MAU_100GBASEKR2 },
+       { ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, LLDP_DOT3_MAU_100GBASESR2 },
+       { ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, LLDP_DOT3_MAU_100GBASECR2 },
+       { ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT, LLDP_DOT3_MAU_100GBASER },
+       { ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, LLDP_DOT3_MAU_100GBASEDR },
+       { ETHTOOL_LINK_MODE_100000baseKR_Full_BIT, LLDP_DOT3_MAU_100GBASER },
+       { ETHTOOL_LINK_MODE_100000baseSR_Full_BIT, LLDP_DOT3_MAU_100GBASER },
+       { ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT, LLDP_DOT3_MAU_100GBASER },
+       { ETHTOOL_LINK_MODE_100000baseCR_Full_BIT, LLDP_DOT3_MAU_100GBASER },
+       { ETHTOOL_LINK_MODE_100000baseDR_Full_BIT, LLDP_DOT3_MAU_100GBASEDR },
+       { -1, 0 }
+};
+
+static const struct ethtool_to_mau_type ethtool_200g_to_mau[] = {
+       { ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT, LLDP_DOT3_MAU_200GBASEKR4 },
+       { ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT, LLDP_DOT3_MAU_200GBASESR4 },
+       { ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT, LLDP_DOT3_MAU_200GBASELR4 },
+       { ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT, LLDP_DOT3_MAU_200GBASEDR4 },
+       { ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, LLDP_DOT3_MAU_200GBASECR4 },
+       { ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT, LLDP_DOT3_MAU_200GBASER },
+       { ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, LLDP_DOT3_MAU_200GBASER },
+       { ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT, LLDP_DOT3_MAU_200GBASER },
+       { ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, LLDP_DOT3_MAU_200GBASER },
+       { ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT, LLDP_DOT3_MAU_200GBASER },
+       { -1, 0 }
+};
+
+static const struct ethtool_to_mau_type ethtool_400g_to_mau[] = {
+       { ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT, LLDP_DOT3_MAU_400GBASELR8 },
+       { ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, LLDP_DOT3_MAU_400GBASEDR4 },
+       { ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT, LLDP_DOT3_MAU_400GBASER },
+       { ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, LLDP_DOT3_MAU_400GBASER },
+       { ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, LLDP_DOT3_MAU_400GBASER },
+       { ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, LLDP_DOT3_MAU_400GBASER },
+       { ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, LLDP_DOT3_MAU_400GBASER },
+       { ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT, LLDP_DOT3_MAU_400GBASER },
+       { ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, LLDP_DOT3_MAU_400GBASER },
+       { ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, LLDP_DOT3_MAU_400GBASER },
+       { -1, 0 }
+};
+
 /* Fill up MAC/PHY for a given hardware port */
 static void
 iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
        struct ethtool_link_usettings uset = {};
        struct lldpd_port *port = &hardware->h_lport;
-       int j;
+       int j, mau;
        int advertised_ethtool_to_rfc3636[][2] = {
                { ETHTOOL_LINK_MODE_10baseT_Half_BIT, LLDP_DOT3_LINK_AUTONEG_10BASE_T },
                { ETHTOOL_LINK_MODE_10baseT_Full_BIT,
@@ -631,42 +732,70 @@ iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
                        port->p_macphy.mau_type = LLDP_DOT3_MAU_5GIGT;
                        break;
                case SPEED_10000:
-                       // Distinguish between RJ45 BaseT, DAC BaseCX4, or Fibre BaseLR
-                       if (uset.base.port == PORT_TP) {
+                       if ((mau = iflinux_ethtool_to_mau_type(
+                                ethtool_10g_to_mau, uset.link_modes.supported)))
+                               port->p_macphy.mau_type = mau;
+                       else if (uset.base.port == PORT_TP)
                                port->p_macphy.mau_type = LLDP_DOT3_MAU_10GBASET;
-                       } else if (uset.base.port == PORT_FIBRE) {
+                       else if (uset.base.port == PORT_FIBRE)
                                port->p_macphy.mau_type = LLDP_DOT3_MAU_10GIGBASELR;
-                       } else if (uset.base.port == PORT_DA) {
+                       else if (uset.base.port == PORT_DA)
                                port->p_macphy.mau_type = LLDP_DOT3_MAU_10GIGBASECX4;
-                       }
                        break;
                case SPEED_25000:
-                       // Distinguish between RJ45 BaseT, DAC BaseCR, or Fibre BaseLR
-                       if (uset.base.port == PORT_TP) {
+                       if ((mau = iflinux_ethtool_to_mau_type(
+                                ethtool_25g_to_mau, uset.link_modes.supported)))
+                               port->p_macphy.mau_type = mau;
+                       else if (uset.base.port == PORT_TP)
                                port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASET;
-                       } else if (uset.base.port == PORT_FIBRE) {
+                       else if (uset.base.port == PORT_FIBRE)
                                port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASELR;
-                       } else if (uset.base.port == PORT_DA) {
+                       else if (uset.base.port == PORT_DA)
                                port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASECR;
-                       }
                        break;
                case SPEED_40000:
-                       // Same kind of approximation.
-                       port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ?
-                           LLDP_DOT3_MAU_40GBASELR4 :
-                           LLDP_DOT3_MAU_40GBASECR4;
+                       if ((mau = iflinux_ethtool_to_mau_type(
+                                ethtool_40g_to_mau, uset.link_modes.supported)))
+                               port->p_macphy.mau_type = mau;
+                       else
+                               port->p_macphy.mau_type =
+                                   (uset.base.port == PORT_FIBRE) ?
+                                   LLDP_DOT3_MAU_40GBASELR4 :
+                                   LLDP_DOT3_MAU_40GBASECR4;
                        break;
                case SPEED_50000:
-                       // Same kind of approximation.
-                       port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ?
-                           LLDP_DOT3_MAU_50GBASELR :
-                           LLDP_DOT3_MAU_50GBASECR;
+                       if ((mau = iflinux_ethtool_to_mau_type(
+                                ethtool_50g_to_mau, uset.link_modes.supported)))
+                               port->p_macphy.mau_type = mau;
+                       else
+                               port->p_macphy.mau_type =
+                                   (uset.base.port == PORT_FIBRE) ?
+                                   LLDP_DOT3_MAU_50GBASELR :
+                                   LLDP_DOT3_MAU_50GBASECR;
                        break;
                case SPEED_100000:
-                       // Ditto
-                       port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ?
-                           LLDP_DOT3_MAU_100GBASELR4 :
-                           LLDP_DOT3_MAU_100GBASECR4;
+                       if ((mau = iflinux_ethtool_to_mau_type(
+                                ethtool_100g_to_mau, uset.link_modes.supported)))
+                               port->p_macphy.mau_type = mau;
+                       else
+                               port->p_macphy.mau_type =
+                                   (uset.base.port == PORT_FIBRE) ?
+                                   LLDP_DOT3_MAU_100GBASELR4 :
+                                   LLDP_DOT3_MAU_100GBASECR4;
+                       break;
+               case SPEED_200000:
+                       if ((mau = iflinux_ethtool_to_mau_type(
+                                ethtool_200g_to_mau, uset.link_modes.supported)))
+                               port->p_macphy.mau_type = mau;
+                       else
+                               port->p_macphy.mau_type = LLDP_DOT3_MAU_200GBASER;
+                       break;
+               case SPEED_400000:
+                       if ((mau = iflinux_ethtool_to_mau_type(
+                                ethtool_400g_to_mau, uset.link_modes.supported)))
+                               port->p_macphy.mau_type = mau;
+                       else
+                               port->p_macphy.mau_type = LLDP_DOT3_MAU_400GBASER;
                        break;
                }
                if (uset.base.port == PORT_AUI)