Blob Blame History Raw
From 6d9a72f3b9b4d00ec80051503e5e3d4d7cd46c05 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Wed, 15 Sep 2021 01:28:29 +0900
Subject: [PATCH 1/5] ethtool-util: use sizeof()

---
 src/shared/ethtool-util.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
index af3b917c75cb..d1f5eac63334 100644
--- a/src/shared/ethtool-util.c
+++ b/src/shared/ethtool-util.c
@@ -214,7 +214,7 @@ int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
         if (r < 0)
@@ -254,7 +254,7 @@ int ethtool_get_link_info(
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
         if (r < 0)
@@ -303,7 +303,7 @@ int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct et
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
         if (r < 0)
@@ -362,7 +362,7 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
         if (r < 0)
@@ -405,7 +405,7 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
         if (r < 0)
@@ -538,7 +538,7 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         r = get_stringset(*ethtool_fd, &ifr, ETH_SS_FEATURES, &strings);
         if (r < 0)
@@ -787,7 +787,7 @@ int ethtool_set_glinksettings(
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         r = get_glinksettings(*fd, &ifr, &u);
         if (r < 0) {
@@ -857,7 +857,7 @@ int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *cha
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         r = ioctl(*fd, SIOCETHTOOL, &ifr);
         if (r < 0)
@@ -906,7 +906,7 @@ int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int au
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         r = ioctl(*fd, SIOCETHTOOL, &ifr);
         if (r < 0)
@@ -974,7 +974,7 @@ int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
         if (r < 0)

From 4253dab576b3ff17887c3e0d97380aab2aa29d82 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Wed, 15 Sep 2021 01:41:15 +0900
Subject: [PATCH 2/5] ethtool-util: shorten code a bit

Also fixes a error code in debugging log.
---
 src/shared/ethtool-util.c | 70 ++++++++++++---------------------------
 1 file changed, 22 insertions(+), 48 deletions(-)

diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
index d1f5eac63334..ac21ef0f61a8 100644
--- a/src/shared/ethtool-util.c
+++ b/src/shared/ethtool-util.c
@@ -216,8 +216,7 @@ int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
 
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         if (isempty(ecmd.driver))
@@ -256,8 +255,7 @@ int ethtool_get_link_info(
 
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         if (ret_autonegotiation)
@@ -305,8 +303,7 @@ int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct et
 
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         if (epaddr.addr.size != 6)
@@ -364,8 +361,7 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
 
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         UPDATE(ecmd.wolopts, wolopts, need_update);
@@ -374,8 +370,7 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
                 return 0;
 
         ecmd.cmd = ETHTOOL_SWOL;
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         return 0;
@@ -407,8 +402,7 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
 
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         if (ring->rx.set)
@@ -427,8 +421,7 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
                 return 0;
 
         ecmd.cmd = ETHTOOL_SRINGPARAM;
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         return 0;
@@ -446,7 +439,6 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
                 },
         };
         unsigned len;
-        int r;
 
         assert(ethtool_fd >= 0);
         assert(ifr);
@@ -454,8 +446,7 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
 
         ifr->ifr_data = (void *) &buffer.info;
 
-        r = ioctl(ethtool_fd, SIOCETHTOOL, ifr);
-        if (r < 0)
+        if (ioctl(ethtool_fd, SIOCETHTOOL, ifr) < 0)
                 return -errno;
 
         if (!buffer.info.sset_mask)
@@ -478,8 +469,7 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
 
         ifr->ifr_data = (void *) strings;
 
-        r = ioctl(ethtool_fd, SIOCETHTOOL, ifr);
-        if (r < 0)
+        if (ioctl(ethtool_fd, SIOCETHTOOL, ifr) < 0)
                 return -errno;
 
         *ret = TAKE_PTR(strings);
@@ -559,9 +549,8 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
 
         ifr.ifr_data = (void *) sfeatures;
 
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
-                return log_debug_errno(r, "ethtool: could not set ethtool features for %s", ifname);
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
+                return log_debug_errno(errno, "ethtool: could not set ethtool features for %s", ifname);
 
         return 0;
 }
@@ -575,7 +564,6 @@ static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_uset
         };
         struct ethtool_link_usettings *u;
         unsigned offset;
-        int r;
 
         assert(fd >= 0);
         assert(ifr);
@@ -591,8 +579,7 @@ static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_uset
 
         ifr->ifr_data = (void *) &ecmd;
 
-        r = ioctl(fd, SIOCETHTOOL, ifr);
-        if (r < 0)
+        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
                 return -errno;
 
         if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
@@ -602,8 +589,7 @@ static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_uset
 
         ifr->ifr_data = (void *) &ecmd;
 
-        r = ioctl(fd, SIOCETHTOOL, ifr);
-        if (r < 0)
+        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
                 return -errno;
 
         if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
@@ -636,7 +622,6 @@ static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **r
         struct ethtool_cmd ecmd = {
                 .cmd = ETHTOOL_GSET,
         };
-        int r;
 
         assert(fd >= 0);
         assert(ifr);
@@ -644,8 +629,7 @@ static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **r
 
         ifr->ifr_data = (void *) &ecmd;
 
-        r = ioctl(fd, SIOCETHTOOL, ifr);
-        if (r < 0)
+        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
                 return -errno;
 
         e = new(struct ethtool_link_usettings, 1);
@@ -678,7 +662,6 @@ static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_lin
                 __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
         } ecmd = {};
         unsigned offset;
-        int r;
 
         assert(fd >= 0);
         assert(ifr);
@@ -700,8 +683,7 @@ static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_lin
 
         ifr->ifr_data = (void *) &ecmd;
 
-        r = ioctl(fd, SIOCETHTOOL, ifr);
-        if (r < 0)
+        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
                 return -errno;
 
         return 0;
@@ -711,7 +693,6 @@ static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettin
         struct ethtool_cmd ecmd = {
                 .cmd = ETHTOOL_SSET,
         };
-        int r;
 
         assert(fd >= 0);
         assert(ifr);
@@ -736,8 +717,7 @@ static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettin
 
         ifr->ifr_data = (void *) &ecmd;
 
-        r = ioctl(fd, SIOCETHTOOL, ifr);
-        if (r < 0)
+        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
                 return -errno;
 
         return 0;
@@ -859,8 +839,7 @@ int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *cha
 
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
-        r = ioctl(*fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         if (channels->rx.set)
@@ -879,8 +858,7 @@ int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *cha
                 return 0;
 
         ecmd.cmd = ETHTOOL_SCHANNELS;
-        r = ioctl(*fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         return 0;
@@ -908,8 +886,7 @@ int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int au
 
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
-        r = ioctl(*fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         if (rx >= 0)
@@ -925,8 +902,7 @@ int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int au
                 return 0;
 
         ecmd.cmd = ETHTOOL_SPAUSEPARAM;
-        r = ioctl(*fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         return 0;
@@ -976,8 +952,7 @@ int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const
 
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         if (coalesce->use_adaptive_rx_coalesce >= 0)
@@ -1050,8 +1025,7 @@ int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const
                 return 0;
 
         ecmd.cmd = ETHTOOL_SCOALESCE;
-        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-        if (r < 0)
+        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         return 0;

From 008d3a370ccdea13290ab9277b32cc582b886b17 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 14 Sep 2021 17:42:52 +0900
Subject: [PATCH 3/5] ethtool: do not set unavailable or never_changed bits

---
 src/shared/ethtool-util.c | 138 ++++++++++++++++++++++++++------------
 1 file changed, 96 insertions(+), 42 deletions(-)

diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
index ac21ef0f61a8..59b1bd86f085 100644
--- a/src/shared/ethtool-util.c
+++ b/src/shared/ethtool-util.c
@@ -427,30 +427,31 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
         return 0;
 }
 
-static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **ret) {
+static int get_stringset(int ethtool_fd, const char *ifname, enum ethtool_stringset stringset_id, struct ethtool_gstrings **ret) {
         _cleanup_free_ struct ethtool_gstrings *strings = NULL;
         struct {
                 struct ethtool_sset_info info;
                 uint32_t space;
         } buffer = {
-                .info = {
-                        .cmd = ETHTOOL_GSSET_INFO,
-                        .sset_mask = UINT64_C(1) << stringset_id,
-                },
+                .info.cmd = ETHTOOL_GSSET_INFO,
+                .info.sset_mask = UINT64_C(1) << stringset_id,
         };
-        unsigned len;
+        struct ifreq ifr = {
+                .ifr_data = (void*) &buffer,
+        };
+        uint32_t len;
 
         assert(ethtool_fd >= 0);
-        assert(ifr);
+        assert(ifname);
         assert(ret);
 
-        ifr->ifr_data = (void *) &buffer.info;
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
-        if (ioctl(ethtool_fd, SIOCETHTOOL, ifr) < 0)
+        if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
-        if (!buffer.info.sset_mask)
-                return -EINVAL;
+        if (buffer.info.sset_mask == 0)
+                return -EOPNOTSUPP;
 
 #pragma GCC diagnostic push
 #if HAVE_ZERO_LENGTH_BOUNDS
@@ -458,8 +459,10 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
 #endif
         len = buffer.info.data[0];
 #pragma GCC diagnostic pop
+        if (len == 0)
+                return -EOPNOTSUPP;
 
-        strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN);
+        strings = malloc0(offsetof(struct ethtool_gstrings, data) + len * ETH_GSTRING_LEN);
         if (!strings)
                 return -ENOMEM;
 
@@ -467,47 +470,92 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
         strings->string_set = stringset_id;
         strings->len = len;
 
-        ifr->ifr_data = (void *) strings;
+        ifr.ifr_data = (void*) strings;
 
-        if (ioctl(ethtool_fd, SIOCETHTOOL, ifr) < 0)
+        if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
         *ret = TAKE_PTR(strings);
+        return 0;
+}
+
+static int get_features(int ethtool_fd, const char *ifname, uint32_t n_features, struct ethtool_gfeatures **ret) {
+        _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
+        struct ifreq ifr;
+
+        assert(ethtool_fd >= 0);
+        assert(ifname);
+        assert(ret);
+        assert(n_features > 0);
+
+        gfeatures = malloc0(offsetof(struct ethtool_gfeatures, features) +
+                            DIV_ROUND_UP(n_features, 32U) * sizeof(gfeatures->features[0]));
+        if (!gfeatures)
+                return -ENOMEM;
+
+        gfeatures->cmd = ETHTOOL_GFEATURES;
+        gfeatures->size = DIV_ROUND_UP(n_features, 32U);
+
+        ifr = (struct ifreq) {
+                .ifr_data = (void*) gfeatures,
+        };
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
+
+        if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
+                return -errno;
 
+        *ret = TAKE_PTR(gfeatures);
         return 0;
 }
 
 static int set_features_bit(
                 const struct ethtool_gstrings *strings,
+                const struct ethtool_gfeatures *gfeatures,
+                struct ethtool_sfeatures *sfeatures,
                 const char *feature,
-                bool flag,
-                struct ethtool_sfeatures *sfeatures) {
+                int flag) {
+
         bool found = false;
+        int r = -ENODATA;
 
         assert(strings);
-        assert(feature);
+        assert(gfeatures);
         assert(sfeatures);
+        assert(feature);
+
+        if (flag < 0)
+                return 0;
+
+        for (uint32_t i = 0; i < strings->len; i++) {
+                uint32_t block, mask;
 
-        for (size_t i = 0; i < strings->len; i++)
-                if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature) ||
-                    (endswith(feature, "-") && startswith((char *) &strings->data[i * ETH_GSTRING_LEN], feature))) {
-                        size_t block, bit;
+                if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN) &&
+                    !(endswith(feature, "-") && startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature)))
+                        continue;
 
-                        block = i / 32;
-                        bit = i % 32;
+                block = i / 32;
+                mask = UINT32_C(1) << (i % 32);
 
-                        sfeatures->features[block].valid |= 1 << bit;
-                        SET_FLAG(sfeatures->features[block].requested, 1 << bit, flag);
-                        found = true;
+                if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
+                    FLAGS_SET(gfeatures->features[block].never_changed, mask)) {
+                        r = -EOPNOTSUPP;
+                        continue;
                 }
 
-        return found ? 0 : -ENODATA;
+                sfeatures->features[block].valid |= mask;
+                SET_FLAG(sfeatures->features[block].requested, mask, flag);
+
+                found = true;
+        }
+
+        return found ? 0 : r;
 }
 
 int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]) {
         _cleanup_free_ struct ethtool_gstrings *strings = NULL;
-        struct ethtool_sfeatures *sfeatures;
-        struct ifreq ifr = {};
+        _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
+        _cleanup_free_ struct ethtool_sfeatures *sfeatures = NULL;
+        struct ifreq ifr;
         bool have = false;
         int r;
 
@@ -528,26 +576,32 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
         if (r < 0)
                 return r;
 
-        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
+        r = get_stringset(*ethtool_fd, ifname, ETH_SS_FEATURES, &strings);
+        if (r < 0)
+                return log_debug_errno(r, "ethtool: could not get ethtool feature strings: %m");
 
-        r = get_stringset(*ethtool_fd, &ifr, ETH_SS_FEATURES, &strings);
+        r = get_features(*ethtool_fd, ifname, strings->len, &gfeatures);
         if (r < 0)
-                return log_debug_errno(r, "ethtool: could not get ethtool features for %s", ifname);
+                return log_debug_errno(r, "ethtool: could not get ethtool features for %s: %m", ifname);
+
+        sfeatures = malloc0(offsetof(struct ethtool_sfeatures, features) +
+                            DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
+        if (!sfeatures)
+                return log_oom_debug();
 
-        sfeatures = alloca0(sizeof(struct ethtool_sfeatures) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
         sfeatures->cmd = ETHTOOL_SFEATURES;
         sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
 
-        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
-                if (features[i] >= 0) {
-                        r = set_features_bit(strings, netdev_feature_table[i], features[i], sfeatures);
-                        if (r < 0) {
-                                log_debug_errno(r, "ethtool: could not find feature, ignoring: %s", netdev_feature_table[i]);
-                                continue;
-                        }
-                }
+        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++) {
+                r = set_features_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
+                if (r < 0)
+                        log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
+        }
 
-        ifr.ifr_data = (void *) sfeatures;
+        ifr = (struct ifreq) {
+                .ifr_data = (void*) sfeatures,
+        };
+        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
         if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return log_debug_errno(errno, "ethtool: could not set ethtool features for %s", ifname);

From 7a4f203547c62cdc7611f38d97058b530570048f Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Wed, 15 Sep 2021 01:48:59 +0900
Subject: [PATCH 4/5] ethtool-util: apply tx-checksum-* features at last

NET_DEV_FEAT_TX matches multiple features. In the next commit, all
features whose strings start with "tx-checksum-" will be added.
To make them take precedence over NET_DEV_FEAT_TX, it will be applied
only when each explicit feature is not applied.
---
 src/shared/ethtool-util.c | 55 ++++++++++++++++++++++++++++++++++++---
 src/shared/ethtool-util.h |  4 ++-
 2 files changed, 54 insertions(+), 5 deletions(-)

diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
index 59b1bd86f085..e95ce1a20917 100644
--- a/src/shared/ethtool-util.c
+++ b/src/shared/ethtool-util.c
@@ -71,13 +71,14 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse P
 
 static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
         [NET_DEV_FEAT_RX]     = "rx-checksum",
-        [NET_DEV_FEAT_TX]     = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
         [NET_DEV_FEAT_GSO]    = "tx-generic-segmentation",
         [NET_DEV_FEAT_GRO]    = "rx-gro",
         [NET_DEV_FEAT_GRO_HW] = "rx-gro-hw",
         [NET_DEV_FEAT_LRO]    = "rx-lro",
         [NET_DEV_FEAT_TSO]    = "tx-tcp-segmentation",
         [NET_DEV_FEAT_TSO6]   = "tx-tcp6-segmentation",
+
+        [NET_DEV_FEAT_TX]     = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
 };
 
 static const char* const ethtool_link_mode_bit_table[] = {
@@ -515,6 +516,43 @@ static int set_features_bit(
                 const char *feature,
                 int flag) {
 
+        assert(strings);
+        assert(gfeatures);
+        assert(sfeatures);
+        assert(feature);
+
+        if (flag < 0)
+                return 0;
+
+        for (uint32_t i = 0; i < strings->len; i++) {
+                uint32_t block, mask;
+
+                if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN))
+                        continue;
+
+                block = i / 32;
+                mask = UINT32_C(1) << (i % 32);
+
+                if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
+                    FLAGS_SET(gfeatures->features[block].never_changed, mask))
+                        return -EOPNOTSUPP;
+
+                sfeatures->features[block].valid |= mask;
+                SET_FLAG(sfeatures->features[block].requested, mask, flag);
+
+                return 0;
+        }
+
+        return -ENODATA;
+}
+
+static int set_features_multiple_bit(
+                const struct ethtool_gstrings *strings,
+                const struct ethtool_gfeatures *gfeatures,
+                struct ethtool_sfeatures *sfeatures,
+                const char *feature,
+                int flag) {
+
         bool found = false;
         int r = -ENODATA;
 
@@ -529,8 +567,7 @@ static int set_features_bit(
         for (uint32_t i = 0; i < strings->len; i++) {
                 uint32_t block, mask;
 
-                if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN) &&
-                    !(endswith(feature, "-") && startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature)))
+                if (!startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature))
                         continue;
 
                 block = i / 32;
@@ -542,6 +579,10 @@ static int set_features_bit(
                         continue;
                 }
 
+                /* The flags is explicitly set by set_features_bit() */
+                if (FLAGS_SET(sfeatures->features[block].valid, mask))
+                        continue;
+
                 sfeatures->features[block].valid |= mask;
                 SET_FLAG(sfeatures->features[block].requested, mask, flag);
 
@@ -592,12 +633,18 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
         sfeatures->cmd = ETHTOOL_SFEATURES;
         sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
 
-        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++) {
+        for (size_t i = 0; i < _NET_DEV_FEAT_SIMPLE_MAX; i++) {
                 r = set_features_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
                 if (r < 0)
                         log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
         }
 
+        for (size_t i = _NET_DEV_FEAT_SIMPLE_MAX; i < _NET_DEV_FEAT_MAX; i++) {
+                r = set_features_multiple_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
+                if (r < 0)
+                        log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
+        }
+
         ifr = (struct ifreq) {
                 .ifr_data = (void*) sfeatures,
         };
diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h
index 6e180995055b..3f2252563304 100644
--- a/src/shared/ethtool-util.h
+++ b/src/shared/ethtool-util.h
@@ -20,13 +20,15 @@ typedef enum Duplex {
 
 typedef enum NetDevFeature {
         NET_DEV_FEAT_RX,
-        NET_DEV_FEAT_TX,
         NET_DEV_FEAT_GSO,
         NET_DEV_FEAT_GRO,
         NET_DEV_FEAT_GRO_HW,
         NET_DEV_FEAT_LRO,
         NET_DEV_FEAT_TSO,
         NET_DEV_FEAT_TSO6,
+        _NET_DEV_FEAT_SIMPLE_MAX,
+
+        NET_DEV_FEAT_TX = _NET_DEV_FEAT_SIMPLE_MAX,
         _NET_DEV_FEAT_MAX,
         _NET_DEV_FEAT_INVALID = -EINVAL,
 } NetDevFeature;

From 77bf5c31de1d01edd49ac6aa25cdbe7734a11a25 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 14 Sep 2021 22:12:42 +0900
Subject: [PATCH 5/5] ethtool-util: add more network device features

Then, we can easily add new settings to configure features in .link
file.
---
 src/shared/ethtool-util.c            | 73 ++++++++++++++++++++++++----
 src/shared/ethtool-util.h            | 59 +++++++++++++++++++++-
 src/udev/net/link-config-gperf.gperf |  4 +-
 3 files changed, 123 insertions(+), 13 deletions(-)

diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
index e95ce1a20917..00060abff40f 100644
--- a/src/shared/ethtool-util.c
+++ b/src/shared/ethtool-util.c
@@ -70,15 +70,70 @@ DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
 
 static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
-        [NET_DEV_FEAT_RX]     = "rx-checksum",
-        [NET_DEV_FEAT_GSO]    = "tx-generic-segmentation",
-        [NET_DEV_FEAT_GRO]    = "rx-gro",
-        [NET_DEV_FEAT_GRO_HW] = "rx-gro-hw",
-        [NET_DEV_FEAT_LRO]    = "rx-lro",
-        [NET_DEV_FEAT_TSO]    = "tx-tcp-segmentation",
-        [NET_DEV_FEAT_TSO6]   = "tx-tcp6-segmentation",
-
-        [NET_DEV_FEAT_TX]     = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
+        [NET_DEV_FEAT_SG]                  = "tx-scatter-gather",
+        [NET_DEV_FEAT_IP_CSUM]             = "tx-checksum-ipv4",
+        [NET_DEV_FEAT_HW_CSUM]             = "tx-checksum-ip-generic",
+        [NET_DEV_FEAT_IPV6_CSUM]           = "tx-checksum-ipv6",
+        [NET_DEV_FEAT_HIGHDMA]             = "highdma",
+        [NET_DEV_FEAT_FRAGLIST]            = "tx-scatter-gather-fraglist",
+        [NET_DEV_FEAT_HW_VLAN_CTAG_TX]     = "tx-vlan-hw-insert",
+        [NET_DEV_FEAT_HW_VLAN_CTAG_RX]     = "rx-vlan-hw-parse",
+        [NET_DEV_FEAT_HW_VLAN_CTAG_FILTER] = "rx-vlan-filter",
+        [NET_DEV_FEAT_HW_VLAN_STAG_TX]     = "tx-vlan-stag-hw-insert",
+        [NET_DEV_FEAT_HW_VLAN_STAG_RX]     = "rx-vlan-stag-hw-parse",
+        [NET_DEV_FEAT_HW_VLAN_STAG_FILTER] = "rx-vlan-stag-filter",
+        [NET_DEV_FEAT_VLAN_CHALLENGED]     = "vlan-challenged",
+        [NET_DEV_FEAT_GSO]                 = "tx-generic-segmentation",
+        [NET_DEV_FEAT_LLTX]                = "tx-lockless",
+        [NET_DEV_FEAT_NETNS_LOCAL]         = "netns-local",
+        [NET_DEV_FEAT_GRO]                 = "rx-gro",
+        [NET_DEV_FEAT_GRO_HW]              = "rx-gro-hw",
+        [NET_DEV_FEAT_LRO]                 = "rx-lro",
+        [NET_DEV_FEAT_TSO]                 = "tx-tcp-segmentation",
+        [NET_DEV_FEAT_GSO_ROBUST]          = "tx-gso-robust",
+        [NET_DEV_FEAT_TSO_ECN]             = "tx-tcp-ecn-segmentation",
+        [NET_DEV_FEAT_TSO_MANGLEID]        = "tx-tcp-mangleid-segmentation",
+        [NET_DEV_FEAT_TSO6]                = "tx-tcp6-segmentation",
+        [NET_DEV_FEAT_FSO]                 = "tx-fcoe-segmentation",
+        [NET_DEV_FEAT_GSO_GRE]             = "tx-gre-segmentation",
+        [NET_DEV_FEAT_GSO_GRE_CSUM]        = "tx-gre-csum-segmentation",
+        [NET_DEV_FEAT_GSO_IPXIP4]          = "tx-ipxip4-segmentation",
+        [NET_DEV_FEAT_GSO_IPXIP6]          = "tx-ipxip6-segmentation",
+        [NET_DEV_FEAT_GSO_UDP_TUNNEL]      = "tx-udp_tnl-segmentation",
+        [NET_DEV_FEAT_GSO_UDP_TUNNEL_CSUM] = "tx-udp_tnl-csum-segmentation",
+        [NET_DEV_FEAT_GSO_PARTIAL]         = "tx-gso-partial",
+        [NET_DEV_FEAT_GSO_TUNNEL_REMCSUM]  = "tx-tunnel-remcsum-segmentation",
+        [NET_DEV_FEAT_GSO_SCTP]            = "tx-sctp-segmentation",
+        [NET_DEV_FEAT_GSO_ESP]             = "tx-esp-segmentation",
+        [NET_DEV_FEAT_GSO_UDP_L4]          = "tx-udp-segmentation",
+        [NET_DEV_FEAT_GSO_FRAGLIST]        = "tx-gso-list",
+        [NET_DEV_FEAT_FCOE_CRC]            = "tx-checksum-fcoe-crc",
+        [NET_DEV_FEAT_SCTP_CRC]            = "tx-checksum-sctp",
+        [NET_DEV_FEAT_FCOE_MTU]            = "fcoe-mtu",
+        [NET_DEV_FEAT_NTUPLE]              = "rx-ntuple-filter",
+        [NET_DEV_FEAT_RXHASH]              = "rx-hashing",
+        [NET_DEV_FEAT_RXCSUM]              = "rx-checksum",
+        [NET_DEV_FEAT_NOCACHE_COPY]        = "tx-nocache-copy",
+        [NET_DEV_FEAT_LOOPBACK]            = "loopback",
+        [NET_DEV_FEAT_RXFCS]               = "rx-fcs",
+        [NET_DEV_FEAT_RXALL]               = "rx-all",
+        [NET_DEV_FEAT_HW_L2FW_DOFFLOAD]    = "l2-fwd-offload",
+        [NET_DEV_FEAT_HW_TC]               = "hw-tc-offload",
+        [NET_DEV_FEAT_HW_ESP]              = "esp-hw-offload",
+        [NET_DEV_FEAT_HW_ESP_TX_CSUM]      = "esp-tx-csum-hw-offload",
+        [NET_DEV_FEAT_RX_UDP_TUNNEL_PORT]  = "rx-udp_tunnel-port-offload",
+        [NET_DEV_FEAT_HW_TLS_RECORD]       = "tls-hw-record",
+        [NET_DEV_FEAT_HW_TLS_TX]           = "tls-hw-tx-offload",
+        [NET_DEV_FEAT_HW_TLS_RX]           = "tls-hw-rx-offload",
+        [NET_DEV_FEAT_GRO_FRAGLIST]        = "rx-gro-list",
+        [NET_DEV_FEAT_HW_MACSEC]           = "macsec-hw-offload",
+        [NET_DEV_FEAT_GRO_UDP_FWD]         = "rx-udp-gro-forwarding",
+        [NET_DEV_FEAT_HW_HSR_TAG_INS]      = "hsr-tag-ins-offload",
+        [NET_DEV_FEAT_HW_HSR_TAG_RM]       = "hsr-tag-rm-offload",
+        [NET_DEV_FEAT_HW_HSR_FWD]          = "hsr-fwd-offload",
+        [NET_DEV_FEAT_HW_HSR_DUP]          = "hsr-dup-offload",
+
+        [NET_DEV_FEAT_TXCSUM]              = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
 };
 
 static const char* const ethtool_link_mode_bit_table[] = {
diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h
index 3f2252563304..cc0655893175 100644
--- a/src/shared/ethtool-util.h
+++ b/src/shared/ethtool-util.h
@@ -19,16 +19,71 @@ typedef enum Duplex {
 } Duplex;
 
 typedef enum NetDevFeature {
-        NET_DEV_FEAT_RX,
+        NET_DEV_FEAT_SG,
+        NET_DEV_FEAT_IP_CSUM,
+        NET_DEV_FEAT_HW_CSUM,
+        NET_DEV_FEAT_IPV6_CSUM,
+        NET_DEV_FEAT_HIGHDMA,
+        NET_DEV_FEAT_FRAGLIST,
+        NET_DEV_FEAT_HW_VLAN_CTAG_TX,
+        NET_DEV_FEAT_HW_VLAN_CTAG_RX,
+        NET_DEV_FEAT_HW_VLAN_CTAG_FILTER,
+        NET_DEV_FEAT_HW_VLAN_STAG_TX,
+        NET_DEV_FEAT_HW_VLAN_STAG_RX,
+        NET_DEV_FEAT_HW_VLAN_STAG_FILTER,
+        NET_DEV_FEAT_VLAN_CHALLENGED,
         NET_DEV_FEAT_GSO,
+        NET_DEV_FEAT_LLTX,
+        NET_DEV_FEAT_NETNS_LOCAL,
         NET_DEV_FEAT_GRO,
         NET_DEV_FEAT_GRO_HW,
         NET_DEV_FEAT_LRO,
         NET_DEV_FEAT_TSO,
+        NET_DEV_FEAT_GSO_ROBUST,
+        NET_DEV_FEAT_TSO_ECN,
+        NET_DEV_FEAT_TSO_MANGLEID,
         NET_DEV_FEAT_TSO6,
+        NET_DEV_FEAT_FSO,
+        NET_DEV_FEAT_GSO_GRE,
+        NET_DEV_FEAT_GSO_GRE_CSUM,
+        NET_DEV_FEAT_GSO_IPXIP4,
+        NET_DEV_FEAT_GSO_IPXIP6,
+        NET_DEV_FEAT_GSO_UDP_TUNNEL,
+        NET_DEV_FEAT_GSO_UDP_TUNNEL_CSUM,
+        NET_DEV_FEAT_GSO_PARTIAL,
+        NET_DEV_FEAT_GSO_TUNNEL_REMCSUM,
+        NET_DEV_FEAT_GSO_SCTP,
+        NET_DEV_FEAT_GSO_ESP,
+        NET_DEV_FEAT_GSO_UDP_L4,
+        NET_DEV_FEAT_GSO_FRAGLIST,
+        NET_DEV_FEAT_FCOE_CRC,
+        NET_DEV_FEAT_SCTP_CRC,
+        NET_DEV_FEAT_FCOE_MTU,
+        NET_DEV_FEAT_NTUPLE,
+        NET_DEV_FEAT_RXHASH,
+        NET_DEV_FEAT_RXCSUM,
+        NET_DEV_FEAT_NOCACHE_COPY,
+        NET_DEV_FEAT_LOOPBACK,
+        NET_DEV_FEAT_RXFCS,
+        NET_DEV_FEAT_RXALL,
+        NET_DEV_FEAT_HW_L2FW_DOFFLOAD,
+        NET_DEV_FEAT_HW_TC,
+        NET_DEV_FEAT_HW_ESP,
+        NET_DEV_FEAT_HW_ESP_TX_CSUM,
+        NET_DEV_FEAT_RX_UDP_TUNNEL_PORT,
+        NET_DEV_FEAT_HW_TLS_RECORD,
+        NET_DEV_FEAT_HW_TLS_TX,
+        NET_DEV_FEAT_HW_TLS_RX,
+        NET_DEV_FEAT_GRO_FRAGLIST,
+        NET_DEV_FEAT_HW_MACSEC,
+        NET_DEV_FEAT_GRO_UDP_FWD,
+        NET_DEV_FEAT_HW_HSR_TAG_INS,
+        NET_DEV_FEAT_HW_HSR_TAG_RM,
+        NET_DEV_FEAT_HW_HSR_FWD,
+        NET_DEV_FEAT_HW_HSR_DUP,
         _NET_DEV_FEAT_SIMPLE_MAX,
 
-        NET_DEV_FEAT_TX = _NET_DEV_FEAT_SIMPLE_MAX,
+        NET_DEV_FEAT_TXCSUM = _NET_DEV_FEAT_SIMPLE_MAX,
         _NET_DEV_FEAT_MAX,
         _NET_DEV_FEAT_INVALID = -EINVAL,
 } NetDevFeature;
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index 44b46cb17c0b..e3cdaaee0509 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -50,8 +50,8 @@ Link.Duplex,                              config_parse_duplex,
 Link.AutoNegotiation,                     config_parse_tristate,                 0,                             offsetof(LinkConfig, autonegotiation)
 Link.WakeOnLan,                           config_parse_wol,                      0,                             offsetof(LinkConfig, wol)
 Link.Port,                                config_parse_port,                     0,                             offsetof(LinkConfig, port)
-Link.ReceiveChecksumOffload,              config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_RX])
-Link.TransmitChecksumOffload,             config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TX])
+Link.ReceiveChecksumOffload,              config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_RXCSUM])
+Link.TransmitChecksumOffload,             config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TXCSUM])
 Link.GenericSegmentationOffload,          config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_GSO])
 Link.TCPSegmentationOffload,              config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TSO])
 Link.TCP6SegmentationOffload,             config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TSO6])