summaryrefslogtreecommitdiff
path: root/src/udev/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/udev/net')
-rw-r--r--src/udev/net/ethtool-util.c789
-rw-r--r--src/udev/net/ethtool-util.h107
-rw-r--r--src/udev/net/link-config-gperf.gperf10
-rw-r--r--src/udev/net/link-config.c4
-rw-r--r--src/udev/net/link-config.h2
5 files changed, 9 insertions, 903 deletions
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
deleted file mode 100644
index c94977e7be..0000000000
--- a/src/udev/net/ethtool-util.c
+++ /dev/null
@@ -1,789 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#include <net/if.h>
-#include <sys/ioctl.h>
-#include <linux/ethtool.h>
-#include <linux/sockios.h>
-
-#include "conf-parser.h"
-#include "ethtool-util.h"
-#include "link-config.h"
-#include "log.h"
-#include "memory-util.h"
-#include "missing.h"
-#include "socket-util.h"
-#include "string-table.h"
-#include "strxcpyx.h"
-
-static const char* const duplex_table[_DUP_MAX] = {
- [DUP_FULL] = "full",
- [DUP_HALF] = "half"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting");
-
-static const char* const wol_table[_WOL_MAX] = {
- [WOL_PHY] = "phy",
- [WOL_UCAST] = "unicast",
- [WOL_MCAST] = "multicast",
- [WOL_BCAST] = "broadcast",
- [WOL_ARP] = "arp",
- [WOL_MAGIC] = "magic",
- [WOL_MAGICSECURE] = "secureon",
- [WOL_OFF] = "off",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
-
-static const char* const port_table[] = {
- [NET_DEV_PORT_TP] = "tp",
- [NET_DEV_PORT_AUI] = "aui",
- [NET_DEV_PORT_MII] = "mii",
- [NET_DEV_PORT_FIBRE] = "fibre",
- [NET_DEV_PORT_BNC] = "bnc",
-};
-
-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_GSO] = "tx-generic-segmentation",
- [NET_DEV_FEAT_GRO] = "rx-gro",
- [NET_DEV_FEAT_LRO] = "rx-lro",
- [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation",
- [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation",
-};
-
-static const char* const ethtool_link_mode_bit_table[] = {
- [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = "10baset-half",
- [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = "10baset-full",
- [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = "100baset-half",
- [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = "100baset-full",
- [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = "1000baset-half",
- [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = "1000baset-full",
- [ETHTOOL_LINK_MODE_Autoneg_BIT] = "autonegotiation",
- [ETHTOOL_LINK_MODE_TP_BIT] = "tp",
- [ETHTOOL_LINK_MODE_AUI_BIT] = "aui",
- [ETHTOOL_LINK_MODE_MII_BIT] = "mii",
- [ETHTOOL_LINK_MODE_FIBRE_BIT] = "fibre",
- [ETHTOOL_LINK_MODE_BNC_BIT] = "bnc",
- [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = "10000baset-full",
- [ETHTOOL_LINK_MODE_Pause_BIT] = "pause",
- [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = "asym-pause",
- [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = "2500basex-full",
- [ETHTOOL_LINK_MODE_Backplane_BIT] = "backplane",
- [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = "1000basekx-full",
- [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = "10000basekx4-full",
- [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = "10000basekr-full",
- [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = "10000baser-fec",
- [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = "20000basemld2-full",
- [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = "20000basekr2-full",
- [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = "40000basekr4-full",
- [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = "40000basecr4-full",
- [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = "40000basesr4-full",
- [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = "40000baselr4-full",
- [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = "56000basekr4-full",
- [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = "56000basecr4-full",
- [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = "56000basesr4-full",
- [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = "56000baselr4-full",
- [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = "25000basecr-full",
- [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = "25000basekr-full",
- [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = "25000basesr-full",
- [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = "50000basecr2-full",
- [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = "50000basekr2-full",
- [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = "100000basekr4-full",
- [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = "100000basesr4-full",
- [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = "100000basecr4-full",
- [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = "100000baselr4-er4-full",
- [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = "50000basesr2-full",
- [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = "1000basex-full",
- [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = "10000basecr-full",
- [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = "10000basesr-full",
- [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = "10000baselr-full",
- [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = "10000baselrm-full",
- [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = "10000baseer-full",
- [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = "2500baset-full",
- [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = "5000baset-full",
- [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = "fec-none",
- [ETHTOOL_LINK_MODE_FEC_RS_BIT] = "fec-rs",
- [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = "fec-baser",
-};
-/* Make sure the array is large enough to fit all bits */
-assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < ELEMENTSOF(((struct link_config){}).advertise));
-
-DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices);
-
-int ethtool_connect(int *ret) {
- int fd;
-
- assert_return(ret, -EINVAL);
-
- fd = socket_ioctl_fd();
- if (fd < 0)
- return fd;
-
- *ret = fd;
-
- return 0;
-}
-
-int ethtool_get_driver(int *fd, const char *ifname, char **ret) {
- struct ethtool_drvinfo ecmd = {
- .cmd = ETHTOOL_GDRVINFO
- };
- struct ifreq ifr = {
- .ifr_data = (void*) &ecmd
- };
- char *d;
- int r;
-
- if (*fd < 0) {
- r = ethtool_connect(fd);
- if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
- }
-
- strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
-
- r = ioctl(*fd, SIOCETHTOOL, &ifr);
- if (r < 0)
- return -errno;
-
- d = strdup(ecmd.driver);
- if (!d)
- return -ENOMEM;
-
- *ret = d;
- return 0;
-}
-
-int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex) {
- struct ethtool_cmd ecmd = {
- .cmd = ETHTOOL_GSET
- };
- struct ifreq ifr = {
- .ifr_data = (void*) &ecmd
- };
- bool need_update = false;
- int r;
-
- if (speed == 0 && duplex == _DUP_INVALID)
- return 0;
-
- if (*fd < 0) {
- r = ethtool_connect(fd);
- if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
- }
-
- strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
-
- r = ioctl(*fd, SIOCETHTOOL, &ifr);
- if (r < 0)
- return -errno;
-
- if (ethtool_cmd_speed(&ecmd) != speed) {
- ethtool_cmd_speed_set(&ecmd, speed);
- need_update = true;
- }
-
- switch (duplex) {
- case DUP_HALF:
- if (ecmd.duplex != DUPLEX_HALF) {
- ecmd.duplex = DUPLEX_HALF;
- need_update = true;
- }
- break;
- case DUP_FULL:
- if (ecmd.duplex != DUPLEX_FULL) {
- ecmd.duplex = DUPLEX_FULL;
- need_update = true;
- }
- break;
- default:
- break;
- }
-
- if (need_update) {
- ecmd.cmd = ETHTOOL_SSET;
-
- r = ioctl(*fd, SIOCETHTOOL, &ifr);
- if (r < 0)
- return -errno;
- }
-
- return 0;
-}
-
-int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) {
- struct ethtool_wolinfo ecmd = {
- .cmd = ETHTOOL_GWOL
- };
- struct ifreq ifr = {
- .ifr_data = (void*) &ecmd
- };
- bool need_update = false;
- int r;
-
- if (wol == _WOL_INVALID)
- return 0;
-
- if (*fd < 0) {
- r = ethtool_connect(fd);
- if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
- }
-
- strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
-
- r = ioctl(*fd, SIOCETHTOOL, &ifr);
- if (r < 0)
- return -errno;
-
- switch (wol) {
- case WOL_PHY:
- if (ecmd.wolopts != WAKE_PHY) {
- ecmd.wolopts = WAKE_PHY;
- need_update = true;
- }
- break;
- case WOL_UCAST:
- if (ecmd.wolopts != WAKE_UCAST) {
- ecmd.wolopts = WAKE_UCAST;
- need_update = true;
- }
- break;
- case WOL_MCAST:
- if (ecmd.wolopts != WAKE_MCAST) {
- ecmd.wolopts = WAKE_MCAST;
- need_update = true;
- }
- break;
- case WOL_BCAST:
- if (ecmd.wolopts != WAKE_BCAST) {
- ecmd.wolopts = WAKE_BCAST;
- need_update = true;
- }
- break;
- case WOL_ARP:
- if (ecmd.wolopts != WAKE_ARP) {
- ecmd.wolopts = WAKE_ARP;
- need_update = true;
- }
- break;
- case WOL_MAGIC:
- if (ecmd.wolopts != WAKE_MAGIC) {
- ecmd.wolopts = WAKE_MAGIC;
- need_update = true;
- }
- break;
- case WOL_MAGICSECURE:
- if (ecmd.wolopts != WAKE_MAGICSECURE) {
- ecmd.wolopts = WAKE_MAGICSECURE;
- need_update = true;
- }
- break;
- case WOL_OFF:
- if (ecmd.wolopts != 0) {
- ecmd.wolopts = 0;
- need_update = true;
- }
- break;
- default:
- break;
- }
-
- if (need_update) {
- ecmd.cmd = ETHTOOL_SWOL;
-
- r = ioctl(*fd, SIOCETHTOOL, &ifr);
- if (r < 0)
- return -errno;
- }
-
- return 0;
-}
-
-static int get_stringset(int fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) {
- _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,
- },
- };
- unsigned len;
- int r;
-
- ifr->ifr_data = (void *) &buffer.info;
-
- r = ioctl(fd, SIOCETHTOOL, ifr);
- if (r < 0)
- return -errno;
-
- if (!buffer.info.sset_mask)
- return -EINVAL;
-
- len = buffer.info.data[0];
-
- strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN);
- if (!strings)
- return -ENOMEM;
-
- strings->cmd = ETHTOOL_GSTRINGS;
- strings->string_set = stringset_id;
- strings->len = len;
-
- ifr->ifr_data = (void *) strings;
-
- r = ioctl(fd, SIOCETHTOOL, ifr);
- if (r < 0)
- return -errno;
-
- *gstrings = TAKE_PTR(strings);
-
- return 0;
-}
-
-static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) {
- unsigned i;
-
- for (i = 0; i < strings->len; i++) {
- if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature))
- return i;
- }
-
- return -ENODATA;
-}
-
-int ethtool_set_features(int *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 = {};
-
- if (*fd < 0) {
- r = ethtool_connect(fd);
- if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
- }
-
- strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
-
- r = get_stringset(*fd, &ifr, ETH_SS_FEATURES, &strings);
- if (r < 0)
- return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname);
-
- 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 (i = 0; i < _NET_DEV_FEAT_MAX; i++) {
-
- if (features[i] != -1) {
-
- r = find_feature_index(strings, netdev_feature_table[i]);
- if (r < 0) {
- log_warning_errno(r, "link_config: could not find feature: %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;
-
- r = ioctl(*fd, SIOCETHTOOL, &ifr);
- if (r < 0)
- return log_warning_errno(r, "link_config: could not set ethtool features for %s", ifname);
-
- return 0;
-}
-
-static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **g) {
- struct ecmd {
- struct ethtool_link_settings req;
- __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
- } ecmd = {
- .req.cmd = ETHTOOL_GLINKSETTINGS,
- };
- struct ethtool_link_usettings *u;
- unsigned offset;
- int r;
-
- /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
- handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
- agree with user, it returns the bitmap length it is expecting from user as a negative
- length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
- all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
- https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
- */
-
- ifr->ifr_data = (void *) &ecmd;
-
- r = ioctl(fd, SIOCETHTOOL, ifr);
- if (r < 0)
- return -errno;
-
- if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
- return -EOPNOTSUPP;
-
- ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
-
- ifr->ifr_data = (void *) &ecmd;
-
- r = ioctl(fd, SIOCETHTOOL, ifr);
- if (r < 0)
- return -errno;
-
- if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
- return -EOPNOTSUPP;
-
- u = new0(struct ethtool_link_usettings , 1);
- if (!u)
- return -ENOMEM;
-
- u->base = ecmd.req;
-
- offset = 0;
- memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
-
- offset += ecmd.req.link_mode_masks_nwords;
- memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
-
- offset += ecmd.req.link_mode_masks_nwords;
- memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
-
- *g = u;
-
- return 0;
-}
-
-static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **u) {
- struct ethtool_link_usettings *e;
- struct ethtool_cmd ecmd = {
- .cmd = ETHTOOL_GSET,
- };
- int r;
-
- ifr->ifr_data = (void *) &ecmd;
-
- r = ioctl(fd, SIOCETHTOOL, ifr);
- if (r < 0)
- return -errno;
-
- e = new0(struct ethtool_link_usettings, 1);
- if (!e)
- return -ENOMEM;
-
- e->base.cmd = ETHTOOL_GSET;
-
- e->base.link_mode_masks_nwords = 1;
- e->base.speed = ethtool_cmd_speed(&ecmd);
- e->base.duplex = ecmd.duplex;
- e->base.port = ecmd.port;
- e->base.phy_address = ecmd.phy_address;
- e->base.autoneg = ecmd.autoneg;
- e->base.mdio_support = ecmd.mdio_support;
-
- e->link_modes.supported[0] = ecmd.supported;
- e->link_modes.advertising[0] = ecmd.advertising;
- e->link_modes.lp_advertising[0] = ecmd.lp_advertising;
-
- *u = e;
-
- return 0;
-}
-
-static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
- struct {
- struct ethtool_link_settings req;
- __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
- } ecmd = {};
- unsigned offset;
- int r;
-
- if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
- return -EINVAL;
-
- ecmd.req = u->base;
- ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
- offset = 0;
- memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords);
-
- offset += ecmd.req.link_mode_masks_nwords;
- memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords);
-
- offset += ecmd.req.link_mode_masks_nwords;
- memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords);
-
- ifr->ifr_data = (void *) &ecmd;
-
- r = ioctl(fd, SIOCETHTOOL, ifr);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
- struct ethtool_cmd ecmd = {
- .cmd = ETHTOOL_SSET,
- };
- int r;
-
- if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0)
- return -EINVAL;
-
- ecmd.supported = u->link_modes.supported[0];
- ecmd.advertising = u->link_modes.advertising[0];
- ecmd.lp_advertising = u->link_modes.lp_advertising[0];
-
- ethtool_cmd_speed_set(&ecmd, u->base.speed);
-
- ecmd.duplex = u->base.duplex;
- ecmd.port = u->base.port;
- ecmd.phy_address = u->base.phy_address;
- ecmd.autoneg = u->base.autoneg;
- ecmd.mdio_support = u->base.mdio_support;
- ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
- ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
-
- ifr->ifr_data = (void *) &ecmd;
-
- r = ioctl(fd, SIOCETHTOOL, ifr);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-/* If autonegotiation is disabled, the speed and duplex represent the fixed link
- * mode and are writable if the driver supports multiple link modes. If it is
- * enabled then they are read-only. If the link is up they represent the negotiated
- * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
- * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
- */
-int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link) {
- _cleanup_free_ struct ethtool_link_usettings *u = NULL;
- struct ifreq ifr = {};
- int r;
-
- if (link->autonegotiation != AUTONEG_DISABLE && eqzero(link->advertise)) {
- log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable.");
- return 0;
- }
-
- if (*fd < 0) {
- r = ethtool_connect(fd);
- if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
- }
-
- strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
-
- r = get_glinksettings(*fd, &ifr, &u);
- if (r < 0) {
- r = get_gset(*fd, &ifr, &u);
- if (r < 0)
- return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname);
- }
-
- if (link->speed)
- u->base.speed = DIV_ROUND_UP(link->speed, 1000000);
-
- if (link->duplex != _DUP_INVALID)
- u->base.duplex = link->duplex;
-
- if (link->port != _NET_DEV_PORT_INVALID)
- u->base.port = link->port;
-
- if (link->autonegotiation >= 0)
- u->base.autoneg = link->autonegotiation;
-
- if (!eqzero(link->advertise)) {
- u->base.autoneg = AUTONEG_ENABLE;
- memcpy(&u->link_modes.advertising, link->advertise, sizeof(link->advertise));
- memzero((uint8_t*) &u->link_modes.advertising + sizeof(link->advertise),
- ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(link->advertise));
- }
-
- if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
- r = set_slinksettings(*fd, &ifr, u);
- else
- r = set_sset(*fd, &ifr, u);
- if (r < 0)
- return log_warning_errno(r, "link_config: Cannot set device settings for %s : %m", ifname);
-
- return r;
-}
-
-int config_parse_channel(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
- link_config *config = data;
- uint32_t k;
- int r;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = safe_atou32(rvalue, &k);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse channel value, ignoring: %s", rvalue);
- return 0;
- }
-
- if (k < 1) {
- log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid %s value, ignoring: %s", lvalue, rvalue);
- return 0;
- }
-
- if (streq(lvalue, "RxChannels")) {
- config->channels.rx_count = k;
- config->channels.rx_count_set = true;
- } else if (streq(lvalue, "TxChannels")) {
- config->channels.tx_count = k;
- config->channels.tx_count_set = true;
- } else if (streq(lvalue, "OtherChannels")) {
- config->channels.other_count = k;
- config->channels.other_count_set = true;
- } else if (streq(lvalue, "CombinedChannels")) {
- config->channels.combined_count = k;
- config->channels.combined_count_set = true;
- }
-
- return 0;
-}
-
-int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) {
- struct ethtool_channels ecmd = {
- .cmd = ETHTOOL_GCHANNELS
- };
- struct ifreq ifr = {
- .ifr_data = (void*) &ecmd
- };
-
- bool need_update = false;
- int r;
-
- if (*fd < 0) {
- r = ethtool_connect(fd);
- if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
- }
-
- strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
-
- r = ioctl(*fd, SIOCETHTOOL, &ifr);
- if (r < 0)
- return -errno;
-
- if (channels->rx_count_set && ecmd.rx_count != channels->rx_count) {
- ecmd.rx_count = channels->rx_count;
- need_update = true;
- }
-
- if (channels->tx_count_set && ecmd.tx_count != channels->tx_count) {
- ecmd.tx_count = channels->tx_count;
- need_update = true;
- }
-
- if (channels->other_count_set && ecmd.other_count != channels->other_count) {
- ecmd.other_count = channels->other_count;
- need_update = true;
- }
-
- if (channels->combined_count_set && ecmd.combined_count != channels->combined_count) {
- ecmd.combined_count = channels->combined_count;
- need_update = true;
- }
-
- if (need_update) {
- ecmd.cmd = ETHTOOL_SCHANNELS;
-
- r = ioctl(*fd, SIOCETHTOOL, &ifr);
- if (r < 0)
- return -errno;
- }
-
- return 0;
-}
-
-int config_parse_advertise(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
- link_config *config = data;
- const char *p;
- int r;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (isempty(rvalue)) {
- /* Empty string resets the value. */
- zero(config->advertise);
- return 0;
- }
-
- for (p = rvalue;;) {
- _cleanup_free_ char *w = NULL;
- enum ethtool_link_mode_bit_indices mode;
-
- r = extract_first_word(&p, &w, NULL, 0);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split advertise modes '%s', ignoring: %m", rvalue);
- break;
- }
- if (r == 0)
- break;
-
- mode = ethtool_link_mode_bit_from_string(w);
- /* We reuse the kernel provided enum which does not contain negative value. So, the cast
- * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
- if ((int) mode < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse advertise mode, ignoring: %s", w);
- continue;
- }
-
- config->advertise[mode / 32] |= 1UL << (mode % 32);
- }
-
- return 0;
-}
diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h
deleted file mode 100644
index 7ca703d22c..0000000000
--- a/src/udev/net/ethtool-util.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-#pragma once
-
-#include <macro.h>
-#include <linux/ethtool.h>
-
-#include "conf-parser.h"
-
-struct link_config;
-
-/* we can't use DUPLEX_ prefix, as it
- * clashes with <linux/ethtool.h> */
-typedef enum Duplex {
- DUP_HALF = DUPLEX_HALF,
- DUP_FULL = DUPLEX_FULL,
- _DUP_MAX,
- _DUP_INVALID = -1
-} Duplex;
-
-typedef enum WakeOnLan {
- WOL_PHY,
- WOL_UCAST,
- WOL_MCAST,
- WOL_BCAST,
- WOL_ARP,
- WOL_MAGIC,
- WOL_MAGICSECURE,
- WOL_OFF,
- _WOL_MAX,
- _WOL_INVALID = -1
-} WakeOnLan;
-
-typedef enum NetDevFeature {
- NET_DEV_FEAT_GSO,
- NET_DEV_FEAT_GRO,
- NET_DEV_FEAT_LRO,
- NET_DEV_FEAT_TSO,
- NET_DEV_FEAT_TSO6,
- _NET_DEV_FEAT_MAX,
- _NET_DEV_FEAT_INVALID = -1
-} NetDevFeature;
-
-typedef enum NetDevPort {
- NET_DEV_PORT_TP = PORT_TP,
- NET_DEV_PORT_AUI = PORT_AUI,
- NET_DEV_PORT_MII = PORT_MII,
- NET_DEV_PORT_FIBRE = PORT_FIBRE,
- NET_DEV_PORT_BNC = PORT_BNC,
- NET_DEV_PORT_DA = PORT_DA,
- NET_DEV_PORT_NONE = PORT_NONE,
- NET_DEV_PORT_OTHER = PORT_OTHER,
- _NET_DEV_PORT_MAX,
- _NET_DEV_PORT_INVALID = -1
-} NetDevPort;
-
-#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
-#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES (4 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
-
-/* layout of the struct passed from/to userland */
-struct ethtool_link_usettings {
- struct ethtool_link_settings base;
-
- struct {
- uint32_t supported[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
- uint32_t advertising[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
- uint32_t lp_advertising[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
- } link_modes;
-};
-
-typedef struct netdev_channels {
- uint32_t rx_count;
- uint32_t tx_count;
- uint32_t other_count;
- uint32_t combined_count;
-
- bool rx_count_set;
- bool tx_count_set;
- bool other_count_set;
- bool combined_count_set;
-} netdev_channels;
-
-int ethtool_connect(int *ret);
-
-int ethtool_get_driver(int *fd, const char *ifname, char **ret);
-int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex);
-int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol);
-int ethtool_set_features(int *fd, const char *ifname, int *features);
-int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link);
-int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels);
-
-const char *duplex_to_string(Duplex d) _const_;
-Duplex duplex_from_string(const char *d) _pure_;
-
-const char *wol_to_string(WakeOnLan wol) _const_;
-WakeOnLan wol_from_string(const char *wol) _pure_;
-
-const char *port_to_string(NetDevPort port) _const_;
-NetDevPort port_from_string(const char *port) _pure_;
-
-const char *ethtool_link_mode_bit_to_string(enum ethtool_link_mode_bit_indices val) _const_;
-enum ethtool_link_mode_bit_indices ethtool_link_mode_bit_from_string(const char *str) _pure_;
-
-CONFIG_PARSER_PROTOTYPE(config_parse_duplex);
-CONFIG_PARSER_PROTOTYPE(config_parse_wol);
-CONFIG_PARSER_PROTOTYPE(config_parse_port);
-CONFIG_PARSER_PROTOTYPE(config_parse_channel);
-CONFIG_PARSER_PROTOTYPE(config_parse_advertise);
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index dff849a34a..9698211d1d 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -47,8 +47,8 @@ Link.TCP6SegmentationOffload, config_parse_tristate, 0,
Link.UDPSegmentationOffload, config_parse_warn_compat, DISABLED_LEGACY, 0
Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GRO])
Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_LRO])
-Link.RxChannels, config_parse_channel, 0, 0
-Link.TxChannels, config_parse_channel, 0, 0
-Link.OtherChannels, config_parse_channel, 0, 0
-Link.CombinedChannels, config_parse_channel, 0, 0
-Link.Advertise, config_parse_advertise, 0, 0
+Link.RxChannels, config_parse_channel, 0, offsetof(link_config, channels)
+Link.TxChannels, config_parse_channel, 0, offsetof(link_config, channels)
+Link.OtherChannels, config_parse_channel, 0, offsetof(link_config, channels)
+Link.CombinedChannels, config_parse_channel, 0, offsetof(link_config, channels)
+Link.Advertise, config_parse_advertise, 0, offsetof(link_config, advertise)
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index b983f28f2f..611add9ae0 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -354,7 +354,9 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
if (r < 0)
return r;
- r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, config);
+ r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name,
+ config->autonegotiation, config->advertise,
+ config->speed, config->duplex, config->port);
if (r < 0) {
if (config->port != _NET_DEV_PORT_INVALID)
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index efe5f2ce3a..a45a0e709a 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -52,7 +52,7 @@ struct link_config {
size_t speed;
Duplex duplex;
int autonegotiation;
- uint32_t advertise[2];
+ uint32_t advertise[N_ADVERTISE];
WakeOnLan wol;
NetDevPort port;
int features[_NET_DEV_FEAT_MAX];