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=