diff --git a/man/systemd.link.xml b/man/systemd.link.xml
index 9104dd7f8f..3574dd41a1 100644
--- a/man/systemd.link.xml
+++ b/man/systemd.link.xml
@@ -612,6 +612,20 @@
+
+ ReceiveChecksumOffload=
+
+ Takes a boolean. If set to true, the hardware offload for checksumming of ingress
+ network packets is enabled. When unset, the kernel's default will be used.
+
+
+
+ TransmitChecksumOffload=
+
+ Takes a boolean. If set to true, the hardware offload for checksumming of egress
+ network packets is enabled. When unset, the kernel's default will be used.
+
+
TCPSegmentationOffload=
@@ -633,7 +647,7 @@
When unset, the kernel's default will be used.
-
+
GenericReceiveOffload=
Takes a boolean. If set to true, the Generic Receive Offload (GRO) is enabled.
diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
index d9c1231fa8..00a71d64a6 100644
--- a/src/shared/ethtool-util.c
+++ b/src/shared/ethtool-util.c
@@ -50,6 +50,8 @@ 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_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_LRO] = "rx-lro",
@@ -498,22 +500,38 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
return 0;
}
-static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) {
- unsigned i;
+static int set_features_bit(
+ const struct ethtool_gstrings *strings,
+ const char *feature,
+ bool flag,
+ struct ethtool_sfeatures *sfeatures) {
+ bool found = false;
- for (i = 0; i < strings->len; i++) {
- if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature))
- return i;
- }
+ assert(strings);
+ assert(feature);
+ assert(sfeatures);
- return -ENODATA;
+ 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;
+
+ block = i / 32;
+ bit = i % 32;
+
+ sfeatures->features[block].valid |= 1 << bit;
+ SET_FLAG(sfeatures->features[block].requested, 1 << bit, flag);
+ found = true;
+ }
+
+ return found ? 0 : -ENODATA;
}
int ethtool_set_features(int *ethtool_fd, const char *ifname, int *features) {
_cleanup_free_ struct ethtool_gstrings *strings = NULL;
struct ethtool_sfeatures *sfeatures;
- int block, bit, i, r;
struct ifreq ifr = {};
+ int i, r;
if (*ethtool_fd < 0) {
r = ethtool_connect_or_warn(ethtool_fd, true);
@@ -531,27 +549,14 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, int *features) {
sfeatures->cmd = ETHTOOL_SFEATURES;
sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
- for (i = 0; i < _NET_DEV_FEAT_MAX; i++) {
-
+ for (i = 0; i < _NET_DEV_FEAT_MAX; i++)
if (features[i] != -1) {
-
- r = find_feature_index(strings, netdev_feature_table[i]);
+ r = set_features_bit(strings, netdev_feature_table[i], features[i], sfeatures);
if (r < 0) {
- log_warning_errno(r, "ethtool: could not find feature: %s", netdev_feature_table[i]);
+ log_warning_errno(r, "ethtool: could not find feature, ignoring: %s", netdev_feature_table[i]);
continue;
}
-
- block = r / 32;
- bit = r % 32;
-
- sfeatures->features[block].valid |= 1 << bit;
-
- if (features[i])
- sfeatures->features[block].requested |= 1 << bit;
- else
- sfeatures->features[block].requested &= ~(1 << bit);
}
- }
ifr.ifr_data = (void *) sfeatures;
diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h
index 8d76287640..c1d5d7590e 100644
--- a/src/shared/ethtool-util.h
+++ b/src/shared/ethtool-util.h
@@ -32,6 +32,8 @@ typedef enum WakeOnLan {
} WakeOnLan;
typedef enum NetDevFeature {
+ NET_DEV_FEAT_RX,
+ NET_DEV_FEAT_TX,
NET_DEV_FEAT_GSO,
NET_DEV_FEAT_GRO,
NET_DEV_FEAT_LRO,
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index 1e794efdcb..43d1c59b94 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -45,6 +45,8 @@ Link.Duplex, config_parse_duplex, 0,
Link.AutoNegotiation, config_parse_tristate, 0, offsetof(link_config, autonegotiation)
Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol)
Link.Port, config_parse_port, 0, offsetof(link_config, port)
+Link.ReceiveChecksumOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_RX])
+Link.TransmitChecksumOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TX])
Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO])
Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO])
Link.TCP6SegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO6])
diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link
index ba8760f12b..b304ad0ddb 100644
--- a/test/fuzz/fuzz-link-parser/directives.link
+++ b/test/fuzz/fuzz-link-parser/directives.link
@@ -26,6 +26,8 @@ Duplex=
AutoNegotiation=
WakeOnLan=
Port=
+ReceiveChecksumOffload=
+TransmitChecksumOffload=
GenericSegmentationOffload=
TCPSegmentationOffload=
TCP6SegmentationOffload=