2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2013-10-26 18:54:16 +02:00
|
|
|
|
|
|
|
#include <net/if.h>
|
2015-10-26 22:31:05 +01:00
|
|
|
#include <sys/ioctl.h>
|
2013-10-26 18:54:16 +02:00
|
|
|
#include <linux/ethtool.h>
|
2019-01-07 12:16:18 +01:00
|
|
|
#include <linux/netdevice.h>
|
2013-10-26 18:54:16 +02:00
|
|
|
#include <linux/sockios.h>
|
|
|
|
|
2015-10-26 22:31:05 +01:00
|
|
|
#include "conf-parser.h"
|
2013-10-26 18:54:16 +02:00
|
|
|
#include "ethtool-util.h"
|
2019-06-17 07:52:55 +02:00
|
|
|
#include "extract-word.h"
|
2020-01-08 12:02:01 +01:00
|
|
|
#include "fd-util.h"
|
2018-01-05 13:33:10 +01:00
|
|
|
#include "log.h"
|
2019-03-13 12:02:21 +01:00
|
|
|
#include "memory-util.h"
|
2016-10-06 15:48:15 +02:00
|
|
|
#include "socket-util.h"
|
2015-10-26 22:31:05 +01:00
|
|
|
#include "string-table.h"
|
2013-10-26 18:54:16 +02:00
|
|
|
#include "strxcpyx.h"
|
2013-10-28 20:59:56 +01:00
|
|
|
|
2014-03-07 21:38:48 +01:00
|
|
|
static const char* const duplex_table[_DUP_MAX] = {
|
2013-10-28 20:59:56 +01:00
|
|
|
[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");
|
|
|
|
|
2014-03-07 21:38:48 +01:00
|
|
|
static const char* const wol_table[_WOL_MAX] = {
|
2017-08-31 12:44:43 +02:00
|
|
|
[WOL_PHY] = "phy",
|
|
|
|
[WOL_UCAST] = "unicast",
|
|
|
|
[WOL_MCAST] = "multicast",
|
|
|
|
[WOL_BCAST] = "broadcast",
|
|
|
|
[WOL_ARP] = "arp",
|
|
|
|
[WOL_MAGIC] = "magic",
|
|
|
|
[WOL_MAGICSECURE] = "secureon",
|
2019-01-07 19:29:21 +01:00
|
|
|
[WOL_OFF] = "off",
|
2013-10-28 20:59:56 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
|
|
|
|
DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
|
2013-10-26 18:54:16 +02:00
|
|
|
|
2019-01-07 19:29:21 +01:00
|
|
|
static const char* const port_table[] = {
|
2017-06-26 00:42:57 +02:00
|
|
|
[NET_DEV_PORT_TP] = "tp",
|
|
|
|
[NET_DEV_PORT_AUI] = "aui",
|
|
|
|
[NET_DEV_PORT_MII] = "mii",
|
|
|
|
[NET_DEV_PORT_FIBRE] = "fibre",
|
2019-01-07 19:29:21 +01:00
|
|
|
[NET_DEV_PORT_BNC] = "bnc",
|
2017-06-26 00:42:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
|
|
|
|
DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
|
|
|
|
|
2016-08-30 16:52:04 +02:00
|
|
|
static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
|
2020-01-27 11:49:25 +01:00
|
|
|
[NET_DEV_FEAT_RX] = "rx-checksum",
|
|
|
|
[NET_DEV_FEAT_TX] = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
|
2017-09-19 10:49:58 +02:00
|
|
|
[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",
|
2016-08-30 16:52:04 +02:00
|
|
|
};
|
|
|
|
|
2018-11-18 10:13:32 +01:00
|
|
|
static const char* const ethtool_link_mode_bit_table[] = {
|
2019-06-25 04:55:59 +02:00
|
|
|
[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",
|
2020-09-28 15:30:07 +02:00
|
|
|
[ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] = "50000basekr-full",
|
|
|
|
[ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] = "50000basesr-full",
|
|
|
|
[ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] = "50000basecr-full",
|
|
|
|
[ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] = "50000baselr-er-fr-full",
|
|
|
|
[ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] = "50000basedr-full",
|
|
|
|
[ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] = "100000basekr2-full",
|
|
|
|
[ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] = "100000basesr2-full",
|
|
|
|
[ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] = "100000basecr2-full",
|
|
|
|
[ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = "100000baselr2-er2-fr2-full",
|
|
|
|
[ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] = "100000basedr2-full",
|
|
|
|
[ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] = "200000basekr4-full",
|
|
|
|
[ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] = "200000basesr4-full",
|
|
|
|
[ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = "200000baselr4-er4-fr4-full",
|
|
|
|
[ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] = "200000basedr4-full",
|
|
|
|
[ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] = "200000basecr4-full",
|
2020-09-28 15:49:33 +02:00
|
|
|
[ETHTOOL_LINK_MODE_100baseT1_Full_BIT] = "100baset1-full",
|
|
|
|
[ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] = "1000baset1-full",
|
|
|
|
[ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] = "400000basekr8-full",
|
|
|
|
[ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] = "400000basesr8-full",
|
|
|
|
[ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] = "400000baselr8-er8-fr8-full",
|
|
|
|
[ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] = "400000basedr8-full",
|
|
|
|
[ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] = "400000basecr8-full",
|
|
|
|
[ETHTOOL_LINK_MODE_FEC_LLRS_BIT] = "fec-llrs",
|
|
|
|
[ETHTOOL_LINK_MODE_100000baseKR_Full_BIT] = "100000basekr-full",
|
|
|
|
[ETHTOOL_LINK_MODE_100000baseSR_Full_BIT] = "100000basesr-full",
|
|
|
|
[ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT] = "100000baselr-er-fr-full",
|
|
|
|
[ETHTOOL_LINK_MODE_100000baseCR_Full_BIT] = "100000basecr-full",
|
|
|
|
[ETHTOOL_LINK_MODE_100000baseDR_Full_BIT] = "100000basedr-full",
|
|
|
|
[ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT] = "200000basekr2-full",
|
|
|
|
[ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT] = "200000basesr2-full",
|
|
|
|
[ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT] = "200000baselr2-er2-fr2-full",
|
|
|
|
[ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT] = "200000basedr2-full",
|
|
|
|
[ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT] = "200000basecr2-full",
|
|
|
|
[ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT] = "400000basekr4-full",
|
|
|
|
[ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT] = "400000basesr4-full",
|
|
|
|
[ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT] = "400000baselr4-er4-fr4-full",
|
|
|
|
[ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT] = "400000basedr4-full",
|
|
|
|
[ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT] = "400000basecr4-full",
|
2017-09-16 20:36:56 +02:00
|
|
|
};
|
2018-11-18 10:12:14 +01:00
|
|
|
/* Make sure the array is large enough to fit all bits */
|
2019-06-17 07:52:55 +02:00
|
|
|
assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE);
|
2017-09-16 20:36:56 +02:00
|
|
|
|
2018-11-17 15:08:22 +01:00
|
|
|
DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices);
|
2017-09-16 20:36:56 +02:00
|
|
|
|
2019-06-19 02:09:58 +02:00
|
|
|
static int ethtool_connect_or_warn(int *ret, bool warn) {
|
2013-10-26 18:54:16 +02:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
assert_return(ret, -EINVAL);
|
|
|
|
|
2016-10-06 15:48:15 +02:00
|
|
|
fd = socket_ioctl_fd();
|
2015-09-08 23:03:38 +02:00
|
|
|
if (fd < 0)
|
2019-06-19 02:09:58 +02:00
|
|
|
return log_full_errno(warn ? LOG_WARNING: LOG_DEBUG, fd,
|
|
|
|
"ethtool: could not create control socket: %m");
|
2018-01-05 13:36:38 +01:00
|
|
|
|
2013-10-26 18:54:16 +02:00
|
|
|
*ret = fd;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
|
2014-02-21 21:23:40 +01:00
|
|
|
struct ethtool_drvinfo ecmd = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.cmd = ETHTOOL_GDRVINFO,
|
2014-02-21 21:23:40 +01:00
|
|
|
};
|
|
|
|
struct ifreq ifr = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.ifr_data = (void*) &ecmd,
|
2014-02-21 21:23:40 +01:00
|
|
|
};
|
|
|
|
char *d;
|
2014-02-21 16:54:00 +01:00
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(ethtool_fd);
|
|
|
|
assert(ifname);
|
|
|
|
assert(ret);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
if (*ethtool_fd < 0) {
|
|
|
|
r = ethtool_connect_or_warn(ethtool_fd, true);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-06-19 02:09:58 +02:00
|
|
|
return r;
|
2014-09-09 15:36:56 +02:00
|
|
|
}
|
|
|
|
|
2014-02-21 16:54:00 +01:00
|
|
|
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
2014-02-21 16:54:00 +01:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2020-11-09 17:14:38 +01:00
|
|
|
if (isempty(ecmd.driver))
|
|
|
|
return -ENODATA;
|
|
|
|
|
2014-02-21 21:23:40 +01:00
|
|
|
d = strdup(ecmd.driver);
|
|
|
|
if (!d)
|
2014-02-21 16:54:00 +01:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-02-21 21:23:40 +01:00
|
|
|
*ret = d;
|
2014-02-21 16:54:00 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
int ethtool_get_link_info(
|
|
|
|
int *ethtool_fd,
|
|
|
|
const char *ifname,
|
|
|
|
int *ret_autonegotiation,
|
|
|
|
uint64_t *ret_speed,
|
|
|
|
Duplex *ret_duplex,
|
|
|
|
NetDevPort *ret_port) {
|
|
|
|
|
2019-06-17 08:31:20 +02:00
|
|
|
struct ethtool_cmd ecmd = {
|
|
|
|
.cmd = ETHTOOL_GSET,
|
|
|
|
};
|
|
|
|
struct ifreq ifr = {
|
|
|
|
.ifr_data = (void*) &ecmd,
|
|
|
|
};
|
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(ethtool_fd);
|
|
|
|
assert(ifname);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
if (*ethtool_fd < 0) {
|
|
|
|
r = ethtool_connect_or_warn(ethtool_fd, false);
|
2019-06-17 08:31:20 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
2019-06-17 08:31:20 +02:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (ret_autonegotiation)
|
|
|
|
*ret_autonegotiation = ecmd.autoneg;
|
|
|
|
|
2019-06-25 04:10:07 +02:00
|
|
|
if (ret_speed) {
|
|
|
|
uint32_t speed;
|
|
|
|
|
|
|
|
speed = ethtool_cmd_speed(&ecmd);
|
|
|
|
*ret_speed = speed == (uint32_t) SPEED_UNKNOWN ?
|
networkctl: use uint64_t for link speed throughout
format-table used size_t/uint64_t interchangeably for TABLE_BPS,
and ethtool-util used SIZE_MAX to indicate SPEED_UNKNOWN,
which worked only on ABIs with 64-bit pointers.
For example, the tg3 driver returns SPEED_UNKNOWN with no link (cf.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/broadcom/tg3.c?id=3eb2efbea193789397c36f52b17d8692ac79bf68#n12190)
which on x32 (and other 32-bit ABIs, presumably) caused
"networkctl status" to mark it with "Speed: 4Gbps":
nabijaczleweli@szarotka:~$ networkctl --version
systemd 245 (245.5-2)
nabijaczleweli@szarotka:~$ file $(which networkctl)
/bin/networkctl: ELF 32-bit LSB shared object, x86-64, version 1 (SYSV),
dynamically linked, interpreter /libx32/ld-linux-x32.so.2,
BuildID[sha1]=36d684cb1fc8fb5060050d32b969e5aa172fa607, for GNU/Linux
3.4.0, stripped
nabijaczleweli@szarotka:~$ networkctl status onboard1
● 4: onboard1
Driver: tg3
Model: NetXtreme BCM5755 Gigabit Ethernet PCI Express
Speed: 4Gbps
Whereas on 64-bit-pointer ABIs (here: amd64):
nabijaczleweli@szarotka:~$ networkctl --version
systemd 245 (245.5-2)
nabijaczleweli@szarotka:~$ file $(which networkctl)
/bin/networkctl: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=7a3e406e54968d7774ad467fc3f6a9d35ff7aea2, for GNU/Linux
3.2.0, stripped
nabijaczleweli@szarotka:~$ networkctl status onboard1
● 4: onboard1
Driver: tg3
Model: NetXtreme BCM5755 Gigabit Ethernet PCI Express
Speed: n/a
With this patch, networkctl returns, for x32:
nabijaczleweli@szarotka:~$ networkctl --version
systemd 245 (245.5-2.1~networkctl-4g-v2)
nabijaczleweli@szarotka:~$ file $(which networkctl)
/bin/networkctl: ELF 32-bit LSB shared object, x86-64, version 1 (SYSV),
dynamically linked, interpreter /libx32/ld-linux-x32.so.2,
BuildID[sha1]=36d684cb1fc8fb5060050d32b969e5aa172fa607, for GNU/Linux
3.4.0, stripped
nabijaczleweli@szarotka:~$ networkctl status onboard1
● 4: onboard1
Driver: tg3
Model: NetXtreme BCM5755 Gigabit Ethernet PCI Express
Speed: n/a
And for amd64:
nabijaczleweli@szarotka:~$ file $(which networkctl)
/bin/networkctl: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=7a3e406e54968d7774ad467fc3f6a9d35ff7aea2, for GNU/Linux
3.2.0, stripped
nabijaczleweli@szarotka:~$ networkctl status onboard1
● 4: onboard1
Driver: tg3
Model: NetXtreme BCM5755 Gigabit Ethernet PCI Express
Speed: n/a
2020-05-07 21:34:39 +02:00
|
|
|
UINT64_MAX : (uint64_t) speed * 1000 * 1000;
|
2019-06-25 04:10:07 +02:00
|
|
|
}
|
2019-06-17 08:31:20 +02:00
|
|
|
|
|
|
|
if (ret_duplex)
|
|
|
|
*ret_duplex = ecmd.duplex;
|
|
|
|
|
|
|
|
if (ret_port)
|
|
|
|
*ret_port = ecmd.port;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret) {
|
2020-01-08 12:02:01 +01:00
|
|
|
_cleanup_close_ int fd = -1;
|
2020-01-08 12:08:48 +01:00
|
|
|
struct {
|
|
|
|
struct ethtool_perm_addr addr;
|
|
|
|
uint8_t space[MAX_ADDR_LEN];
|
|
|
|
} epaddr = {
|
|
|
|
.addr.cmd = ETHTOOL_GPERMADDR,
|
|
|
|
.addr.size = MAX_ADDR_LEN,
|
|
|
|
};
|
|
|
|
struct ifreq ifr = {
|
|
|
|
.ifr_data = (caddr_t) &epaddr,
|
|
|
|
};
|
2019-01-07 12:16:18 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(ifname);
|
|
|
|
assert(ret);
|
|
|
|
|
2020-01-08 12:02:01 +01:00
|
|
|
if (!ethtool_fd)
|
|
|
|
ethtool_fd = &fd;
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
if (*ethtool_fd < 0) {
|
|
|
|
r = ethtool_connect_or_warn(ethtool_fd, false);
|
2019-01-07 12:16:18 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
2019-01-07 12:16:18 +01:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2020-01-08 12:08:48 +01:00
|
|
|
if (epaddr.addr.size != 6)
|
2019-01-07 12:16:18 +01:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2020-05-09 09:09:11 +02:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#if HAVE_ZERO_LENGTH_BOUNDS
|
|
|
|
# pragma GCC diagnostic ignored "-Wzero-length-bounds"
|
|
|
|
#endif
|
2020-01-08 12:08:48 +01:00
|
|
|
for (size_t i = 0; i < epaddr.addr.size; i++)
|
|
|
|
ret->ether_addr_octet[i] = epaddr.addr.data[i];
|
2020-05-09 09:09:11 +02:00
|
|
|
#pragma GCC diagnostic pop
|
2019-01-07 12:16:18 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
int ethtool_set_speed(int *ethtool_fd, const char *ifname, unsigned speed, Duplex duplex) {
|
2014-02-21 21:32:38 +01:00
|
|
|
struct ethtool_cmd ecmd = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.cmd = ETHTOOL_GSET,
|
2014-02-21 21:32:38 +01:00
|
|
|
};
|
|
|
|
struct ifreq ifr = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.ifr_data = (void*) &ecmd,
|
2014-02-21 21:32:38 +01:00
|
|
|
};
|
2013-11-06 11:18:01 +01:00
|
|
|
bool need_update = false;
|
2013-10-26 18:54:16 +02:00
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(ethtool_fd);
|
|
|
|
assert(ifname);
|
|
|
|
|
2013-10-28 20:59:56 +01:00
|
|
|
if (speed == 0 && duplex == _DUP_INVALID)
|
2013-10-26 18:54:16 +02:00
|
|
|
return 0;
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
if (*ethtool_fd < 0) {
|
|
|
|
r = ethtool_connect_or_warn(ethtool_fd, true);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-06-19 02:09:58 +02:00
|
|
|
return r;
|
2014-09-09 15:36:56 +02:00
|
|
|
}
|
|
|
|
|
2013-10-26 18:54:16 +02:00
|
|
|
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
2013-10-26 18:54:16 +02:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (ethtool_cmd_speed(&ecmd) != speed) {
|
|
|
|
ethtool_cmd_speed_set(&ecmd, speed);
|
|
|
|
need_update = true;
|
|
|
|
}
|
|
|
|
|
2013-10-28 20:59:56 +01:00
|
|
|
switch (duplex) {
|
|
|
|
case DUP_HALF:
|
2013-10-26 18:54:16 +02:00
|
|
|
if (ecmd.duplex != DUPLEX_HALF) {
|
|
|
|
ecmd.duplex = DUPLEX_HALF;
|
|
|
|
need_update = true;
|
|
|
|
}
|
2013-10-28 20:59:56 +01:00
|
|
|
break;
|
|
|
|
case DUP_FULL:
|
2013-10-26 18:54:16 +02:00
|
|
|
if (ecmd.duplex != DUPLEX_FULL) {
|
|
|
|
ecmd.duplex = DUPLEX_FULL;
|
|
|
|
need_update = true;
|
|
|
|
}
|
2013-10-28 20:59:56 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2013-10-26 18:54:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (need_update) {
|
|
|
|
ecmd.cmd = ETHTOOL_SSET;
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
2013-10-26 18:54:16 +02:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol) {
|
2014-02-21 21:32:38 +01:00
|
|
|
struct ethtool_wolinfo ecmd = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.cmd = ETHTOOL_GWOL,
|
2014-02-21 21:32:38 +01:00
|
|
|
};
|
|
|
|
struct ifreq ifr = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.ifr_data = (void*) &ecmd,
|
2014-02-21 21:32:38 +01:00
|
|
|
};
|
2013-11-06 11:18:01 +01:00
|
|
|
bool need_update = false;
|
2013-10-26 18:54:16 +02:00
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(ethtool_fd);
|
|
|
|
assert(ifname);
|
|
|
|
|
2013-10-28 20:59:56 +01:00
|
|
|
if (wol == _WOL_INVALID)
|
2013-10-26 18:54:16 +02:00
|
|
|
return 0;
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
if (*ethtool_fd < 0) {
|
|
|
|
r = ethtool_connect_or_warn(ethtool_fd, true);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-06-19 02:09:58 +02:00
|
|
|
return r;
|
2014-09-09 15:36:56 +02:00
|
|
|
}
|
|
|
|
|
2013-10-26 18:54:16 +02:00
|
|
|
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
2013-10-26 18:54:16 +02:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2013-10-28 20:59:56 +01:00
|
|
|
switch (wol) {
|
2017-08-31 12:44:43 +02:00
|
|
|
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;
|
2013-10-28 20:59:56 +01:00
|
|
|
}
|
2013-10-26 18:54:16 +02:00
|
|
|
|
|
|
|
if (need_update) {
|
|
|
|
ecmd.cmd = ETHTOOL_SWOL;
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
2013-10-26 18:54:16 +02:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-08-30 16:52:04 +02:00
|
|
|
|
2020-09-15 03:40:54 +02:00
|
|
|
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) {
|
2019-09-23 16:51:02 +02:00
|
|
|
struct ethtool_ringparam ecmd = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.cmd = ETHTOOL_GRINGPARAM,
|
2019-09-23 16:51:02 +02:00
|
|
|
};
|
|
|
|
struct ifreq ifr = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.ifr_data = (void*) &ecmd,
|
2019-09-23 16:51:02 +02:00
|
|
|
};
|
|
|
|
bool need_update = false;
|
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(ethtool_fd);
|
|
|
|
assert(ifname);
|
|
|
|
assert(ring);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
if (*ethtool_fd < 0) {
|
|
|
|
r = ethtool_connect_or_warn(ethtool_fd, true);
|
2019-09-23 16:51:02 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
2019-09-23 16:51:02 +02:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2020-04-29 18:38:56 +02:00
|
|
|
if (ring->rx_pending_set && ecmd.rx_pending != ring->rx_pending) {
|
|
|
|
ecmd.rx_pending = ring->rx_pending;
|
|
|
|
need_update = true;
|
2019-09-23 16:51:02 +02:00
|
|
|
}
|
|
|
|
|
2020-04-29 02:00:25 +02:00
|
|
|
if (ring->rx_mini_pending_set && ecmd.rx_mini_pending != ring->rx_mini_pending) {
|
|
|
|
ecmd.rx_mini_pending = ring->rx_mini_pending;
|
|
|
|
need_update = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ring->rx_jumbo_pending_set && ecmd.rx_jumbo_pending != ring->rx_jumbo_pending) {
|
|
|
|
ecmd.rx_jumbo_pending = ring->rx_jumbo_pending;
|
|
|
|
need_update = true;
|
|
|
|
}
|
|
|
|
|
2020-04-29 18:38:56 +02:00
|
|
|
if (ring->tx_pending_set && ecmd.tx_pending != ring->tx_pending) {
|
|
|
|
ecmd.tx_pending = ring->tx_pending;
|
|
|
|
need_update = true;
|
2019-09-23 16:51:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (need_update) {
|
|
|
|
ecmd.cmd = ETHTOOL_SRINGPARAM;
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
2019-09-23 16:51:02 +02:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **ret) {
|
2016-08-30 16:52:04 +02:00
|
|
|
_cleanup_free_ struct ethtool_gstrings *strings = NULL;
|
2016-09-02 12:36:58 +02:00
|
|
|
struct {
|
|
|
|
struct ethtool_sset_info info;
|
|
|
|
uint32_t space;
|
|
|
|
} buffer = {
|
|
|
|
.info = {
|
|
|
|
.cmd = ETHTOOL_GSSET_INFO,
|
|
|
|
.sset_mask = UINT64_C(1) << stringset_id,
|
|
|
|
},
|
2016-08-30 16:52:04 +02:00
|
|
|
};
|
|
|
|
unsigned len;
|
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(ethtool_fd >= 0);
|
|
|
|
assert(ifr);
|
|
|
|
assert(ret);
|
|
|
|
|
2016-09-02 12:36:58 +02:00
|
|
|
ifr->ifr_data = (void *) &buffer.info;
|
2016-08-30 16:52:04 +02:00
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(ethtool_fd, SIOCETHTOOL, ifr);
|
2016-08-30 16:52:04 +02:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2016-09-02 12:36:58 +02:00
|
|
|
if (!buffer.info.sset_mask)
|
2016-08-30 16:52:04 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2020-05-09 09:09:11 +02:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#if HAVE_ZERO_LENGTH_BOUNDS
|
|
|
|
# pragma GCC diagnostic ignored "-Wzero-length-bounds"
|
|
|
|
#endif
|
2016-09-02 12:36:58 +02:00
|
|
|
len = buffer.info.data[0];
|
2020-05-09 09:09:11 +02:00
|
|
|
#pragma GCC diagnostic pop
|
2016-08-30 16:52:04 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(ethtool_fd, SIOCETHTOOL, ifr);
|
2016-08-30 16:52:04 +02:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
*ret = TAKE_PTR(strings);
|
2016-08-30 16:52:04 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-27 11:49:25 +01:00
|
|
|
static int set_features_bit(
|
|
|
|
const struct ethtool_gstrings *strings,
|
|
|
|
const char *feature,
|
|
|
|
bool flag,
|
|
|
|
struct ethtool_sfeatures *sfeatures) {
|
|
|
|
bool found = false;
|
2016-08-30 16:52:04 +02:00
|
|
|
|
2020-01-27 11:49:25 +01:00
|
|
|
assert(strings);
|
|
|
|
assert(feature);
|
|
|
|
assert(sfeatures);
|
|
|
|
|
|
|
|
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;
|
2016-08-30 16:52:04 +02:00
|
|
|
|
2020-01-27 11:49:25 +01:00
|
|
|
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;
|
2016-08-30 16:52:04 +02:00
|
|
|
}
|
|
|
|
|
2020-09-15 03:40:54 +02:00
|
|
|
int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features) {
|
2016-08-30 16:52:04 +02:00
|
|
|
_cleanup_free_ struct ethtool_gstrings *strings = NULL;
|
|
|
|
struct ethtool_sfeatures *sfeatures;
|
2016-09-02 12:36:58 +02:00
|
|
|
struct ifreq ifr = {};
|
2020-01-27 11:49:25 +01:00
|
|
|
int i, r;
|
2016-08-30 16:52:04 +02:00
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(ethtool_fd);
|
|
|
|
assert(ifname);
|
|
|
|
assert(features);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
if (*ethtool_fd < 0) {
|
|
|
|
r = ethtool_connect_or_warn(ethtool_fd, true);
|
2016-08-30 16:52:04 +02:00
|
|
|
if (r < 0)
|
2019-06-19 02:09:58 +02:00
|
|
|
return r;
|
2016-08-30 16:52:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = get_stringset(*ethtool_fd, &ifr, ETH_SS_FEATURES, &strings);
|
2016-08-30 16:52:04 +02:00
|
|
|
if (r < 0)
|
2019-06-17 07:52:55 +02:00
|
|
|
return log_warning_errno(r, "ethtool: could not get ethtool features for %s", ifname);
|
2016-08-30 16:52:04 +02:00
|
|
|
|
2018-08-07 16:01:18 +02:00
|
|
|
sfeatures = alloca0(sizeof(struct ethtool_sfeatures) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
|
2016-08-30 16:52:04 +02:00
|
|
|
sfeatures->cmd = ETHTOOL_SFEATURES;
|
|
|
|
sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
|
|
|
|
|
2020-01-27 11:49:25 +01:00
|
|
|
for (i = 0; i < _NET_DEV_FEAT_MAX; i++)
|
2016-08-30 16:52:04 +02:00
|
|
|
if (features[i] != -1) {
|
2020-01-27 11:49:25 +01:00
|
|
|
r = set_features_bit(strings, netdev_feature_table[i], features[i], sfeatures);
|
2016-08-30 16:52:04 +02:00
|
|
|
if (r < 0) {
|
2020-01-27 11:49:25 +01:00
|
|
|
log_warning_errno(r, "ethtool: could not find feature, ignoring: %s", netdev_feature_table[i]);
|
2016-08-30 16:52:04 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ifr.ifr_data = (void *) sfeatures;
|
|
|
|
|
2020-01-08 11:55:07 +01:00
|
|
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
2016-08-30 16:52:04 +02:00
|
|
|
if (r < 0)
|
2019-06-17 07:52:55 +02:00
|
|
|
return log_warning_errno(r, "ethtool: could not set ethtool features for %s", ifname);
|
2016-08-30 16:52:04 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-11-04 10:55:07 +01:00
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
|
2016-11-04 10:55:07 +01:00
|
|
|
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;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(fd >= 0);
|
|
|
|
assert(ifr);
|
|
|
|
assert(ret);
|
|
|
|
|
2016-11-04 10:55:07 +01:00
|
|
|
/* 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;
|
|
|
|
|
2018-01-05 13:36:38 +01:00
|
|
|
r = ioctl(fd, SIOCETHTOOL, ifr);
|
2016-11-04 10:55:07 +01:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
|
2018-01-05 13:41:33 +01:00
|
|
|
return -EOPNOTSUPP;
|
2016-11-04 10:55:07 +01:00
|
|
|
|
|
|
|
ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
|
|
|
|
|
|
|
|
ifr->ifr_data = (void *) &ecmd;
|
|
|
|
|
2018-01-05 13:36:38 +01:00
|
|
|
r = ioctl(fd, SIOCETHTOOL, ifr);
|
2016-11-04 10:55:07 +01:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
|
2018-01-05 13:41:33 +01:00
|
|
|
return -EOPNOTSUPP;
|
2016-11-04 10:55:07 +01:00
|
|
|
|
2019-06-17 07:57:54 +02:00
|
|
|
u = new(struct ethtool_link_usettings, 1);
|
2016-11-04 10:55:07 +01:00
|
|
|
if (!u)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-06-17 07:57:54 +02:00
|
|
|
*u = (struct ethtool_link_usettings) {
|
|
|
|
.base = ecmd.req,
|
|
|
|
};
|
2016-11-04 10:55:07 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
*ret = u;
|
2016-11-04 10:55:07 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
|
2016-11-04 10:55:07 +01:00
|
|
|
struct ethtool_link_usettings *e;
|
|
|
|
struct ethtool_cmd ecmd = {
|
|
|
|
.cmd = ETHTOOL_GSET,
|
|
|
|
};
|
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(fd >= 0);
|
|
|
|
assert(ifr);
|
|
|
|
assert(ret);
|
|
|
|
|
2016-11-04 10:55:07 +01:00
|
|
|
ifr->ifr_data = (void *) &ecmd;
|
|
|
|
|
2018-01-05 13:36:38 +01:00
|
|
|
r = ioctl(fd, SIOCETHTOOL, ifr);
|
2016-11-04 10:55:07 +01:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2019-06-17 07:57:54 +02:00
|
|
|
e = new(struct ethtool_link_usettings, 1);
|
2016-11-04 10:55:07 +01:00
|
|
|
if (!e)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-06-17 07:57:54 +02:00
|
|
|
*e = (struct ethtool_link_usettings) {
|
|
|
|
.base.cmd = ETHTOOL_GSET,
|
|
|
|
.base.link_mode_masks_nwords = 1,
|
|
|
|
.base.speed = ethtool_cmd_speed(&ecmd),
|
|
|
|
.base.duplex = ecmd.duplex,
|
|
|
|
.base.port = ecmd.port,
|
|
|
|
.base.phy_address = ecmd.phy_address,
|
|
|
|
.base.autoneg = ecmd.autoneg,
|
|
|
|
.base.mdio_support = ecmd.mdio_support,
|
|
|
|
|
|
|
|
.link_modes.supported[0] = ecmd.supported,
|
|
|
|
.link_modes.advertising[0] = ecmd.advertising,
|
|
|
|
.link_modes.lp_advertising[0] = ecmd.lp_advertising,
|
|
|
|
};
|
2016-11-04 10:55:07 +01:00
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
*ret = e;
|
2016-11-04 10:55:07 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-01-05 13:36:38 +01:00
|
|
|
static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
|
2016-11-04 10:55:07 +01:00
|
|
|
struct {
|
|
|
|
struct ethtool_link_settings req;
|
|
|
|
__u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
|
2018-01-05 10:02:38 +01:00
|
|
|
} ecmd = {};
|
2018-10-19 20:00:46 +02:00
|
|
|
unsigned offset;
|
2016-11-04 10:55:07 +01:00
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(fd >= 0);
|
|
|
|
assert(ifr);
|
|
|
|
assert(u);
|
|
|
|
|
2016-11-04 10:55:07 +01:00
|
|
|
if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2018-01-05 13:32:39 +01:00
|
|
|
ecmd.req = u->base;
|
2018-01-05 10:02:38 +01:00
|
|
|
ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
|
2016-11-04 10:55:07 +01:00
|
|
|
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;
|
|
|
|
|
2018-01-05 13:36:38 +01:00
|
|
|
r = ioctl(fd, SIOCETHTOOL, ifr);
|
2016-11-04 10:55:07 +01:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-01-05 13:36:38 +01:00
|
|
|
static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
|
2016-11-04 10:55:07 +01:00
|
|
|
struct ethtool_cmd ecmd = {
|
|
|
|
.cmd = ETHTOOL_SSET,
|
|
|
|
};
|
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(fd >= 0);
|
|
|
|
assert(ifr);
|
|
|
|
assert(u);
|
|
|
|
|
2016-11-04 10:55:07 +01:00
|
|
|
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;
|
2017-09-16 20:36:56 +02:00
|
|
|
ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
|
|
|
|
ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
|
2016-11-04 10:55:07 +01:00
|
|
|
|
|
|
|
ifr->ifr_data = (void *) &ecmd;
|
|
|
|
|
2018-01-05 13:36:38 +01:00
|
|
|
r = ioctl(fd, SIOCETHTOOL, ifr);
|
2016-11-04 10:55:07 +01:00
|
|
|
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
|
2018-11-26 03:53:21 +01:00
|
|
|
* enabled then they are read-only. If the link is up they represent the negotiated
|
2016-11-04 10:55:07 +01:00
|
|
|
* 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.
|
|
|
|
*/
|
2019-06-17 07:52:55 +02:00
|
|
|
int ethtool_set_glinksettings(
|
|
|
|
int *fd,
|
|
|
|
const char *ifname,
|
|
|
|
int autonegotiation,
|
2020-09-15 03:40:54 +02:00
|
|
|
const uint32_t advertise[static N_ADVERTISE],
|
2020-01-21 12:06:40 +01:00
|
|
|
uint64_t speed,
|
2019-06-17 07:52:55 +02:00
|
|
|
Duplex duplex,
|
|
|
|
NetDevPort port) {
|
2020-11-09 17:04:36 +01:00
|
|
|
|
2016-11-04 10:55:07 +01:00
|
|
|
_cleanup_free_ struct ethtool_link_usettings *u = NULL;
|
|
|
|
struct ifreq ifr = {};
|
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(fd);
|
|
|
|
assert(ifname);
|
2019-07-12 16:39:07 +02:00
|
|
|
assert(advertise);
|
|
|
|
|
2019-06-17 07:52:55 +02:00
|
|
|
if (autonegotiation != AUTONEG_DISABLE && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
|
|
|
|
log_info("ethtool: autonegotiation is unset or enabled, the speed and duplex are not writable.");
|
2016-11-04 10:55:07 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*fd < 0) {
|
2019-06-19 02:09:58 +02:00
|
|
|
r = ethtool_connect_or_warn(fd, true);
|
2016-11-04 10:55:07 +01:00
|
|
|
if (r < 0)
|
2019-06-19 02:09:58 +02:00
|
|
|
return r;
|
2016-11-04 10:55:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
|
|
|
|
|
2018-01-05 13:36:38 +01:00
|
|
|
r = get_glinksettings(*fd, &ifr, &u);
|
2016-11-04 10:55:07 +01:00
|
|
|
if (r < 0) {
|
2018-01-05 13:36:38 +01:00
|
|
|
r = get_gset(*fd, &ifr, &u);
|
2016-11-04 10:55:07 +01:00
|
|
|
if (r < 0)
|
2019-06-17 07:52:55 +02:00
|
|
|
return log_warning_errno(r, "ethtool: Cannot get device settings for %s : %m", ifname);
|
2016-11-04 10:55:07 +01:00
|
|
|
}
|
|
|
|
|
2019-06-17 07:52:55 +02:00
|
|
|
if (speed > 0)
|
|
|
|
u->base.speed = DIV_ROUND_UP(speed, 1000000);
|
2017-06-26 00:42:57 +02:00
|
|
|
|
2019-06-17 07:52:55 +02:00
|
|
|
if (duplex != _DUP_INVALID)
|
|
|
|
u->base.duplex = duplex;
|
2016-11-04 10:55:07 +01:00
|
|
|
|
2019-06-17 07:52:55 +02:00
|
|
|
if (port != _NET_DEV_PORT_INVALID)
|
|
|
|
u->base.port = port;
|
2016-11-04 10:55:07 +01:00
|
|
|
|
2019-06-17 07:52:55 +02:00
|
|
|
if (autonegotiation >= 0)
|
|
|
|
u->base.autoneg = autonegotiation;
|
2016-11-04 10:55:07 +01:00
|
|
|
|
2019-06-17 07:52:55 +02:00
|
|
|
if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
|
2019-01-15 15:46:32 +01:00
|
|
|
u->base.autoneg = AUTONEG_ENABLE;
|
2019-06-17 07:52:55 +02:00
|
|
|
memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE);
|
|
|
|
memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
|
|
|
|
ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
|
2018-11-18 10:12:14 +01:00
|
|
|
}
|
2017-09-16 20:36:56 +02:00
|
|
|
|
2016-11-04 10:55:07 +01:00
|
|
|
if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
|
2018-01-05 13:36:38 +01:00
|
|
|
r = set_slinksettings(*fd, &ifr, u);
|
2016-11-04 10:55:07 +01:00
|
|
|
else
|
2018-01-05 13:36:38 +01:00
|
|
|
r = set_sset(*fd, &ifr, u);
|
2016-11-04 10:55:07 +01:00
|
|
|
if (r < 0)
|
2019-11-26 06:01:25 +01:00
|
|
|
return log_warning_errno(r, "ethtool: Cannot set device settings for %s: %m", ifname);
|
2016-11-04 10:55:07 +01:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
2018-05-08 13:03:41 +02:00
|
|
|
|
2020-09-15 03:40:54 +02:00
|
|
|
int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *channels) {
|
2018-05-08 13:03:41 +02:00
|
|
|
struct ethtool_channels ecmd = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.cmd = ETHTOOL_GCHANNELS,
|
2018-05-08 13:03:41 +02:00
|
|
|
};
|
|
|
|
struct ifreq ifr = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.ifr_data = (void*) &ecmd,
|
2018-05-08 13:03:41 +02:00
|
|
|
};
|
|
|
|
bool need_update = false;
|
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(fd);
|
|
|
|
assert(ifname);
|
|
|
|
assert(channels);
|
|
|
|
|
2018-05-08 13:03:41 +02:00
|
|
|
if (*fd < 0) {
|
2019-06-19 02:09:58 +02:00
|
|
|
r = ethtool_connect_or_warn(fd, true);
|
2018-05-08 13:03:41 +02:00
|
|
|
if (r < 0)
|
2019-06-19 02:09:58 +02:00
|
|
|
return r;
|
2018-05-08 13:03:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2020-02-07 12:06:44 +01:00
|
|
|
|
|
|
|
r = ioctl(*fd, SIOCETHTOOL, &ifr);
|
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
|
|
|
|
struct ethtool_pauseparam ecmd = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.cmd = ETHTOOL_GPAUSEPARAM,
|
2020-02-07 12:06:44 +01:00
|
|
|
};
|
|
|
|
struct ifreq ifr = {
|
2020-11-09 17:04:36 +01:00
|
|
|
.ifr_data = (void*) &ecmd,
|
2020-02-07 12:06:44 +01:00
|
|
|
};
|
|
|
|
bool need_update = false;
|
|
|
|
int r;
|
|
|
|
|
2020-11-09 17:04:36 +01:00
|
|
|
assert(fd);
|
|
|
|
assert(ifname);
|
|
|
|
|
2020-02-07 12:06:44 +01:00
|
|
|
if (*fd < 0) {
|
|
|
|
r = ethtool_connect_or_warn(fd, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
|
|
|
|
|
|
|
|
r = ioctl(*fd, SIOCETHTOOL, &ifr);
|
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (rx >= 0 && ecmd.rx_pause != (uint32_t) rx) {
|
|
|
|
ecmd.rx_pause = rx;
|
|
|
|
need_update = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx >= 0 && ecmd.tx_pause != (uint32_t) tx) {
|
|
|
|
ecmd.tx_pause = tx;
|
|
|
|
need_update = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (autoneg >= 0 && ecmd.autoneg != (uint32_t) autoneg) {
|
|
|
|
ecmd.autoneg = autoneg;
|
|
|
|
need_update = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (need_update) {
|
|
|
|
ecmd.cmd = ETHTOOL_SPAUSEPARAM;
|
2018-05-08 13:03:41 +02:00
|
|
|
|
|
|
|
r = ioctl(*fd, SIOCETHTOOL, &ifr);
|
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-09-16 20:36:56 +02:00
|
|
|
|
2019-06-17 07:52:55 +02:00
|
|
|
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) {
|
|
|
|
netdev_channels *channels = data;
|
|
|
|
uint32_t k;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(section);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
|
|
|
r = safe_atou32(rvalue, &k);
|
|
|
|
if (r < 0) {
|
2020-09-10 06:50:10 +02:00
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
|
|
"Failed to parse channel value for %s=, ignoring: %s", lvalue, rvalue);
|
2019-06-17 07:52:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (k < 1) {
|
2020-09-10 06:50:10 +02:00
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
|
|
|
"Invalid %s= value, ignoring: %s", lvalue, rvalue);
|
2019-06-17 07:52:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (streq(lvalue, "RxChannels")) {
|
|
|
|
channels->rx_count = k;
|
|
|
|
channels->rx_count_set = true;
|
|
|
|
} else if (streq(lvalue, "TxChannels")) {
|
|
|
|
channels->tx_count = k;
|
|
|
|
channels->tx_count_set = true;
|
|
|
|
} else if (streq(lvalue, "OtherChannels")) {
|
|
|
|
channels->other_count = k;
|
|
|
|
channels->other_count_set = true;
|
|
|
|
} else if (streq(lvalue, "CombinedChannels")) {
|
|
|
|
channels->combined_count = k;
|
|
|
|
channels->combined_count_set = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-16 20:36:56 +02:00
|
|
|
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) {
|
2019-06-17 07:52:55 +02:00
|
|
|
uint32_t *advertise = data;
|
2017-09-16 20:36:56 +02:00
|
|
|
const char *p;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(section);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
|
|
|
if (isempty(rvalue)) {
|
|
|
|
/* Empty string resets the value. */
|
2019-06-17 07:52:55 +02:00
|
|
|
memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
|
2017-09-16 20:36:56 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (p = rvalue;;) {
|
|
|
|
_cleanup_free_ char *w = NULL;
|
2018-11-18 10:12:14 +01:00
|
|
|
enum ethtool_link_mode_bit_indices mode;
|
2017-09-16 20:36:56 +02:00
|
|
|
|
|
|
|
r = extract_first_word(&p, &w, NULL, 0);
|
|
|
|
if (r == -ENOMEM)
|
|
|
|
return log_oom();
|
|
|
|
if (r < 0) {
|
2020-09-10 06:50:10 +02:00
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
|
|
"Failed to split advertise modes '%s', ignoring assignment: %m", rvalue);
|
|
|
|
return 0;
|
2017-09-16 20:36:56 +02:00
|
|
|
}
|
|
|
|
if (r == 0)
|
2020-09-10 06:50:10 +02:00
|
|
|
return 0;
|
2017-09-16 20:36:56 +02:00
|
|
|
|
2018-11-17 15:08:22 +01:00
|
|
|
mode = ethtool_link_mode_bit_from_string(w);
|
2019-02-25 04:35:40 +01:00
|
|
|
/* 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) {
|
2020-09-10 06:50:10 +02:00
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
|
|
|
"Failed to parse advertise mode, ignoring: %s", w);
|
2017-09-16 20:36:56 +02:00
|
|
|
continue;
|
|
|
|
}
|
2018-11-17 15:08:22 +01:00
|
|
|
|
2019-06-17 07:52:55 +02:00
|
|
|
advertise[mode / 32] |= 1UL << (mode % 32);
|
2018-11-17 15:08:22 +01:00
|
|
|
}
|
2017-09-16 20:36:56 +02:00
|
|
|
}
|
2019-09-23 16:51:02 +02:00
|
|
|
|
|
|
|
int config_parse_nic_buffer_size(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) {
|
|
|
|
netdev_ring_param *ring = data;
|
|
|
|
uint32_t k;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(section);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
|
|
|
r = safe_atou32(rvalue, &k);
|
|
|
|
if (r < 0) {
|
2020-09-10 06:50:10 +02:00
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
|
|
"Failed to parse interface buffer value, ignoring: %s", rvalue);
|
2019-09-23 16:51:02 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (k < 1) {
|
2020-09-10 06:50:10 +02:00
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
|
|
|
"Invalid %s= value, ignoring: %s", lvalue, rvalue);
|
2019-09-23 16:51:02 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (streq(lvalue, "RxBufferSize")) {
|
|
|
|
ring->rx_pending = k;
|
|
|
|
ring->rx_pending_set = true;
|
2020-04-29 02:00:25 +02:00
|
|
|
} else if (streq(lvalue, "RxMiniBufferSize")) {
|
|
|
|
ring->rx_mini_pending = k;
|
|
|
|
ring->rx_mini_pending_set = true;
|
|
|
|
} else if (streq(lvalue, "RxJumboBufferSize")) {
|
|
|
|
ring->rx_jumbo_pending = k;
|
|
|
|
ring->rx_jumbo_pending_set = true;
|
2019-09-23 16:51:02 +02:00
|
|
|
} else if (streq(lvalue, "TxBufferSize")) {
|
|
|
|
ring->tx_pending = k;
|
|
|
|
ring->tx_pending_set = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|