]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ethtool-util: fix setting advertising link modes
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 2 Sep 2025 14:41:18 +0000 (23:41 +0900)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 4 Sep 2025 10:57:10 +0000 (12:57 +0200)
Fixes a regression caused by d307410327d14398cb60b72db9d0034b12950a72.

The link_mode_masks flex array in struct ethtool_link_settings contains
three packed arrays, and the length of each array is given by
link_mode_masks_nwords field:
```
        __u32   link_mode_masks[];
        /* layout of link_mode_masks fields:
         * __u32 map_supported[link_mode_masks_nwords];
         * __u32 map_advertising[link_mode_masks_nwords];
         * __u32 map_lp_advertising[link_mode_masks_nwords];
         */
```
Hence, we cannot use the received data as is through the union, but need
to shift the array to make each map accessible through the union.

src/shared/ethtool-util.c

index bf0095a5ff6cee9946e66c49dedcf432b9c1b81a..d961a81eca90cc846fcddca409b0ad83d364ecf9 100644 (file)
@@ -687,10 +687,19 @@ static int get_glinksettings(int fd, struct ifreq *ifr, union ethtool_link_usett
         if (ecmd.base.link_mode_masks_nwords <= 0 || ecmd.base.cmd != ETHTOOL_GLINKSETTINGS)
                 return -EOPNOTSUPP;
 
-        union ethtool_link_usettings *u = newdup(union ethtool_link_usettings, &ecmd, 1);
+        union ethtool_link_usettings *u = new0(union ethtool_link_usettings, 1);
         if (!u)
                 return -ENOMEM;
 
+        u->base = ecmd.base;
+
+        uint32_t *p = ecmd.base.link_mode_masks;
+        memcpy(u->link_modes.supported, p, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
+        p += ecmd.base.link_mode_masks_nwords;
+        memcpy(u->link_modes.advertising, p, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
+        p += ecmd.base.link_mode_masks_nwords;
+        memcpy(u->link_modes.lp_advertising, p, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
+
         *ret = u;
         return 0;
 }
@@ -742,8 +751,14 @@ static int set_slinksettings(int fd, struct ifreq *ifr, const union ethtool_link
         if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
                 return -EINVAL;
 
-        union ethtool_link_usettings ecmd = *u;
+        union ethtool_link_usettings ecmd = { .base = u->base };
         ecmd.base.cmd = ETHTOOL_SLINKSETTINGS;
+
+        uint32_t *p = ecmd.base.link_mode_masks;
+        p = mempcpy(p, u->link_modes.supported, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
+        p = mempcpy(p, u->link_modes.advertising, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
+        memcpy(p, u->link_modes.lp_advertising, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
+
         ifr->ifr_data = (void *) &ecmd;
 
         return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));