d63baa
From 6d9a72f3b9b4d00ec80051503e5e3d4d7cd46c05 Mon Sep 17 00:00:00 2001
d63baa
From: Yu Watanabe <watanabe.yu+github@gmail.com>
d63baa
Date: Wed, 15 Sep 2021 01:28:29 +0900
d63baa
Subject: [PATCH 1/5] ethtool-util: use sizeof()
d63baa
d63baa
---
d63baa
 src/shared/ethtool-util.c | 20 ++++++++++----------
d63baa
 1 file changed, 10 insertions(+), 10 deletions(-)
d63baa
d63baa
diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
d63baa
index af3b917c75cb..d1f5eac63334 100644
d63baa
--- a/src/shared/ethtool-util.c
d63baa
+++ b/src/shared/ethtool-util.c
d63baa
@@ -214,7 +214,7 @@ int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
         if (r < 0)
d63baa
@@ -254,7 +254,7 @@ int ethtool_get_link_info(
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
         if (r < 0)
d63baa
@@ -303,7 +303,7 @@ int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct et
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
         if (r < 0)
d63baa
@@ -362,7 +362,7 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
         if (r < 0)
d63baa
@@ -405,7 +405,7 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
         if (r < 0)
d63baa
@@ -538,7 +538,7 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         r = get_stringset(*ethtool_fd, &ifr, ETH_SS_FEATURES, &strings);
d63baa
         if (r < 0)
d63baa
@@ -787,7 +787,7 @@ int ethtool_set_glinksettings(
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         r = get_glinksettings(*fd, &ifr, &u);
d63baa
         if (r < 0) {
d63baa
@@ -857,7 +857,7 @@ int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *cha
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         r = ioctl(*fd, SIOCETHTOOL, &ifr);
d63baa
         if (r < 0)
d63baa
@@ -906,7 +906,7 @@ int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int au
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         r = ioctl(*fd, SIOCETHTOOL, &ifr);
d63baa
         if (r < 0)
d63baa
@@ -974,7 +974,7 @@ int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
         if (r < 0)
d63baa
d63baa
From 4253dab576b3ff17887c3e0d97380aab2aa29d82 Mon Sep 17 00:00:00 2001
d63baa
From: Yu Watanabe <watanabe.yu+github@gmail.com>
d63baa
Date: Wed, 15 Sep 2021 01:41:15 +0900
d63baa
Subject: [PATCH 2/5] ethtool-util: shorten code a bit
d63baa
d63baa
Also fixes a error code in debugging log.
d63baa
---
d63baa
 src/shared/ethtool-util.c | 70 ++++++++++++---------------------------
d63baa
 1 file changed, 22 insertions(+), 48 deletions(-)
d63baa
d63baa
diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
d63baa
index d1f5eac63334..ac21ef0f61a8 100644
d63baa
--- a/src/shared/ethtool-util.c
d63baa
+++ b/src/shared/ethtool-util.c
d63baa
@@ -216,8 +216,7 @@ int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
d63baa
 
d63baa
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         if (isempty(ecmd.driver))
d63baa
@@ -256,8 +255,7 @@ int ethtool_get_link_info(
d63baa
 
d63baa
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         if (ret_autonegotiation)
d63baa
@@ -305,8 +303,7 @@ int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct et
d63baa
 
d63baa
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         if (epaddr.addr.size != 6)
d63baa
@@ -364,8 +361,7 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
d63baa
 
d63baa
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         UPDATE(ecmd.wolopts, wolopts, need_update);
d63baa
@@ -374,8 +370,7 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
d63baa
                 return 0;
d63baa
 
d63baa
         ecmd.cmd = ETHTOOL_SWOL;
d63baa
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         return 0;
d63baa
@@ -407,8 +402,7 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
d63baa
 
d63baa
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         if (ring->rx.set)
d63baa
@@ -427,8 +421,7 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
d63baa
                 return 0;
d63baa
 
d63baa
         ecmd.cmd = ETHTOOL_SRINGPARAM;
d63baa
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         return 0;
d63baa
@@ -446,7 +439,6 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
d63baa
                 },
d63baa
         };
d63baa
         unsigned len;
d63baa
-        int r;
d63baa
 
d63baa
         assert(ethtool_fd >= 0);
d63baa
         assert(ifr);
d63baa
@@ -454,8 +446,7 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
d63baa
 
d63baa
         ifr->ifr_data = (void *) &buffer.info;
d63baa
 
d63baa
-        r = ioctl(ethtool_fd, SIOCETHTOOL, ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(ethtool_fd, SIOCETHTOOL, ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         if (!buffer.info.sset_mask)
d63baa
@@ -478,8 +469,7 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
d63baa
 
d63baa
         ifr->ifr_data = (void *) strings;
d63baa
 
d63baa
-        r = ioctl(ethtool_fd, SIOCETHTOOL, ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(ethtool_fd, SIOCETHTOOL, ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         *ret = TAKE_PTR(strings);
d63baa
@@ -559,9 +549,8 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
d63baa
 
d63baa
         ifr.ifr_data = (void *) sfeatures;
d63baa
 
d63baa
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
-                return log_debug_errno(r, "ethtool: could not set ethtool features for %s", ifname);
d63baa
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
+                return log_debug_errno(errno, "ethtool: could not set ethtool features for %s", ifname);
d63baa
 
d63baa
         return 0;
d63baa
 }
d63baa
@@ -575,7 +564,6 @@ static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_uset
d63baa
         };
d63baa
         struct ethtool_link_usettings *u;
d63baa
         unsigned offset;
d63baa
-        int r;
d63baa
 
d63baa
         assert(fd >= 0);
d63baa
         assert(ifr);
d63baa
@@ -591,8 +579,7 @@ static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_uset
d63baa
 
d63baa
         ifr->ifr_data = (void *) &ecm;;
d63baa
 
d63baa
-        r = ioctl(fd, SIOCETHTOOL, ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
d63baa
@@ -602,8 +589,7 @@ static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_uset
d63baa
 
d63baa
         ifr->ifr_data = (void *) &ecm;;
d63baa
 
d63baa
-        r = ioctl(fd, SIOCETHTOOL, ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
d63baa
@@ -636,7 +622,6 @@ static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **r
d63baa
         struct ethtool_cmd ecmd = {
d63baa
                 .cmd = ETHTOOL_GSET,
d63baa
         };
d63baa
-        int r;
d63baa
 
d63baa
         assert(fd >= 0);
d63baa
         assert(ifr);
d63baa
@@ -644,8 +629,7 @@ static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **r
d63baa
 
d63baa
         ifr->ifr_data = (void *) &ecm;;
d63baa
 
d63baa
-        r = ioctl(fd, SIOCETHTOOL, ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         e = new(struct ethtool_link_usettings, 1);
d63baa
@@ -678,7 +662,6 @@ static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_lin
d63baa
                 __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
d63baa
         } ecmd = {};
d63baa
         unsigned offset;
d63baa
-        int r;
d63baa
 
d63baa
         assert(fd >= 0);
d63baa
         assert(ifr);
d63baa
@@ -700,8 +683,7 @@ static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_lin
d63baa
 
d63baa
         ifr->ifr_data = (void *) &ecm;;
d63baa
 
d63baa
-        r = ioctl(fd, SIOCETHTOOL, ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         return 0;
d63baa
@@ -711,7 +693,6 @@ static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettin
d63baa
         struct ethtool_cmd ecmd = {
d63baa
                 .cmd = ETHTOOL_SSET,
d63baa
         };
d63baa
-        int r;
d63baa
 
d63baa
         assert(fd >= 0);
d63baa
         assert(ifr);
d63baa
@@ -736,8 +717,7 @@ static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettin
d63baa
 
d63baa
         ifr->ifr_data = (void *) &ecm;;
d63baa
 
d63baa
-        r = ioctl(fd, SIOCETHTOOL, ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         return 0;
d63baa
@@ -859,8 +839,7 @@ int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *cha
d63baa
 
d63baa
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
-        r = ioctl(*fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         if (channels->rx.set)
d63baa
@@ -879,8 +858,7 @@ int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *cha
d63baa
                 return 0;
d63baa
 
d63baa
         ecmd.cmd = ETHTOOL_SCHANNELS;
d63baa
-        r = ioctl(*fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         return 0;
d63baa
@@ -908,8 +886,7 @@ int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int au
d63baa
 
d63baa
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
-        r = ioctl(*fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         if (rx >= 0)
d63baa
@@ -925,8 +902,7 @@ int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int au
d63baa
                 return 0;
d63baa
 
d63baa
         ecmd.cmd = ETHTOOL_SPAUSEPARAM;
d63baa
-        r = ioctl(*fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         return 0;
d63baa
@@ -976,8 +952,7 @@ int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const
d63baa
 
d63baa
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         if (coalesce->use_adaptive_rx_coalesce >= 0)
d63baa
@@ -1050,8 +1025,7 @@ int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const
d63baa
                 return 0;
d63baa
 
d63baa
         ecmd.cmd = ETHTOOL_SCOALESCE;
d63baa
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
d63baa
-        if (r < 0)
d63baa
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         return 0;
d63baa
d63baa
From 008d3a370ccdea13290ab9277b32cc582b886b17 Mon Sep 17 00:00:00 2001
d63baa
From: Yu Watanabe <watanabe.yu+github@gmail.com>
d63baa
Date: Tue, 14 Sep 2021 17:42:52 +0900
d63baa
Subject: [PATCH 3/5] ethtool: do not set unavailable or never_changed bits
d63baa
d63baa
---
d63baa
 src/shared/ethtool-util.c | 138 ++++++++++++++++++++++++++------------
d63baa
 1 file changed, 96 insertions(+), 42 deletions(-)
d63baa
d63baa
diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
d63baa
index ac21ef0f61a8..59b1bd86f085 100644
d63baa
--- a/src/shared/ethtool-util.c
d63baa
+++ b/src/shared/ethtool-util.c
d63baa
@@ -427,30 +427,31 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
d63baa
         return 0;
d63baa
 }
d63baa
 
d63baa
-static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **ret) {
d63baa
+static int get_stringset(int ethtool_fd, const char *ifname, enum ethtool_stringset stringset_id, struct ethtool_gstrings **ret) {
d63baa
         _cleanup_free_ struct ethtool_gstrings *strings = NULL;
d63baa
         struct {
d63baa
                 struct ethtool_sset_info info;
d63baa
                 uint32_t space;
d63baa
         } buffer = {
d63baa
-                .info = {
d63baa
-                        .cmd = ETHTOOL_GSSET_INFO,
d63baa
-                        .sset_mask = UINT64_C(1) << stringset_id,
d63baa
-                },
d63baa
+                .info.cmd = ETHTOOL_GSSET_INFO,
d63baa
+                .info.sset_mask = UINT64_C(1) << stringset_id,
d63baa
         };
d63baa
-        unsigned len;
d63baa
+        struct ifreq ifr = {
d63baa
+                .ifr_data = (void*) &buffer,
d63baa
+        };
d63baa
+        uint32_t len;
d63baa
 
d63baa
         assert(ethtool_fd >= 0);
d63baa
-        assert(ifr);
d63baa
+        assert(ifname);
d63baa
         assert(ret);
d63baa
 
d63baa
-        ifr->ifr_data = (void *) &buffer.info;
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
-        if (ioctl(ethtool_fd, SIOCETHTOOL, ifr) < 0)
d63baa
+        if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
-        if (!buffer.info.sset_mask)
d63baa
-                return -EINVAL;
d63baa
+        if (buffer.info.sset_mask == 0)
d63baa
+                return -EOPNOTSUPP;
d63baa
 
d63baa
 #pragma GCC diagnostic push
d63baa
 #if HAVE_ZERO_LENGTH_BOUNDS
d63baa
@@ -458,8 +459,10 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
d63baa
 #endif
d63baa
         len = buffer.info.data[0];
d63baa
 #pragma GCC diagnostic pop
d63baa
+        if (len == 0)
d63baa
+                return -EOPNOTSUPP;
d63baa
 
d63baa
-        strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN);
d63baa
+        strings = malloc0(offsetof(struct ethtool_gstrings, data) + len * ETH_GSTRING_LEN);
d63baa
         if (!strings)
d63baa
                 return -ENOMEM;
d63baa
 
d63baa
@@ -467,47 +470,92 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
d63baa
         strings->string_set = stringset_id;
d63baa
         strings->len = len;
d63baa
 
d63baa
-        ifr->ifr_data = (void *) strings;
d63baa
+        ifr.ifr_data = (void*) strings;
d63baa
 
d63baa
-        if (ioctl(ethtool_fd, SIOCETHTOOL, ifr) < 0)
d63baa
+        if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return -errno;
d63baa
 
d63baa
         *ret = TAKE_PTR(strings);
d63baa
+        return 0;
d63baa
+}
d63baa
+
d63baa
+static int get_features(int ethtool_fd, const char *ifname, uint32_t n_features, struct ethtool_gfeatures **ret) {
d63baa
+        _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
d63baa
+        struct ifreq ifr;
d63baa
+
d63baa
+        assert(ethtool_fd >= 0);
d63baa
+        assert(ifname);
d63baa
+        assert(ret);
d63baa
+        assert(n_features > 0);
d63baa
+
d63baa
+        gfeatures = malloc0(offsetof(struct ethtool_gfeatures, features) +
d63baa
+                            DIV_ROUND_UP(n_features, 32U) * sizeof(gfeatures->features[0]));
d63baa
+        if (!gfeatures)
d63baa
+                return -ENOMEM;
d63baa
+
d63baa
+        gfeatures->cmd = ETHTOOL_GFEATURES;
d63baa
+        gfeatures->size = DIV_ROUND_UP(n_features, 32U);
d63baa
+
d63baa
+        ifr = (struct ifreq) {
d63baa
+                .ifr_data = (void*) gfeatures,
d63baa
+        };
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
+
d63baa
+        if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
+                return -errno;
d63baa
 
d63baa
+        *ret = TAKE_PTR(gfeatures);
d63baa
         return 0;
d63baa
 }
d63baa
 
d63baa
 static int set_features_bit(
d63baa
                 const struct ethtool_gstrings *strings,
d63baa
+                const struct ethtool_gfeatures *gfeatures,
d63baa
+                struct ethtool_sfeatures *sfeatures,
d63baa
                 const char *feature,
d63baa
-                bool flag,
d63baa
-                struct ethtool_sfeatures *sfeatures) {
d63baa
+                int flag) {
d63baa
+
d63baa
         bool found = false;
d63baa
+        int r = -ENODATA;
d63baa
 
d63baa
         assert(strings);
d63baa
-        assert(feature);
d63baa
+        assert(gfeatures);
d63baa
         assert(sfeatures);
d63baa
+        assert(feature);
d63baa
+
d63baa
+        if (flag < 0)
d63baa
+                return 0;
d63baa
+
d63baa
+        for (uint32_t i = 0; i < strings->len; i++) {
d63baa
+                uint32_t block, mask;
d63baa
 
d63baa
-        for (size_t i = 0; i < strings->len; i++)
d63baa
-                if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature) ||
d63baa
-                    (endswith(feature, "-") && startswith((char *) &strings->data[i * ETH_GSTRING_LEN], feature))) {
d63baa
-                        size_t block, bit;
d63baa
+                if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN) &&
d63baa
+                    !(endswith(feature, "-") && startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature)))
d63baa
+                        continue;
d63baa
 
d63baa
-                        block = i / 32;
d63baa
-                        bit = i % 32;
d63baa
+                block = i / 32;
d63baa
+                mask = UINT32_C(1) << (i % 32);
d63baa
 
d63baa
-                        sfeatures->features[block].valid |= 1 << bit;
d63baa
-                        SET_FLAG(sfeatures->features[block].requested, 1 << bit, flag);
d63baa
-                        found = true;
d63baa
+                if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
d63baa
+                    FLAGS_SET(gfeatures->features[block].never_changed, mask)) {
d63baa
+                        r = -EOPNOTSUPP;
d63baa
+                        continue;
d63baa
                 }
d63baa
 
d63baa
-        return found ? 0 : -ENODATA;
d63baa
+                sfeatures->features[block].valid |= mask;
d63baa
+                SET_FLAG(sfeatures->features[block].requested, mask, flag);
d63baa
+
d63baa
+                found = true;
d63baa
+        }
d63baa
+
d63baa
+        return found ? 0 : r;
d63baa
 }
d63baa
 
d63baa
 int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]) {
d63baa
         _cleanup_free_ struct ethtool_gstrings *strings = NULL;
d63baa
-        struct ethtool_sfeatures *sfeatures;
d63baa
-        struct ifreq ifr = {};
d63baa
+        _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
d63baa
+        _cleanup_free_ struct ethtool_sfeatures *sfeatures = NULL;
d63baa
+        struct ifreq ifr;
d63baa
         bool have = false;
d63baa
         int r;
d63baa
 
d63baa
@@ -528,26 +576,32 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
d63baa
         if (r < 0)
d63baa
                 return r;
d63baa
 
d63baa
-        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
+        r = get_stringset(*ethtool_fd, ifname, ETH_SS_FEATURES, &strings);
d63baa
+        if (r < 0)
d63baa
+                return log_debug_errno(r, "ethtool: could not get ethtool feature strings: %m");
d63baa
 
d63baa
-        r = get_stringset(*ethtool_fd, &ifr, ETH_SS_FEATURES, &strings);
d63baa
+        r = get_features(*ethtool_fd, ifname, strings->len, &gfeatures);
d63baa
         if (r < 0)
d63baa
-                return log_debug_errno(r, "ethtool: could not get ethtool features for %s", ifname);
d63baa
+                return log_debug_errno(r, "ethtool: could not get ethtool features for %s: %m", ifname);
d63baa
+
d63baa
+        sfeatures = malloc0(offsetof(struct ethtool_sfeatures, features) +
d63baa
+                            DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
d63baa
+        if (!sfeatures)
d63baa
+                return log_oom_debug();
d63baa
 
d63baa
-        sfeatures = alloca0(sizeof(struct ethtool_sfeatures) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
d63baa
         sfeatures->cmd = ETHTOOL_SFEATURES;
d63baa
         sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
d63baa
 
d63baa
-        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
d63baa
-                if (features[i] >= 0) {
d63baa
-                        r = set_features_bit(strings, netdev_feature_table[i], features[i], sfeatures);
d63baa
-                        if (r < 0) {
d63baa
-                                log_debug_errno(r, "ethtool: could not find feature, ignoring: %s", netdev_feature_table[i]);
d63baa
-                                continue;
d63baa
-                        }
d63baa
-                }
d63baa
+        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++) {
d63baa
+                r = set_features_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
d63baa
+                if (r < 0)
d63baa
+                        log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
d63baa
+        }
d63baa
 
d63baa
-        ifr.ifr_data = (void *) sfeatures;
d63baa
+        ifr = (struct ifreq) {
d63baa
+                .ifr_data = (void*) sfeatures,
d63baa
+        };
d63baa
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
d63baa
 
d63baa
         if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
d63baa
                 return log_debug_errno(errno, "ethtool: could not set ethtool features for %s", ifname);
d63baa
d63baa
From 7a4f203547c62cdc7611f38d97058b530570048f Mon Sep 17 00:00:00 2001
d63baa
From: Yu Watanabe <watanabe.yu+github@gmail.com>
d63baa
Date: Wed, 15 Sep 2021 01:48:59 +0900
d63baa
Subject: [PATCH 4/5] ethtool-util: apply tx-checksum-* features at last
d63baa
d63baa
NET_DEV_FEAT_TX matches multiple features. In the next commit, all
d63baa
features whose strings start with "tx-checksum-" will be added.
d63baa
To make them take precedence over NET_DEV_FEAT_TX, it will be applied
d63baa
only when each explicit feature is not applied.
d63baa
---
d63baa
 src/shared/ethtool-util.c | 55 ++++++++++++++++++++++++++++++++++++---
d63baa
 src/shared/ethtool-util.h |  4 ++-
d63baa
 2 files changed, 54 insertions(+), 5 deletions(-)
d63baa
d63baa
diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
d63baa
index 59b1bd86f085..e95ce1a20917 100644
d63baa
--- a/src/shared/ethtool-util.c
d63baa
+++ b/src/shared/ethtool-util.c
d63baa
@@ -71,13 +71,14 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse P
d63baa
 
d63baa
 static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
d63baa
         [NET_DEV_FEAT_RX]     = "rx-checksum",
d63baa
-        [NET_DEV_FEAT_TX]     = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
d63baa
         [NET_DEV_FEAT_GSO]    = "tx-generic-segmentation",
d63baa
         [NET_DEV_FEAT_GRO]    = "rx-gro",
d63baa
         [NET_DEV_FEAT_GRO_HW] = "rx-gro-hw",
d63baa
         [NET_DEV_FEAT_LRO]    = "rx-lro",
d63baa
         [NET_DEV_FEAT_TSO]    = "tx-tcp-segmentation",
d63baa
         [NET_DEV_FEAT_TSO6]   = "tx-tcp6-segmentation",
d63baa
+
d63baa
+        [NET_DEV_FEAT_TX]     = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
d63baa
 };
d63baa
 
d63baa
 static const char* const ethtool_link_mode_bit_table[] = {
d63baa
@@ -515,6 +516,43 @@ static int set_features_bit(
d63baa
                 const char *feature,
d63baa
                 int flag) {
d63baa
 
d63baa
+        assert(strings);
d63baa
+        assert(gfeatures);
d63baa
+        assert(sfeatures);
d63baa
+        assert(feature);
d63baa
+
d63baa
+        if (flag < 0)
d63baa
+                return 0;
d63baa
+
d63baa
+        for (uint32_t i = 0; i < strings->len; i++) {
d63baa
+                uint32_t block, mask;
d63baa
+
d63baa
+                if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN))
d63baa
+                        continue;
d63baa
+
d63baa
+                block = i / 32;
d63baa
+                mask = UINT32_C(1) << (i % 32);
d63baa
+
d63baa
+                if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
d63baa
+                    FLAGS_SET(gfeatures->features[block].never_changed, mask))
d63baa
+                        return -EOPNOTSUPP;
d63baa
+
d63baa
+                sfeatures->features[block].valid |= mask;
d63baa
+                SET_FLAG(sfeatures->features[block].requested, mask, flag);
d63baa
+
d63baa
+                return 0;
d63baa
+        }
d63baa
+
d63baa
+        return -ENODATA;
d63baa
+}
d63baa
+
d63baa
+static int set_features_multiple_bit(
d63baa
+                const struct ethtool_gstrings *strings,
d63baa
+                const struct ethtool_gfeatures *gfeatures,
d63baa
+                struct ethtool_sfeatures *sfeatures,
d63baa
+                const char *feature,
d63baa
+                int flag) {
d63baa
+
d63baa
         bool found = false;
d63baa
         int r = -ENODATA;
d63baa
 
d63baa
@@ -529,8 +567,7 @@ static int set_features_bit(
d63baa
         for (uint32_t i = 0; i < strings->len; i++) {
d63baa
                 uint32_t block, mask;
d63baa
 
d63baa
-                if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN) &&
d63baa
-                    !(endswith(feature, "-") && startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature)))
d63baa
+                if (!startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature))
d63baa
                         continue;
d63baa
 
d63baa
                 block = i / 32;
d63baa
@@ -542,6 +579,10 @@ static int set_features_bit(
d63baa
                         continue;
d63baa
                 }
d63baa
 
d63baa
+                /* The flags is explicitly set by set_features_bit() */
d63baa
+                if (FLAGS_SET(sfeatures->features[block].valid, mask))
d63baa
+                        continue;
d63baa
+
d63baa
                 sfeatures->features[block].valid |= mask;
d63baa
                 SET_FLAG(sfeatures->features[block].requested, mask, flag);
d63baa
 
d63baa
@@ -592,12 +633,18 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
d63baa
         sfeatures->cmd = ETHTOOL_SFEATURES;
d63baa
         sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
d63baa
 
d63baa
-        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++) {
d63baa
+        for (size_t i = 0; i < _NET_DEV_FEAT_SIMPLE_MAX; i++) {
d63baa
                 r = set_features_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
d63baa
                 if (r < 0)
d63baa
                         log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
d63baa
         }
d63baa
 
d63baa
+        for (size_t i = _NET_DEV_FEAT_SIMPLE_MAX; i < _NET_DEV_FEAT_MAX; i++) {
d63baa
+                r = set_features_multiple_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
d63baa
+                if (r < 0)
d63baa
+                        log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
d63baa
+        }
d63baa
+
d63baa
         ifr = (struct ifreq) {
d63baa
                 .ifr_data = (void*) sfeatures,
d63baa
         };
d63baa
diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h
d63baa
index 6e180995055b..3f2252563304 100644
d63baa
--- a/src/shared/ethtool-util.h
d63baa
+++ b/src/shared/ethtool-util.h
d63baa
@@ -20,13 +20,15 @@ typedef enum Duplex {
d63baa
 
d63baa
 typedef enum NetDevFeature {
d63baa
         NET_DEV_FEAT_RX,
d63baa
-        NET_DEV_FEAT_TX,
d63baa
         NET_DEV_FEAT_GSO,
d63baa
         NET_DEV_FEAT_GRO,
d63baa
         NET_DEV_FEAT_GRO_HW,
d63baa
         NET_DEV_FEAT_LRO,
d63baa
         NET_DEV_FEAT_TSO,
d63baa
         NET_DEV_FEAT_TSO6,
d63baa
+        _NET_DEV_FEAT_SIMPLE_MAX,
d63baa
+
d63baa
+        NET_DEV_FEAT_TX = _NET_DEV_FEAT_SIMPLE_MAX,
d63baa
         _NET_DEV_FEAT_MAX,
d63baa
         _NET_DEV_FEAT_INVALID = -EINVAL,
d63baa
 } NetDevFeature;
d63baa
d63baa
From 77bf5c31de1d01edd49ac6aa25cdbe7734a11a25 Mon Sep 17 00:00:00 2001
d63baa
From: Yu Watanabe <watanabe.yu+github@gmail.com>
d63baa
Date: Tue, 14 Sep 2021 22:12:42 +0900
d63baa
Subject: [PATCH 5/5] ethtool-util: add more network device features
d63baa
d63baa
Then, we can easily add new settings to configure features in .link
d63baa
file.
d63baa
---
d63baa
 src/shared/ethtool-util.c            | 73 ++++++++++++++++++++++++----
d63baa
 src/shared/ethtool-util.h            | 59 +++++++++++++++++++++-
d63baa
 src/udev/net/link-config-gperf.gperf |  4 +-
d63baa
 3 files changed, 123 insertions(+), 13 deletions(-)
d63baa
d63baa
diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
d63baa
index e95ce1a20917..00060abff40f 100644
d63baa
--- a/src/shared/ethtool-util.c
d63baa
+++ b/src/shared/ethtool-util.c
d63baa
@@ -70,15 +70,70 @@ DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
d63baa
 DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
d63baa
 
d63baa
 static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
d63baa
-        [NET_DEV_FEAT_RX]     = "rx-checksum",
d63baa
-        [NET_DEV_FEAT_GSO]    = "tx-generic-segmentation",
d63baa
-        [NET_DEV_FEAT_GRO]    = "rx-gro",
d63baa
-        [NET_DEV_FEAT_GRO_HW] = "rx-gro-hw",
d63baa
-        [NET_DEV_FEAT_LRO]    = "rx-lro",
d63baa
-        [NET_DEV_FEAT_TSO]    = "tx-tcp-segmentation",
d63baa
-        [NET_DEV_FEAT_TSO6]   = "tx-tcp6-segmentation",
d63baa
-
d63baa
-        [NET_DEV_FEAT_TX]     = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
d63baa
+        [NET_DEV_FEAT_SG]                  = "tx-scatter-gather",
d63baa
+        [NET_DEV_FEAT_IP_CSUM]             = "tx-checksum-ipv4",
d63baa
+        [NET_DEV_FEAT_HW_CSUM]             = "tx-checksum-ip-generic",
d63baa
+        [NET_DEV_FEAT_IPV6_CSUM]           = "tx-checksum-ipv6",
d63baa
+        [NET_DEV_FEAT_HIGHDMA]             = "highdma",
d63baa
+        [NET_DEV_FEAT_FRAGLIST]            = "tx-scatter-gather-fraglist",
d63baa
+        [NET_DEV_FEAT_HW_VLAN_CTAG_TX]     = "tx-vlan-hw-insert",
d63baa
+        [NET_DEV_FEAT_HW_VLAN_CTAG_RX]     = "rx-vlan-hw-parse",
d63baa
+        [NET_DEV_FEAT_HW_VLAN_CTAG_FILTER] = "rx-vlan-filter",
d63baa
+        [NET_DEV_FEAT_HW_VLAN_STAG_TX]     = "tx-vlan-stag-hw-insert",
d63baa
+        [NET_DEV_FEAT_HW_VLAN_STAG_RX]     = "rx-vlan-stag-hw-parse",
d63baa
+        [NET_DEV_FEAT_HW_VLAN_STAG_FILTER] = "rx-vlan-stag-filter",
d63baa
+        [NET_DEV_FEAT_VLAN_CHALLENGED]     = "vlan-challenged",
d63baa
+        [NET_DEV_FEAT_GSO]                 = "tx-generic-segmentation",
d63baa
+        [NET_DEV_FEAT_LLTX]                = "tx-lockless",
d63baa
+        [NET_DEV_FEAT_NETNS_LOCAL]         = "netns-local",
d63baa
+        [NET_DEV_FEAT_GRO]                 = "rx-gro",
d63baa
+        [NET_DEV_FEAT_GRO_HW]              = "rx-gro-hw",
d63baa
+        [NET_DEV_FEAT_LRO]                 = "rx-lro",
d63baa
+        [NET_DEV_FEAT_TSO]                 = "tx-tcp-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_ROBUST]          = "tx-gso-robust",
d63baa
+        [NET_DEV_FEAT_TSO_ECN]             = "tx-tcp-ecn-segmentation",
d63baa
+        [NET_DEV_FEAT_TSO_MANGLEID]        = "tx-tcp-mangleid-segmentation",
d63baa
+        [NET_DEV_FEAT_TSO6]                = "tx-tcp6-segmentation",
d63baa
+        [NET_DEV_FEAT_FSO]                 = "tx-fcoe-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_GRE]             = "tx-gre-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_GRE_CSUM]        = "tx-gre-csum-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_IPXIP4]          = "tx-ipxip4-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_IPXIP6]          = "tx-ipxip6-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_UDP_TUNNEL]      = "tx-udp_tnl-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_UDP_TUNNEL_CSUM] = "tx-udp_tnl-csum-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_PARTIAL]         = "tx-gso-partial",
d63baa
+        [NET_DEV_FEAT_GSO_TUNNEL_REMCSUM]  = "tx-tunnel-remcsum-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_SCTP]            = "tx-sctp-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_ESP]             = "tx-esp-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_UDP_L4]          = "tx-udp-segmentation",
d63baa
+        [NET_DEV_FEAT_GSO_FRAGLIST]        = "tx-gso-list",
d63baa
+        [NET_DEV_FEAT_FCOE_CRC]            = "tx-checksum-fcoe-crc",
d63baa
+        [NET_DEV_FEAT_SCTP_CRC]            = "tx-checksum-sctp",
d63baa
+        [NET_DEV_FEAT_FCOE_MTU]            = "fcoe-mtu",
d63baa
+        [NET_DEV_FEAT_NTUPLE]              = "rx-ntuple-filter",
d63baa
+        [NET_DEV_FEAT_RXHASH]              = "rx-hashing",
d63baa
+        [NET_DEV_FEAT_RXCSUM]              = "rx-checksum",
d63baa
+        [NET_DEV_FEAT_NOCACHE_COPY]        = "tx-nocache-copy",
d63baa
+        [NET_DEV_FEAT_LOOPBACK]            = "loopback",
d63baa
+        [NET_DEV_FEAT_RXFCS]               = "rx-fcs",
d63baa
+        [NET_DEV_FEAT_RXALL]               = "rx-all",
d63baa
+        [NET_DEV_FEAT_HW_L2FW_DOFFLOAD]    = "l2-fwd-offload",
d63baa
+        [NET_DEV_FEAT_HW_TC]               = "hw-tc-offload",
d63baa
+        [NET_DEV_FEAT_HW_ESP]              = "esp-hw-offload",
d63baa
+        [NET_DEV_FEAT_HW_ESP_TX_CSUM]      = "esp-tx-csum-hw-offload",
d63baa
+        [NET_DEV_FEAT_RX_UDP_TUNNEL_PORT]  = "rx-udp_tunnel-port-offload",
d63baa
+        [NET_DEV_FEAT_HW_TLS_RECORD]       = "tls-hw-record",
d63baa
+        [NET_DEV_FEAT_HW_TLS_TX]           = "tls-hw-tx-offload",
d63baa
+        [NET_DEV_FEAT_HW_TLS_RX]           = "tls-hw-rx-offload",
d63baa
+        [NET_DEV_FEAT_GRO_FRAGLIST]        = "rx-gro-list",
d63baa
+        [NET_DEV_FEAT_HW_MACSEC]           = "macsec-hw-offload",
d63baa
+        [NET_DEV_FEAT_GRO_UDP_FWD]         = "rx-udp-gro-forwarding",
d63baa
+        [NET_DEV_FEAT_HW_HSR_TAG_INS]      = "hsr-tag-ins-offload",
d63baa
+        [NET_DEV_FEAT_HW_HSR_TAG_RM]       = "hsr-tag-rm-offload",
d63baa
+        [NET_DEV_FEAT_HW_HSR_FWD]          = "hsr-fwd-offload",
d63baa
+        [NET_DEV_FEAT_HW_HSR_DUP]          = "hsr-dup-offload",
d63baa
+
d63baa
+        [NET_DEV_FEAT_TXCSUM]              = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
d63baa
 };
d63baa
 
d63baa
 static const char* const ethtool_link_mode_bit_table[] = {
d63baa
diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h
d63baa
index 3f2252563304..cc0655893175 100644
d63baa
--- a/src/shared/ethtool-util.h
d63baa
+++ b/src/shared/ethtool-util.h
d63baa
@@ -19,16 +19,71 @@ typedef enum Duplex {
d63baa
 } Duplex;
d63baa
 
d63baa
 typedef enum NetDevFeature {
d63baa
-        NET_DEV_FEAT_RX,
d63baa
+        NET_DEV_FEAT_SG,
d63baa
+        NET_DEV_FEAT_IP_CSUM,
d63baa
+        NET_DEV_FEAT_HW_CSUM,
d63baa
+        NET_DEV_FEAT_IPV6_CSUM,
d63baa
+        NET_DEV_FEAT_HIGHDMA,
d63baa
+        NET_DEV_FEAT_FRAGLIST,
d63baa
+        NET_DEV_FEAT_HW_VLAN_CTAG_TX,
d63baa
+        NET_DEV_FEAT_HW_VLAN_CTAG_RX,
d63baa
+        NET_DEV_FEAT_HW_VLAN_CTAG_FILTER,
d63baa
+        NET_DEV_FEAT_HW_VLAN_STAG_TX,
d63baa
+        NET_DEV_FEAT_HW_VLAN_STAG_RX,
d63baa
+        NET_DEV_FEAT_HW_VLAN_STAG_FILTER,
d63baa
+        NET_DEV_FEAT_VLAN_CHALLENGED,
d63baa
         NET_DEV_FEAT_GSO,
d63baa
+        NET_DEV_FEAT_LLTX,
d63baa
+        NET_DEV_FEAT_NETNS_LOCAL,
d63baa
         NET_DEV_FEAT_GRO,
d63baa
         NET_DEV_FEAT_GRO_HW,
d63baa
         NET_DEV_FEAT_LRO,
d63baa
         NET_DEV_FEAT_TSO,
d63baa
+        NET_DEV_FEAT_GSO_ROBUST,
d63baa
+        NET_DEV_FEAT_TSO_ECN,
d63baa
+        NET_DEV_FEAT_TSO_MANGLEID,
d63baa
         NET_DEV_FEAT_TSO6,
d63baa
+        NET_DEV_FEAT_FSO,
d63baa
+        NET_DEV_FEAT_GSO_GRE,
d63baa
+        NET_DEV_FEAT_GSO_GRE_CSUM,
d63baa
+        NET_DEV_FEAT_GSO_IPXIP4,
d63baa
+        NET_DEV_FEAT_GSO_IPXIP6,
d63baa
+        NET_DEV_FEAT_GSO_UDP_TUNNEL,
d63baa
+        NET_DEV_FEAT_GSO_UDP_TUNNEL_CSUM,
d63baa
+        NET_DEV_FEAT_GSO_PARTIAL,
d63baa
+        NET_DEV_FEAT_GSO_TUNNEL_REMCSUM,
d63baa
+        NET_DEV_FEAT_GSO_SCTP,
d63baa
+        NET_DEV_FEAT_GSO_ESP,
d63baa
+        NET_DEV_FEAT_GSO_UDP_L4,
d63baa
+        NET_DEV_FEAT_GSO_FRAGLIST,
d63baa
+        NET_DEV_FEAT_FCOE_CRC,
d63baa
+        NET_DEV_FEAT_SCTP_CRC,
d63baa
+        NET_DEV_FEAT_FCOE_MTU,
d63baa
+        NET_DEV_FEAT_NTUPLE,
d63baa
+        NET_DEV_FEAT_RXHASH,
d63baa
+        NET_DEV_FEAT_RXCSUM,
d63baa
+        NET_DEV_FEAT_NOCACHE_COPY,
d63baa
+        NET_DEV_FEAT_LOOPBACK,
d63baa
+        NET_DEV_FEAT_RXFCS,
d63baa
+        NET_DEV_FEAT_RXALL,
d63baa
+        NET_DEV_FEAT_HW_L2FW_DOFFLOAD,
d63baa
+        NET_DEV_FEAT_HW_TC,
d63baa
+        NET_DEV_FEAT_HW_ESP,
d63baa
+        NET_DEV_FEAT_HW_ESP_TX_CSUM,
d63baa
+        NET_DEV_FEAT_RX_UDP_TUNNEL_PORT,
d63baa
+        NET_DEV_FEAT_HW_TLS_RECORD,
d63baa
+        NET_DEV_FEAT_HW_TLS_TX,
d63baa
+        NET_DEV_FEAT_HW_TLS_RX,
d63baa
+        NET_DEV_FEAT_GRO_FRAGLIST,
d63baa
+        NET_DEV_FEAT_HW_MACSEC,
d63baa
+        NET_DEV_FEAT_GRO_UDP_FWD,
d63baa
+        NET_DEV_FEAT_HW_HSR_TAG_INS,
d63baa
+        NET_DEV_FEAT_HW_HSR_TAG_RM,
d63baa
+        NET_DEV_FEAT_HW_HSR_FWD,
d63baa
+        NET_DEV_FEAT_HW_HSR_DUP,
d63baa
         _NET_DEV_FEAT_SIMPLE_MAX,
d63baa
 
d63baa
-        NET_DEV_FEAT_TX = _NET_DEV_FEAT_SIMPLE_MAX,
d63baa
+        NET_DEV_FEAT_TXCSUM = _NET_DEV_FEAT_SIMPLE_MAX,
d63baa
         _NET_DEV_FEAT_MAX,
d63baa
         _NET_DEV_FEAT_INVALID = -EINVAL,
d63baa
 } NetDevFeature;
d63baa
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
d63baa
index 44b46cb17c0b..e3cdaaee0509 100644
d63baa
--- a/src/udev/net/link-config-gperf.gperf
d63baa
+++ b/src/udev/net/link-config-gperf.gperf
d63baa
@@ -50,8 +50,8 @@ Link.Duplex,                              config_parse_duplex,
d63baa
 Link.AutoNegotiation,                     config_parse_tristate,                 0,                             offsetof(LinkConfig, autonegotiation)
d63baa
 Link.WakeOnLan,                           config_parse_wol,                      0,                             offsetof(LinkConfig, wol)
d63baa
 Link.Port,                                config_parse_port,                     0,                             offsetof(LinkConfig, port)
d63baa
-Link.ReceiveChecksumOffload,              config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_RX])
d63baa
-Link.TransmitChecksumOffload,             config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TX])
d63baa
+Link.ReceiveChecksumOffload,              config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_RXCSUM])
d63baa
+Link.TransmitChecksumOffload,             config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TXCSUM])
d63baa
 Link.GenericSegmentationOffload,          config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_GSO])
d63baa
 Link.TCPSegmentationOffload,              config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TSO])
d63baa
 Link.TCP6SegmentationOffload,             config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TSO6])