aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaciej Żenczykowski <maze@google.com>2023-10-27 07:10:33 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-10-27 07:10:33 +0000
commita7f02f0e55730d38617cbd16bba6651ba896b780 (patch)
treef6601a3df2c2b32e80e6dabcb128c869f50e5a2b
parentce9e8b60cec958495d29fcfa1104f8d97cdb3d6b (diff)
parent9f265c9270d3b4edf7c24169cec9c9cde846d2ad (diff)
downloadethtool-a7f02f0e55730d38617cbd16bba6651ba896b780.tar.gz
Merge upstream ethtool v5.10 am: 9f265c9270
Original change: https://android-review.googlesource.com/c/platform/external/ethtool/+/2804536 Change-Id: I0f4cd02c3fc6872be20e79ff0c96ea3ad24eecd3 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--Makefile.am21
-rw-r--r--NEWS69
-rw-r--r--amd8111e.c2
-rw-r--r--at76c50x-usb.c2
-rw-r--r--bnxt.c97
-rw-r--r--common.c30
-rw-r--r--common.h19
-rw-r--r--configure.ac2
-rw-r--r--de2104x.c4
-rw-r--r--dsa.c204
-rw-r--r--e100.c2
-rw-r--r--e1000.c2
-rw-r--r--et131x.c2
-rw-r--r--ethtool.8.in169
-rw-r--r--ethtool.c971
-rw-r--r--ethtool.spec.in1
-rw-r--r--fec.c4
-rw-r--r--fec_8xx.c2
-rw-r--r--fjes.c2
-rw-r--r--ibm_emac.c4
-rw-r--r--igb.c2
-rw-r--r--igc.c284
-rw-r--r--internal.h15
-rw-r--r--ixgb.c2
-rw-r--r--ixgbe.c2
-rw-r--r--ixgbevf.c2
-rw-r--r--json_print.c228
-rw-r--r--json_print.h67
-rw-r--r--json_writer.c391
-rw-r--r--json_writer.h76
-rw-r--r--lan78xx.c2
-rw-r--r--marvell.c6
-rw-r--r--natsemi.c6
-rw-r--r--netlink/bitset.c43
-rw-r--r--netlink/bitset.h2
-rw-r--r--netlink/cable_test.c593
-rw-r--r--netlink/channels.c141
-rw-r--r--netlink/coalesce.c269
-rw-r--r--netlink/desc-ethtool.c250
-rw-r--r--netlink/desc-genlctrl.c57
-rw-r--r--netlink/eee.c189
-rw-r--r--netlink/extapi.h57
-rw-r--r--netlink/features.c532
-rw-r--r--netlink/monitor.c87
-rw-r--r--netlink/msgbuff.c1
-rw-r--r--netlink/msgbuff.h6
-rw-r--r--netlink/netlink.c368
-rw-r--r--netlink/netlink.h77
-rw-r--r--netlink/nlsock.c46
-rw-r--r--netlink/parser.c130
-rw-r--r--netlink/parser.h7
-rw-r--r--netlink/pause.c308
-rw-r--r--netlink/prettymsg.c21
-rw-r--r--netlink/prettymsg.h24
-rw-r--r--netlink/privflags.c158
-rw-r--r--netlink/rings.c141
-rw-r--r--netlink/settings.c648
-rw-r--r--netlink/tsinfo.c124
-rw-r--r--netlink/tunnels.c236
-rw-r--r--qsfp-dd.c333
-rw-r--r--qsfp-dd.h125
-rw-r--r--qsfp.c148
-rw-r--r--qsfp.h44
-rw-r--r--realtek.c2
-rw-r--r--rxclass.c8
-rw-r--r--sfc.c3
-rw-r--r--sff-common.c51
-rw-r--r--sff-common.h26
-rw-r--r--sfpdiag.c2
-rw-r--r--sfpid.c68
-rw-r--r--smsc911x.c2
-rw-r--r--stmmac.c4
-rw-r--r--tg3.c8
-rw-r--r--tse.c2
-rw-r--r--uapi/linux/ethtool.h115
-rw-r--r--uapi/linux/ethtool_netlink.h404
-rw-r--r--uapi/linux/genetlink.h13
-rw-r--r--uapi/linux/if_link.h265
-rw-r--r--uapi/linux/net_tstamp.h6
-rw-r--r--uapi/linux/netlink.h107
-rw-r--r--uapi/linux/rtnetlink.h52
-rw-r--r--vioc.c2
-rw-r--r--vmxnet3.c2
83 files changed, 8314 insertions, 685 deletions
diff --git a/Makefile.am b/Makefile.am
index 0f8465f..e3e311d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-AM_CFLAGS = -Wall
+AM_CFLAGS = -Wall -Wextra
AM_CPPFLAGS = -I$(top_srcdir)/uapi
LDADD = -lm
@@ -7,7 +7,8 @@ EXTRA_DIST = LICENSE ethtool.8 ethtool.spec.in aclocal.m4 ChangeLog autogen.sh
sbin_PROGRAMS = ethtool
ethtool_SOURCES = ethtool.c uapi/linux/ethtool.h internal.h \
- uapi/linux/net_tstamp.h rxclass.c common.c common.h
+ uapi/linux/net_tstamp.h rxclass.c common.c common.h \
+ json_writer.c json_writer.h json_print.c json_print.h
if ETHTOOL_ENABLE_PRETTY_DUMP
ethtool_SOURCES += \
amd8111e.c de2104x.c dsa.c e100.c e1000.c et131x.c igb.c \
@@ -15,7 +16,8 @@ ethtool_SOURCES += \
pcnet32.c realtek.c tg3.c marvell.c vioc.c \
smsc911x.c at76c50x-usb.c sfc.c stmmac.c \
sff-common.c sff-common.h sfpid.c sfpdiag.c \
- ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h fjes.c lan78xx.c
+ ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h fjes.c lan78xx.c \
+ igc.c qsfp-dd.c qsfp-dd.h bnxt.c
endif
if ENABLE_BASH_COMPLETION
@@ -31,8 +33,11 @@ ethtool_SOURCES += \
netlink/monitor.c netlink/bitset.c netlink/bitset.h \
netlink/settings.c netlink/parser.c netlink/parser.h \
netlink/permaddr.c netlink/prettymsg.c netlink/prettymsg.h \
+ netlink/features.c netlink/privflags.c netlink/rings.c \
+ netlink/channels.c netlink/coalesce.c netlink/pause.c \
+ netlink/eee.c netlink/tsinfo.c \
netlink/desc-ethtool.c netlink/desc-genlctrl.c \
- netlink/desc-rtnl.c \
+ netlink/desc-rtnl.c netlink/cable_test.c netlink/tunnels.c \
uapi/linux/ethtool_netlink.h \
uapi/linux/netlink.h uapi/linux/genetlink.h \
uapi/linux/rtnetlink.h uapi/linux/if_link.h
@@ -40,12 +45,16 @@ AM_CPPFLAGS += @MNL_CFLAGS@
LDADD += @MNL_LIBS@
endif
-TESTS = test-cmdline test-features
-check_PROGRAMS = test-cmdline test-features
+TESTS = test-cmdline
+check_PROGRAMS = test-cmdline
test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES)
test_cmdline_CFLAGS = -DTEST_ETHTOOL
+if !ETHTOOL_ENABLE_NETLINK
+TESTS += test-features
+check_PROGRAMS += test-features
test_features_SOURCES = test-features.c test-common.c $(ethtool_SOURCES)
test_features_CFLAGS = -DTEST_ETHTOOL
+endif
dist-hook:
cp $(top_srcdir)/ethtool.spec $(distdir)
diff --git a/NEWS b/NEWS
index 557905d..c43fed7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,72 @@
+Version 5.10 - Dec 16, 2020
+ * Feature: infrastructure for JSON output
+ * Feature: separate FLAGS in -h output
+ * Feature: use policy dumps to check flags support
+ * Feature: show pause stats (-a)
+ * Feature: pretty printing of policy dumps
+ * Feature: improve error message when SFP module is missing
+ * Fix: use after free in netlink_run_handler()
+ * Fix: leaked instances of struct nl_socket
+ * Fix: improve compatibility between netlink and ioctl (-s)
+
+Version 5.9 - Oct 15, 2020
+ * Feature: extended link state
+ * Feature: QSFP-DD support
+ * Feature: tunnel information (--show-tunnels)
+ * Feature: Broadcom bnxt support
+ * Fix: improve compatibility between ioctl and netlink output
+ * Fix: cable test TDR amplitude output
+ * Fix: get rid of build warnings
+ * Fix: null pointer dereference running against old kernel (no arg)
+ * Fix: update link mode tables
+ * Fix: fix memory leaks and error handling found by static analysis
+
+Version 5.8 - Aug 4, 2020
+ * Feature: more ethtool netlink message format descriptions
+ * Feature: omit test-features if netlink is enabled
+ * Feature: netlink handler for gfeatures (-k)
+ * Feature: netlink handler for sfeatures (-K)
+ * Feature: netlink handler for gprivflags (--show-priv-flags)
+ * Feature: netlink handler for sprivflags (--set-priv-flags)
+ * Feature: netlink handler for gring (-g)
+ * Feature: netlink handler for sring (-G)
+ * Feature: netlink handler for gchannels (-l)
+ * Feature: netlink handler for schannels (-L)
+ * Feature: netlink handler for gcoalesce (-c)
+ * Feature: netlink handler for scoalesce (-C)
+ * Feature: netlink handler for gpause (-a)
+ * Feature: netlink handler for spause (-A)
+ * Feature: netlink handler for geee (--show-eee)
+ * Feature: netlink handler for seee (--set-eee)
+ * Feature: netlink handler for tsinfo (-T)
+ * Feature: master/slave configuration support
+ * Feature: LINKSTATE SQI support
+ * Feature: cable test support
+ * Feature: cable test TDR support
+ * Feature: JSON output for cable test commands
+ * Feature: igc driver support
+ * Feature: support for get/set ethtool_tunable
+ * Feature: dsa: mv88e6xxx: add pretty dump for 88E6352 SERDES
+ * Fix: fix build warnings
+ * Fix: fix nest type grouping in parser
+ * Fix: fix msgbuff_append() helper
+ * Fix: fix unwanted switch fall through in family_info_cb()
+ * Fix: fix netlink error message suppression
+ * Fix: fix netlink bitmasks when sent as NOMASK
+ * Fix: use "Not reported" when no FEC modes are provided
+ * Fix: ioctl: do not pass transceiver value back to kernel
+ * Fix: ethtool.spec: Add bash completion script
+
+Version 5.7 - Jun 4, 2020
+ * Feature: ethtool: Add support for Low Latency Reed Solomon
+ * Fix: ethtool.c: Report transceiver correctly
+ * Feature: features: accept long legacy flag names when setting features
+ * Feature: refactor interface between ioctl and netlink code
+ * Feature: netlink: use genetlink ops information to decide about fallback
+ * Feature: netlink: show netlink error even without extack
+ * Feature: ethtool: add support for newer SFF-8024 compliance codes
+ * Feature: Rewrite printf() due to -Werror=format-security
+
Version 5.6 - May 12, 2020
* Feature: add --debug option to control debugging messages
* Feature: use named initializers in command line option list
diff --git a/amd8111e.c b/amd8111e.c
index 5a056b3..175516b 100644
--- a/amd8111e.c
+++ b/amd8111e.c
@@ -152,7 +152,7 @@ typedef enum {
#define PHY_SPEED_100 0x3
-int amd8111e_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int amd8111e_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
diff --git a/at76c50x-usb.c b/at76c50x-usb.c
index 0121e98..fad41bf 100644
--- a/at76c50x-usb.c
+++ b/at76c50x-usb.c
@@ -12,7 +12,7 @@ static char *hw_versions[] = {
" 505AMX",
};
-int at76c50x_usb_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int at76c50x_usb_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u8 version = (u8)(regs->version >> 24);
diff --git a/bnxt.c b/bnxt.c
new file mode 100644
index 0000000..b46db72
--- /dev/null
+++ b/bnxt.c
@@ -0,0 +1,97 @@
+/* Code to dump registers for NetXtreme-E/NetXtreme-C Broadcom devices.
+ *
+ * Copyright (c) 2020 Broadcom Inc.
+ */
+#include <stdio.h>
+#include "internal.h"
+
+#define BNXT_PXP_REG_LEN 0x3110
+#define BNXT_PCIE_STATS_LEN (12 * sizeof(u64))
+
+struct bnxt_pcie_stat {
+ const char *name;
+ u16 offset;
+ u8 size;
+ const char *format;
+};
+
+static const struct bnxt_pcie_stat bnxt_pcie_stats[] = {
+ { .name = "PL Signal integrity errors", .offset = 0, .size = 4, .format = "%llu" },
+ { .name = "DL Signal integrity errors", .offset = 4, .size = 4, .format = "%llu" },
+ { .name = "TLP Signal integrity errors", .offset = 8, .size = 4, .format = "%llu" },
+ { .name = "Link integrity", .offset = 12, .size = 4, .format = "%llu" },
+ { .name = "TX TLP traffic rate", .offset = 16, .size = 4, .format = "%llu" },
+ { .name = "RX TLP traffic rate", .offset = 20, .size = 4, .format = "%llu" },
+ { .name = "TX DLLP traffic rate", .offset = 24, .size = 4, .format = "%llu" },
+ { .name = "RX DLLP traffic rate", .offset = 28, .size = 4, .format = "%llu" },
+ { .name = "Equalization Phase 0 time(ms)", .offset = 33, .size = 1, .format = "0x%x" },
+ { .name = "Equalization Phase 1 time(ms)", .offset = 32, .size = 1, .format = "0x%x" },
+ { .name = "Equalization Phase 2 time(ms)", .offset = 35, .size = 1, .format = "0x%x" },
+ { .name = "Equalization Phase 3 time(ms)", .offset = 34, .size = 1, .format = "0x%x" },
+ { .name = "PHY LTSSM Histogram 0", .offset = 36, .size = 2, .format = "0x%x"},
+ { .name = "PHY LTSSM Histogram 1", .offset = 38, .size = 2, .format = "0x%x"},
+ { .name = "PHY LTSSM Histogram 2", .offset = 40, .size = 2, .format = "0x%x"},
+ { .name = "PHY LTSSM Histogram 3", .offset = 42, .size = 2, .format = "0x%x"},
+ { .name = "Recovery Histogram 0", .offset = 44, .size = 2, .format = "0x%x"},
+ { .name = "Recovery Histogram 1", .offset = 46, .size = 2, .format = "0x%x"},
+};
+
+int bnxt_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs)
+{
+ const struct bnxt_pcie_stat *stats = bnxt_pcie_stats;
+ u16 *pcie_stats, pcie_stat16;
+ u32 reg, i, pcie_stat32;
+ u64 pcie_stat64;
+
+ if (regs->len < BNXT_PXP_REG_LEN) {
+ fprintf(stdout, "Length too short, expected at least 0x%x\n",
+ BNXT_PXP_REG_LEN);
+ return -1;
+ }
+
+ fprintf(stdout, "PXP Registers\n");
+ fprintf(stdout, "Offset\tValue\n");
+ fprintf(stdout, "------\t-------\n");
+ for (i = 0; i < BNXT_PXP_REG_LEN; i += sizeof(reg)) {
+ memcpy(&reg, &regs->data[i], sizeof(reg));
+ if (reg)
+ fprintf(stdout, "0x%04x\t0x%08x\n", i, reg);
+ }
+ fprintf(stdout, "\n");
+
+ if (!regs->version)
+ return 0;
+
+ if (regs->len < (BNXT_PXP_REG_LEN + BNXT_PCIE_STATS_LEN)) {
+ fprintf(stdout, "Length is too short, expected 0x%lx\n",
+ BNXT_PXP_REG_LEN + BNXT_PCIE_STATS_LEN);
+ return -1;
+ }
+
+ pcie_stats = (u16 *)(regs->data + BNXT_PXP_REG_LEN);
+ fprintf(stdout, "PCIe statistics:\n");
+ fprintf(stdout, "----------------\n");
+ for (i = 0; i < ARRAY_SIZE(bnxt_pcie_stats); i++) {
+ fprintf(stdout, "%-30s : ", stats[i].name);
+ switch (stats[i].size) {
+ case 1:
+ pcie_stat16 = 0;
+ memcpy(&pcie_stat16, &pcie_stats[stats[i].offset], sizeof(u16));
+ fprintf(stdout, stats[i].format, pcie_stat16);
+ break;
+ case 2:
+ pcie_stat32 = 0;
+ memcpy(&pcie_stat32, &pcie_stats[stats[i].offset], sizeof(u32));
+ fprintf(stdout, stats[i].format, pcie_stat32);
+ break;
+ case 4:
+ pcie_stat64 = 0;
+ memcpy(&pcie_stat64, &pcie_stats[stats[i].offset], sizeof(u64));
+ fprintf(stdout, stats[i].format, pcie_stat64);
+ break;
+ }
+ fprintf(stdout, "\n");
+ }
+
+ return 0;
+}
diff --git a/common.c b/common.c
index f9c41a3..2630e73 100644
--- a/common.c
+++ b/common.c
@@ -47,6 +47,36 @@ const struct flag_info flags_msglvl[] = {
};
const unsigned int n_flags_msglvl = ARRAY_SIZE(flags_msglvl) - 1;
+const struct off_flag_def off_flag_def[] = {
+ { "rx", "rx-checksumming", "rx-checksum",
+ ETHTOOL_GRXCSUM, ETHTOOL_SRXCSUM, ETH_FLAG_RXCSUM, 0 },
+ { "tx", "tx-checksumming", "tx-checksum-*",
+ ETHTOOL_GTXCSUM, ETHTOOL_STXCSUM, ETH_FLAG_TXCSUM, 0 },
+ { "sg", "scatter-gather", "tx-scatter-gather*",
+ ETHTOOL_GSG, ETHTOOL_SSG, ETH_FLAG_SG, 0 },
+ { "tso", "tcp-segmentation-offload", "tx-tcp*-segmentation",
+ ETHTOOL_GTSO, ETHTOOL_STSO, ETH_FLAG_TSO, 0 },
+ { "ufo", "udp-fragmentation-offload", "tx-udp-fragmentation",
+ ETHTOOL_GUFO, ETHTOOL_SUFO, ETH_FLAG_UFO, 0 },
+ { "gso", "generic-segmentation-offload", "tx-generic-segmentation",
+ ETHTOOL_GGSO, ETHTOOL_SGSO, ETH_FLAG_GSO, 0 },
+ { "gro", "generic-receive-offload", "rx-gro",
+ ETHTOOL_GGRO, ETHTOOL_SGRO, ETH_FLAG_GRO, 0 },
+ { "lro", "large-receive-offload", "rx-lro",
+ 0, 0, ETH_FLAG_LRO,
+ KERNEL_VERSION(2,6,24) },
+ { "rxvlan", "rx-vlan-offload", "rx-vlan-hw-parse",
+ 0, 0, ETH_FLAG_RXVLAN,
+ KERNEL_VERSION(2,6,37) },
+ { "txvlan", "tx-vlan-offload", "tx-vlan-hw-insert",
+ 0, 0, ETH_FLAG_TXVLAN,
+ KERNEL_VERSION(2,6,37) },
+ { "ntuple", "ntuple-filters", "rx-ntuple-filter",
+ 0, 0, ETH_FLAG_NTUPLE, 0 },
+ { "rxhash", "receive-hashing", "rx-hashing",
+ 0, 0, ETH_FLAG_RXHASH, 0 },
+};
+
void print_flags(const struct flag_info *info, unsigned int n_info, u32 value)
{
const char *sep = "";
diff --git a/common.h b/common.h
index 3a68011..b74fdfa 100644
--- a/common.h
+++ b/common.h
@@ -19,6 +19,25 @@ struct flag_info {
extern const struct flag_info flags_msglvl[];
extern const unsigned int n_flags_msglvl;
+struct off_flag_def {
+ const char *short_name;
+ const char *long_name;
+ const char *kernel_name;
+ u32 get_cmd, set_cmd;
+ u32 value;
+ /* For features exposed through ETHTOOL_GFLAGS, the oldest
+ * kernel version for which we can trust the result. Where
+ * the flag was added at the same time the kernel started
+ * supporting the feature, this is 0 (to allow for backports).
+ * Where the feature was supported before the flag was added,
+ * it is the version that introduced the flag.
+ */
+ u32 min_kernel_ver;
+};
+
+#define OFF_FLAG_DEF_SIZE 12
+extern const struct off_flag_def off_flag_def[OFF_FLAG_DEF_SIZE];
+
void print_flags(const struct flag_info *info, unsigned int n_info, u32 value);
int dump_wol(struct ethtool_wolinfo *wol);
void dump_mdix(u8 mdix, u8 mdix_ctrl);
diff --git a/configure.ac b/configure.ac
index 1169b7f..13c2bc0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script.
-AC_INIT(ethtool, 5.6, netdev@vger.kernel.org)
+AC_INIT(ethtool, 5.10, netdev@vger.kernel.org)
AC_PREREQ(2.52)
AC_CONFIG_SRCDIR([ethtool.c])
AM_INIT_AUTOMAKE([gnu subdir-objects])
diff --git a/de2104x.c b/de2104x.c
index cc03533..190422f 100644
--- a/de2104x.c
+++ b/de2104x.c
@@ -111,7 +111,7 @@ print_rx_missed(u32 csr8)
}
}
-static void de21040_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+static void de21040_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 tmp, v, *data = (u32 *)regs->data;
@@ -417,7 +417,7 @@ static void de21040_dump_regs(struct ethtool_drvinfo *info maybe_unused,
v & (1<<0) ? " Jabber disable\n" : "");
}
-static void de21041_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+static void de21041_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 tmp, v, *data = (u32 *)regs->data;
diff --git a/dsa.c b/dsa.c
index 50a171b..33c1d39 100644
--- a/dsa.c
+++ b/dsa.c
@@ -3,6 +3,8 @@
#include "internal.h"
+#define SERDES_OFFSET 32
+
/* Macros and dump functions for the 16-bit mv88e6xxx per-port registers */
#define REG(_reg, _name, _val) \
@@ -405,21 +407,204 @@ static void dsa_mv88e6352(int reg, u16 val)
case 19:
REG(reg, "Rx Frame Counter", val);
break;
+ case 20 ... 21:
+ REG(reg, "Reserved", val);
+ break;
case 22:
REG(reg, "LED Control", val);
break;
+ case 23:
+ REG(reg, "Reserved", val);
+ break;
case 24:
REG(reg, "Tag Remap 0-3", val);
break;
case 25:
REG(reg, "Tag Remap 4-7", val);
break;
+ case 26:
+ REG(reg, "Reserved", val);
+ break;
case 27:
REG(reg, "Queue Counters", val);
break;
- default:
+ case 28 ... 31:
REG(reg, "Reserved", val);
break;
+ case SERDES_OFFSET + 0:
+ REG(reg - SERDES_OFFSET, "Fiber Control", val);
+ FIELD("Fiber Reset", "%u", !!(val & 0x8000));
+ FIELD("Loopback", "%u", !!(val & 0x4000));
+ FIELD("Speed", "%s",
+ (val & (0x2000 | 0x0040)) == 0x0000 ? "10 Mbps" :
+ (val & (0x2000 | 0x0040)) == 0x2000 ? "100 Mbps" :
+ (val & (0x2000 | 0x0040)) == 0x0040 ? "1000 Mbps" :
+ (val & (0x2000 | 0x0040)) == (0x2000 | 0x0040) ?
+ "Reserved" : "?");
+ FIELD("Autoneg Enable", "%u", !!(val & 0x1000));
+ FIELD("Power down", "%u", !!(val & 0x0800));
+ FIELD("Isolate", "%u", !!(val & 0x0400));
+ FIELD("Restart Autoneg", "%u", !!(val & 0x0200));
+ FIELD("Duplex", "%s", val & 0x0100 ? "Full" : "Half");
+ break;
+ case SERDES_OFFSET + 1:
+ REG(reg - SERDES_OFFSET, "Fiber Status", val);
+ FIELD("100Base-X FD", "%u", !!(val & 0x4000));
+ FIELD("100Base-X HD", "%u", !!(val & 0x2000));
+ FIELD("Autoneg Complete", "%u", !!(val & 0x0020));
+ FIELD("Remote Fault", "%u", !!(val & 0x0010));
+ FIELD("Autoneg Ability", "%u", !!(val & 0x0008));
+ FIELD("Link Status", "%s", val & 0x0004 ? "Up" : "Down");
+ break;
+ case SERDES_OFFSET + 2:
+ REG(reg - SERDES_OFFSET, "PHY ID 1", val);
+ break;
+ case SERDES_OFFSET + 3:
+ REG(reg - SERDES_OFFSET, "PHY ID 2", val);
+ break;
+ case SERDES_OFFSET + 4:
+ REG(reg - SERDES_OFFSET, "Fiber Autoneg Advertisement", val);
+ FIELD("Remote Fault", "%s",
+ (val & 0x3000) == 0x0000 ? "No error, link OK" :
+ (val & 0x3000) == 0x1000 ? "Link failure" :
+ (val & 0x3000) == 0x2000 ? "Offline" :
+ (val & 0x3000) == 0x3000 ? "Autoneg Error" : "?");
+ FIELD("Pause", "%s",
+ (val & 0x0180) == 0x0000 ? "No Pause" :
+ (val & 0x0180) == 0x0080 ? "Symmetric Pause" :
+ (val & 0x0180) == 0x0100 ? "Asymmetric Pause" :
+ (val & 0x0180) == 0x0180 ? "Symmetric & Asymmetric Pause" :
+ "?");
+ FIELD("1000BaseX HD", "%u", !!(val & 0x0040));
+ FIELD("1000BaseX FD", "%u", !!(val & 0x0020));
+ break;
+ case SERDES_OFFSET + 5:
+ REG(reg - SERDES_OFFSET, "Fiber Link Autoneg Ability", val);
+ FIELD("Acknowledge", "%u", !!(val & 0x4000));
+ FIELD("Remote Fault", "%s",
+ (val & 0x3000) == 0x0000 ? "No error, link OK" :
+ (val & 0x3000) == 0x1000 ? "Link failure" :
+ (val & 0x3000) == 0x2000 ? "Offline" :
+ (val & 0x3000) == 0x3000 ? "Autoneg Error" : "?");
+ FIELD("Pause", "%s",
+ (val & 0x0180) == 0x0000 ? "No Pause" :
+ (val & 0x0180) == 0x0080 ? "Symmetric Pause" :
+ (val & 0x0180) == 0x0100 ? "Asymmetric Pause" :
+ (val & 0x0180) == 0x0180 ? "Symmetric & Asymmetric Pause" :
+ "?");
+ FIELD("1000BaseX HD", "%u", !!(val & 0x0040));
+ FIELD("1000BaseX FD", "%u", !!(val & 0x0020));
+ break;
+ case SERDES_OFFSET + 6:
+ REG(reg - SERDES_OFFSET, "Fiber Autoneg Expansion", val);
+ FIELD("Link Partner Next Page Ability", "%u", !!(val & 0x0008));
+ FIELD("Page Received", "%u", !!(val & 0x0002));
+ FIELD("Link Partner Autoneg Ability", "%u", !!(val & 0x0001));
+ break;
+ case SERDES_OFFSET + 7:
+ REG(reg - SERDES_OFFSET, "Fiber Next Page Transmit", val);
+ break;
+ case SERDES_OFFSET + 8:
+ REG(reg - SERDES_OFFSET, "Fiber Link Partner Next Page", val);
+ break;
+ case SERDES_OFFSET + 9 ... SERDES_OFFSET + 14:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
+ case SERDES_OFFSET + 15:
+ REG(reg - SERDES_OFFSET, "Extended Status", val);
+ break;
+ case SERDES_OFFSET + 16:
+ REG(reg - SERDES_OFFSET, "Fiber Specific Control", val);
+ FIELD("Fiber Transmit FIFO Depth", "%s",
+ (val & 0xc000) == 0x0000 ? "16 Bits" :
+ (val & 0xc000) == 0x4000 ? "24 Bits" :
+ (val & 0xc000) == 0x8000 ? "32 Bits" :
+ (val & 0xc000) == 0xc000 ? "40 Bits" : "?");
+ FIELD("SERDES Loopback", "%u", !!(val & 0x1000));
+ FIELD("Force Link Good", "%u", !!(val & 0x0400));
+ FIELD("MAC Interface Power Down", "%u", !!(val & 0x0008));
+ FIELD("Mode", "%s",
+ (val & 0x0003) == 0x0000 ? "100BaseFX" :
+ (val & 0x0003) == 0x0001 ? "1000BaseX" :
+ (val & 0x0003) == 0x0002 ? "SGMII System" :
+ (val & 0x0003) == 0x0003 ? "SGMII Media" : "?");
+ break;
+ case SERDES_OFFSET + 17:
+ REG(reg - SERDES_OFFSET, "Fiber Specific Status", val);
+ FIELD("Speed", "%s",
+ (val & 0xc000) == 0x0000 ? "10 Mbps" :
+ (val & 0xc000) == 0x4000 ? "100 Mbps" :
+ (val & 0xc000) == 0x8000 ? "1000 Mbps" :
+ (val & 0xc000) == 0xc000 ? "Reserved" : "?");
+ FIELD("Duplex", "%s", val & 0x2000 ? "Full" : "Half");
+ FIELD("Page Received", "%u", !!(val & 0x1000));
+ FIELD("Speed/Duplex Resolved", "%u", !!(val & 0x0800));
+ FIELD("Link", "%s", val & 0x0400 ? "Up" : "Down");
+ FIELD("Sync", "%u", !!(val & 0x0020));
+ FIELD("Energy Detect", "%s", val & 0x010 ? "False" : "True");
+ FIELD("Transmit Pause", "%u", !!(val & 0x0008));
+ FIELD("Receive Pause", "%u", !!(val & 0x00004));
+ break;
+ case SERDES_OFFSET + 18:
+ REG(reg - SERDES_OFFSET, "Fiber Interrupt Enable", val);
+ FIELD("Speed Changed", "%u", !!(val & 0x4000));
+ FIELD("Duplex Changed", "%u", !!(val & 0x2000));
+ FIELD("Page Received", "%u", !!(val & 0x1000));
+ FIELD("Autoneg Complete", "%u", !!(val & 0x0800));
+ FIELD("Link Status Change", "%u", !!(val & 0x0400));
+ FIELD("Symbol Error", "%u", !!(val & 0x0200));
+ FIELD("False Carrier", "%u", !!(val & 0x0100));
+ FIELD("Energy Detect", "%u", !!(val & 0x0010));
+ break;
+ case SERDES_OFFSET + 19:
+ REG(reg - SERDES_OFFSET, "Fiber Interrupt Status", val);
+ FIELD("Speed Changed", "%u", !!(val & 0x4000));
+ FIELD("Duplex Changed", "%u", !!(val & 0x2000));
+ FIELD("Page Received", "%u", !!(val & 0x1000));
+ FIELD("Autoneg Complete", "%u", !!(val & 0x0800));
+ FIELD("Link Status Change", "%u", !!(val & 0x0400));
+ FIELD("Symbol Error", "%u", !!(val & 0x0200));
+ FIELD("False Carrier", "%u", !!(val & 0x0100));
+ FIELD("Energy Detect", "%u", !!(val & 0x0010));
+ break;
+ case SERDES_OFFSET + 20:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
+ case SERDES_OFFSET + 21:
+ REG(reg - SERDES_OFFSET, "Fiber Receive Error Counter", val);
+ break;
+ case SERDES_OFFSET + 22:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
+ case SERDES_OFFSET + 23:
+ REG(reg - SERDES_OFFSET, "PRBS Control", val);
+ break;
+ case SERDES_OFFSET + 24:
+ REG(reg - SERDES_OFFSET, "PRBS Error Counter LSB", val);
+ break;
+ case SERDES_OFFSET + 25:
+ REG(reg - SERDES_OFFSET, "PRBS Error Counter MSB", val);
+ break;
+ case SERDES_OFFSET + 26:
+ REG(reg - SERDES_OFFSET, "Fiber Specific Control 2", val);
+ FIELD("1000BaseX Noise Filtering", "%u", !!(val & 0x4000));
+ FIELD("1000BaseFX Noise Filtering", "%u", !!(val & 0x2000));
+ FIELD("SERDES Autoneg Bypass Enable", "%u", !!(val & 0x0040));
+ FIELD("SERDES Autoneg Bypass Status", "%u", !!(val & 0x0020));
+ FIELD("Fiber Transmitter Disable", "%u", !!(val & 0x0008));
+ FIELD("SGMII/Fiber Output Amplitude", "%s",
+ (val & 0x0007) == 0x0000 ? "14mV" :
+ (val & 0x0007) == 0x0001 ? "112mV" :
+ (val & 0x0007) == 0x0002 ? "210mV" :
+ (val & 0x0007) == 0x0003 ? "308mV" :
+ (val & 0x0007) == 0x0004 ? "406mV" :
+ (val & 0x0007) == 0x0005 ? "504mV" :
+ (val & 0x0007) == 0x0006 ? "602mV" :
+ (val & 0x0007) == 0x0007 ? "700mV" : "?");
+ break;
+ default:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
}
};
@@ -639,11 +824,11 @@ static int dsa_mv88e6xxx_dump_regs(struct ethtool_regs *regs)
{
const struct dsa_mv88e6xxx_switch *sw = NULL;
const u16 *data = (u16 *)regs->data;
+ unsigned int i;
u16 id;
- int i;
/* Marvell chips have 32 per-port 16-bit registers */
- if (regs->len < 32 * 2)
+ if (regs->len < 32 * sizeof(u16))
return 1;
id = regs->version & 0xfff0;
@@ -667,6 +852,17 @@ static int dsa_mv88e6xxx_dump_regs(struct ethtool_regs *regs)
else
REG(i, "", data[i]);
+ /* Dump the SERDES registers, if provided */
+ if (regs->len > SERDES_OFFSET * sizeof(u16)) {
+ printf("\n%s Switch Port SERDES Registers\n", sw->name);
+ printf("-------------------------------------\n");
+ for (i = SERDES_OFFSET; i < regs->len / 2; i++)
+ if (sw->dump)
+ sw->dump(i, data[i]);
+ else
+ REG(i - SERDES_OFFSET, "", data[i]);
+ }
+
return 0;
}
@@ -674,7 +870,7 @@ static int dsa_mv88e6xxx_dump_regs(struct ethtool_regs *regs)
#undef FIELD
#undef REG
-int dsa_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int dsa_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
/* DSA per-driver register dump */
diff --git a/e100.c b/e100.c
index 540ae35..fd4bd03 100644
--- a/e100.c
+++ b/e100.c
@@ -36,7 +36,7 @@
#define CU_CMD 0x00F0
#define RU_CMD 0x0007
-int e100_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int e100_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/e1000.c b/e1000.c
index 91e5bc1..dbd6eb5 100644
--- a/e1000.c
+++ b/e1000.c
@@ -363,7 +363,7 @@ static enum e1000_mac_type e1000_get_mac_type(u16 device_id)
return mac_type;
}
-int e1000_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int e1000_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/et131x.c b/et131x.c
index 1b06071..a23f7a2 100644
--- a/et131x.c
+++ b/et131x.c
@@ -2,7 +2,7 @@
#include <string.h>
#include "internal.h"
-int et131x_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int et131x_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u8 version = (u8)(regs->version >> 24);
diff --git a/ethtool.8.in b/ethtool.8.in
index acc8d84..ba4e245 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -52,6 +52,10 @@
.\"
.ds MA \fIxx\fP\fB:\fP\fIyy\fP\fB:\fP\fIzz\fP\fB:\fP\fIaa\fP\fB:\fP\fIbb\fP\fB:\fP\fIcc\fP
.\"
+.\" \(*MS - master-slave property
+.\"
+.ds MS \fBpreferred-master\fP|\fBpreferred-slave\fP|\fBforced-master\fP|\fBforced-slave\fP
+.\"
.\" \(*PA - IP address
.\"
.ds PA \fIip-address\fP
@@ -113,7 +117,7 @@
. hy \\n(HY
..
.
-.TH ETHTOOL 8 "May 2020" "Ethtool version @VERSION@"
+.TH ETHTOOL 8 "Dec 2020" "Ethtool version @VERSION@"
.SH NAME
ethtool \- query or control network driver and hardware settings
.
@@ -133,6 +137,12 @@ ethtool \- query or control network driver and hardware settings
.BN --debug
.I args
.HP
+.B ethtool [--json]
+.I args
+.HP
+.B ethtool [-I | --include-statistics]
+.I args
+.HP
.B ethtool \-\-monitor
[
.I command
@@ -255,6 +265,7 @@ ethtool \- query or control network driver and hardware settings
.RB [ wol \ \fIN\fP[\fB/\fP\fIM\fP]
.RB | \ wol \ \*(WO]
.RB [ sopass \ \*(MA]
+.RB [ master-slave \ \*(MS]
.RB [ msglvl
.IR N\fP[/\fIM\fP] \ |
.BI msglvl \ type
@@ -390,6 +401,18 @@ ethtool \- query or control network driver and hardware settings
.RB [ fast-link-down ]
.RB [ energy-detect-power-down ]
.HP
+.B ethtool \-\-get\-tunable
+.I devname
+.RB [ rx-copybreak ]
+.RB [ tx-copybreak ]
+.RB [ pfc-prevention-tout ]
+.HP
+.B ethtool \-\-set\-tunable
+.I devname
+.BN rx\-copybreak
+.BN tx\-copybreak
+.BN pfc\-prevention\-tout
+.HP
.B ethtool \-\-reset
.I devname
.BN flags
@@ -420,7 +443,7 @@ ethtool \- query or control network driver and hardware settings
.B ethtool \-\-set\-fec
.I devname
.B encoding
-.BR auto | off | rs | baser \ [...]
+.BR auto | off | rs | baser | llrs \ [...]
.HP
.B ethtool \-Q|\-\-per\-queue
.I devname
@@ -429,6 +452,19 @@ ethtool \- query or control network driver and hardware settings
.I sub_command
.RB ...
.
+.HP
+.B ethtool \-\-cable\-test
+.I devname
+.HP
+.B ethtool \-\-cable\-test\-tdr
+.I devname
+.BN first N
+.BN last N
+.BN step N
+.BN pair N
+.HP
+.B ethtool \-\-show\-tunnels
+.I devname
.
.\" Adjust lines (i.e. full justification) and hyphenate.
.ad
@@ -461,6 +497,15 @@ lB l.
0x01 Parser information
.TE
.TP
+.BI \-\-json
+Output results in JavaScript Object Notation (JSON). Only a subset of
+options support this. Those which do not will continue to output
+plain text in the presence of this option.
+.TP
+.B \-I \-\-include\-statistics
+Include command-related statistics in the output. This option allows
+displaying relevant device statistics for selected get commands.
+.TP
.B \-a \-\-show\-pause
Queries the specified Ethernet device for pause parameter information.
.TP
@@ -646,6 +691,20 @@ Sets full or half duplex mode.
.A4 port tp aui bnc mii fibre da
Selects device port.
.TP
+.BR master-slave \ \*(MS
+Configure MASTER/SLAVE role of the PHY. When the PHY is configured as MASTER,
+the PMA Transmit function shall source TX_TCLK from a local clock source. When
+configured as SLAVE, the PMA Transmit function shall source TX_TCLK from the
+clock recovered from data stream provided by MASTER. Not all devices support this.
+.TS
+nokeep;
+lB l.
+preferred-master Prefer MASTER role on autonegotiation
+preferred-slave Prefer SLAVE role on autonegotiation
+forced-master Force the PHY in MASTER role. Can be used without autonegotiation
+forced-slave Force the PHY in SLAVE role. Can be used without autonegotiation
+.TE
+.TP
.A3 mdix auto on off
Selects MDI-X mode for port. May be used to override the automatic
detection feature of most adapters. An argument of \fBauto\fR means
@@ -670,23 +729,25 @@ lB l lB.
0x004 100baseT Half
0x008 100baseT Full
0x80000000000000000 100baseT1 Full
+0x40000000000000000000000 100baseFX Half
+0x80000000000000000000000 100baseFX Full
0x010 1000baseT Half (not supported by IEEE standards)
0x020 1000baseT Full
-0x100000000000000000 1000baseT1 Full
0x20000 1000baseKX Full
0x20000000000 1000baseX Full
-0x800000000000 2500baseT Full
+0x100000000000000000 1000baseT1 Full
0x8000 2500baseX Full (not supported by IEEE standards)
+0x800000000000 2500baseT Full
0x1000000000000 5000baseT Full
0x1000 10000baseT Full
0x40000 10000baseKX4 Full
0x80000 10000baseKR Full
0x100000 10000baseR_FEC
-0x40000000000 10000baseCR Full
-0x80000000000 10000baseSR Full
-0x100000000000 10000baseLR Full
+0x40000000000 10000baseCR Full
+0x80000000000 10000baseSR Full
+0x100000000000 10000baseLR Full
0x200000000000 10000baseLRM Full
-0x400000000000 10000baseER Full
+0x400000000000 10000baseER Full
0x200000 20000baseMLD2 Full (not supported by IEEE standards)
0x400000 20000baseKR2 Full (not supported by IEEE standards)
0x80000000 25000baseCR Full
@@ -717,11 +778,31 @@ lB l lB.
0x800000000000000 100000baseCR2 Full
0x1000000000000000 100000baseLR2_ER2_FR2 Full
0x2000000000000000 100000baseDR2 Full
+0x8000000000000000000 100000baseKR Full
+0x10000000000000000000 100000baseSR Full
+0x20000000000000000000 100000baseLR_ER_FR Full
+0x40000000000000000000 100000baseCR Full
+0x80000000000000000000 100000baseDR Full
0x4000000000000000 200000baseKR4 Full
0x8000000000000000 200000baseSR4 Full
0x10000000000000000 200000baseLR4_ER4_FR4 Full
0x20000000000000000 200000baseDR4 Full
0x40000000000000000 200000baseCR4 Full
+0x100000000000000000000 200000baseKR2 Full
+0x200000000000000000000 200000baseSR2 Full
+0x400000000000000000000 200000baseLR2_ER2_FR2 Full
+0x800000000000000000000 200000baseDR2 Full
+0x1000000000000000000000 200000baseCR2 Full
+0x200000000000000000 400000baseKR8 Full
+0x400000000000000000 400000baseSR8 Full
+0x800000000000000000 400000baseLR8_ER8_FR8 Full
+0x1000000000000000000 400000baseDR8 Full
+0x2000000000000000000 400000baseCR8 Full
+0x2000000000000000000000 400000baseKR4 Full
+0x4000000000000000000000 400000baseSR4 Full
+0x8000000000000000000000 400000baseLR4_ER4_FR4 Full
+0x10000000000000000000000 400000baseDR4 Full
+0x20000000000000000000000 400000baseCR4 Full
.TE
.TP
.BI phyad \ N
@@ -1176,6 +1257,34 @@ Gets the current configured setting for Energy Detect Power Down (if supported).
.RE
.TP
+.B \-\-get\-tunable
+Get the tunable parameters.
+.RS 4
+.TP
+.B rx\-copybreak
+Get the current rx copybreak value in bytes.
+.TP
+.B tx\-copybreak
+Get the current tx copybreak value in bytes.
+.TP
+.B pfc\-prevention\-tout
+Get the current pfc prevention timeout value in msecs.
+.RE
+.TP
+.B \-\-set\-tunable
+Set driver's tunable parameters.
+.RS 4
+.TP
+.BI rx\-copybreak \ N
+Set the rx copybreak value in bytes.
+.TP
+.BI tx\-copybreak \ N
+Set the tx copybreak value in bytes.
+.TP
+.BI pfc\-prevention\-tout \ N
+Set pfc prevention timeout in msecs. Value of 0 means disable and 65535 means auto.
+.RE
+.TP
.B \-\-reset
Reset hardware components specified by flags and components listed below
.RS 4
@@ -1228,7 +1337,7 @@ current FEC mode, the driver or firmware must take the link down
administratively and report the problem in the system logs for users to correct.
.RS 4
.TP
-.BR encoding\ auto | off | rs | baser \ [...]
+.BR encoding\ auto | off | rs | baser | llrs \ [...]
Sets the FEC encoding for the device. Combinations of options are specified as
e.g.
@@ -1241,6 +1350,7 @@ auto Use the driver's default encoding
off Turn off FEC
RS Force RS-FEC encoding
BaseR Force BaseR encoding
+LLRS Force LLRS-FEC encoding
.TE
.RE
.TP
@@ -1257,6 +1367,41 @@ Sub command to apply. The supported sub commands include --show-coalesce and
--coalesce.
.RE
.TP
+q.B \-\-cable\-test
+Perform a cable test and report the results. What results are returned depends
+on the capabilities of the network interface. Typically open pairs and shorted
+pairs can be reported, along with pairs being O.K. When a fault is detected
+the approximate distance to the fault may be reported.
+.TP
+.B \-\-cable\-test\-tdr
+Perform a cable test and report the raw Time Domain Reflectometer
+data. A pulse is sent down a cable pair and the amplitude of the
+reflection, for a given distance, is reported. A break in the cable
+returns a big reflection. Minor damage to the cable returns a small
+reflection. If the cable is shorted, the amplitude of the reflection
+can be negative. By default, data is returned for lengths between 0
+and 150m at 1m steps, for all pairs. However parameters can be passed
+to restrict the collection of data. It should be noted, that the
+interface will round the distances to whatever granularity is actually
+implemented. This is often 0.8 of a meter. The results should include
+the actual rounded first and last distance and step size.
+.RS 4
+.TP
+.B first \ N
+Distance along the cable, in meters, where the first measurement
+should be made.
+.TP
+.B last \ N
+Distance along the cable, in meters, where the last measurement should
+be made.
+.TP
+.B step \ N
+Distance, in meters, between each measurement.
+.TP
+.B pair \ N
+Which pair should be measured. Typically a cable has 4 pairs. 0 = Pair A, 1 = Pair B, ...
+.RE
+.TP
.B \-\-monitor
Listens to netlink notification and displays them.
.RS 4
@@ -1270,6 +1415,12 @@ shown.
If a device name is used as argument, only notification for this device are
shown. Default is to show notifications for all devices.
.RE
+.TP
+.B \-\-show\-tunnels
+Show tunnel-related device capabilities and state.
+List UDP ports kernel has programmed the device to parse as VxLAN,
+or GENEVE tunnels.
+.RE
.SH BUGS
Not supported (in part or whole) on all network drivers.
.SH AUTHOR
diff --git a/ethtool.c b/ethtool.c
index 1b4e08b..585aafa 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -40,6 +40,7 @@
#include <sys/utsname.h>
#include <limits.h>
#include <ctype.h>
+#include <inttypes.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -102,51 +103,6 @@ struct cmdline_info {
void *seen_val;
};
-struct off_flag_def {
- const char *short_name;
- const char *long_name;
- const char *kernel_name;
- u32 get_cmd, set_cmd;
- u32 value;
- /* For features exposed through ETHTOOL_GFLAGS, the oldest
- * kernel version for which we can trust the result. Where
- * the flag was added at the same time the kernel started
- * supporting the feature, this is 0 (to allow for backports).
- * Where the feature was supported before the flag was added,
- * it is the version that introduced the flag.
- */
- u32 min_kernel_ver;
-};
-static const struct off_flag_def off_flag_def[] = {
- { "rx", "rx-checksumming", "rx-checksum",
- ETHTOOL_GRXCSUM, ETHTOOL_SRXCSUM, ETH_FLAG_RXCSUM, 0 },
- { "tx", "tx-checksumming", "tx-checksum-*",
- ETHTOOL_GTXCSUM, ETHTOOL_STXCSUM, ETH_FLAG_TXCSUM, 0 },
- { "sg", "scatter-gather", "tx-scatter-gather*",
- ETHTOOL_GSG, ETHTOOL_SSG, ETH_FLAG_SG, 0 },
- { "tso", "tcp-segmentation-offload", "tx-tcp*-segmentation",
- ETHTOOL_GTSO, ETHTOOL_STSO, ETH_FLAG_TSO, 0 },
- { "ufo", "udp-fragmentation-offload", "tx-udp-fragmentation",
- ETHTOOL_GUFO, ETHTOOL_SUFO, ETH_FLAG_UFO, 0 },
- { "gso", "generic-segmentation-offload", "tx-generic-segmentation",
- ETHTOOL_GGSO, ETHTOOL_SGSO, ETH_FLAG_GSO, 0 },
- { "gro", "generic-receive-offload", "rx-gro",
- ETHTOOL_GGRO, ETHTOOL_SGRO, ETH_FLAG_GRO, 0 },
- { "lro", "large-receive-offload", "rx-lro",
- 0, 0, ETH_FLAG_LRO,
- KERNEL_VERSION(2,6,24) },
- { "rxvlan", "rx-vlan-offload", "rx-vlan-hw-parse",
- 0, 0, ETH_FLAG_RXVLAN,
- KERNEL_VERSION(2,6,37) },
- { "txvlan", "tx-vlan-offload", "tx-vlan-hw-insert",
- 0, 0, ETH_FLAG_TXVLAN,
- KERNEL_VERSION(2,6,37) },
- { "ntuple", "ntuple-filters", "rx-ntuple-filter",
- 0, 0, ETH_FLAG_NTUPLE, 0 },
- { "rxhash", "receive-hashing", "rx-hashing",
- 0, 0, ETH_FLAG_RXHASH, 0 },
-};
-
struct feature_def {
char name[ETH_GSTRING_LEN];
int off_flag_index; /* index in off_flag_def; negative if none match */
@@ -155,7 +111,7 @@ struct feature_def {
struct feature_defs {
size_t n_features;
/* Number of features each offload flag is associated with */
- unsigned int off_flag_matched[ARRAY_SIZE(off_flag_def)];
+ unsigned int off_flag_matched[OFF_FLAG_DEF_SIZE];
/* Name and offload flag index for each feature */
struct feature_def def[0];
};
@@ -267,9 +223,9 @@ static void parse_generic_cmdline(struct cmd_context *ctx,
struct cmdline_info *info,
unsigned int n_info)
{
- int argc = ctx->argc;
+ unsigned int argc = ctx->argc;
char **argp = ctx->argp;
- int i, idx;
+ unsigned int i, idx;
int found;
for (i = 0; i < argc; i++) {
@@ -410,7 +366,7 @@ static int rxflow_str_to_type(const char *str)
return flow_type;
}
-static int do_version(struct cmd_context *ctx maybe_unused)
+static int do_version(struct cmd_context *ctx __maybe_unused)
{
fprintf(stdout,
PACKAGE " version " VERSION
@@ -436,9 +392,9 @@ static void init_global_link_mode_masks(void)
ETHTOOL_LINK_MODE_100baseT_Full_BIT,
ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
- ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
- ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
@@ -487,6 +443,28 @@ static void init_global_link_mode_masks(void)
ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
ETHTOOL_LINK_MODE_1000baseT1_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseDR_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
+ ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
};
static const enum ethtool_link_mode_bit_indices
additional_advertised_flags_bits[] = {
@@ -502,6 +480,7 @@ static void init_global_link_mode_masks(void)
ETHTOOL_LINK_MODE_FEC_NONE_BIT,
ETHTOOL_LINK_MODE_FEC_RS_BIT,
ETHTOOL_LINK_MODE_FEC_BASER_BIT,
+ ETHTOOL_LINK_MODE_FEC_LLRS_BIT,
};
unsigned int i;
@@ -576,20 +555,16 @@ static void dump_link_caps(const char *prefix, const char *an_prefix,
"100baseT/Half" },
{ 1, ETHTOOL_LINK_MODE_100baseT_Full_BIT,
"100baseT/Full" },
- { 0, ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
- "100baseT1/Full" },
{ 0, ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
"1000baseT/Half" },
{ 1, ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
"1000baseT/Full" },
- { 0, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT,
- "1000baseT1/Full" },
- { 0, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
- "1000baseKX/Full" },
- { 0, ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
- "2500baseX/Full" },
{ 0, ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
"10000baseT/Full" },
+ { 0, ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+ "2500baseX/Full" },
+ { 0, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+ "1000baseKX/Full" },
{ 0, ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
"10000baseKX4/Full" },
{ 0, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
@@ -682,10 +657,59 @@ static void dump_link_caps(const char *prefix, const char *an_prefix,
"200000baseDR4/Full" },
{ 0, ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
"200000baseCR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+ "100baseT1/Full" },
+ { 0, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT,
+ "1000baseT1/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT,
+ "400000baseKR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
+ "400000baseSR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
+ "400000baseLR8_ER8_FR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
+ "400000baseDR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
+ "400000baseCR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
+ "100000baseKR/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
+ "100000baseSR/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
+ "100000baseLR_ER_FR/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseDR_Full_BIT,
+ "100000baseDR/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
+ "100000baseCR/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
+ "200000baseKR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
+ "200000baseSR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
+ "200000baseLR2_ER2_FR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT,
+ "200000baseDR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
+ "200000baseCR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT,
+ "400000baseKR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT,
+ "400000baseSR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
+ "400000baseLR4_ER4_FR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT,
+ "400000baseDR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT,
+ "400000baseCR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
+ "100baseFX/Half" },
+ { 1, ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
+ "100baseFX/Full" },
};
int indent;
- int did1, new_line_pend, i;
+ int did1, new_line_pend;
int fecreported = 0;
+ unsigned int i;
/* Indent just like the separate functions used to */
indent = strlen(prefix) + 14;
@@ -754,6 +778,12 @@ static void dump_link_caps(const char *prefix, const char *an_prefix,
fprintf(stdout, " RS");
fecreported = 1;
}
+ if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT,
+ mask)) {
+ fprintf(stdout, " LLRS");
+ fecreported = 1;
+ }
+
if (!fecreported)
fprintf(stdout, " Not reported");
fprintf(stdout, "\n");
@@ -1087,6 +1117,8 @@ static const struct {
{ "lan78xx", lan78xx_dump_regs },
{ "dsa", dsa_dump_regs },
{ "fec", fec_dump_regs },
+ { "igc", igc_dump_regs },
+ { "bnxt_en", bnxt_dump_regs },
#endif
};
@@ -1107,7 +1139,7 @@ void dump_hex(FILE *file, const u8 *data, int len, int offset)
static int dump_regs(int gregs_dump_raw, int gregs_dump_hex,
struct ethtool_drvinfo *info, struct ethtool_regs *regs)
{
- int i;
+ unsigned int i;
if (gregs_dump_raw) {
fwrite(regs->data, regs->len, 1, stdout);
@@ -1142,7 +1174,7 @@ nested:
}
static int dump_eeprom(int geeprom_dump_raw,
- struct ethtool_drvinfo *info maybe_unused,
+ struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_eeprom *ee)
{
if (geeprom_dump_raw) {
@@ -1164,7 +1196,8 @@ static int dump_eeprom(int geeprom_dump_raw,
static int dump_test(struct ethtool_test *test,
struct ethtool_gstrings *strings)
{
- int i, rc;
+ unsigned int i;
+ int rc;
rc = test->flags & ETH_TEST_FL_FAILED;
fprintf(stdout, "The test result is %s\n", rc ? "FAIL" : "PASS");
@@ -1395,7 +1428,7 @@ static void dump_one_feature(const char *indent, const char *name,
: "");
}
-static int linux_version_code(void)
+static unsigned int linux_version_code(void)
{
struct utsname utsname;
unsigned version, patchlevel, sublevel = 0;
@@ -1411,12 +1444,12 @@ static void dump_features(const struct feature_defs *defs,
const struct feature_state *state,
const struct feature_state *ref_state)
{
- int kernel_ver = linux_version_code();
- u32 value;
+ unsigned int kernel_ver = linux_version_code();
+ unsigned int i, j;
int indent;
- int i, j;
+ u32 value;
- for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
/* Don't show features whose state is unknown on this
* kernel version
*/
@@ -1447,7 +1480,7 @@ static void dump_features(const struct feature_defs *defs,
/* Show matching features */
for (j = 0; j < defs->n_features; j++) {
- if (defs->def[j].off_flag_index != i)
+ if (defs->def[j].off_flag_index != (int)i)
continue;
if (defs->off_flag_matched[i] != 1)
/* Show all matching feature states */
@@ -1562,6 +1595,8 @@ static void dump_fec(u32 fec)
fprintf(stdout, " BaseR");
if (fec & ETHTOOL_FEC_RS)
fprintf(stdout, " RS");
+ if (fec & ETHTOOL_FEC_LLRS)
+ fprintf(stdout, " LLRS");
}
#define N_SOTS 7
@@ -1666,7 +1701,9 @@ get_stringset(struct cmd_context *ctx, enum ethtool_stringset set_id,
sset_info.hdr.reserved = 0;
sset_info.hdr.sset_mask = 1ULL << set_id;
if (send_ioctl(ctx, &sset_info) == 0) {
- len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0;
+ const u32 *sset_lengths = sset_info.hdr.data;
+
+ len = sset_info.hdr.sset_mask ? sset_lengths[0] : 0;
} else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
/* Fallback for old kernel versions */
drvinfo.cmd = ETHTOOL_GDRVINFO;
@@ -1700,8 +1737,8 @@ static struct feature_defs *get_feature_defs(struct cmd_context *ctx)
{
struct ethtool_gstrings *names;
struct feature_defs *defs;
+ unsigned int i, j;
u32 n_features;
- int i, j;
names = get_stringset(ctx, ETH_SS_FEATURES, 0, 1);
if (names) {
@@ -1733,7 +1770,7 @@ static struct feature_defs *get_feature_defs(struct cmd_context *ctx)
defs->def[i].off_flag_index = -1;
for (j = 0;
- j < ARRAY_SIZE(off_flag_def) &&
+ j < OFF_FLAG_DEF_SIZE &&
defs->def[i].off_flag_index < 0;
j++) {
const char *pattern =
@@ -1855,10 +1892,24 @@ static int do_spause(struct cmd_context *ctx)
int pause_rx_wanted = -1;
int pause_tx_wanted = -1;
struct cmdline_info cmdline_pause[] = {
- { "autoneg", CMDL_BOOL, &pause_autoneg_wanted,
- &epause.autoneg },
- { "rx", CMDL_BOOL, &pause_rx_wanted, &epause.rx_pause },
- { "tx", CMDL_BOOL, &pause_tx_wanted, &epause.tx_pause },
+ {
+ .name = "autoneg",
+ .type = CMDL_BOOL,
+ .wanted_val = &pause_autoneg_wanted,
+ .ioctl_val = &epause.autoneg,
+ },
+ {
+ .name = "rx",
+ .type = CMDL_BOOL,
+ .wanted_val = &pause_rx_wanted,
+ .ioctl_val = &epause.rx_pause,
+ },
+ {
+ .name = "tx",
+ .type = CMDL_BOOL,
+ .wanted_val = &pause_tx_wanted,
+ .ioctl_val = &epause.tx_pause,
+ },
};
int err, changed = 0;
@@ -1898,12 +1949,30 @@ static int do_sring(struct cmd_context *ctx)
s32 ring_rx_jumbo_wanted = -1;
s32 ring_tx_wanted = -1;
struct cmdline_info cmdline_ring[] = {
- { "rx", CMDL_S32, &ring_rx_wanted, &ering.rx_pending },
- { "rx-mini", CMDL_S32, &ring_rx_mini_wanted,
- &ering.rx_mini_pending },
- { "rx-jumbo", CMDL_S32, &ring_rx_jumbo_wanted,
- &ering.rx_jumbo_pending },
- { "tx", CMDL_S32, &ring_tx_wanted, &ering.tx_pending },
+ {
+ .name = "rx",
+ .type = CMDL_S32,
+ .wanted_val = &ring_rx_wanted,
+ .ioctl_val = &ering.rx_pending,
+ },
+ {
+ .name = "rx-mini",
+ .type = CMDL_S32,
+ .wanted_val = &ring_rx_mini_wanted,
+ .ioctl_val = &ering.rx_mini_pending,
+ },
+ {
+ .name = "rx-jumbo",
+ .type = CMDL_S32,
+ .wanted_val = &ring_rx_jumbo_wanted,
+ .ioctl_val = &ering.rx_jumbo_pending,
+ },
+ {
+ .name = "tx",
+ .type = CMDL_S32,
+ .wanted_val = &ring_tx_wanted,
+ .ioctl_val = &ering.tx_pending,
+ },
};
int err, changed = 0;
@@ -1967,12 +2036,30 @@ static int do_schannels(struct cmd_context *ctx)
s32 channels_other_wanted = -1;
s32 channels_combined_wanted = -1;
struct cmdline_info cmdline_channels[] = {
- { "rx", CMDL_S32, &channels_rx_wanted, &echannels.rx_count },
- { "tx", CMDL_S32, &channels_tx_wanted, &echannels.tx_count },
- { "other", CMDL_S32, &channels_other_wanted,
- &echannels.other_count },
- { "combined", CMDL_S32, &channels_combined_wanted,
- &echannels.combined_count },
+ {
+ .name = "rx",
+ .type = CMDL_S32,
+ .wanted_val = &channels_rx_wanted,
+ .ioctl_val = &echannels.rx_count,
+ },
+ {
+ .name = "tx",
+ .type = CMDL_S32,
+ .wanted_val = &channels_tx_wanted,
+ .ioctl_val = &echannels.tx_count,
+ },
+ {
+ .name = "other",
+ .type = CMDL_S32,
+ .wanted_val = &channels_other_wanted,
+ .ioctl_val = &echannels.other_count,
+ },
+ {
+ .name = "combined",
+ .type = CMDL_S32,
+ .wanted_val = &channels_combined_wanted,
+ .ioctl_val = &echannels.combined_count,
+ },
};
int err, changed = 0;
@@ -2082,50 +2169,138 @@ static int do_gcoalesce(struct cmd_context *ctx)
#define COALESCE_CMDLINE_INFO(__ecoal) \
{ \
- { "adaptive-rx", CMDL_BOOL, &coal_adaptive_rx_wanted, \
- &__ecoal.use_adaptive_rx_coalesce }, \
- { "adaptive-tx", CMDL_BOOL, &coal_adaptive_tx_wanted, \
- &__ecoal.use_adaptive_tx_coalesce }, \
- { "sample-interval", CMDL_S32, &coal_sample_rate_wanted, \
- &__ecoal.rate_sample_interval }, \
- { "stats-block-usecs", CMDL_S32, &coal_stats_wanted, \
- &__ecoal.stats_block_coalesce_usecs }, \
- { "pkt-rate-low", CMDL_S32, &coal_pkt_rate_low_wanted, \
- &__ecoal.pkt_rate_low }, \
- { "pkt-rate-high", CMDL_S32, &coal_pkt_rate_high_wanted, \
- &__ecoal.pkt_rate_high }, \
- { "rx-usecs", CMDL_S32, &coal_rx_usec_wanted, \
- &__ecoal.rx_coalesce_usecs }, \
- { "rx-frames", CMDL_S32, &coal_rx_frames_wanted, \
- &__ecoal.rx_max_coalesced_frames }, \
- { "rx-usecs-irq", CMDL_S32, &coal_rx_usec_irq_wanted, \
- &__ecoal.rx_coalesce_usecs_irq }, \
- { "rx-frames-irq", CMDL_S32, &coal_rx_frames_irq_wanted, \
- &__ecoal.rx_max_coalesced_frames_irq }, \
- { "tx-usecs", CMDL_S32, &coal_tx_usec_wanted, \
- &__ecoal.tx_coalesce_usecs }, \
- { "tx-frames", CMDL_S32, &coal_tx_frames_wanted, \
- &__ecoal.tx_max_coalesced_frames }, \
- { "tx-usecs-irq", CMDL_S32, &coal_tx_usec_irq_wanted, \
- &__ecoal.tx_coalesce_usecs_irq }, \
- { "tx-frames-irq", CMDL_S32, &coal_tx_frames_irq_wanted, \
- &__ecoal.tx_max_coalesced_frames_irq }, \
- { "rx-usecs-low", CMDL_S32, &coal_rx_usec_low_wanted, \
- &__ecoal.rx_coalesce_usecs_low }, \
- { "rx-frames-low", CMDL_S32, &coal_rx_frames_low_wanted, \
- &__ecoal.rx_max_coalesced_frames_low }, \
- { "tx-usecs-low", CMDL_S32, &coal_tx_usec_low_wanted, \
- &__ecoal.tx_coalesce_usecs_low }, \
- { "tx-frames-low", CMDL_S32, &coal_tx_frames_low_wanted, \
- &__ecoal.tx_max_coalesced_frames_low }, \
- { "rx-usecs-high", CMDL_S32, &coal_rx_usec_high_wanted, \
- &__ecoal.rx_coalesce_usecs_high }, \
- { "rx-frames-high", CMDL_S32, &coal_rx_frames_high_wanted, \
- &__ecoal.rx_max_coalesced_frames_high }, \
- { "tx-usecs-high", CMDL_S32, &coal_tx_usec_high_wanted, \
- &__ecoal.tx_coalesce_usecs_high }, \
- { "tx-frames-high", CMDL_S32, &coal_tx_frames_high_wanted, \
- &__ecoal.tx_max_coalesced_frames_high }, \
+ { \
+ .name = "adaptive-rx", \
+ .type = CMDL_BOOL, \
+ .wanted_val = &coal_adaptive_rx_wanted, \
+ .ioctl_val = &__ecoal.use_adaptive_rx_coalesce, \
+ }, \
+ { \
+ .name = "adaptive-tx", \
+ .type = CMDL_BOOL, \
+ .wanted_val = &coal_adaptive_tx_wanted, \
+ .ioctl_val = &__ecoal.use_adaptive_tx_coalesce, \
+ }, \
+ { \
+ .name = "sample-interval", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_sample_rate_wanted, \
+ .ioctl_val = &__ecoal.rate_sample_interval, \
+ }, \
+ { \
+ .name = "stats-block-usecs", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_stats_wanted, \
+ .ioctl_val = &__ecoal.stats_block_coalesce_usecs, \
+ }, \
+ { \
+ .name = "pkt-rate-low", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_pkt_rate_low_wanted, \
+ .ioctl_val = &__ecoal.pkt_rate_low, \
+ }, \
+ { \
+ .name = "pkt-rate-high", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_pkt_rate_high_wanted, \
+ .ioctl_val = &__ecoal.pkt_rate_high, \
+ }, \
+ { \
+ .name = "rx-usecs", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_usec_wanted, \
+ .ioctl_val = &__ecoal.rx_coalesce_usecs, \
+ }, \
+ { \
+ .name = "rx-frames", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_frames_wanted, \
+ .ioctl_val = &__ecoal.rx_max_coalesced_frames, \
+ }, \
+ { \
+ .name = "rx-usecs-irq", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_usec_irq_wanted, \
+ .ioctl_val = &__ecoal.rx_coalesce_usecs_irq, \
+ }, \
+ { \
+ .name = "rx-frames-irq", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_frames_irq_wanted, \
+ .ioctl_val = &__ecoal.rx_max_coalesced_frames_irq, \
+ }, \
+ { \
+ .name = "tx-usecs", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_usec_wanted, \
+ .ioctl_val = &__ecoal.tx_coalesce_usecs, \
+ }, \
+ { \
+ .name = "tx-frames", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_frames_wanted, \
+ .ioctl_val = &__ecoal.tx_max_coalesced_frames, \
+ }, \
+ { \
+ .name = "tx-usecs-irq", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_usec_irq_wanted, \
+ .ioctl_val = &__ecoal.tx_coalesce_usecs_irq, \
+ }, \
+ { \
+ .name = "tx-frames-irq", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_frames_irq_wanted, \
+ .ioctl_val = &__ecoal.tx_max_coalesced_frames_irq, \
+ }, \
+ { \
+ .name = "rx-usecs-low", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_usec_low_wanted, \
+ .ioctl_val = &__ecoal.rx_coalesce_usecs_low, \
+ }, \
+ { \
+ .name = "rx-frames-low", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_frames_low_wanted, \
+ .ioctl_val = &__ecoal.rx_max_coalesced_frames_low, \
+ }, \
+ { \
+ .name = "tx-usecs-low", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_usec_low_wanted, \
+ .ioctl_val = &__ecoal.tx_coalesce_usecs_low, \
+ }, \
+ { \
+ .name = "tx-frames-low", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_frames_low_wanted, \
+ .ioctl_val = &__ecoal.tx_max_coalesced_frames_low, \
+ }, \
+ { \
+ .name = "rx-usecs-high", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_usec_high_wanted, \
+ .ioctl_val = &__ecoal.rx_coalesce_usecs_high, \
+ }, \
+ { \
+ .name = "rx-frames-high", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_frames_high_wanted, \
+ .ioctl_val = &__ecoal.rx_max_coalesced_frames_high,\
+ }, \
+ { \
+ .name = "tx-usecs-high", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_usec_high_wanted, \
+ .ioctl_val = &__ecoal.tx_coalesce_usecs_high, \
+ }, \
+ { \
+ .name = "tx-frames-high", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_frames_high_wanted, \
+ .ioctl_val = &__ecoal.tx_max_coalesced_frames_high,\
+ }, \
}
static int do_scoalesce(struct cmd_context *ctx)
@@ -2181,7 +2356,7 @@ get_features(struct cmd_context *ctx, const struct feature_defs *defs)
state->off_flags = 0;
- for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
value = off_flag_def[i].value;
if (!off_flag_def[i].get_cmd)
continue;
@@ -2264,12 +2439,13 @@ static int do_sfeatures(struct cmd_context *ctx)
int any_changed = 0, any_mismatch = 0;
u32 off_flags_wanted = 0;
u32 off_flags_mask = 0;
- struct ethtool_sfeatures *efeatures;
+ struct ethtool_sfeatures *efeatures = NULL;
+ struct feature_state *old_state = NULL;
+ struct feature_state *new_state = NULL;
struct cmdline_info *cmdline_features;
- struct feature_state *old_state, *new_state;
struct ethtool_value eval;
+ unsigned int i, j;
int err, rc;
- int i, j;
defs = get_feature_defs(ctx);
if (!defs) {
@@ -2290,33 +2466,37 @@ static int do_sfeatures(struct cmd_context *ctx)
memset(efeatures->features, 0,
FEATURE_BITS_TO_BLOCKS(defs->n_features) *
sizeof(efeatures->features[0]));
- } else {
- efeatures = NULL;
}
/* Generate cmdline_info for legacy flags and kernel-named
* features, and parse our arguments.
*/
- cmdline_features = calloc(ARRAY_SIZE(off_flag_def) + defs->n_features,
+ cmdline_features = calloc(2 * OFF_FLAG_DEF_SIZE + defs->n_features,
sizeof(cmdline_features[0]));
if (!cmdline_features) {
perror("Cannot parse arguments");
rc = 1;
goto err;
}
- for (i = 0; i < ARRAY_SIZE(off_flag_def); i++)
+ j = 0;
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
flag_to_cmdline_info(off_flag_def[i].short_name,
off_flag_def[i].value,
&off_flags_wanted, &off_flags_mask,
- &cmdline_features[i]);
+ &cmdline_features[j++]);
+ flag_to_cmdline_info(off_flag_def[i].long_name,
+ off_flag_def[i].value,
+ &off_flags_wanted, &off_flags_mask,
+ &cmdline_features[j++]);
+ }
for (i = 0; i < defs->n_features; i++)
flag_to_cmdline_info(
defs->def[i].name, FEATURE_FIELD_FLAG(i),
&FEATURE_WORD(efeatures->features, i, requested),
&FEATURE_WORD(efeatures->features, i, valid),
- &cmdline_features[ARRAY_SIZE(off_flag_def) + i]);
+ &cmdline_features[j++]);
parse_generic_cmdline(ctx, &any_changed, cmdline_features,
- ARRAY_SIZE(off_flag_def) + defs->n_features);
+ 2 * OFF_FLAG_DEF_SIZE + defs->n_features);
free(cmdline_features);
if (!any_changed) {
@@ -2336,14 +2516,14 @@ static int do_sfeatures(struct cmd_context *ctx)
* related features that the user did not specify and that
* are not fixed. Warn if all related features are fixed.
*/
- for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
int fixed = 1;
if (!(off_flags_mask & off_flag_def[i].value))
continue;
for (j = 0; j < defs->n_features; j++) {
- if (defs->def[j].off_flag_index != i ||
+ if (defs->def[j].off_flag_index != (int)i ||
!FEATURE_BIT_IS_SET(
old_state->features.features,
j, available) ||
@@ -2377,7 +2557,7 @@ static int do_sfeatures(struct cmd_context *ctx)
goto err;
}
} else {
- for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
if (!off_flag_def[i].set_cmd)
continue;
if (off_flags_mask & off_flag_def[i].value) {
@@ -2444,9 +2624,11 @@ static int do_sfeatures(struct cmd_context *ctx)
rc = 0;
err:
+ free(new_state);
+ free(old_state);
free(defs);
- if (efeatures)
- free(efeatures);
+ free(efeatures);
+
return rc;
}
@@ -2497,8 +2679,8 @@ do_ioctl_glinksettings(struct cmd_context *ctx)
if (link_usettings == NULL)
return NULL;
- /* keep transceiver 0 */
memcpy(&link_usettings->base, &ecmd.req, sizeof(link_usettings->base));
+ link_usettings->deprecated.transceiver = ecmd.req.transceiver;
/* copy link mode bitmaps */
u32_offs = 0;
@@ -2750,9 +2932,9 @@ static int do_sset(struct cmd_context *ctx)
u32 msglvl_wanted = 0;
u32 msglvl_mask = 0;
struct cmdline_info cmdline_msglvl[n_flags_msglvl];
- int argc = ctx->argc;
+ unsigned int argc = ctx->argc;
char **argp = ctx->argp;
- int i;
+ unsigned int i;
int err = 0;
for (i = 0; i < n_flags_msglvl; i++)
@@ -2936,6 +3118,9 @@ static int do_sset(struct cmd_context *ctx)
link_usettings = do_ioctl_glinksettings(ctx);
if (link_usettings == NULL)
link_usettings = do_ioctl_gset(ctx);
+ else
+ memset(&link_usettings->deprecated, 0,
+ sizeof(link_usettings->deprecated));
if (link_usettings == NULL) {
perror("Cannot get current device settings");
err = -1;
@@ -3112,9 +3297,21 @@ static int do_gregs(struct cmd_context *ctx)
int gregs_dump_hex = 0;
char *gregs_dump_file = NULL;
struct cmdline_info cmdline_gregs[] = {
- { "raw", CMDL_BOOL, &gregs_dump_raw, NULL },
- { "hex", CMDL_BOOL, &gregs_dump_hex, NULL },
- { "file", CMDL_STR, &gregs_dump_file, NULL },
+ {
+ .name = "raw",
+ .type = CMDL_BOOL,
+ .wanted_val = &gregs_dump_raw,
+ },
+ {
+ .name = "hex",
+ .type = CMDL_BOOL,
+ .wanted_val = &gregs_dump_hex,
+ },
+ {
+ .name = "file",
+ .type = CMDL_STR,
+ .wanted_val = &gregs_dump_file,
+ },
};
int err;
struct ethtool_drvinfo drvinfo;
@@ -3208,11 +3405,25 @@ static int do_geeprom(struct cmd_context *ctx)
int geeprom_changed = 0;
int geeprom_dump_raw = 0;
u32 geeprom_offset = 0;
- u32 geeprom_length = -1;
+ u32 geeprom_length = 0;
+ int geeprom_length_seen = 0;
struct cmdline_info cmdline_geeprom[] = {
- { "offset", CMDL_U32, &geeprom_offset, NULL },
- { "length", CMDL_U32, &geeprom_length, NULL },
- { "raw", CMDL_BOOL, &geeprom_dump_raw, NULL },
+ {
+ .name = "offset",
+ .type = CMDL_U32,
+ .wanted_val = &geeprom_offset,
+ },
+ {
+ .name = "length",
+ .type = CMDL_U32,
+ .wanted_val = &geeprom_length,
+ .seen_val = &geeprom_length_seen,
+ },
+ {
+ .name = "raw",
+ .type = CMDL_BOOL,
+ .wanted_val = &geeprom_dump_raw,
+ },
};
int err;
struct ethtool_drvinfo drvinfo;
@@ -3228,7 +3439,7 @@ static int do_geeprom(struct cmd_context *ctx)
return 74;
}
- if (geeprom_length == -1)
+ if (!geeprom_length_seen)
geeprom_length = drvinfo.eedump_len;
if (drvinfo.eedump_len < geeprom_offset + geeprom_length)
@@ -3258,16 +3469,34 @@ static int do_seeprom(struct cmd_context *ctx)
{
int seeprom_changed = 0;
u32 seeprom_magic = 0;
- u32 seeprom_length = -1;
+ u32 seeprom_length = 0;
u32 seeprom_offset = 0;
u8 seeprom_value = 0;
+ int seeprom_length_seen = 0;
int seeprom_value_seen = 0;
struct cmdline_info cmdline_seeprom[] = {
- { "magic", CMDL_U32, &seeprom_magic, NULL },
- { "offset", CMDL_U32, &seeprom_offset, NULL },
- { "length", CMDL_U32, &seeprom_length, NULL },
- { "value", CMDL_U8, &seeprom_value, NULL,
- 0, &seeprom_value_seen },
+ {
+ .name = "magic",
+ .type = CMDL_U32,
+ .wanted_val = &seeprom_magic,
+ },
+ {
+ .name = "offset",
+ .type = CMDL_U32,
+ .wanted_val = &seeprom_offset,
+ },
+ {
+ .name = "length",
+ .type = CMDL_U32,
+ .wanted_val = &seeprom_length,
+ .seen_val = &seeprom_length_seen,
+ },
+ {
+ .name = "value",
+ .type = CMDL_U8,
+ .wanted_val = &seeprom_value,
+ .seen_val = &seeprom_value_seen,
+ },
};
int err;
struct ethtool_drvinfo drvinfo;
@@ -3286,7 +3515,7 @@ static int do_seeprom(struct cmd_context *ctx)
if (seeprom_value_seen)
seeprom_length = 1;
- if (seeprom_length == -1)
+ if (!seeprom_length_seen)
seeprom_length = drvinfo.eedump_len;
if (drvinfo.eedump_len < seeprom_offset + seeprom_length) {
@@ -3691,7 +3920,7 @@ static int do_grxfh(struct cmd_context *ctx)
struct ethtool_rxfh *rss;
u32 rss_context = 0;
u32 i, indir_bytes;
- int arg_num = 0;
+ unsigned int arg_num = 0;
char *hkey;
int err;
@@ -3889,7 +4118,7 @@ static int do_srxfh(struct cmd_context *ctx)
char *hfunc_name = NULL;
char *hkey = NULL;
int err = 0;
- int i;
+ unsigned int i;
u32 arg_num = 0, indir_bytes = 0;
u32 req_hfunc = 0;
u32 entry_size = sizeof(rss_head.rss_config[0]);
@@ -4155,7 +4384,8 @@ static int do_flash(struct cmd_context *ctx)
static int do_permaddr(struct cmd_context *ctx)
{
- int i, err;
+ unsigned int i;
+ int err;
struct ethtool_perm_addr *epaddr;
epaddr = malloc(sizeof(struct ethtool_perm_addr) + MAX_ADDR_LEN);
@@ -4562,17 +4792,35 @@ static int do_getmodule(struct cmd_context *ctx)
struct ethtool_modinfo modinfo;
struct ethtool_eeprom *eeprom;
u32 geeprom_offset = 0;
- u32 geeprom_length = -1;
+ u32 geeprom_length = 0;
int geeprom_changed = 0;
int geeprom_dump_raw = 0;
int geeprom_dump_hex = 0;
+ int geeprom_length_seen = 0;
int err;
struct cmdline_info cmdline_geeprom[] = {
- { "offset", CMDL_U32, &geeprom_offset, NULL },
- { "length", CMDL_U32, &geeprom_length, NULL },
- { "raw", CMDL_BOOL, &geeprom_dump_raw, NULL },
- { "hex", CMDL_BOOL, &geeprom_dump_hex, NULL },
+ {
+ .name = "offset",
+ .type = CMDL_U32,
+ .wanted_val = &geeprom_offset,
+ },
+ {
+ .name = "length",
+ .type = CMDL_U32,
+ .wanted_val = &geeprom_length,
+ .seen_val = &geeprom_length_seen,
+ },
+ {
+ .name = "raw",
+ .type = CMDL_BOOL,
+ .wanted_val = &geeprom_dump_raw,
+ },
+ {
+ .name = "hex",
+ .type = CMDL_BOOL,
+ .wanted_val = &geeprom_dump_hex,
+ },
};
parse_generic_cmdline(ctx, &geeprom_changed,
@@ -4590,7 +4838,7 @@ static int do_getmodule(struct cmd_context *ctx)
return 1;
}
- if (geeprom_length == -1)
+ if (!geeprom_length_seen)
geeprom_length = modinfo.eeprom_len;
if (modinfo.eeprom_len < geeprom_offset + geeprom_length)
@@ -4607,7 +4855,12 @@ static int do_getmodule(struct cmd_context *ctx)
eeprom->offset = geeprom_offset;
err = send_ioctl(ctx, eeprom);
if (err < 0) {
+ int saved_errno = errno;
+
perror("Cannot get Module EEPROM data");
+ if (saved_errno == ENODEV || saved_errno == EIO ||
+ saved_errno == ENXIO)
+ fprintf(stderr, "SFP module not in cage?\n");
free(eeprom);
return 1;
}
@@ -4684,10 +4937,30 @@ static int do_seee(struct cmd_context *ctx)
int change = -1, change2 = 0;
struct ethtool_eee eeecmd;
struct cmdline_info cmdline_eee[] = {
- { "advertise", CMDL_U32, &adv_c, &eeecmd.advertised },
- { "tx-lpi", CMDL_BOOL, &lpi_c, &eeecmd.tx_lpi_enabled },
- { "tx-timer", CMDL_U32, &lpi_time_c, &eeecmd.tx_lpi_timer},
- { "eee", CMDL_BOOL, &eee_c, &eeecmd.eee_enabled},
+ {
+ .name = "advertise",
+ .type = CMDL_U32,
+ .wanted_val = &adv_c,
+ .ioctl_val = &eeecmd.advertised,
+ },
+ {
+ .name = "tx-lpi",
+ .type = CMDL_BOOL,
+ .wanted_val = &lpi_c,
+ .ioctl_val = &eeecmd.tx_lpi_enabled,
+ },
+ {
+ .name = "tx-timer",
+ .type = CMDL_U32,
+ .wanted_val = &lpi_time_c,
+ .ioctl_val = &eeecmd.tx_lpi_timer,
+ },
+ {
+ .name = "eee",
+ .type = CMDL_BOOL,
+ .wanted_val = &eee_c,
+ .ioctl_val = &eeecmd.eee_enabled,
+ },
};
if (ctx->argc == 0)
@@ -4715,9 +4988,185 @@ static int do_seee(struct cmd_context *ctx)
return 0;
}
+/* copy of net/ethtool/common.c */
+char
+tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
+ [ETHTOOL_ID_UNSPEC] = "Unspec",
+ [ETHTOOL_RX_COPYBREAK] = "rx-copybreak",
+ [ETHTOOL_TX_COPYBREAK] = "tx-copybreak",
+ [ETHTOOL_PFC_PREVENTION_TOUT] = "pfc-prevention-tout",
+};
+
+union ethtool_tunable_info_val {
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ int8_t s8;
+ int16_t s16;
+ int32_t s32;
+ int64_t s64;
+};
+
+struct ethtool_tunable_info {
+ enum tunable_id t_id;
+ enum tunable_type_id t_type_id;
+ size_t size;
+ cmdline_type_t type;
+ union ethtool_tunable_info_val wanted;
+ int seen;
+};
+
+static struct ethtool_tunable_info tunables_info[] = {
+ { .t_id = ETHTOOL_RX_COPYBREAK,
+ .t_type_id = ETHTOOL_TUNABLE_U32,
+ .size = sizeof(u32),
+ .type = CMDL_U32,
+ },
+ { .t_id = ETHTOOL_TX_COPYBREAK,
+ .t_type_id = ETHTOOL_TUNABLE_U32,
+ .size = sizeof(u32),
+ .type = CMDL_U32,
+ },
+ { .t_id = ETHTOOL_PFC_PREVENTION_TOUT,
+ .t_type_id = ETHTOOL_TUNABLE_U16,
+ .size = sizeof(u16),
+ .type = CMDL_U16,
+ },
+};
+#define TUNABLES_INFO_SIZE ARRAY_SIZE(tunables_info)
+
+static int do_stunable(struct cmd_context *ctx)
+{
+ struct cmdline_info cmdline_tunable[TUNABLES_INFO_SIZE];
+ struct ethtool_tunable_info *tinfo = tunables_info;
+ int changed = 0;
+ unsigned int i;
+
+ for (i = 0; i < TUNABLES_INFO_SIZE; i++) {
+ cmdline_tunable[i].name = tunable_strings[tinfo[i].t_id];
+ cmdline_tunable[i].type = tinfo[i].type;
+ cmdline_tunable[i].wanted_val = &tinfo[i].wanted;
+ cmdline_tunable[i].seen_val = &tinfo[i].seen;
+ }
+
+ parse_generic_cmdline(ctx, &changed, cmdline_tunable, TUNABLES_INFO_SIZE);
+ if (!changed)
+ exit_bad_args();
+
+ for (i = 0; i < TUNABLES_INFO_SIZE; i++) {
+ struct ethtool_tunable *tuna;
+ size_t size;
+ int ret;
+
+ if (!tinfo[i].seen)
+ continue;
+
+ size = sizeof(*tuna) + tinfo[i].size;
+ tuna = calloc(1, size);
+ if (!tuna) {
+ perror(tunable_strings[tinfo[i].t_id]);
+ return 1;
+ }
+ tuna->cmd = ETHTOOL_STUNABLE;
+ tuna->id = tinfo[i].t_id;
+ tuna->type_id = tinfo[i].t_type_id;
+ tuna->len = tinfo[i].size;
+ memcpy(tuna->data, &tinfo[i].wanted, tuna->len);
+ ret = send_ioctl(ctx, tuna);
+ if (ret) {
+ perror(tunable_strings[tuna->id]);
+ return ret;
+ }
+ free(tuna);
+ }
+ return 0;
+}
+
+static void print_tunable(struct ethtool_tunable *tuna)
+{
+ char *name = tunable_strings[tuna->id];
+ union ethtool_tunable_info_val *val;
+
+ val = (union ethtool_tunable_info_val *)tuna->data;
+ switch (tuna->type_id) {
+ case ETHTOOL_TUNABLE_U8:
+ fprintf(stdout, "%s: %" PRIu8 "\n", name, val->u8);
+ break;
+ case ETHTOOL_TUNABLE_U16:
+ fprintf(stdout, "%s: %" PRIu16 "\n", name, val->u16);
+ break;
+ case ETHTOOL_TUNABLE_U32:
+ fprintf(stdout, "%s: %" PRIu32 "\n", name, val->u32);
+ break;
+ case ETHTOOL_TUNABLE_U64:
+ fprintf(stdout, "%s: %" PRIu64 "\n", name, val->u64);
+ break;
+ case ETHTOOL_TUNABLE_S8:
+ fprintf(stdout, "%s: %" PRId8 "\n", name, val->s8);
+ break;
+ case ETHTOOL_TUNABLE_S16:
+ fprintf(stdout, "%s: %" PRId16 "\n", name, val->s16);
+ break;
+ case ETHTOOL_TUNABLE_S32:
+ fprintf(stdout, "%s: %" PRId32 "\n", name, val->s32);
+ break;
+ case ETHTOOL_TUNABLE_S64:
+ fprintf(stdout, "%s: %" PRId64 "\n", name, val->s64);
+ break;
+ default:
+ fprintf(stdout, "%s: Unknown format\n", name);
+ }
+}
+
+static int do_gtunable(struct cmd_context *ctx)
+{
+ struct ethtool_tunable_info *tinfo = tunables_info;
+ char **argp = ctx->argp;
+ unsigned int argc = ctx->argc;
+ unsigned int i, j;
+
+ if (argc < 1)
+ exit_bad_args();
+
+ for (i = 0; i < argc; i++) {
+ int valid = 0;
+
+ for (j = 0; j < TUNABLES_INFO_SIZE; j++) {
+ char *ts = tunable_strings[tinfo[j].t_id];
+ struct ethtool_tunable *tuna;
+ int ret;
+
+ if (strcmp(argp[i], ts))
+ continue;
+ valid = 1;
+
+ tuna = calloc(1, sizeof(*tuna) + tinfo[j].size);
+ if (!tuna) {
+ perror(ts);
+ return 1;
+ }
+ tuna->cmd = ETHTOOL_GTUNABLE;
+ tuna->id = tinfo[j].t_id;
+ tuna->type_id = tinfo[j].t_type_id;
+ tuna->len = tinfo[j].size;
+ ret = send_ioctl(ctx, tuna);
+ if (ret) {
+ fprintf(stderr, "%s: Cannot get tunable\n", ts);
+ return ret;
+ }
+ print_tunable(tuna);
+ free(tuna);
+ }
+ if (!valid)
+ exit_bad_args();
+ }
+ return 0;
+}
+
static int do_get_phy_tunable(struct cmd_context *ctx)
{
- int argc = ctx->argc;
+ unsigned int argc = ctx->argc;
char **argp = ctx->argp;
if (argc < 1)
@@ -4821,9 +5270,9 @@ static int do_reset(struct cmd_context *ctx)
{
struct ethtool_value resetinfo;
__u32 data;
- int argc = ctx->argc;
+ unsigned int argc = ctx->argc;
char **argp = ctx->argp;
- int i;
+ unsigned int i;
if (argc == 0)
exit_bad_args();
@@ -5074,7 +5523,8 @@ static int fecmode_str_to_type(const char *str)
return ETHTOOL_FEC_RS;
if (!strcasecmp(str, "baser"))
return ETHTOOL_FEC_BASER;
-
+ if (!strcasecmp(str, "llrs"))
+ return ETHTOOL_FEC_LLRS;
return 0;
}
@@ -5110,7 +5560,8 @@ static int do_sfec(struct cmd_context *ctx)
enum { ARG_NONE, ARG_ENCODING } state = ARG_NONE;
struct ethtool_fecparam feccmd;
int fecmode = 0, newmode;
- int rv, i;
+ unsigned int i;
+ int rv;
for (i = 0; i < ctx->argc; i++) {
if (!strcmp(ctx->argp[i], "encoding")) {
@@ -5157,7 +5608,7 @@ struct option {
const char *opts;
bool no_dev;
int (*func)(struct cmd_context *);
- int (*nlfunc)(struct cmd_context *);
+ nl_func_t nlfunc;
const char *help;
const char *xhelp;
};
@@ -5179,15 +5630,18 @@ static const struct option args[] = {
" [ wol %d[/%d] | p|u|m|b|a|g|s|f|d... ]\n"
" [ sopass %x:%x:%x:%x:%x:%x ]\n"
" [ msglvl %d[/%d] | type on|off ... [--] ]\n"
+ " [ master-slave master-preferred|slave-preferred|master-force|slave-force ]\n"
},
{
.opts = "-a|--show-pause",
.func = do_gpause,
+ .nlfunc = nl_gpause,
.help = "Show pause options"
},
{
.opts = "-A|--pause",
.func = do_spause,
+ .nlfunc = nl_spause,
.help = "Set pause options",
.xhelp = " [ autoneg on|off ]\n"
" [ rx on|off ]\n"
@@ -5196,11 +5650,13 @@ static const struct option args[] = {
{
.opts = "-c|--show-coalesce",
.func = do_gcoalesce,
+ .nlfunc = nl_gcoalesce,
.help = "Show coalesce options"
},
{
.opts = "-C|--coalesce",
.func = do_scoalesce,
+ .nlfunc = nl_scoalesce,
.help = "Set coalesce options",
.xhelp = " [adaptive-rx on|off]\n"
" [adaptive-tx on|off]\n"
@@ -5228,11 +5684,13 @@ static const struct option args[] = {
{
.opts = "-g|--show-ring",
.func = do_gring,
+ .nlfunc = nl_gring,
.help = "Query RX/TX ring parameters"
},
{
.opts = "-G|--set-ring",
.func = do_sring,
+ .nlfunc = nl_sring,
.help = "Set RX/TX ring parameters",
.xhelp = " [ rx N ]\n"
" [ rx-mini N ]\n"
@@ -5242,11 +5700,13 @@ static const struct option args[] = {
{
.opts = "-k|--show-features|--show-offload",
.func = do_gfeatures,
+ .nlfunc = nl_gfeatures,
.help = "Get state of protocol offload and other features"
},
{
.opts = "-K|--features|--offload",
.func = do_sfeatures,
+ .nlfunc = nl_sfeatures,
.help = "Set protocol offload and other features",
.xhelp = " FEATURE on|off ...\n"
},
@@ -5345,6 +5805,7 @@ static const struct option args[] = {
{
.opts = "-T|--show-time-stamping",
.func = do_tsinfo,
+ .nlfunc = nl_tsinfo,
.help = "Show time stamping capabilities"
},
{
@@ -5390,11 +5851,13 @@ static const struct option args[] = {
{
.opts = "-l|--show-channels",
.func = do_gchannels,
+ .nlfunc = nl_gchannels,
.help = "Query Channels"
},
{
.opts = "-L|--set-channels",
.func = do_schannels,
+ .nlfunc = nl_schannels,
.help = "Set Channels",
.xhelp = " [ rx N ]\n"
" [ tx N ]\n"
@@ -5404,11 +5867,13 @@ static const struct option args[] = {
{
.opts = "--show-priv-flags",
.func = do_gprivflags,
+ .nlfunc = nl_gprivflags,
.help = "Query private flags"
},
{
.opts = "--set-priv-flags",
.func = do_sprivflags,
+ .nlfunc = nl_sprivflags,
.help = "Set private flags",
.xhelp = " FLAG on|off ...\n"
},
@@ -5424,11 +5889,13 @@ static const struct option args[] = {
{
.opts = "--show-eee",
.func = do_geee,
+ .nlfunc = nl_geee,
.help = "Show EEE settings",
},
{
.opts = "--set-eee",
.func = do_seee,
+ .nlfunc = nl_seee,
.help = "Set EEE settings",
.xhelp = " [ eee on|off ]\n"
" [ advertise %x ]\n"
@@ -5452,6 +5919,22 @@ static const struct option args[] = {
" [ energy-detect-power-down ]\n"
},
{
+ .opts = "--get-tunable",
+ .func = do_gtunable,
+ .help = "Get tunable",
+ .xhelp = " [ rx-copybreak ]\n"
+ " [ tx-copybreak ]\n"
+ " [ pfc-precention-tout ]\n"
+ },
+ {
+ .opts = "--set-tunable",
+ .func = do_stunable,
+ .help = "Set tunable",
+ .xhelp = " [ rx-copybreak N]\n"
+ " [ tx-copybreak N]\n"
+ " [ pfc-precention-tout N]\n"
+ },
+ {
.opts = "--reset",
.func = do_reset,
.help = "Reset components",
@@ -5486,7 +5969,7 @@ static const struct option args[] = {
.opts = "--set-fec",
.func = do_sfec,
.help = "Set FEC settings",
- .xhelp = " [ encoding auto|off|rs|baser [...]]\n"
+ .xhelp = " [ encoding auto|off|rs|baser|llrs [...]]\n"
},
{
.opts = "-Q|--per-queue",
@@ -5496,6 +5979,25 @@ static const struct option args[] = {
" [queue_mask %x] SUB_COMMAND\n",
},
{
+ .opts = "--cable-test",
+ .nlfunc = nl_cable_test,
+ .help = "Perform a cable test",
+ },
+ {
+ .opts = "--cable-test-tdr",
+ .nlfunc = nl_cable_test_tdr,
+ .help = "Print cable test time domain reflectrometery data",
+ .xhelp = " [ first N ]\n"
+ " [ last N ]\n"
+ " [ step N ]\n"
+ " [ pair N ]\n"
+ },
+ {
+ .opts = "--show-tunnels",
+ .nlfunc = nl_gtunnels,
+ .help = "Show NIC tunnel offload information",
+ },
+ {
.opts = "-h|--help",
.no_dev = true,
.func = show_usage,
@@ -5510,7 +6012,7 @@ static const struct option args[] = {
{}
};
-static int show_usage(struct cmd_context *ctx maybe_unused)
+static int show_usage(struct cmd_context *ctx __maybe_unused)
{
int i;
@@ -5518,10 +6020,10 @@ static int show_usage(struct cmd_context *ctx maybe_unused)
fprintf(stdout, PACKAGE " version " VERSION "\n");
fprintf(stdout,
"Usage:\n"
- " ethtool [ --debug MASK ] DEVNAME\t"
+ " ethtool [ FLAGS ] DEVNAME\t"
"Display standard information about device\n");
for (i = 0; args[i].opts; i++) {
- fputs(" ethtool [ --debug MASK ] ", stdout);
+ fputs(" ethtool [ FLAGS ] ", stdout);
fprintf(stdout, "%s %s\t%s\n",
args[i].opts,
args[i].no_dev ? "\t" : "DEVNAME",
@@ -5530,6 +6032,11 @@ static int show_usage(struct cmd_context *ctx maybe_unused)
fputs(args[i].xhelp, stdout);
}
nl_monitor_usage();
+ fprintf(stdout, "\n");
+ fprintf(stdout, "FLAGS:\n");
+ fprintf(stdout, " --debug MASK turn on debugging messages\n");
+ fprintf(stdout, " --json enable JSON output format (not supported by all commands)\n");
+ fprintf(stdout, " -I|--include-statistics request device statistics related to the command (not supported by all commands)\n");
return 0;
}
@@ -5673,6 +6180,8 @@ static int do_perqueue(struct cmd_context *ctx)
"The sub commands will be applied to all %d queues\n",
n_queues);
} else {
+ if (ctx->argc <= 2)
+ exit_bad_args();
ctx->argc--;
ctx->argp++;
if (parse_hex_u32_bitmap(*ctx->argp, MAX_NUM_QUEUE,
@@ -5731,6 +6240,11 @@ static int ioctl_init(struct cmd_context *ctx, bool no_dev)
ctx->fd = -1;
return 0;
}
+ if (strlen(ctx->devname) >= IFNAMSIZ) {
+ fprintf(stderr, "Device name longer than %u characters\n",
+ IFNAMSIZ - 1);
+ exit_bad_args();
+ }
/* Setup our control structures. */
memset(&ctx->ifr, 0, sizeof(ctx->ifr));
@@ -5750,9 +6264,9 @@ static int ioctl_init(struct cmd_context *ctx, bool no_dev)
int main(int argc, char **argp)
{
- int (*nlfunc)(struct cmd_context *) = NULL;
int (*func)(struct cmd_context *);
struct cmd_context ctx = {};
+ nl_func_t nlfunc = NULL;
bool no_dev;
int ret;
int k;
@@ -5763,31 +6277,41 @@ int main(int argc, char **argp)
argp++;
argc--;
- if (*argp && !strcmp(*argp, "--debug")) {
- char *eptr;
+ while (true) {
+ if (*argp && !strcmp(*argp, "--debug")) {
+ char *eptr;
- if (argc < 2)
- exit_bad_args();
- ctx.debug = strtoul(argp[1], &eptr, 0);
- if (!argp[1][0] || *eptr)
- exit_bad_args();
+ if (argc < 2)
+ exit_bad_args();
+ ctx.debug = strtoul(argp[1], &eptr, 0);
+ if (!argp[1][0] || *eptr)
+ exit_bad_args();
- argp += 2;
- argc -= 2;
+ argp += 2;
+ argc -= 2;
+ continue;
+ }
+ if (*argp && !strcmp(*argp, "--json")) {
+ ctx.json = true;
+ argp += 1;
+ argc -= 1;
+ continue;
+ }
+ if (*argp && (!strcmp(*argp, "--include-statistics") ||
+ !strcmp(*argp, "-I"))) {
+ ctx.show_stats = true;
+ argp += 1;
+ argc -= 1;
+ continue;
+ }
+ break;
}
-#ifdef ETHTOOL_ENABLE_NETLINK
if (*argp && !strcmp(*argp, "--monitor")) {
- if (netlink_init(&ctx)) {
- fprintf(stderr,
- "Option --monitor is only available with netlink.\n");
- return 1;
- } else {
- ctx.argp = ++argp;
- ctx.argc = --argc;
- return nl_monitor(&ctx);
- }
+ ctx.argp = ++argp;
+ ctx.argc = --argc;
+ ret = nl_monitor(&ctx);
+ return ret ? 1 : 0;
}
-#endif
/* First argument must be either a valid option or a device
* name to get settings for (which we don't expect to begin
@@ -5812,40 +6336,17 @@ int main(int argc, char **argp)
no_dev = false;
opt_found:
- if (nlfunc) {
- if (netlink_init(&ctx))
- nlfunc = NULL; /* fallback to ioctl() */
- }
-
if (!no_dev) {
ctx.devname = *argp++;
argc--;
- /* netlink supports altnames, we will have to recheck against
- * IFNAMSIZ later in case of fallback to ioctl
- */
- if (!ctx.devname || strlen(ctx.devname) >= ALTIFNAMSIZ) {
- netlink_done(&ctx);
+ if (!ctx.devname)
exit_bad_args();
- }
}
-
ctx.argc = argc;
ctx.argp = argp;
+ netlink_run_handler(&ctx, nlfunc, !func);
- if (nlfunc) {
- ret = nlfunc(&ctx);
- netlink_done(&ctx);
- if ((ret != -EOPNOTSUPP) || !func)
- return (ret >= 0) ? ret : 1;
- }
-
- if (ctx.devname && strlen(ctx.devname) >= IFNAMSIZ) {
- fprintf(stderr,
- "ethtool: device names longer than %u characters are only allowed with netlink\n",
- IFNAMSIZ - 1);
- exit_bad_args();
- }
ret = ioctl_init(&ctx, no_dev);
if (ret)
return ret;
diff --git a/ethtool.spec.in b/ethtool.spec.in
index 9c01b07..75f9be6 100644
--- a/ethtool.spec.in
+++ b/ethtool.spec.in
@@ -34,6 +34,7 @@ make install DESTDIR=${RPM_BUILD_ROOT}
%defattr(-,root,root)
%{_sbindir}/ethtool
%{_mandir}/man8/ethtool.8*
+%{_datadir}/bash-completion/completions/ethtool
%doc AUTHORS COPYING NEWS README
diff --git a/fec.c b/fec.c
index 22bc09f..d2373d6 100644
--- a/fec.c
+++ b/fec.c
@@ -194,11 +194,11 @@ static void fec_dump_reg_v2(int reg, u32 val)
#undef FIELD
#undef REG
-int fec_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int fec_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
const u32 *data = (u32 *)regs->data;
- int offset;
+ unsigned int offset;
u32 val;
for (offset = 0; offset < regs->len; offset += 4) {
diff --git a/fec_8xx.c b/fec_8xx.c
index 02ecaef..63352fc 100644
--- a/fec_8xx.c
+++ b/fec_8xx.c
@@ -47,7 +47,7 @@ struct fec {
(unsigned long)(offsetof(struct fec, x)), \
#x, f->x)
-int fec_8xx_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int fec_8xx_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
struct fec *f = (struct fec *)regs->data;
diff --git a/fjes.c b/fjes.c
index 4c5f6bc..05bd245 100644
--- a/fjes.c
+++ b/fjes.c
@@ -2,7 +2,7 @@
#include <stdio.h>
#include "internal.h"
-int fjes_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int fjes_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/ibm_emac.c b/ibm_emac.c
index 3259c17..9f7cae6 100644
--- a/ibm_emac.c
+++ b/ibm_emac.c
@@ -238,7 +238,7 @@ static void *print_mal_regs(void *buf)
{
struct emac_ethtool_regs_subhdr *hdr = buf;
struct mal_regs *p = (struct mal_regs *)(hdr + 1);
- int i;
+ unsigned int i;
printf("MAL%d Registers\n", hdr->index);
printf("-----------------\n");
@@ -314,7 +314,7 @@ static void *print_tah_regs(void *buf)
return p + 1;
}
-int ibm_emac_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int ibm_emac_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
struct emac_ethtool_regs_hdr *hdr =
diff --git a/igb.c b/igb.c
index 89b5cdb..f358f53 100644
--- a/igb.c
+++ b/igb.c
@@ -88,7 +88,7 @@
#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */
-int igb_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int igb_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/igc.c b/igc.c
new file mode 100644
index 0000000..1550ac0
--- /dev/null
+++ b/igc.c
@@ -0,0 +1,284 @@
+/* Copyright (c) 2020 Intel Corporation */
+#include <stdio.h>
+#include "internal.h"
+
+#define RAH_RAH 0x0000FFFF
+#define RAH_ASEL 0x00010000
+#define RAH_QSEL 0x000C0000
+#define RAH_QSEL_EN 0x10000000
+#define RAH_AV 0x80000000
+#define RCTL_RXEN 0x00000002
+#define RCTL_SBP 0x00000004
+#define RCTL_UPE 0x00000008
+#define RCTL_MPE 0x00000010
+#define RCTL_LPE 0x00000020
+#define RCTL_LBM 0x000000C0
+#define RCTL_LBM_PHY 0x00000000
+#define RCTL_LBM_MAC 0x00000040
+#define RCTL_HSEL 0x00000300
+#define RCTL_HSEL_MULTICAST 0x00000000
+#define RCTL_HSEL_UNICAST 0x00000100
+#define RCTL_HSEL_BOTH 0x00000200
+#define RCTL_MO 0x00003000
+#define RCTL_MO_47_36 0x00000000
+#define RCTL_MO_43_32 0x00001000
+#define RCTL_MO_39_28 0x00002000
+#define RCTL_BAM 0x00008000
+#define RCTL_BSIZE 0x00030000
+#define RCTL_BSIZE_2048 0x00000000
+#define RCTL_BSIZE_1024 0x00010000
+#define RCTL_BSIZE_512 0x00020000
+#define RCTL_VFE 0x00040000
+#define RCTL_CFIEN 0x00080000
+#define RCTL_CFI 0x00100000
+#define RCTL_PSP 0x00200000
+#define RCTL_DPF 0x00400000
+#define RCTL_PMCF 0x00800000
+#define RCTL_SECRC 0x04000000
+#define VLANPQF_VP0QSEL 0x00000003
+#define VLANPQF_VP0PBSEL 0x00000004
+#define VLANPQF_VLANP0V 0x00000008
+#define VLANPQF_VP1QSEL 0x00000030
+#define VLANPQF_VP1PBSEL 0x00000040
+#define VLANPQF_VLANP1V 0x00000080
+#define VLANPQF_VP2QSEL 0x00000300
+#define VLANPQF_VP2PBSEL 0x00000400
+#define VLANPQF_VLANP2V 0x00000800
+#define VLANPQF_VP3QSEL 0x00003000
+#define VLANPQF_VP3PBSEL 0x00004000
+#define VLANPQF_VLANP3V 0x00008000
+#define VLANPQF_VP4QSEL 0x00030000
+#define VLANPQF_VP4PBSEL 0x00040000
+#define VLANPQF_VLANP4V 0x00080000
+#define VLANPQF_VP5QSEL 0x00300000
+#define VLANPQF_VP5PBSEL 0x00400000
+#define VLANPQF_VLANP5V 0x00800000
+#define VLANPQF_VP6QSEL 0x03000000
+#define VLANPQF_VP6PBSEL 0x04000000
+#define VLANPQF_VLANP6V 0x08000000
+#define VLANPQF_VP7QSEL 0x30000000
+#define VLANPQF_VP7PBSEL 0x40000000
+#define VLANPQF_VLANP7V 0x80000000
+#define ETQF_ETYPE 0x0000FFFF
+#define ETQF_QUEUE 0x00070000
+#define ETQF_ETYPE_LEN 0x01F00000
+#define ETQF_ETYPE_LEN_EN 0x02000000
+#define ETQF_FILTER_EN 0x04000000
+#define ETQF_IMMEDIATE_INTR 0x20000000
+#define ETQF_1588_TIMESTAMP 0x40000000
+#define ETQF_QUEUE_EN 0x80000000
+
+#define RAH_QSEL_SHIFT 18
+#define VLANPQF_VP1QSEL_SHIFT 4
+#define VLANPQF_VP2QSEL_SHIFT 8
+#define VLANPQF_VP3QSEL_SHIFT 12
+#define VLANPQF_VP4QSEL_SHIFT 16
+#define VLANPQF_VP5QSEL_SHIFT 20
+#define VLANPQF_VP6QSEL_SHIFT 24
+#define VLANPQF_VP7QSEL_SHIFT 28
+#define ETQF_QUEUE_SHIFT 16
+#define ETQF_ETYPE_LEN_SHIFT 20
+
+static const char *bit_to_boolean(u32 val)
+{
+ return val ? "yes" : "no";
+}
+
+static const char *bit_to_enable(u32 val)
+{
+ return val ? "enabled" : "disabled";
+}
+
+static const char *bit_to_prio(u32 val)
+{
+ return val ? "low" : "high";
+}
+
+int igc_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 reg;
+ int offset, i;
+ u32 *regs_buff = (u32 *)regs->data;
+ u8 version = (u8)(regs->version >> 24);
+
+ if (version != 2)
+ return -1;
+
+ for (offset = 0; offset < 24; offset++) {
+ reg = regs_buff[offset];
+ printf("%04d: 0x%08X\n", offset, reg);
+ }
+
+ offset = 24;
+
+ reg = regs_buff[offset];
+ printf("%04d: RCTL (Receive Control Register) \n"
+ " Receiver: %s\n"
+ " Stop Bad Packets: %s\n"
+ " Unicast Promiscuous: %s\n"
+ " Multicast Promiscuous: %s\n"
+ " Long Packet Reception: %s\n"
+ " Loopback Model: %s\n"
+ " Hash Select for MTA: %s\n"
+ " Multicast/Unicast Table Offset: %s\n"
+ " Broadcast Accept Mode: %s\n"
+ " Receive Buffer Size: %s\n"
+ " VLAN Filter: %s\n"
+ " Canonical Form Indicator: %s\n"
+ " Canonical Form Indicator Bit: %s\n"
+ " Pad Small Receive Packets: %s\n"
+ " Discard Pause Frames: %s\n"
+ " Pass MAC Control Frames: %s\n"
+ " Strip Ethernet CRC: %s\n",
+ offset,
+ bit_to_enable(reg & RCTL_RXEN),
+ bit_to_enable(reg & RCTL_SBP),
+ bit_to_enable(reg & RCTL_UPE),
+ bit_to_enable(reg & RCTL_MPE),
+ bit_to_enable(reg & RCTL_LPE),
+ (reg & RCTL_LBM) == RCTL_LBM_PHY ? "PHY" :
+ (reg & RCTL_LBM) == RCTL_LBM_MAC ? "MAC" :
+ "undefined",
+ (reg & RCTL_HSEL) == RCTL_HSEL_MULTICAST ? "multicast only" :
+ (reg & RCTL_HSEL) == RCTL_HSEL_UNICAST ? "unicast only" :
+ (reg & RCTL_HSEL) == RCTL_HSEL_BOTH ? "multicast and unicast" :
+ "reserved",
+ (reg & RCTL_MO) == RCTL_MO_47_36 ? "bits [47:36]" :
+ (reg & RCTL_MO) == RCTL_MO_43_32 ? "bits [43:32]" :
+ (reg & RCTL_MO) == RCTL_MO_39_28 ? "bits [39:28]" :
+ "bits [35:24]",
+ bit_to_enable(reg & RCTL_BAM),
+ (reg & RCTL_BSIZE) == RCTL_BSIZE_2048 ? "2048 bytes" :
+ (reg & RCTL_BSIZE) == RCTL_BSIZE_1024 ? "1024 bytes" :
+ (reg & RCTL_BSIZE) == RCTL_BSIZE_512 ? "512 bytes" :
+ "256 bytes",
+ bit_to_enable(reg & RCTL_VFE),
+ bit_to_enable(reg & RCTL_CFIEN),
+ reg & RCTL_CFI ? "discarded" : "accepted",
+ bit_to_enable(reg & RCTL_PSP),
+ bit_to_enable(reg & RCTL_DPF),
+ bit_to_enable(reg & RCTL_PMCF),
+ bit_to_enable(reg & RCTL_SECRC));
+
+ for (offset = 25; offset < 172; offset++) {
+ reg = regs_buff[offset];
+ printf("%04d: 0x%08X\n", offset, reg);
+ }
+
+ offset = 172;
+
+ for (i = 0; i < 16; i++) {
+ reg = regs_buff[offset + i];
+ printf("%04d: RAL (Receive Address Low %02d) \n"
+ " Receive Address Low: %08X\n",
+ offset + i, i,
+ reg);
+ }
+
+ offset = 188;
+
+ for (i = 0; i < 16; i++) {
+ reg = regs_buff[offset + i];
+ printf("%04d: RAH (Receive Address High %02d) \n"
+ " Receive Address High: %04X\n"
+ " Address Select: %s\n"
+ " Queue Select: %d\n"
+ " Queue Select Enable: %s\n"
+ " Address Valid: %s\n",
+ offset + i, i,
+ reg & RAH_RAH,
+ reg & RAH_ASEL ? "source" : "destination",
+ (reg & RAH_QSEL) >> RAH_QSEL_SHIFT,
+ bit_to_boolean(reg & RAH_QSEL_EN),
+ bit_to_boolean(reg & RAH_AV));
+ }
+
+ offset = 204;
+
+ reg = regs_buff[offset];
+ printf("%04d: VLANPQF (VLAN Priority Queue Filter) \n"
+ " Priority 0 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 1 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 2 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 3 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 4 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 5 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 6 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 7 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n",
+ offset,
+ reg & VLANPQF_VP0QSEL,
+ bit_to_prio(reg & VLANPQF_VP0PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP0V),
+ (reg & VLANPQF_VP1QSEL) >> VLANPQF_VP1QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP1PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP1V),
+ (reg & VLANPQF_VP2QSEL) >> VLANPQF_VP2QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP2PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP2V),
+ (reg & VLANPQF_VP3QSEL) >> VLANPQF_VP3QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP3PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP3V),
+ (reg & VLANPQF_VP4QSEL) >> VLANPQF_VP4QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP4PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP4V),
+ (reg & VLANPQF_VP5QSEL) >> VLANPQF_VP5QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP5PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP5V),
+ (reg & VLANPQF_VP6QSEL) >> VLANPQF_VP6QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP6PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP6V),
+ (reg & VLANPQF_VP7QSEL) >> VLANPQF_VP7QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP7PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP7V));
+
+ offset = 205;
+
+ for (i = 0; i < 8; i++) {
+ reg = regs_buff[offset + i];
+ printf("%04d: ETQF (EType Queue Filter %d) \n"
+ " EType: %04X\n"
+ " EType Length: %d\n"
+ " EType Length Enable: %s\n"
+ " Queue: %d\n"
+ " Queue Enable: %s\n"
+ " Immediate Interrupt: %s\n"
+ " 1588 Time Stamp: %s\n"
+ " Filter Enable: %s\n",
+ offset + i, i,
+ reg & ETQF_ETYPE,
+ (reg & ETQF_ETYPE_LEN) >> ETQF_ETYPE_LEN_SHIFT,
+ bit_to_boolean(reg & ETQF_ETYPE_LEN_EN),
+ (reg & ETQF_QUEUE) >> ETQF_QUEUE_SHIFT,
+ bit_to_boolean(reg & ETQF_QUEUE_EN),
+ bit_to_enable(reg & ETQF_IMMEDIATE_INTR),
+ bit_to_enable(reg & ETQF_1588_TIMESTAMP),
+ bit_to_boolean(reg & ETQF_FILTER_EN));
+ }
+
+ return 0;
+}
diff --git a/internal.h b/internal.h
index edb07bd..27da8ea 100644
--- a/internal.h
+++ b/internal.h
@@ -23,7 +23,10 @@
#include <sys/ioctl.h>
#include <net/if.h>
-#define maybe_unused __attribute__((__unused__))
+#include "json_writer.h"
+#include "json_print.h"
+
+#define __maybe_unused __attribute__((__unused__))
/* internal for netlink interface */
#ifdef ETHTOOL_ENABLE_NETLINK
@@ -218,9 +221,11 @@ struct cmd_context {
const char *devname; /* net device name */
int fd; /* socket suitable for ethtool ioctl */
struct ifreq ifr; /* ifreq suitable for ethtool ioctl */
- int argc; /* number of arguments to the sub-command */
+ unsigned int argc; /* number of arguments to the sub-command */
char **argp; /* arguments to the sub-command */
unsigned long debug; /* debugging mask */
+ bool json; /* Output JSON, if supported */
+ bool show_stats; /* include command-specific stats */
#ifdef ETHTOOL_ENABLE_NETLINK
struct nl_context *nlctx; /* netlink context (opaque) */
#endif
@@ -389,4 +394,10 @@ int dsa_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
/* i.MX Fast Ethernet Controller */
int fec_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+/* Intel(R) Ethernet Controller I225-LM/I225-V adapter family */
+int igc_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Broadcom Ethernet Controller */
+int bnxt_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
#endif /* ETHTOOL_INTERNAL_H__ */
diff --git a/ixgb.c b/ixgb.c
index 7c16c6e..8aec9a9 100644
--- a/ixgb.c
+++ b/ixgb.c
@@ -38,7 +38,7 @@
#define IXGB_RAH_ASEL_SRC 0x00010000
#define IXGB_RAH_AV 0x80000000
-int ixgb_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int ixgb_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/ixgbe.c b/ixgbe.c
index 9754b2a..6d509c8 100644
--- a/ixgbe.c
+++ b/ixgbe.c
@@ -168,7 +168,7 @@ ixgbe_get_mac_type(u16 device_id)
}
int
-ixgbe_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+ixgbe_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/ixgbevf.c b/ixgbevf.c
index 265e0bf..91c2b2c 100644
--- a/ixgbevf.c
+++ b/ixgbevf.c
@@ -3,7 +3,7 @@
#include "internal.h"
int
-ixgbevf_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+ixgbevf_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;
diff --git a/json_print.c b/json_print.c
new file mode 100644
index 0000000..56d5b43
--- /dev/null
+++ b/json_print.c
@@ -0,0 +1,228 @@
+/*
+ * json_print.c "print regular or json output, based on json_writer".
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Julien Fortin, <julien@cumulusnetworks.com>
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "json_print.h"
+
+#define SPRINT_BSIZE 64
+#define SPRINT_BUF(x) char x[SPRINT_BSIZE]
+
+static json_writer_t *_jw;
+
+#define _IS_JSON_CONTEXT(type) ((type & PRINT_JSON || type & PRINT_ANY) && _jw)
+#define _IS_FP_CONTEXT(type) (!_jw && (type & PRINT_FP || type & PRINT_ANY))
+
+void new_json_obj(int json)
+{
+ if (json) {
+ _jw = jsonw_new(stdout);
+ if (!_jw) {
+ perror("json object");
+ exit(1);
+ }
+ jsonw_pretty(_jw, true);
+ jsonw_start_array(_jw);
+ }
+}
+
+void delete_json_obj(void)
+{
+ if (_jw) {
+ jsonw_end_array(_jw);
+ jsonw_destroy(&_jw);
+ }
+}
+
+bool is_json_context(void)
+{
+ return _jw != NULL;
+}
+
+json_writer_t *get_json_writer(void)
+{
+ return _jw;
+}
+
+void open_json_object(const char *str)
+{
+ if (_IS_JSON_CONTEXT(PRINT_JSON)) {
+ if (str)
+ jsonw_name(_jw, str);
+ jsonw_start_object(_jw);
+ }
+}
+
+void close_json_object(void)
+{
+ if (_IS_JSON_CONTEXT(PRINT_JSON))
+ jsonw_end_object(_jw);
+}
+
+/*
+ * Start json array or string array using
+ * the provided string as json key (if not null)
+ * or as array delimiter in non-json context.
+ */
+void open_json_array(enum output_type type, const char *str)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ if (str)
+ jsonw_name(_jw, str);
+ jsonw_start_array(_jw);
+ } else if (_IS_FP_CONTEXT(type)) {
+ printf("%s", str);
+ }
+}
+
+/*
+ * End json array or string array
+ */
+void close_json_array(enum output_type type, const char *str)
+{
+ if (_IS_JSON_CONTEXT(type))
+ jsonw_end_array(_jw);
+ else if (_IS_FP_CONTEXT(type))
+ printf("%s", str);
+}
+
+/*
+ * pre-processor directive to generate similar
+ * functions handling different types
+ */
+#define _PRINT_FUNC(type_name, type) \
+ __attribute__((format(printf, 3, 0))) \
+ void print_##type_name(enum output_type t, \
+ const char *key, \
+ const char *fmt, \
+ type value) \
+ { \
+ if (_IS_JSON_CONTEXT(t)) { \
+ if (!key) \
+ jsonw_##type_name(_jw, value); \
+ else \
+ jsonw_##type_name##_field(_jw, key, value); \
+ } else if (_IS_FP_CONTEXT(t)) { \
+ fprintf(stdout, fmt, value); \
+ } \
+ }
+_PRINT_FUNC(int, int);
+_PRINT_FUNC(s64, int64_t);
+_PRINT_FUNC(hhu, unsigned char);
+_PRINT_FUNC(hu, unsigned short);
+_PRINT_FUNC(uint, unsigned int);
+_PRINT_FUNC(u64, uint64_t);
+_PRINT_FUNC(luint, unsigned long);
+_PRINT_FUNC(lluint, unsigned long long);
+_PRINT_FUNC(float, double);
+#undef _PRINT_FUNC
+
+void print_string(enum output_type type,
+ const char *key,
+ const char *fmt,
+ const char *value)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ if (key && !value)
+ jsonw_name(_jw, key);
+ else if (!key && value)
+ jsonw_string(_jw, value);
+ else
+ jsonw_string_field(_jw, key, value);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, value);
+ }
+}
+
+/*
+ * value's type is bool. When using this function in FP context you can't pass
+ * a value to it, you will need to use "is_json_context()" to have different
+ * branch for json and regular output. grep -r "print_bool" for example
+ */
+void print_bool(enum output_type type,
+ const char *key,
+ const char *fmt,
+ bool value)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ if (key)
+ jsonw_bool_field(_jw, key, value);
+ else
+ jsonw_bool(_jw, value);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, value ? "true" : "false");
+ }
+}
+
+/*
+ * In JSON context uses hardcode %#x format: 42 -> 0x2a
+ */
+void print_0xhex(enum output_type type,
+ const char *key,
+ const char *fmt,
+ unsigned long long hex)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ SPRINT_BUF(b1);
+
+ snprintf(b1, sizeof(b1), "%#llx", hex);
+ print_string(PRINT_JSON, key, NULL, b1);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, hex);
+ }
+}
+
+void print_hex(enum output_type type,
+ const char *key,
+ const char *fmt,
+ unsigned int hex)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ SPRINT_BUF(b1);
+
+ snprintf(b1, sizeof(b1), "%x", hex);
+ if (key)
+ jsonw_string_field(_jw, key, b1);
+ else
+ jsonw_string(_jw, b1);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, hex);
+ }
+}
+
+/*
+ * In JSON context we don't use the argument "value" we simply call jsonw_null
+ * whereas FP context can use "value" to output anything
+ */
+void print_null(enum output_type type,
+ const char *key,
+ const char *fmt,
+ const char *value)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ if (key)
+ jsonw_null_field(_jw, key);
+ else
+ jsonw_null(_jw);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, value);
+ }
+}
+
+/* Print line separator (if not in JSON mode) */
+void print_nl(void)
+{
+ if (!_jw)
+ printf("%s", "\n");
+}
diff --git a/json_print.h b/json_print.h
new file mode 100644
index 0000000..cc0c2ea
--- /dev/null
+++ b/json_print.h
@@ -0,0 +1,67 @@
+/*
+ * json_print.h "print regular or json output, based on json_writer".
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Julien Fortin, <julien@cumulusnetworks.com>
+ */
+
+#ifndef _JSON_PRINT_H_
+#define _JSON_PRINT_H_
+
+#include "json_writer.h"
+
+json_writer_t *get_json_writer(void);
+
+/*
+ * use:
+ * - PRINT_ANY for context based output
+ * - PRINT_FP for non json specific output
+ * - PRINT_JSON for json specific output
+ */
+enum output_type {
+ PRINT_FP = 1,
+ PRINT_JSON = 2,
+ PRINT_ANY = 4,
+};
+
+void new_json_obj(int json);
+void delete_json_obj(void);
+
+bool is_json_context(void);
+
+void fflush_fp(void);
+
+void open_json_object(const char *str);
+void close_json_object(void);
+void open_json_array(enum output_type type, const char *delim);
+void close_json_array(enum output_type type, const char *delim);
+
+void print_nl(void);
+
+#define _PRINT_FUNC(type_name, type) \
+ void print_##type_name(enum output_type t, \
+ const char *key, \
+ const char *fmt, \
+ type value) \
+
+_PRINT_FUNC(int, int);
+_PRINT_FUNC(s64, int64_t);
+_PRINT_FUNC(bool, bool);
+_PRINT_FUNC(null, const char*);
+_PRINT_FUNC(string, const char*);
+_PRINT_FUNC(uint, unsigned int);
+_PRINT_FUNC(u64, uint64_t);
+_PRINT_FUNC(hhu, unsigned char);
+_PRINT_FUNC(hu, unsigned short);
+_PRINT_FUNC(hex, unsigned int);
+_PRINT_FUNC(0xhex, unsigned long long);
+_PRINT_FUNC(luint, unsigned long);
+_PRINT_FUNC(lluint, unsigned long long);
+_PRINT_FUNC(float, double);
+#undef _PRINT_FUNC
+
+#endif /* _JSON_PRINT_H_ */
diff --git a/json_writer.c b/json_writer.c
new file mode 100644
index 0000000..e8b3926
--- /dev/null
+++ b/json_writer.c
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) //
+/*
+ * Simple streaming JSON writer
+ *
+ * This takes care of the annoying bits of JSON syntax like the commas
+ * after elements
+ *
+ * Authors: Stephen Hemminger <stephen@networkplumber.org>
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <malloc.h>
+#include <inttypes.h>
+#include <stdint.h>
+
+#include "json_writer.h"
+
+struct json_writer {
+ FILE *out; /* output file */
+ unsigned int depth; /* nesting */
+ bool pretty; /* optional whitepace */
+ char sep; /* either nul or comma */
+};
+
+/* indentation for pretty print */
+static void jsonw_indent(json_writer_t *self)
+{
+ unsigned int i;
+
+ for (i = 0; i < self->depth; ++i)
+ fputs(" ", self->out);
+}
+
+/* end current line and indent if pretty printing */
+static void jsonw_eol(json_writer_t *self)
+{
+ if (!self->pretty)
+ return;
+
+ putc('\n', self->out);
+ jsonw_indent(self);
+}
+
+/* If current object is not empty print a comma */
+static void jsonw_eor(json_writer_t *self)
+{
+ if (self->sep != '\0')
+ putc(self->sep, self->out);
+ self->sep = ',';
+}
+
+
+/* Output JSON encoded string */
+/* Handles C escapes, does not do Unicode */
+static void jsonw_puts(json_writer_t *self, const char *str)
+{
+ putc('"', self->out);
+ for (; *str; ++str)
+ switch (*str) {
+ case '\t':
+ fputs("\\t", self->out);
+ break;
+ case '\n':
+ fputs("\\n", self->out);
+ break;
+ case '\r':
+ fputs("\\r", self->out);
+ break;
+ case '\f':
+ fputs("\\f", self->out);
+ break;
+ case '\b':
+ fputs("\\b", self->out);
+ break;
+ case '\\':
+ fputs("\\\\", self->out);
+ break;
+ case '"':
+ fputs("\\\"", self->out);
+ break;
+ case '\'':
+ fputs("\\\'", self->out);
+ break;
+ default:
+ putc(*str, self->out);
+ }
+ putc('"', self->out);
+}
+
+/* Create a new JSON stream */
+json_writer_t *jsonw_new(FILE *f)
+{
+ json_writer_t *self = malloc(sizeof(*self));
+
+ if (self) {
+ self->out = f;
+ self->depth = 0;
+ self->pretty = false;
+ self->sep = '\0';
+ }
+ return self;
+}
+
+/* End output to JSON stream */
+void jsonw_destroy(json_writer_t **self_p)
+{
+ json_writer_t *self = *self_p;
+
+ assert(self->depth == 0);
+ fputs("\n", self->out);
+ fflush(self->out);
+ free(self);
+ *self_p = NULL;
+}
+
+void jsonw_pretty(json_writer_t *self, bool on)
+{
+ self->pretty = on;
+}
+
+/* Basic blocks */
+static void jsonw_begin(json_writer_t *self, int c)
+{
+ jsonw_eor(self);
+ putc(c, self->out);
+ ++self->depth;
+ self->sep = '\0';
+}
+
+static void jsonw_end(json_writer_t *self, int c)
+{
+ assert(self->depth > 0);
+
+ --self->depth;
+ if (self->sep != '\0')
+ jsonw_eol(self);
+ putc(c, self->out);
+ self->sep = ',';
+}
+
+
+/* Add a JSON property name */
+void jsonw_name(json_writer_t *self, const char *name)
+{
+ jsonw_eor(self);
+ jsonw_eol(self);
+ self->sep = '\0';
+ jsonw_puts(self, name);
+ putc(':', self->out);
+ if (self->pretty)
+ putc(' ', self->out);
+}
+
+__attribute__((format(printf, 2, 3)))
+void jsonw_printf(json_writer_t *self, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ jsonw_eor(self);
+ vfprintf(self->out, fmt, ap);
+ va_end(ap);
+}
+
+/* Collections */
+void jsonw_start_object(json_writer_t *self)
+{
+ jsonw_begin(self, '{');
+}
+
+void jsonw_end_object(json_writer_t *self)
+{
+ jsonw_end(self, '}');
+}
+
+void jsonw_start_array(json_writer_t *self)
+{
+ jsonw_begin(self, '[');
+ if (self->pretty)
+ putc(' ', self->out);
+}
+
+void jsonw_end_array(json_writer_t *self)
+{
+ if (self->pretty && self->sep)
+ putc(' ', self->out);
+ self->sep = '\0';
+ jsonw_end(self, ']');
+}
+
+/* JSON value types */
+void jsonw_string(json_writer_t *self, const char *value)
+{
+ jsonw_eor(self);
+ jsonw_puts(self, value);
+}
+
+void jsonw_bool(json_writer_t *self, bool val)
+{
+ jsonw_printf(self, "%s", val ? "true" : "false");
+}
+
+void jsonw_null(json_writer_t *self)
+{
+ jsonw_printf(self, "null");
+}
+
+void jsonw_float(json_writer_t *self, double num)
+{
+ jsonw_printf(self, "%g", num);
+}
+
+void jsonw_hhu(json_writer_t *self, unsigned char num)
+{
+ jsonw_printf(self, "%hhu", num);
+}
+
+void jsonw_hu(json_writer_t *self, unsigned short num)
+{
+ jsonw_printf(self, "%hu", num);
+}
+
+void jsonw_uint(json_writer_t *self, unsigned int num)
+{
+ jsonw_printf(self, "%u", num);
+}
+
+void jsonw_u64(json_writer_t *self, uint64_t num)
+{
+ jsonw_printf(self, "%"PRIu64, num);
+}
+
+void jsonw_xint(json_writer_t *self, uint64_t num)
+{
+ jsonw_printf(self, "%"PRIx64, num);
+}
+
+void jsonw_luint(json_writer_t *self, unsigned long num)
+{
+ jsonw_printf(self, "%lu", num);
+}
+
+void jsonw_lluint(json_writer_t *self, unsigned long long num)
+{
+ jsonw_printf(self, "%llu", num);
+}
+
+void jsonw_int(json_writer_t *self, int num)
+{
+ jsonw_printf(self, "%d", num);
+}
+
+void jsonw_s64(json_writer_t *self, int64_t num)
+{
+ jsonw_printf(self, "%"PRId64, num);
+}
+
+/* Basic name/value objects */
+void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
+{
+ jsonw_name(self, prop);
+ jsonw_string(self, val);
+}
+
+void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
+{
+ jsonw_name(self, prop);
+ jsonw_bool(self, val);
+}
+
+void jsonw_float_field(json_writer_t *self, const char *prop, double val)
+{
+ jsonw_name(self, prop);
+ jsonw_float(self, val);
+}
+
+void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num)
+{
+ jsonw_name(self, prop);
+ jsonw_uint(self, num);
+}
+
+void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num)
+{
+ jsonw_name(self, prop);
+ jsonw_u64(self, num);
+}
+
+void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num)
+{
+ jsonw_name(self, prop);
+ jsonw_xint(self, num);
+}
+
+void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num)
+{
+ jsonw_name(self, prop);
+ jsonw_hhu(self, num);
+}
+
+void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
+{
+ jsonw_name(self, prop);
+ jsonw_hu(self, num);
+}
+
+void jsonw_luint_field(json_writer_t *self,
+ const char *prop,
+ unsigned long num)
+{
+ jsonw_name(self, prop);
+ jsonw_luint(self, num);
+}
+
+void jsonw_lluint_field(json_writer_t *self,
+ const char *prop,
+ unsigned long long num)
+{
+ jsonw_name(self, prop);
+ jsonw_lluint(self, num);
+}
+
+void jsonw_int_field(json_writer_t *self, const char *prop, int num)
+{
+ jsonw_name(self, prop);
+ jsonw_int(self, num);
+}
+
+void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num)
+{
+ jsonw_name(self, prop);
+ jsonw_s64(self, num);
+}
+
+void jsonw_null_field(json_writer_t *self, const char *prop)
+{
+ jsonw_name(self, prop);
+ jsonw_null(self);
+}
+
+#ifdef TEST
+int main(int argc, char **argv)
+{
+ json_writer_t *wr = jsonw_new(stdout);
+
+ jsonw_start_object(wr);
+ jsonw_pretty(wr, true);
+ jsonw_name(wr, "Vyatta");
+ jsonw_start_object(wr);
+ jsonw_string_field(wr, "url", "http://vyatta.com");
+ jsonw_uint_field(wr, "downloads", 2000000ul);
+ jsonw_float_field(wr, "stock", 8.16);
+
+ jsonw_name(wr, "ARGV");
+ jsonw_start_array(wr);
+ while (--argc)
+ jsonw_string(wr, *++argv);
+ jsonw_end_array(wr);
+
+ jsonw_name(wr, "empty");
+ jsonw_start_array(wr);
+ jsonw_end_array(wr);
+
+ jsonw_name(wr, "NIL");
+ jsonw_start_object(wr);
+ jsonw_end_object(wr);
+
+ jsonw_null_field(wr, "my_null");
+
+ jsonw_name(wr, "special chars");
+ jsonw_start_array(wr);
+ jsonw_string_field(wr, "slash", "/");
+ jsonw_string_field(wr, "newline", "\n");
+ jsonw_string_field(wr, "tab", "\t");
+ jsonw_string_field(wr, "ff", "\f");
+ jsonw_string_field(wr, "quote", "\"");
+ jsonw_string_field(wr, "tick", "\'");
+ jsonw_string_field(wr, "backslash", "\\");
+ jsonw_end_array(wr);
+
+ jsonw_end_object(wr);
+
+ jsonw_end_object(wr);
+ jsonw_destroy(&wr);
+ return 0;
+}
+
+#endif
diff --git a/json_writer.h b/json_writer.h
new file mode 100644
index 0000000..b52dc2d
--- /dev/null
+++ b/json_writer.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
+/*
+ * Simple streaming JSON writer
+ *
+ * This takes care of the annoying bits of JSON syntax like the commas
+ * after elements
+ *
+ * Authors: Stephen Hemminger <stephen@networkplumber.org>
+ */
+
+#ifndef _JSON_WRITER_H_
+#define _JSON_WRITER_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/* Opaque class structure */
+typedef struct json_writer json_writer_t;
+
+/* Create a new JSON stream */
+json_writer_t *jsonw_new(FILE *f);
+/* End output to JSON stream */
+void jsonw_destroy(json_writer_t **self_p);
+
+/* Cause output to have pretty whitespace */
+void jsonw_pretty(json_writer_t *self, bool on);
+
+/* Add property name */
+void jsonw_name(json_writer_t *self, const char *name);
+
+/* Add value */
+__attribute__((format(printf, 2, 3)))
+void jsonw_printf(json_writer_t *self, const char *fmt, ...);
+void jsonw_string(json_writer_t *self, const char *value);
+void jsonw_bool(json_writer_t *self, bool value);
+void jsonw_float(json_writer_t *self, double number);
+void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
+void jsonw_uint(json_writer_t *self, unsigned int number);
+void jsonw_u64(json_writer_t *self, uint64_t number);
+void jsonw_xint(json_writer_t *self, uint64_t number);
+void jsonw_hhu(json_writer_t *self, unsigned char num);
+void jsonw_hu(json_writer_t *self, unsigned short number);
+void jsonw_int(json_writer_t *self, int number);
+void jsonw_s64(json_writer_t *self, int64_t number);
+void jsonw_null(json_writer_t *self);
+void jsonw_luint(json_writer_t *self, unsigned long num);
+void jsonw_lluint(json_writer_t *self, unsigned long long num);
+
+/* Useful Combinations of name and value */
+void jsonw_string_field(json_writer_t *self, const char *prop, const char *val);
+void jsonw_bool_field(json_writer_t *self, const char *prop, bool value);
+void jsonw_float_field(json_writer_t *self, const char *prop, double num);
+void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num);
+void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num);
+void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num);
+void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num);
+void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
+void jsonw_int_field(json_writer_t *self, const char *prop, int num);
+void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num);
+void jsonw_null_field(json_writer_t *self, const char *prop);
+void jsonw_luint_field(json_writer_t *self, const char *prop,
+ unsigned long num);
+void jsonw_lluint_field(json_writer_t *self, const char *prop,
+ unsigned long long num);
+
+/* Collections */
+void jsonw_start_object(json_writer_t *self);
+void jsonw_end_object(json_writer_t *self);
+
+void jsonw_start_array(json_writer_t *self);
+void jsonw_end_array(json_writer_t *self);
+
+/* Override default exception handling */
+typedef void (jsonw_err_handler_fn)(const char *);
+
+#endif /* _JSON_WRITER_H_ */
diff --git a/lan78xx.c b/lan78xx.c
index 46ade1c..75ee048 100644
--- a/lan78xx.c
+++ b/lan78xx.c
@@ -2,7 +2,7 @@
#include <string.h>
#include "internal.h"
-int lan78xx_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int lan78xx_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
unsigned int *lan78xx_reg = (unsigned int *)regs->data;
diff --git a/marvell.c b/marvell.c
index 9e5440d..d3d570e 100644
--- a/marvell.c
+++ b/marvell.c
@@ -130,7 +130,7 @@ static void dump_fifo(const char *name, const void *p)
static void dump_gmac_fifo(const char *name, const void *p)
{
const u32 *r = p;
- int i;
+ unsigned int i;
static const char *regs[] = {
"End Address",
"Almost Full Thresh",
@@ -259,7 +259,7 @@ static void dump_control(u8 *r)
printf("General Purpose I/O 0x%08X\n", *(u32 *) (r + 0x15c));
}
-int skge_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int skge_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
const u32 *r = (const u32 *) regs->data;
@@ -380,7 +380,7 @@ static void dump_prefetch(const char *name, const void *r)
}
}
-int sky2_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int sky2_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
const u16 *r16 = (const u16 *) regs->data;
diff --git a/natsemi.c b/natsemi.c
index ce82c42..4d9fc09 100644
--- a/natsemi.c
+++ b/natsemi.c
@@ -323,7 +323,7 @@ static void __print_intr(int d, int intr, const char *name,
} while (0)
int
-natsemi_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+natsemi_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *data = (u32 *)regs->data;
@@ -964,11 +964,11 @@ natsemi_dump_regs(struct ethtool_drvinfo *info maybe_unused,
}
int
-natsemi_dump_eeprom(struct ethtool_drvinfo *info maybe_unused,
+natsemi_dump_eeprom(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_eeprom *ee)
{
- int i;
u16 *eebuf = (u16 *)ee->data;
+ unsigned int i;
if (ee->magic != NATSEMI_MAGIC) {
fprintf(stderr, "Magic number 0x%08x does not match 0x%08x\n",
diff --git a/netlink/bitset.c b/netlink/bitset.c
index ed109ec..10ce8e9 100644
--- a/netlink/bitset.c
+++ b/netlink/bitset.c
@@ -50,6 +50,7 @@ bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx,
DECLARE_ATTR_TB_INFO(bitset_tb);
const struct nlattr *bits;
const struct nlattr *bit;
+ bool nomask;
int ret;
*retptr = 0;
@@ -57,6 +58,15 @@ bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx,
if (ret < 0)
goto err;
+ nomask = bitset_tb[ETHTOOL_A_BITSET_NOMASK];
+ if (mask && nomask) {
+ /* Trying to determine if a bit is set in the mask of a "no
+ * mask" bitset doesn't make sense.
+ */
+ ret = -EFAULT;
+ goto err;
+ }
+
bits = mask ? bitset_tb[ETHTOOL_A_BITSET_MASK] :
bitset_tb[ETHTOOL_A_BITSET_VALUE];
if (bits) {
@@ -87,7 +97,7 @@ bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx,
my_idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);
if (my_idx == idx)
- return mask || tb[ETHTOOL_A_BITSET_BIT_VALUE];
+ return mask || nomask || tb[ETHTOOL_A_BITSET_BIT_VALUE];
}
return false;
@@ -153,6 +163,37 @@ err:
return true;
}
+static uint32_t *get_compact_bitset_attr(const struct nlattr *bitset,
+ uint16_t type)
+{
+ const struct nlattr *tb[ETHTOOL_A_BITSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ unsigned int count;
+ int ret;
+
+ ret = mnl_attr_parse_nested(bitset, attr_cb, &tb_info);
+ if (ret < 0)
+ return NULL;
+ if (!tb[ETHTOOL_A_BITSET_SIZE] || !tb[ETHTOOL_A_BITSET_VALUE] ||
+ !tb[type])
+ return NULL;
+ count = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_SIZE]);
+ if (8 * mnl_attr_get_payload_len(tb[type]) < count)
+ return NULL;
+
+ return mnl_attr_get_payload(tb[type]);
+}
+
+uint32_t *get_compact_bitset_value(const struct nlattr *bitset)
+{
+ return get_compact_bitset_attr(bitset, ETHTOOL_A_BITSET_VALUE);
+}
+
+uint32_t *get_compact_bitset_mask(const struct nlattr *bitset)
+{
+ return get_compact_bitset_attr(bitset, ETHTOOL_A_BITSET_MASK);
+}
+
int walk_bitset(const struct nlattr *bitset, const struct stringset *labels,
bitset_walk_callback cb, void *data)
{
diff --git a/netlink/bitset.h b/netlink/bitset.h
index 4b587d2..4c9cdac 100644
--- a/netlink/bitset.h
+++ b/netlink/bitset.h
@@ -20,6 +20,8 @@ bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx,
int *retptr);
bool bitset_is_compact(const struct nlattr *bitset);
bool bitset_is_empty(const struct nlattr *bitset, bool mask, int *retptr);
+uint32_t *get_compact_bitset_value(const struct nlattr *bitset);
+uint32_t *get_compact_bitset_mask(const struct nlattr *bitset);
int walk_bitset(const struct nlattr *bitset, const struct stringset *labels,
bitset_walk_callback cb, void *data);
diff --git a/netlink/cable_test.c b/netlink/cable_test.c
new file mode 100644
index 0000000..17139f7
--- /dev/null
+++ b/netlink/cable_test.c
@@ -0,0 +1,593 @@
+/*
+ * cable_test.c - netlink implementation of cable test command
+ *
+ * Implementation of ethtool --cable-test <dev>
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+struct cable_test_context {
+ bool breakout;
+};
+
+static int nl_get_cable_test_result(const struct nlattr *nest, uint8_t *pair,
+ uint16_t *code)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_RESULT_MAX+1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 ||
+ !tb[ETHTOOL_A_CABLE_RESULT_PAIR] ||
+ !tb[ETHTOOL_A_CABLE_RESULT_CODE])
+ return -EFAULT;
+
+ *pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_RESULT_PAIR]);
+ *code = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_RESULT_CODE]);
+
+ return 0;
+}
+
+static int nl_get_cable_test_fault_length(const struct nlattr *nest,
+ uint8_t *pair, unsigned int *cm)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_FAULT_LENGTH_MAX+1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 ||
+ !tb[ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR] ||
+ !tb[ETHTOOL_A_CABLE_FAULT_LENGTH_CM])
+ return -EFAULT;
+
+ *pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR]);
+ *cm = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_CM]);
+
+ return 0;
+}
+
+static char *nl_code2txt(uint16_t code)
+{
+ switch (code) {
+ case ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC:
+ default:
+ return "Unknown";
+ case ETHTOOL_A_CABLE_RESULT_CODE_OK:
+ return "OK";
+ case ETHTOOL_A_CABLE_RESULT_CODE_OPEN:
+ return "Open Circuit";
+ case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT:
+ return "Short within Pair";
+ case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT:
+ return "Short to another pair";
+ }
+}
+
+static char *nl_pair2txt(uint8_t pair)
+{
+ switch (pair) {
+ case ETHTOOL_A_CABLE_PAIR_A:
+ return "Pair A";
+ case ETHTOOL_A_CABLE_PAIR_B:
+ return "Pair B";
+ case ETHTOOL_A_CABLE_PAIR_C:
+ return "Pair C";
+ case ETHTOOL_A_CABLE_PAIR_D:
+ return "Pair D";
+ default:
+ return "Unexpected pair";
+ }
+}
+
+static int nl_cable_test_ntf_attr(struct nlattr *evattr)
+{
+ unsigned int cm;
+ uint16_t code;
+ uint8_t pair;
+ int ret;
+
+ switch (mnl_attr_get_type(evattr)) {
+ case ETHTOOL_A_CABLE_NEST_RESULT:
+ ret = nl_get_cable_test_result(evattr, &pair, &code);
+ if (ret < 0)
+ return ret;
+
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "pair", "%s ", nl_pair2txt(pair));
+ print_string(PRINT_ANY, "code", "code %s\n", nl_code2txt(code));
+ close_json_object();
+ break;
+
+ case ETHTOOL_A_CABLE_NEST_FAULT_LENGTH:
+ ret = nl_get_cable_test_fault_length(evattr, &pair, &cm);
+ if (ret < 0)
+ return ret;
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "pair", "%s, ", nl_pair2txt(pair));
+ print_float(PRINT_ANY, "length", "fault length: %0.2fm\n",
+ (float)cm / 100);
+ close_json_object();
+ break;
+ }
+ return 0;
+}
+
+static void cable_test_ntf_nest(const struct nlattr *nest)
+{
+ struct nlattr *pos;
+ int ret;
+
+ mnl_attr_for_each_nested(pos, nest) {
+ ret = nl_cable_test_ntf_attr(pos);
+ if (ret < 0)
+ return;
+ }
+}
+
+/* Returns MNL_CB_STOP when the test is complete. Used when executing
+ * a test, but not suitable for monitor.
+ */
+static int cable_test_ntf_stop_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_TEST_NTF_MAX + 1] = {};
+ u8 status = ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC;
+ struct cable_test_context *ctctx;
+ struct nl_context *nlctx = data;
+ DECLARE_ATTR_TB_INFO(tb);
+ bool silent;
+ int err_ret;
+ int ret;
+
+ ctctx = nlctx->cmd_private;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_CABLE_TEST_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS])
+ status = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]);
+
+ switch (status) {
+ case ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED:
+ print_string(PRINT_FP, "status",
+ "Cable test started for device %s.\n",
+ nlctx->devname);
+ break;
+ case ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED:
+ print_string(PRINT_FP, "status",
+ "Cable test completed for device %s.\n",
+ nlctx->devname);
+ break;
+ default:
+ break;
+ }
+
+ if (tb[ETHTOOL_A_CABLE_TEST_NTF_NEST])
+ cable_test_ntf_nest(tb[ETHTOOL_A_CABLE_TEST_NTF_NEST]);
+
+ if (status == ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED) {
+ if (ctctx)
+ ctctx->breakout = true;
+ return MNL_CB_STOP;
+ }
+
+ return MNL_CB_OK;
+}
+
+/* Wrapper around cable_test_ntf_stop_cb() which does not return STOP,
+ * used for monitor
+ */
+int cable_test_ntf_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ int status = cable_test_ntf_stop_cb(nlhdr, data);
+
+ if (status == MNL_CB_STOP)
+ status = MNL_CB_OK;
+
+ return status;
+}
+
+static int nl_cable_test_results_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+
+ if (ghdr->cmd != ETHTOOL_MSG_CABLE_TEST_NTF)
+ return MNL_CB_OK;
+
+ return cable_test_ntf_stop_cb(nlhdr, data);
+}
+
+/* Receive the broadcasted messages until we get the cable test
+ * results
+ */
+static int nl_cable_test_process_results(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ struct cable_test_context ctctx;
+ int err;
+
+ nlctx->is_monitor = true;
+ nlsk->port = 0;
+ nlsk->seq = 0;
+
+ ctctx.breakout = false;
+ nlctx->cmd_private = &ctctx;
+
+ while (!ctctx.breakout) {
+ err = nlsock_process_reply(nlsk, nl_cable_test_results_cb,
+ nlctx);
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+
+int nl_cable_test(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ uint32_t grpid = nlctx->ethnl_mongrp;
+ int ret;
+
+ /* Join the multicast group so we can receive the results in a
+ * race free way.
+ */
+ if (!grpid) {
+ fprintf(stderr, "multicast group 'monitor' not found\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP,
+ &grpid, sizeof(grpid));
+ if (ret < 0)
+ return ret;
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_CABLE_TEST_ACT,
+ ETHTOOL_A_CABLE_TEST_HEADER, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ fprintf(stderr, "Cannot start cable test\n");
+ else {
+ new_json_obj(ctx->json);
+
+ ret = nl_cable_test_process_results(ctx);
+
+ delete_json_obj();
+ }
+
+ return ret;
+}
+
+static int nl_get_cable_test_tdr_amplitude(const struct nlattr *nest,
+ uint8_t *pair, int16_t *mV)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_AMPLITUDE_MAX+1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ uint16_t mV_unsigned;
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 ||
+ !tb[ETHTOOL_A_CABLE_AMPLITUDE_PAIR] ||
+ !tb[ETHTOOL_A_CABLE_AMPLITUDE_mV])
+ return -EFAULT;
+
+ *pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_AMPLITUDE_PAIR]);
+ mV_unsigned = mnl_attr_get_u16(tb[ETHTOOL_A_CABLE_AMPLITUDE_mV]);
+ *mV = (int16_t)(mV_unsigned);
+
+ return 0;
+}
+
+static int nl_get_cable_test_tdr_pulse(const struct nlattr *nest, uint16_t *mV)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_PULSE_MAX+1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 ||
+ !tb[ETHTOOL_A_CABLE_PULSE_mV])
+ return -EFAULT;
+
+ *mV = mnl_attr_get_u16(tb[ETHTOOL_A_CABLE_PULSE_mV]);
+
+ return 0;
+}
+
+static int nl_get_cable_test_tdr_step(const struct nlattr *nest,
+ uint32_t *first, uint32_t *last,
+ uint32_t *step)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_STEP_MAX+1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 ||
+ !tb[ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE] ||
+ !tb[ETHTOOL_A_CABLE_STEP_LAST_DISTANCE] ||
+ !tb[ETHTOOL_A_CABLE_STEP_STEP_DISTANCE])
+ return -EFAULT;
+
+ *first = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE]);
+ *last = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_LAST_DISTANCE]);
+ *step = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_STEP_DISTANCE]);
+
+ return 0;
+}
+
+static int nl_cable_test_tdr_ntf_attr(struct nlattr *evattr)
+{
+ uint32_t first, last, step;
+ uint8_t pair;
+ int ret;
+
+ switch (mnl_attr_get_type(evattr)) {
+ case ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE: {
+ int16_t mV;
+
+ ret = nl_get_cable_test_tdr_amplitude(
+ evattr, &pair, &mV);
+ if (ret < 0)
+ return ret;
+
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "pair", "%s ", nl_pair2txt(pair));
+ print_int(PRINT_ANY, "amplitude", "Amplitude %4d\n", mV);
+ close_json_object();
+ break;
+ }
+ case ETHTOOL_A_CABLE_TDR_NEST_PULSE: {
+ uint16_t mV;
+
+ ret = nl_get_cable_test_tdr_pulse(evattr, &mV);
+ if (ret < 0)
+ return ret;
+
+ open_json_object(NULL);
+ print_uint(PRINT_ANY, "pulse", "TDR Pulse %dmV\n", mV);
+ close_json_object();
+ break;
+ }
+ case ETHTOOL_A_CABLE_TDR_NEST_STEP:
+ ret = nl_get_cable_test_tdr_step(evattr, &first, &last, &step);
+ if (ret < 0)
+ return ret;
+
+ open_json_object(NULL);
+ print_float(PRINT_ANY, "first", "Step configuration: %.2f-",
+ (float)first / 100);
+ print_float(PRINT_ANY, "last", "%.2f meters ",
+ (float)last / 100);
+ print_float(PRINT_ANY, "step", "in %.2fm steps\n",
+ (float)step / 100);
+ close_json_object();
+ break;
+ }
+ return 0;
+}
+
+static void cable_test_tdr_ntf_nest(const struct nlattr *nest)
+{
+ struct nlattr *pos;
+ int ret;
+
+ mnl_attr_for_each_nested(pos, nest) {
+ ret = nl_cable_test_tdr_ntf_attr(pos);
+ if (ret < 0)
+ return;
+ }
+}
+
+/* Returns MNL_CB_STOP when the test is complete. Used when executing
+ * a test, but not suitable for monitor.
+ */
+int cable_test_tdr_ntf_stop_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_MAX + 1] = {};
+ u8 status = ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC;
+ struct cable_test_context *ctctx;
+ struct nl_context *nlctx = data;
+
+ DECLARE_ATTR_TB_INFO(tb);
+ bool silent;
+ int err_ret;
+ int ret;
+
+ ctctx = nlctx->cmd_private;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS])
+ status = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]);
+
+ switch (status) {
+ case ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED:
+ print_string(PRINT_FP, "status",
+ "Cable test TDR started for device %s.\n",
+ nlctx->devname);
+ break;
+ case ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED:
+ print_string(PRINT_FP, "status",
+ "Cable test TDR completed for device %s.\n",
+ nlctx->devname);
+ break;
+ default:
+ break;
+ }
+
+ if (tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST])
+ cable_test_tdr_ntf_nest(tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST]);
+
+ if (status == ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED) {
+ if (ctctx)
+ ctctx->breakout = true;
+ return MNL_CB_STOP;
+ }
+
+ return MNL_CB_OK;
+}
+
+/* Wrapper around cable_test_tdr_ntf_stop_cb() which does not return
+ * STOP, used for monitor
+ */
+int cable_test_tdr_ntf_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ int status = cable_test_tdr_ntf_stop_cb(nlhdr, data);
+
+ if (status == MNL_CB_STOP)
+ status = MNL_CB_OK;
+
+ return status;
+}
+
+static int nl_cable_test_tdr_results_cb(const struct nlmsghdr *nlhdr,
+ void *data)
+{
+ const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+
+ if (ghdr->cmd != ETHTOOL_MSG_CABLE_TEST_TDR_NTF)
+ return MNL_CB_OK;
+
+ cable_test_tdr_ntf_cb(nlhdr, data);
+
+ return MNL_CB_STOP;
+}
+
+/* Receive the broadcasted messages until we get the cable test
+ * results
+ */
+static int nl_cable_test_tdr_process_results(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ struct cable_test_context ctctx;
+ int err;
+
+ nlctx->is_monitor = true;
+ nlsk->port = 0;
+ nlsk->seq = 0;
+
+ ctctx.breakout = false;
+ nlctx->cmd_private = &ctctx;
+
+ while (!ctctx.breakout) {
+ err = nlsock_process_reply(nlsk, nl_cable_test_tdr_results_cb,
+ nlctx);
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+
+static const struct param_parser tdr_params[] = {
+ {
+ .arg = "first",
+ .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST,
+ .group = ETHTOOL_A_CABLE_TEST_TDR_CFG,
+ .handler = nl_parse_direct_m2cm,
+ },
+ {
+ .arg = "last",
+ .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST,
+ .group = ETHTOOL_A_CABLE_TEST_TDR_CFG,
+ .handler = nl_parse_direct_m2cm,
+ },
+ {
+ .arg = "step",
+ .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP,
+ .group = ETHTOOL_A_CABLE_TEST_TDR_CFG,
+ .handler = nl_parse_direct_m2cm,
+ },
+ {
+ .arg = "pair",
+ .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR,
+ .group = ETHTOOL_A_CABLE_TEST_TDR_CFG,
+ .handler = nl_parse_direct_u8,
+ },
+ {}
+};
+
+int nl_cable_test_tdr(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ uint32_t grpid = nlctx->ethnl_mongrp;
+ struct nl_msg_buff *msgbuff;
+ int ret;
+
+ nlctx->cmd = "--cable-test-tdr";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ msgbuff = &nlsk->msgbuff;
+
+ /* Join the multicast group so we can receive the results in a
+ * race free way.
+ */
+ if (!grpid) {
+ fprintf(stderr, "multicast group 'monitor' not found\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP,
+ &grpid, sizeof(grpid));
+ if (ret < 0)
+ return ret;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_CABLE_TEST_TDR_ACT,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_CABLE_TEST_TDR_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, tdr_params, NULL, PARSER_GROUP_NEST, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ fprintf(stderr, "Cannot start cable test TDR\n");
+ else {
+ new_json_obj(ctx->json);
+
+ ret = nl_cable_test_tdr_process_results(ctx);
+
+ delete_json_obj();
+ }
+
+ return ret;
+}
diff --git a/netlink/channels.c b/netlink/channels.c
new file mode 100644
index 0000000..894c74b
--- /dev/null
+++ b/netlink/channels.c
@@ -0,0 +1,141 @@
+/*
+ * channels.c - netlink implementation of channel commands
+ *
+ * Implementation of "ethtool -l <dev>" and "ethtool -L <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+/* CHANNELS_GET */
+
+int channels_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_CHANNELS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_CHANNELS_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ putchar('\n');
+ printf("Channel parameters for %s:\n", nlctx->devname);
+ printf("Pre-set maximums:\n");
+ show_u32(tb[ETHTOOL_A_CHANNELS_RX_MAX], "RX:\t\t");
+ show_u32(tb[ETHTOOL_A_CHANNELS_TX_MAX], "TX:\t\t");
+ show_u32(tb[ETHTOOL_A_CHANNELS_OTHER_MAX], "Other:\t\t");
+ show_u32(tb[ETHTOOL_A_CHANNELS_COMBINED_MAX], "Combined:\t");
+ printf("Current hardware settings:\n");
+ show_u32(tb[ETHTOOL_A_CHANNELS_RX_COUNT], "RX:\t\t");
+ show_u32(tb[ETHTOOL_A_CHANNELS_TX_COUNT], "TX:\t\t");
+ show_u32(tb[ETHTOOL_A_CHANNELS_OTHER_COUNT], "Other:\t\t");
+ show_u32(tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT], "Combined:\t");
+
+ return MNL_CB_OK;
+}
+
+int nl_gchannels(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_CHANNELS_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_CHANNELS_GET,
+ ETHTOOL_A_CHANNELS_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, channels_reply_cb);
+}
+
+/* CHANNELS_SET */
+
+static const struct param_parser schannels_params[] = {
+ {
+ .arg = "rx",
+ .type = ETHTOOL_A_CHANNELS_RX_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx",
+ .type = ETHTOOL_A_CHANNELS_TX_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "other",
+ .type = ETHTOOL_A_CHANNELS_OTHER_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "combined",
+ .type = ETHTOOL_A_CHANNELS_COMBINED_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_schannels(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_CHANNELS_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-L";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_CHANNELS_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_CHANNELS_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, schannels_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 1;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 1;
+}
diff --git a/netlink/coalesce.c b/netlink/coalesce.c
new file mode 100644
index 0000000..75922a9
--- /dev/null
+++ b/netlink/coalesce.c
@@ -0,0 +1,269 @@
+/*
+ * coalesce.c - netlink implementation of coalescing commands
+ *
+ * Implementation of "ethtool -c <dev>" and "ethtool -C <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+/* COALESCE_GET */
+
+int coalesce_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_COALESCE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_COALESCE_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ putchar('\n');
+ printf("Coalesce parameters for %s:\n", nlctx->devname);
+ show_bool("rx", "Adaptive RX: %s ",
+ tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX]);
+ show_bool("tx", "TX: %s\n", tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX]);
+ show_u32(tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS],
+ "stats-block-usecs: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL],
+ "sample-interval: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], "pkt-rate-low: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], "pkt-rate-high: ");
+ putchar('\n');
+ show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS], "rx-usecs: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], "rx-frames: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], "rx-usecs-irq: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], "rx-frames-irq: ");
+ putchar('\n');
+ show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS], "tx-usecs: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], "tx-frames: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], "tx-usecs-irq: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], "tx-frames-irq: ");
+ putchar('\n');
+ show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], "rx-usecs-low: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], "rx-frame-low: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], "tx-usecs-low: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], "tx-frame-low: ");
+ putchar('\n');
+ show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], "rx-usecs-high: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], "rx-frame-high: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], "tx-usecs-high: ");
+ show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], "tx-frame-high: ");
+ putchar('\n');
+
+ return MNL_CB_OK;
+}
+
+int nl_gcoalesce(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_COALESCE_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_COALESCE_GET,
+ ETHTOOL_A_COALESCE_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, coalesce_reply_cb);
+}
+
+/* COALESCE_SET */
+
+static const struct param_parser scoalesce_params[] = {
+ {
+ .arg = "adaptive-rx",
+ .type = ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "adaptive-tx",
+ .type = ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "sample-interval",
+ .type = ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "stats-block-usecs",
+ .type = ETHTOOL_A_COALESCE_STATS_BLOCK_USECS,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "pkt-rate-low",
+ .type = ETHTOOL_A_COALESCE_PKT_RATE_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "pkt-rate-high",
+ .type = ETHTOOL_A_COALESCE_PKT_RATE_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs",
+ .type = ETHTOOL_A_COALESCE_RX_USECS,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames",
+ .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs-irq",
+ .type = ETHTOOL_A_COALESCE_RX_USECS_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames-irq",
+ .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs",
+ .type = ETHTOOL_A_COALESCE_TX_USECS,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames",
+ .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs-irq",
+ .type = ETHTOOL_A_COALESCE_TX_USECS_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames-irq",
+ .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs-low",
+ .type = ETHTOOL_A_COALESCE_RX_USECS_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames-low",
+ .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs-low",
+ .type = ETHTOOL_A_COALESCE_TX_USECS_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames-low",
+ .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs-high",
+ .type = ETHTOOL_A_COALESCE_RX_USECS_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames-high",
+ .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs-high",
+ .type = ETHTOOL_A_COALESCE_TX_USECS_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames-high",
+ .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_scoalesce(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_COALESCE_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-C";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_COALESCE_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_COALESCE_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, scoalesce_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 1;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 1;
+}
diff --git a/netlink/desc-ethtool.c b/netlink/desc-ethtool.c
index 76c6f13..96291b9 100644
--- a/netlink/desc-ethtool.c
+++ b/netlink/desc-ethtool.c
@@ -4,9 +4,9 @@
* Descriptions of ethtool netlink messages and attributes for pretty print.
*/
+#include "../internal.h"
#include <linux/ethtool_netlink.h>
-#include "../internal.h"
#include "prettymsg.h"
static const struct pretty_nla_desc __header_desc[] = {
@@ -85,12 +85,18 @@ static const struct pretty_nla_desc __linkmodes_desc[] = {
NLATTR_DESC_NESTED(ETHTOOL_A_LINKMODES_PEER, bitset),
NLATTR_DESC_U32(ETHTOOL_A_LINKMODES_SPEED),
NLATTR_DESC_U8(ETHTOOL_A_LINKMODES_DUPLEX),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE),
};
static const struct pretty_nla_desc __linkstate_desc[] = {
NLATTR_DESC_INVALID(ETHTOOL_A_LINKSTATE_UNSPEC),
NLATTR_DESC_NESTED(ETHTOOL_A_LINKSTATE_HEADER, header),
NLATTR_DESC_BOOL(ETHTOOL_A_LINKSTATE_LINK),
+ NLATTR_DESC_U32(ETHTOOL_A_LINKSTATE_SQI),
+ NLATTR_DESC_U32(ETHTOOL_A_LINKSTATE_SQI_MAX),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKSTATE_EXT_STATE),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKSTATE_EXT_SUBSTATE),
};
static const struct pretty_nla_desc __debug_desc[] = {
@@ -106,6 +112,211 @@ static const struct pretty_nla_desc __wol_desc[] = {
NLATTR_DESC_BINARY(ETHTOOL_A_WOL_SOPASS),
};
+static const struct pretty_nla_desc __features_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_FEATURES_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_HW, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_WANTED, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_ACTIVE, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_NOCHANGE, bitset),
+};
+
+static const struct pretty_nla_desc __privflags_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_PRIVFLAGS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PRIVFLAGS_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PRIVFLAGS_FLAGS, bitset),
+};
+
+static const struct pretty_nla_desc __rings_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_RINGS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_RINGS_HEADER, header),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_MINI_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_JUMBO_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_TX_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_MINI),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_JUMBO),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_TX),
+};
+
+static const struct pretty_nla_desc __channels_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CHANNELS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CHANNELS_HEADER, header),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_RX_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_TX_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_OTHER_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_COMBINED_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_RX_COUNT),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_TX_COUNT),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_OTHER_COUNT),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_COMBINED_COUNT),
+};
+
+static const struct pretty_nla_desc __coalesce_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_COALESCE_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_COALESCE_HEADER, header),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS_IRQ),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS_IRQ),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_STATS_BLOCK_USECS),
+ NLATTR_DESC_BOOL(ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX),
+ NLATTR_DESC_BOOL(ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_PKT_RATE_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_PKT_RATE_HIGH),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS_HIGH),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS_HIGH),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL),
+};
+
+static const struct pretty_nla_desc __pause_stats_desc[] = {
+ NLATTR_DESC_BINARY(ETHTOOL_A_PAUSE_STAT_PAD),
+ NLATTR_DESC_U64(ETHTOOL_A_PAUSE_STAT_TX_FRAMES),
+ NLATTR_DESC_U64(ETHTOOL_A_PAUSE_STAT_RX_FRAMES),
+};
+
+static const struct pretty_nla_desc __pause_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_PAUSE_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PAUSE_HEADER, header),
+ NLATTR_DESC_BOOL(ETHTOOL_A_PAUSE_AUTONEG),
+ NLATTR_DESC_BOOL(ETHTOOL_A_PAUSE_RX),
+ NLATTR_DESC_BOOL(ETHTOOL_A_PAUSE_TX),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PAUSE_STATS, pause_stats),
+};
+
+static const struct pretty_nla_desc __eee_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_EEE_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_EEE_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_EEE_MODES_OURS, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_EEE_MODES_PEER, bitset),
+ NLATTR_DESC_BOOL(ETHTOOL_A_EEE_ACTIVE),
+ NLATTR_DESC_BOOL(ETHTOOL_A_EEE_ENABLED),
+ NLATTR_DESC_BOOL(ETHTOOL_A_EEE_TX_LPI_ENABLED),
+ NLATTR_DESC_U32(ETHTOOL_A_EEE_TX_LPI_TIMER),
+};
+
+static const struct pretty_nla_desc __tsinfo_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_TSINFO_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_TIMESTAMPING, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_TX_TYPES, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_RX_FILTERS, bitset),
+ NLATTR_DESC_U32(ETHTOOL_A_TSINFO_PHC_INDEX),
+};
+
+static const struct pretty_nla_desc __cable_test_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_HEADER, header),
+};
+
+static const struct pretty_nla_desc __cable_test_result_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_RESULT_UNSPEC),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_RESULT_PAIR),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_RESULT_CODE),
+};
+
+static const struct pretty_nla_desc __cable_test_flength_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_FAULT_LENGTH_UNSPEC),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_FAULT_LENGTH_CM),
+};
+
+static const struct pretty_nla_desc __cable_nest_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_NEST_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_NEST_RESULT, cable_test_result),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_NEST_FAULT_LENGTH,
+ cable_test_flength),
+};
+
+static const struct pretty_nla_desc __cable_test_ntf_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_NTF_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_NTF_HEADER, header),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_TEST_NTF_STATUS),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_NTF_NEST, cable_nest),
+};
+
+static const struct pretty_nla_desc __cable_test_tdr_cfg_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_TDR_CFG_UNSPEC),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR),
+};
+
+static const struct pretty_nla_desc __cable_test_tdr_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_TDR_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_CFG, cable_test_tdr_cfg),
+};
+
+static const struct pretty_nla_desc __cable_step_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_STEP_UNSPEC),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_STEP_LAST_DISTANCE),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_STEP_STEP_DISTANCE),
+};
+
+static const struct pretty_nla_desc __cable_amplitude_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_AMPLITUDE_UNSPEC),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_AMPLITUDE_PAIR),
+ NLATTR_DESC_S16(ETHTOOL_A_CABLE_AMPLITUDE_mV),
+};
+
+static const struct pretty_nla_desc __cable_pulse_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_PULSE_UNSPEC),
+ NLATTR_DESC_S16(ETHTOOL_A_CABLE_PULSE_mV),
+};
+
+static const struct pretty_nla_desc __cable_test_tdr_nest_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TDR_NEST_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TDR_NEST_STEP, cable_step),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE, cable_amplitude),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TDR_NEST_PULSE, cable_pulse),
+};
+
+static const struct pretty_nla_desc __cable_test_tdr_ntf_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_TDR_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_NTF_HEADER, header),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_TEST_TDR_NTF_STATUS),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST,
+ cable_test_tdr_nest),
+};
+
+const struct pretty_nla_desc __tunnel_udp_entry_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_UDP_ENTRY_UNSPEC),
+ NLATTR_DESC_U16(ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT),
+ NLATTR_DESC_U32(ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE),
+};
+
+const struct pretty_nla_desc __tunnel_udp_table_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_UDP_TABLE_UNSPEC),
+ NLATTR_DESC_U32(ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY, tunnel_udp_entry),
+};
+
+const struct pretty_nla_desc __tunnel_udp_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_UDP_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_UDP_TABLE, tunnel_udp_table),
+};
+
+const struct pretty_nla_desc __tunnel_info_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_INFO_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_INFO_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_INFO_UDP_PORTS, tunnel_udp),
+};
+
const struct pretty_nlmsg_desc ethnl_umsg_desc[] = {
NLMSG_DESC_INVALID(ETHTOOL_MSG_USER_NONE),
NLMSG_DESC(ETHTOOL_MSG_STRSET_GET, strset),
@@ -118,6 +329,24 @@ const struct pretty_nlmsg_desc ethnl_umsg_desc[] = {
NLMSG_DESC(ETHTOOL_MSG_DEBUG_SET, debug),
NLMSG_DESC(ETHTOOL_MSG_WOL_GET, wol),
NLMSG_DESC(ETHTOOL_MSG_WOL_SET, wol),
+ NLMSG_DESC(ETHTOOL_MSG_FEATURES_GET, features),
+ NLMSG_DESC(ETHTOOL_MSG_FEATURES_SET, features),
+ NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_GET, privflags),
+ NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_SET, privflags),
+ NLMSG_DESC(ETHTOOL_MSG_RINGS_GET, rings),
+ NLMSG_DESC(ETHTOOL_MSG_RINGS_SET, rings),
+ NLMSG_DESC(ETHTOOL_MSG_CHANNELS_GET, channels),
+ NLMSG_DESC(ETHTOOL_MSG_CHANNELS_SET, channels),
+ NLMSG_DESC(ETHTOOL_MSG_COALESCE_GET, coalesce),
+ NLMSG_DESC(ETHTOOL_MSG_COALESCE_SET, coalesce),
+ NLMSG_DESC(ETHTOOL_MSG_PAUSE_GET, pause),
+ NLMSG_DESC(ETHTOOL_MSG_PAUSE_SET, pause),
+ NLMSG_DESC(ETHTOOL_MSG_EEE_GET, eee),
+ NLMSG_DESC(ETHTOOL_MSG_EEE_SET, eee),
+ NLMSG_DESC(ETHTOOL_MSG_TSINFO_GET, tsinfo),
+ NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_ACT, cable_test),
+ NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_TDR_ACT, cable_test_tdr),
+ NLMSG_DESC(ETHTOOL_MSG_TUNNEL_INFO_GET, tunnel_info),
};
const unsigned int ethnl_umsg_n_desc = ARRAY_SIZE(ethnl_umsg_desc);
@@ -134,6 +363,25 @@ const struct pretty_nlmsg_desc ethnl_kmsg_desc[] = {
NLMSG_DESC(ETHTOOL_MSG_DEBUG_NTF, debug),
NLMSG_DESC(ETHTOOL_MSG_WOL_GET_REPLY, wol),
NLMSG_DESC(ETHTOOL_MSG_WOL_NTF, wol),
+ NLMSG_DESC(ETHTOOL_MSG_FEATURES_GET_REPLY, features),
+ NLMSG_DESC(ETHTOOL_MSG_FEATURES_SET_REPLY, features),
+ NLMSG_DESC(ETHTOOL_MSG_FEATURES_NTF, features),
+ NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, privflags),
+ NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_NTF, privflags),
+ NLMSG_DESC(ETHTOOL_MSG_RINGS_GET_REPLY, rings),
+ NLMSG_DESC(ETHTOOL_MSG_RINGS_NTF, rings),
+ NLMSG_DESC(ETHTOOL_MSG_CHANNELS_GET_REPLY, channels),
+ NLMSG_DESC(ETHTOOL_MSG_CHANNELS_NTF, channels),
+ NLMSG_DESC(ETHTOOL_MSG_COALESCE_GET_REPLY, coalesce),
+ NLMSG_DESC(ETHTOOL_MSG_COALESCE_NTF, coalesce),
+ NLMSG_DESC(ETHTOOL_MSG_PAUSE_GET_REPLY, pause),
+ NLMSG_DESC(ETHTOOL_MSG_PAUSE_NTF, pause),
+ NLMSG_DESC(ETHTOOL_MSG_EEE_GET_REPLY, eee),
+ NLMSG_DESC(ETHTOOL_MSG_EEE_NTF, eee),
+ NLMSG_DESC(ETHTOOL_MSG_TSINFO_GET_REPLY, tsinfo),
+ NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_NTF, cable_test_ntf),
+ NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_TDR_NTF, cable_test_tdr_ntf),
+ NLMSG_DESC(ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY, tunnel_info),
};
const unsigned int ethnl_kmsg_n_desc = ARRAY_SIZE(ethnl_kmsg_desc);
diff --git a/netlink/desc-genlctrl.c b/netlink/desc-genlctrl.c
index 9840179..43b41ab 100644
--- a/netlink/desc-genlctrl.c
+++ b/netlink/desc-genlctrl.c
@@ -29,6 +29,59 @@ static const struct pretty_nla_desc __mcgrps_desc[] = {
NLATTR_DESC_NESTED(0, mcgrp),
};
+static const char *__policy_attr_type_names[] = {
+ [NL_ATTR_TYPE_INVALID] = "NL_ATTR_TYPE_INVALID",
+ [NL_ATTR_TYPE_FLAG] = "NL_ATTR_TYPE_FLAG",
+ [NL_ATTR_TYPE_U8] = "NL_ATTR_TYPE_U8",
+ [NL_ATTR_TYPE_U16] = "NL_ATTR_TYPE_U16",
+ [NL_ATTR_TYPE_U32] = "NL_ATTR_TYPE_U32",
+ [NL_ATTR_TYPE_U64] = "NL_ATTR_TYPE_U64",
+ [NL_ATTR_TYPE_S8] = "NL_ATTR_TYPE_S8",
+ [NL_ATTR_TYPE_S16] = "NL_ATTR_TYPE_S16",
+ [NL_ATTR_TYPE_S32] = "NL_ATTR_TYPE_S32",
+ [NL_ATTR_TYPE_S64] = "NL_ATTR_TYPE_S64",
+ [NL_ATTR_TYPE_BINARY] = "NL_ATTR_TYPE_BINARY",
+ [NL_ATTR_TYPE_STRING] = "NL_ATTR_TYPE_STRING",
+ [NL_ATTR_TYPE_NUL_STRING] = "NL_ATTR_TYPE_NUL_STRING",
+ [NL_ATTR_TYPE_NESTED] = "NL_ATTR_TYPE_NESTED",
+ [NL_ATTR_TYPE_NESTED_ARRAY] = "NL_ATTR_TYPE_NESTED_ARRAY",
+ [NL_ATTR_TYPE_BITFIELD32] = "NL_ATTR_TYPE_BITFIELD32",
+};
+
+static const struct pretty_nla_desc __policy_attr_desc[] = {
+ NLATTR_DESC_INVALID(NL_POLICY_TYPE_ATTR_UNSPEC),
+ NLATTR_DESC_U32_ENUM(NL_POLICY_TYPE_ATTR_TYPE, policy_attr_type),
+ NLATTR_DESC_S64(NL_POLICY_TYPE_ATTR_MIN_VALUE_S),
+ NLATTR_DESC_S64(NL_POLICY_TYPE_ATTR_MAX_VALUE_S),
+ NLATTR_DESC_U64(NL_POLICY_TYPE_ATTR_MIN_VALUE_U),
+ NLATTR_DESC_U64(NL_POLICY_TYPE_ATTR_MAX_VALUE_U),
+ NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_MIN_LENGTH),
+ NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_MAX_LENGTH),
+ NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_POLICY_IDX),
+ NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE),
+ NLATTR_DESC_X32(NL_POLICY_TYPE_ATTR_BITFIELD32_MASK),
+ NLATTR_DESC_X64(NL_POLICY_TYPE_ATTR_PAD),
+ NLATTR_DESC_BINARY(NL_POLICY_TYPE_ATTR_MASK),
+};
+
+static const struct pretty_nla_desc __policy_attrs_desc[] = {
+ NLATTR_DESC_NESTED(0, policy_attr),
+};
+
+static const struct pretty_nla_desc __policies_desc[] = {
+ NLATTR_DESC_ARRAY(0, policy_attrs),
+};
+
+static const struct pretty_nla_desc __op_policy_desc[] = {
+ NLATTR_DESC_INVALID(CTRL_ATTR_POLICY_UNSPEC),
+ NLATTR_DESC_U32(CTRL_ATTR_POLICY_DO),
+ NLATTR_DESC_U32(CTRL_ATTR_POLICY_DUMP),
+};
+
+static const struct pretty_nla_desc __op_policies_desc[] = {
+ NLATTR_DESC_NESTED(0, op_policy),
+};
+
static const struct pretty_nla_desc __attr_desc[] = {
NLATTR_DESC_INVALID(CTRL_ATTR_UNSPEC),
NLATTR_DESC_U16(CTRL_ATTR_FAMILY_ID),
@@ -38,6 +91,9 @@ static const struct pretty_nla_desc __attr_desc[] = {
NLATTR_DESC_U32(CTRL_ATTR_MAXATTR),
NLATTR_DESC_ARRAY(CTRL_ATTR_OPS, attrops),
NLATTR_DESC_ARRAY(CTRL_ATTR_MCAST_GROUPS, mcgrps),
+ NLATTR_DESC_ARRAY(CTRL_ATTR_POLICY, policies),
+ NLATTR_DESC_ARRAY(CTRL_ATTR_OP_POLICY, op_policies),
+ NLATTR_DESC_U32(CTRL_ATTR_OP),
};
const struct pretty_nlmsg_desc genlctrl_msg_desc[] = {
@@ -51,6 +107,7 @@ const struct pretty_nlmsg_desc genlctrl_msg_desc[] = {
NLMSG_DESC(CTRL_CMD_NEWMCAST_GRP, attr),
NLMSG_DESC(CTRL_CMD_DELMCAST_GRP, attr),
NLMSG_DESC(CTRL_CMD_GETMCAST_GRP, attr),
+ NLMSG_DESC(CTRL_CMD_GETPOLICY, attr),
};
const unsigned int genlctrl_msg_n_desc = ARRAY_SIZE(genlctrl_msg_desc);
diff --git a/netlink/eee.c b/netlink/eee.c
new file mode 100644
index 0000000..04d8f0b
--- /dev/null
+++ b/netlink/eee.c
@@ -0,0 +1,189 @@
+/*
+ * eee.c - netlink implementation of eee commands
+ *
+ * Implementation of "ethtool --show-eee <dev>" and
+ * "ethtool --set-eee <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* EEE_GET */
+
+int eee_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_EEE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ bool enabled, active, tx_lpi_enabled;
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_EEE_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (!tb[ETHTOOL_A_EEE_MODES_OURS] ||
+ !tb[ETHTOOL_A_EEE_ACTIVE] || !tb[ETHTOOL_A_EEE_ENABLED] ||
+ !tb[ETHTOOL_A_EEE_TX_LPI_ENABLED] ||
+ !tb[ETHTOOL_A_EEE_TX_LPI_TIMER]) {
+ fprintf(stderr, "Malformed response from kernel\n");
+ return err_ret;
+ }
+ active = mnl_attr_get_u8(tb[ETHTOOL_A_EEE_ACTIVE]);
+ enabled = mnl_attr_get_u8(tb[ETHTOOL_A_EEE_ENABLED]);
+ tx_lpi_enabled = mnl_attr_get_u8(tb[ETHTOOL_A_EEE_TX_LPI_ENABLED]);
+
+ if (silent)
+ putchar('\n');
+ printf("EEE settings for %s:\n", nlctx->devname);
+ printf("\tEEE status: ");
+ if (bitset_is_empty(tb[ETHTOOL_A_EEE_MODES_OURS], true, &ret)) {
+ printf("not supported\n");
+ return MNL_CB_OK;
+ }
+ if (!enabled)
+ printf("disabled\n");
+ else
+ printf("enabled - %s\n", active ? "active" : "inactive");
+ printf("\tTx LPI: ");
+ if (tx_lpi_enabled)
+ printf("%u (us)\n",
+ mnl_attr_get_u32(tb[ETHTOOL_A_EEE_TX_LPI_TIMER]));
+ else
+ printf("disabled\n");
+
+ ret = dump_link_modes(nlctx, tb[ETHTOOL_A_EEE_MODES_OURS], true,
+ LM_CLASS_REAL,
+ "Supported EEE link modes: ", NULL, "\n",
+ "Not reported");
+ if (ret < 0)
+ return err_ret;
+ ret = dump_link_modes(nlctx, tb[ETHTOOL_A_EEE_MODES_OURS], false,
+ LM_CLASS_REAL,
+ "Advertised EEE link modes: ", NULL, "\n",
+ "Not reported");
+ if (ret < 0)
+ return err_ret;
+ ret = dump_link_modes(nlctx, tb[ETHTOOL_A_EEE_MODES_PEER], false,
+ LM_CLASS_REAL,
+ "Link partner advertised EEE link modes: ", NULL,
+ "\n", "Not reported");
+ if (ret < 0)
+ return err_ret;
+
+ return MNL_CB_OK;
+}
+
+int nl_geee(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_EEE_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_EEE_GET,
+ ETHTOOL_A_EEE_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, eee_reply_cb);
+}
+
+/* EEE_SET */
+
+static const struct bitset_parser_data advertise_parser_data = {
+ .no_mask = false,
+ .force_hex = true,
+};
+
+static const struct param_parser seee_params[] = {
+ {
+ .arg = "advertise",
+ .type = ETHTOOL_A_EEE_MODES_OURS,
+ .handler = nl_parse_bitset,
+ .handler_data = &advertise_parser_data,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-lpi",
+ .type = ETHTOOL_A_EEE_TX_LPI_ENABLED,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-timer",
+ .type = ETHTOOL_A_EEE_TX_LPI_TIMER,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "eee",
+ .type = ETHTOOL_A_EEE_ENABLED,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_seee(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_EEE_SET, false))
+ return -EOPNOTSUPP;
+ if (!ctx->argc) {
+ fprintf(stderr, "ethtool (--set-eee): parameters missing\n");
+ return 1;
+ }
+
+ nlctx->cmd = "--set-eee";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_EEE_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_EEE_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, seee_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 76;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 76;
+}
diff --git a/netlink/extapi.h b/netlink/extapi.h
index d5b3cd9..761cafb 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -10,27 +10,56 @@
struct cmd_context;
struct nl_context;
+typedef int (*nl_func_t)(struct cmd_context *);
+
#ifdef ETHTOOL_ENABLE_NETLINK
-int netlink_init(struct cmd_context *ctx);
-void netlink_done(struct cmd_context *ctx);
+void netlink_run_handler(struct cmd_context *ctx, nl_func_t nlfunc,
+ bool no_fallback);
int nl_gset(struct cmd_context *ctx);
int nl_sset(struct cmd_context *ctx);
int nl_permaddr(struct cmd_context *ctx);
+int nl_gfeatures(struct cmd_context *ctx);
+int nl_sfeatures(struct cmd_context *ctx);
+int nl_gprivflags(struct cmd_context *ctx);
+int nl_sprivflags(struct cmd_context *ctx);
+int nl_gring(struct cmd_context *ctx);
+int nl_sring(struct cmd_context *ctx);
+int nl_gchannels(struct cmd_context *ctx);
+int nl_schannels(struct cmd_context *ctx);
+int nl_gcoalesce(struct cmd_context *ctx);
+int nl_scoalesce(struct cmd_context *ctx);
+int nl_gpause(struct cmd_context *ctx);
+int nl_spause(struct cmd_context *ctx);
+int nl_geee(struct cmd_context *ctx);
+int nl_seee(struct cmd_context *ctx);
+int nl_tsinfo(struct cmd_context *ctx);
+int nl_cable_test(struct cmd_context *ctx);
+int nl_cable_test_tdr(struct cmd_context *ctx);
+int nl_gtunnels(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void nl_monitor_usage(void);
#else /* ETHTOOL_ENABLE_NETLINK */
-static inline int netlink_init(struct cmd_context *ctx maybe_unused)
+static inline void netlink_run_handler(struct cmd_context *ctx __maybe_unused,
+ nl_func_t nlfunc __maybe_unused,
+ bool no_fallback)
{
- return -EOPNOTSUPP;
+ if (no_fallback) {
+ fprintf(stderr,
+ "Command requires kernel netlink support which is not "
+ "enabled in this ethtool binary\n");
+ exit(1);
+ }
}
-static inline void netlink_done(struct cmd_context *ctx maybe_unused)
+static inline int nl_monitor(struct cmd_context *ctx __maybe_unused)
{
+ fprintf(stderr, "Netlink not supported by ethtool, option --monitor unsupported.\n");
+ return -EOPNOTSUPP;
}
static inline void nl_monitor_usage(void)
@@ -40,6 +69,24 @@ static inline void nl_monitor_usage(void)
#define nl_gset NULL
#define nl_sset NULL
#define nl_permaddr NULL
+#define nl_gfeatures NULL
+#define nl_sfeatures NULL
+#define nl_gprivflags NULL
+#define nl_sprivflags NULL
+#define nl_gring NULL
+#define nl_sring NULL
+#define nl_gchannels NULL
+#define nl_schannels NULL
+#define nl_gcoalesce NULL
+#define nl_scoalesce NULL
+#define nl_gpause NULL
+#define nl_spause NULL
+#define nl_geee NULL
+#define nl_seee NULL
+#define nl_tsinfo NULL
+#define nl_cable_test NULL
+#define nl_cable_test_tdr NULL
+#define nl_gtunnels NULL
#endif /* ETHTOOL_ENABLE_NETLINK */
diff --git a/netlink/features.c b/netlink/features.c
new file mode 100644
index 0000000..2a0899e
--- /dev/null
+++ b/netlink/features.c
@@ -0,0 +1,532 @@
+/*
+ * features.c - netlink implementation of netdev features commands
+ *
+ * Implementation of "ethtool -k <dev>".
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "strset.h"
+#include "bitset.h"
+
+/* FEATURES_GET */
+
+struct feature_results {
+ uint32_t *hw;
+ uint32_t *wanted;
+ uint32_t *active;
+ uint32_t *nochange;
+ unsigned int count;
+ unsigned int words;
+};
+
+static int prepare_feature_results(const struct nlattr *const *tb,
+ struct feature_results *dest)
+{
+ unsigned int count;
+ int ret;
+
+ memset(dest, '\0', sizeof(*dest));
+ if (!tb[ETHTOOL_A_FEATURES_HW] || !tb[ETHTOOL_A_FEATURES_WANTED] ||
+ !tb[ETHTOOL_A_FEATURES_ACTIVE] || !tb[ETHTOOL_A_FEATURES_NOCHANGE])
+ return -EFAULT;
+ count = bitset_get_count(tb[ETHTOOL_A_FEATURES_HW], &ret);
+ if (ret < 0)
+ return -EFAULT;
+ if ((bitset_get_count(tb[ETHTOOL_A_FEATURES_WANTED], &ret) != count) ||
+ (bitset_get_count(tb[ETHTOOL_A_FEATURES_ACTIVE], &ret) != count) ||
+ (bitset_get_count(tb[ETHTOOL_A_FEATURES_NOCHANGE], &ret) != count))
+ return -EFAULT;
+ dest->hw = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_HW]);
+ dest->wanted = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_WANTED]);
+ dest->active = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_ACTIVE]);
+ dest->nochange =
+ get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_NOCHANGE]);
+ if (!dest->hw || !dest->wanted || !dest->active || !dest->nochange)
+ return -EFAULT;
+ dest->count = count;
+ dest->words = (count + 31) / 32;
+
+ return 0;
+}
+
+static bool feature_on(const uint32_t *bitmap, unsigned int idx)
+{
+ return bitmap[idx / 32] & (1 << (idx % 32));
+}
+
+static void dump_feature(const struct feature_results *results,
+ const uint32_t *ref, const uint32_t *ref_mask,
+ unsigned int idx, const char *name, const char *prefix)
+{
+ const char *suffix = "";
+
+ if (!name || !*name)
+ return;
+ if (ref) {
+ if (ref_mask && !feature_on(ref_mask, idx))
+ return;
+ if ((!ref_mask || feature_on(ref_mask, idx)) &&
+ (feature_on(results->active, idx) == feature_on(ref, idx)))
+ return;
+ }
+
+ if (!feature_on(results->hw, idx) || feature_on(results->nochange, idx))
+ suffix = " [fixed]";
+ else if (feature_on(results->active, idx) !=
+ feature_on(results->wanted, idx))
+ suffix = feature_on(results->wanted, idx) ?
+ " [requested on]" : " [requested off]";
+ printf("%s%s: %s%s\n", prefix, name,
+ feature_on(results->active, idx) ? "on" : "off", suffix);
+}
+
+/* this assumes pattern contains no more than one asterisk */
+static bool flag_pattern_match(const char *name, const char *pattern)
+{
+ const char *p_ast = strchr(pattern, '*');
+
+ if (p_ast) {
+ size_t name_len = strlen(name);
+ size_t pattern_len = strlen(pattern);
+
+ if (name_len + 1 < pattern_len)
+ return false;
+ if (strncmp(name, pattern, p_ast - pattern))
+ return false;
+ pattern_len -= (p_ast - pattern) + 1;
+ name += name_len - pattern_len;
+ pattern = p_ast + 1;
+ }
+ return !strcmp(name, pattern);
+}
+
+int dump_features(const struct nlattr *const *tb,
+ const struct stringset *feature_names)
+{
+ unsigned int *feature_flags = NULL;
+ struct feature_results results;
+ unsigned int i, j;
+ int ret;
+
+ ret = prepare_feature_results(tb, &results);
+ if (ret < 0)
+ return -EFAULT;
+ feature_flags = calloc(results.count, sizeof(feature_flags[0]));
+ if (!feature_flags)
+ return -ENOMEM;
+
+ /* map netdev features to legacy flags */
+ for (i = 0; i < results.count; i++) {
+ const char *name = get_string(feature_names, i);
+ feature_flags[i] = UINT_MAX;
+
+ if (!name || !*name)
+ continue;
+ for (j = 0; j < OFF_FLAG_DEF_SIZE; j++) {
+ const char *flag_name = off_flag_def[j].kernel_name;
+
+ if (flag_pattern_match(name, flag_name)) {
+ feature_flags[i] = j;
+ break;
+ }
+ }
+ }
+ /* show legacy flags and their matching features first */
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+ unsigned int n_match = 0;
+ bool flag_value = false;
+
+ /* no kernel with netlink interface supports UFO */
+ if (off_flag_def[i].value == ETH_FLAG_UFO)
+ continue;
+
+ for (j = 0; j < results.count; j++) {
+ if (feature_flags[j] == i) {
+ n_match++;
+ flag_value = flag_value ||
+ feature_on(results.active, j);
+ }
+ }
+ if (n_match != 1)
+ printf("%s: %s\n", off_flag_def[i].long_name,
+ flag_value ? "on" : "off");
+ if (n_match == 0)
+ continue;
+ for (j = 0; j < results.count; j++) {
+ const char *name = get_string(feature_names, j);
+
+ if (feature_flags[j] != i)
+ continue;
+ if (n_match == 1)
+ dump_feature(&results, NULL, NULL, j,
+ off_flag_def[i].long_name, "");
+ else
+ dump_feature(&results, NULL, NULL, j, name,
+ "\t");
+ }
+ }
+ /* and, finally, remaining netdev_features not matching legacy flags */
+ for (i = 0; i < results.count; i++) {
+ const char *name = get_string(feature_names, i);
+
+ if (!name || !*name || feature_flags[i] != UINT_MAX)
+ continue;
+ dump_feature(&results, NULL, NULL, i, name, "");
+ }
+
+ free(feature_flags);
+ return 0;
+}
+
+int features_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const struct stringset *feature_names;
+ struct nl_context *nlctx = data;
+ bool silent;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ if (!nlctx->is_monitor) {
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+ }
+ feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl2_socket);
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return silent ? MNL_CB_OK : MNL_CB_ERROR;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_FEATURES_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (silent)
+ putchar('\n');
+ printf("Features for %s:\n", nlctx->devname);
+ ret = dump_features(tb, feature_names);
+ return (silent || !ret) ? MNL_CB_OK : MNL_CB_ERROR;
+}
+
+int nl_gfeatures(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEATURES_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_FEATURES_GET,
+ ETHTOOL_A_FEATURES_HEADER,
+ ETHTOOL_FLAG_COMPACT_BITSETS);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, features_reply_cb);
+}
+
+/* FEATURES_SET */
+
+struct sfeatures_context {
+ bool nothing_changed;
+ uint32_t req_mask[0];
+};
+
+static int find_feature(const char *name,
+ const struct stringset *feature_names)
+{
+ const unsigned int count = get_count(feature_names);
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ if (!strcmp(name, get_string(feature_names, i)))
+ return i;
+
+ return -1;
+}
+
+static int fill_feature(struct nl_msg_buff *msgbuff, const char *name, bool val)
+{
+ struct nlattr *bit_attr;
+
+ bit_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS_BIT);
+ if (!bit_attr)
+ return -EMSGSIZE;
+ if (ethnla_put_strz(msgbuff, ETHTOOL_A_BITSET_BIT_NAME, name))
+ return -EMSGSIZE;
+ if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_BIT_VALUE, val))
+ return -EMSGSIZE;
+ mnl_attr_nest_end(msgbuff->nlhdr, bit_attr);
+
+ return 0;
+}
+
+static void set_sf_req_mask(struct nl_context *nlctx, unsigned int idx)
+{
+ struct sfeatures_context *sfctx = nlctx->cmd_private;
+
+ sfctx->req_mask[idx / 32] |= (1 << (idx % 32));
+}
+
+static int fill_legacy_flag(struct nl_context *nlctx, const char *flag_name,
+ const struct stringset *feature_names, bool val)
+{
+ struct nl_msg_buff *msgbuff = &nlctx->ethnl_socket->msgbuff;
+ const unsigned int count = get_count(feature_names);
+ unsigned int i, j;
+ int ret;
+
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+ const char *pattern;
+
+ if (strcmp(flag_name, off_flag_def[i].short_name) &&
+ strcmp(flag_name, off_flag_def[i].long_name))
+ continue;
+ pattern = off_flag_def[i].kernel_name;
+
+ for (j = 0; j < count; j++) {
+ const char *name = get_string(feature_names, j);
+
+ if (flag_pattern_match(name, pattern)) {
+ ret = fill_feature(msgbuff, name, val);
+ if (ret < 0)
+ return ret;
+ set_sf_req_mask(nlctx, j);
+ }
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+int fill_sfeatures_bitmap(struct nl_context *nlctx,
+ const struct stringset *feature_names)
+{
+ struct nl_msg_buff *msgbuff = &nlctx->ethnl_socket->msgbuff;
+ struct nlattr *bitset_attr;
+ struct nlattr *bits_attr;
+ int ret;
+
+ ret = -EMSGSIZE;
+ bitset_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_FEATURES_WANTED);
+ if (!bitset_attr)
+ return ret;
+ bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS);
+ if (!bits_attr)
+ goto err;
+
+ while (nlctx->argc > 0) {
+ bool val;
+
+ if (!strcmp(*nlctx->argp, "--")) {
+ nlctx->argp++;
+ nlctx->argc--;
+ break;
+ }
+ ret = -EINVAL;
+ if (nlctx->argc < 2 ||
+ (strcmp(nlctx->argp[1], "on") &&
+ strcmp(nlctx->argp[1], "off"))) {
+ fprintf(stderr,
+ "ethtool (%s): flag '%s' for parameter '%s' is"
+ " not followed by 'on' or 'off'\n",
+ nlctx->cmd, nlctx->argp[1], nlctx->param);
+ goto err;
+ }
+
+ val = !strcmp(nlctx->argp[1], "on");
+ ret = fill_legacy_flag(nlctx, nlctx->argp[0], feature_names,
+ val);
+ if (ret > 0) {
+ ret = fill_feature(msgbuff, nlctx->argp[0], val);
+ if (ret == 0) {
+ int idx = find_feature(nlctx->argp[0],
+ feature_names);
+
+ if (idx >= 0)
+ set_sf_req_mask(nlctx, idx);
+ }
+ }
+ if (ret < 0)
+ goto err;
+
+ nlctx->argp += 2;
+ nlctx->argc -= 2;
+ }
+
+ ethnla_nest_end(msgbuff, bits_attr);
+ ethnla_nest_end(msgbuff, bitset_attr);
+ return 0;
+err:
+ ethnla_nest_cancel(msgbuff, bitset_attr);
+ return ret;
+}
+
+static void show_feature_changes(struct nl_context *nlctx,
+ const struct nlattr *const *tb)
+{
+ struct sfeatures_context *sfctx = nlctx->cmd_private;
+ const struct stringset *feature_names;
+ const uint32_t *wanted_mask;
+ const uint32_t *active_mask;
+ const uint32_t *wanted_val;
+ const uint32_t *active_val;
+ unsigned int count, words;
+ unsigned int i;
+ bool diff;
+ int ret;
+
+ feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl_socket);
+ count = get_count(feature_names);
+ words = DIV_ROUND_UP(count, 32);
+
+ if (!tb[ETHTOOL_A_FEATURES_WANTED] || !tb[ETHTOOL_A_FEATURES_ACTIVE])
+ goto err;
+ if (bitset_get_count(tb[ETHTOOL_A_FEATURES_WANTED], &ret) != count ||
+ ret < 0)
+ goto err;
+ if (bitset_get_count(tb[ETHTOOL_A_FEATURES_ACTIVE], &ret) != count ||
+ ret < 0)
+ goto err;
+ wanted_val = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_WANTED]);
+ wanted_mask = get_compact_bitset_mask(tb[ETHTOOL_A_FEATURES_WANTED]);
+ active_val = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_ACTIVE]);
+ active_mask = get_compact_bitset_mask(tb[ETHTOOL_A_FEATURES_ACTIVE]);
+ if (!wanted_val || !wanted_mask || !active_val || !active_mask)
+ goto err;
+
+ sfctx->nothing_changed = true;
+ diff = false;
+ for (i = 0; i < words; i++) {
+ if (wanted_mask[i] != sfctx->req_mask[i])
+ sfctx->nothing_changed = false;
+ if (wanted_mask[i] || (active_mask[i] & ~sfctx->req_mask[i]))
+ diff = true;
+ }
+ if (!diff)
+ return;
+
+ /* result is not exactly as requested, show differences */
+ printf("Actual changes:\n");
+ for (i = 0; i < count; i++) {
+ const char *name = get_string(feature_names, i);
+
+ if (!name)
+ continue;
+ if (!feature_on(wanted_mask, i) && !feature_on(active_mask, i))
+ continue;
+ printf("%s: ", name);
+ if (feature_on(wanted_mask, i))
+ /* we requested a value but result is different */
+ printf("%s [requested %s]",
+ feature_on(wanted_val, i) ? "off" : "on",
+ feature_on(wanted_val, i) ? "on" : "off");
+ else if (!feature_on(sfctx->req_mask, i))
+ /* not requested but changed anyway */
+ printf("%s [not requested]",
+ feature_on(active_val, i) ? "on" : "off");
+ else
+ printf("%s", feature_on(active_val, i) ? "on" : "off");
+ fputc('\n', stdout);
+ }
+
+ return;
+err:
+ fprintf(stderr, "malformed diff info from kernel\n");
+}
+
+int sfeatures_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+ const struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ const char *devname;
+ int ret;
+
+ if (ghdr->cmd != ETHTOOL_MSG_FEATURES_SET_REPLY) {
+ fprintf(stderr, "warning: unexpected reply message type %u\n",
+ ghdr->cmd);
+ return MNL_CB_OK;
+ }
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ devname = get_dev_name(tb[ETHTOOL_A_FEATURES_HEADER]);
+ if (strcmp(devname, nlctx->devname)) {
+ fprintf(stderr, "warning: unexpected message for device %s\n",
+ devname);
+ return MNL_CB_OK;
+ }
+
+ show_feature_changes(nlctx, tb);
+ return MNL_CB_OK;
+}
+
+int nl_sfeatures(struct cmd_context *ctx)
+{
+ const struct stringset *feature_names;
+ struct nl_context *nlctx = ctx->nlctx;
+ struct sfeatures_context *sfctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ unsigned int words;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEATURES_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-K";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->cmd_private = &sfctx;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl_socket);
+ words = (get_count(feature_names) + 31) / 32;
+ sfctx = malloc(sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0]));
+ if (!sfctx)
+ return -ENOMEM;
+ memset(sfctx, '\0',
+ sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0]));
+ nlctx->cmd_private = sfctx;
+
+ nlctx->devname = ctx->devname;
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_FEATURES_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_FEATURES_HEADER, ctx->devname,
+ ETHTOOL_FLAG_COMPACT_BITSETS))
+ return -EMSGSIZE;
+ ret = fill_sfeatures_bitmap(nlctx, feature_names);
+ if (ret < 0)
+ return ret;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 92;
+ ret = nlsock_process_reply(nlsk, sfeatures_reply_cb, nlctx);
+ if (sfctx->nothing_changed) {
+ fprintf(stderr, "Could not change any device features\n");
+ return nlctx->exit_code ?: 1;
+ }
+ if (ret == 0)
+ return 0;
+ return nlctx->exit_code ?: 92;
+}
diff --git a/netlink/monitor.c b/netlink/monitor.c
index 5fce6b6..19f991f 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -31,6 +31,42 @@ static struct {
.cmd = ETHTOOL_MSG_DEBUG_NTF,
.cb = debug_reply_cb,
},
+ {
+ .cmd = ETHTOOL_MSG_FEATURES_NTF,
+ .cb = features_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_PRIVFLAGS_NTF,
+ .cb = privflags_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_RINGS_NTF,
+ .cb = rings_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_CHANNELS_NTF,
+ .cb = channels_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_COALESCE_NTF,
+ .cb = coalesce_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_PAUSE_NTF,
+ .cb = pause_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_EEE_NTF,
+ .cb = eee_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_CABLE_TEST_NTF,
+ .cb = cable_test_ntf_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_CABLE_TEST_TDR_NTF,
+ .cb = cable_test_tdr_ntf_cb,
+ },
};
static void clear_filter(struct nl_context *nlctx)
@@ -102,6 +138,42 @@ static struct monitor_option monitor_opts[] = {
.pattern = "-s|--change",
.cmd = ETHTOOL_MSG_DEBUG_NTF,
},
+ {
+ .pattern = "-k|--show-features|--show-offload|-K|--features|--offload",
+ .cmd = ETHTOOL_MSG_FEATURES_NTF,
+ },
+ {
+ .pattern = "--show-priv-flags|--set-priv-flags",
+ .cmd = ETHTOOL_MSG_PRIVFLAGS_NTF,
+ },
+ {
+ .pattern = "-g|--show-ring|-G|--set-ring",
+ .cmd = ETHTOOL_MSG_RINGS_NTF,
+ },
+ {
+ .pattern = "-l|--show-channels|-L|--set-channels",
+ .cmd = ETHTOOL_MSG_CHANNELS_NTF,
+ },
+ {
+ .pattern = "-c|--show-coalesce|-C|--coalesce",
+ .cmd = ETHTOOL_MSG_COALESCE_NTF,
+ },
+ {
+ .pattern = "-a|--show-pause|-A|--pause",
+ .cmd = ETHTOOL_MSG_PAUSE_NTF,
+ },
+ {
+ .pattern = "--show-eee|--set-eee",
+ .cmd = ETHTOOL_MSG_EEE_NTF,
+ },
+ {
+ .pattern = "--cable-test",
+ .cmd = ETHTOOL_MSG_CABLE_TEST_NTF,
+ },
+ {
+ .pattern = "--cable-test-tdr",
+ .cmd = ETHTOOL_MSG_CABLE_TEST_TDR_NTF,
+ },
};
static bool pattern_match(const char *s, const char *pattern)
@@ -167,16 +239,25 @@ static int parse_monitor(struct cmd_context *ctx)
int nl_monitor(struct cmd_context *ctx)
{
- struct nl_context *nlctx = ctx->nlctx;
- struct nl_socket *nlsk = nlctx->ethnl_socket;
- uint32_t grpid = nlctx->ethnl_mongrp;
+ struct nl_context *nlctx;
+ struct nl_socket *nlsk;
+ uint32_t grpid;
bool is_dev;
int ret;
+ ret = netlink_init(ctx);
+ if (ret < 0) {
+ fprintf(stderr, "Netlink interface initialization failed, option --monitor not supported.\n");
+ return ret;
+ }
+ nlctx = ctx->nlctx;
+ nlsk = nlctx->ethnl_socket;
+ grpid = nlctx->ethnl_mongrp;
if (!grpid) {
fprintf(stderr, "multicast group 'monitor' not found\n");
return -EOPNOTSUPP;
}
+
if (parse_monitor(ctx) < 0)
return 1;
is_dev = ctx->devname && strcmp(ctx->devname, WILDCARD_DEVNAME);
diff --git a/netlink/msgbuff.c b/netlink/msgbuff.c
index 7406570..216f5b9 100644
--- a/netlink/msgbuff.c
+++ b/netlink/msgbuff.c
@@ -79,6 +79,7 @@ int msgbuff_append(struct nl_msg_buff *dest, struct nl_msg_buff *src)
unsigned int dest_len = MNL_ALIGN(msgbuff_len(dest));
int ret;
+ src_len -= GENL_HDRLEN;
ret = msgbuff_realloc(dest, dest_len + src_len);
if (ret < 0)
return ret;
diff --git a/netlink/msgbuff.h b/netlink/msgbuff.h
index 24b99c5..7d6731f 100644
--- a/netlink/msgbuff.h
+++ b/netlink/msgbuff.h
@@ -81,6 +81,12 @@ static inline bool ethnla_put_u32(struct nl_msg_buff *msgbuff, uint16_t type,
return ethnla_put(msgbuff, type, sizeof(uint32_t), &data);
}
+static inline bool ethnla_put_u16(struct nl_msg_buff *msgbuff, uint16_t type,
+ uint16_t data)
+{
+ return ethnla_put(msgbuff, type, sizeof(uint16_t), &data);
+}
+
static inline bool ethnla_put_u8(struct nl_msg_buff *msgbuff, uint16_t type,
uint8_t data)
{
diff --git a/netlink/netlink.c b/netlink/netlink.c
index 39f4063..ffe0633 100644
--- a/netlink/netlink.c
+++ b/netlink/netlink.c
@@ -16,7 +16,7 @@
/* Used as reply callback for requests where no reply is expected (e.g. most
* "set" type commands)
*/
-int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data __maybe_unused)
{
const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
@@ -33,9 +33,9 @@ int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data)
int attr_cb(const struct nlattr *attr, void *data)
{
const struct attr_tb_info *tb_info = data;
- int type = mnl_attr_get_type(attr);
+ uint16_t type = mnl_attr_get_type(attr);
- if (type >= 0 && type <= tb_info->max_type)
+ if (type <= tb_info->max_type)
tb_info->tb[type] = attr;
return MNL_CB_OK;
@@ -92,16 +92,243 @@ int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname)
return 0;
}
-/* initialization */
+/**
+ * netlink_cmd_check() - check support for netlink command
+ * @ctx: ethtool command context
+ * @cmd: netlink command id
+ * @devname: device name from user
+ * @allow_wildcard: wildcard dumps supported
+ *
+ * Check if command @cmd is known to be unsupported based on ops information
+ * from genetlink family id request. Set nlctx->ioctl_fallback if ethtool
+ * should fall back to ioctl, i.e. when we do not know in advance that
+ * netlink request is supported. Set nlctx->wildcard_unsupported if "*" was
+ * used as device name but the request does not support wildcards (on either
+ * side).
+ *
+ * Return: true if we know the netlink request is not supported and should
+ * fail (and possibly fall back) without actually sending it to kernel.
+ */
+bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd,
+ bool allow_wildcard)
+{
+ bool is_dump = !strcmp(ctx->devname, WILDCARD_DEVNAME);
+ uint32_t cap = is_dump ? GENL_CMD_CAP_DUMP : GENL_CMD_CAP_DO;
+ struct nl_context *nlctx = ctx->nlctx;
-struct fam_info {
- const char *fam_name;
- const char *grp_name;
- uint16_t fam_id;
- uint32_t grp_id;
+ if (is_dump && !allow_wildcard) {
+ nlctx->wildcard_unsupported = true;
+ return true;
+ }
+ if (!nlctx->ops_info) {
+ nlctx->ioctl_fallback = true;
+ return false;
+ }
+ if (cmd > ETHTOOL_MSG_USER_MAX || !nlctx->ops_info[cmd].op_flags) {
+ nlctx->ioctl_fallback = true;
+ return true;
+ }
+
+ if (is_dump && !(nlctx->ops_info[cmd].op_flags & GENL_CMD_CAP_DUMP))
+ nlctx->wildcard_unsupported = true;
+
+ return !(nlctx->ops_info[cmd].op_flags & cap);
+}
+
+struct ethtool_op_policy_query_ctx {
+ struct nl_context *nlctx;
+ unsigned int op;
+ unsigned int op_hdr_attr;
+
+ bool op_policy_found;
+ bool hdr_policy_found;
+ unsigned int op_policy_idx;
+ unsigned int hdr_policy_idx;
+ uint64_t flag_mask;
};
-static void find_mc_group(struct nlattr *nest, struct fam_info *info)
+static int family_policy_find_op(struct ethtool_op_policy_query_ctx *policy_ctx,
+ const struct nlattr *op_policy)
+{
+ const struct nlattr *attr;
+ unsigned int type;
+ int ret;
+
+ type = policy_ctx->nlctx->is_dump ?
+ CTRL_ATTR_POLICY_DUMP : CTRL_ATTR_POLICY_DO;
+
+ mnl_attr_for_each_nested(attr, op_policy) {
+ const struct nlattr *tb[CTRL_ATTR_POLICY_DUMP_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+
+ if (mnl_attr_get_type(attr) != policy_ctx->op)
+ continue;
+
+ ret = mnl_attr_parse_nested(attr, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ if (!tb[type])
+ continue;
+
+ policy_ctx->op_policy_found = true;
+ policy_ctx->op_policy_idx = mnl_attr_get_u32(tb[type]);
+ break;
+ }
+
+ return 0;
+}
+
+static int family_policy_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tba[NL_POLICY_TYPE_ATTR_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tba);
+ const struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct ethtool_op_policy_query_ctx *policy_ctx = data;
+ const struct nlattr *policy_attr, *attr_attr, *attr;
+ unsigned int attr_idx, policy_idx;
+ int ret;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+
+ if (!policy_ctx->op_policy_found) {
+ if (!tb[CTRL_ATTR_OP_POLICY]) {
+ fprintf(stderr, "Error: op policy map not present\n");
+ return MNL_CB_ERROR;
+ }
+ ret = family_policy_find_op(policy_ctx, tb[CTRL_ATTR_OP_POLICY]);
+ return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK;
+ }
+
+ if (!tb[CTRL_ATTR_POLICY])
+ return MNL_CB_OK;
+
+ policy_attr = mnl_attr_get_payload(tb[CTRL_ATTR_POLICY]);
+ policy_idx = mnl_attr_get_type(policy_attr);
+ attr_attr = mnl_attr_get_payload(policy_attr);
+ attr_idx = mnl_attr_get_type(attr_attr);
+
+ ret = mnl_attr_parse_nested(attr_attr, attr_cb, &tba_info);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+
+ if (policy_idx == policy_ctx->op_policy_idx &&
+ attr_idx == policy_ctx->op_hdr_attr) {
+ attr = tba[NL_POLICY_TYPE_ATTR_POLICY_IDX];
+ if (!attr) {
+ fprintf(stderr, "Error: no policy index in what was expected to be ethtool header attribute\n");
+ return MNL_CB_ERROR;
+ }
+ policy_ctx->hdr_policy_found = true;
+ policy_ctx->hdr_policy_idx = mnl_attr_get_u32(attr);
+ }
+
+ if (policy_ctx->hdr_policy_found &&
+ policy_ctx->hdr_policy_idx == policy_idx &&
+ attr_idx == ETHTOOL_A_HEADER_FLAGS) {
+ attr = tba[NL_POLICY_TYPE_ATTR_MASK];
+ if (!attr) {
+ fprintf(stderr, "Error: validation mask not reported for ethtool header flags\n");
+ return MNL_CB_ERROR;
+ }
+
+ policy_ctx->flag_mask = mnl_attr_get_u64(attr);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int read_flags_policy(struct nl_context *nlctx, struct nl_socket *nlsk,
+ unsigned int nlcmd, unsigned int hdrattr)
+{
+ struct ethtool_op_policy_query_ctx policy_ctx;
+ struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+ int ret;
+
+ if (nlctx->ops_info[nlcmd].hdr_policy_loaded)
+ return 0;
+
+ memset(&policy_ctx, 0, sizeof(policy_ctx));
+ policy_ctx.nlctx = nlctx;
+ policy_ctx.op = nlcmd;
+ policy_ctx.op_hdr_attr = hdrattr;
+
+ ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETPOLICY,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, 1);
+ if (ret < 0)
+ return ret;
+ ret = -EMSGSIZE;
+ if (ethnla_put_u16(msgbuff, CTRL_ATTR_FAMILY_ID, nlctx->ethnl_fam))
+ return ret;
+ if (ethnla_put_u32(msgbuff, CTRL_ATTR_OP, nlcmd))
+ return ret;
+
+ nlsock_sendmsg(nlsk, NULL);
+ nlsock_process_reply(nlsk, family_policy_cb, &policy_ctx);
+
+ nlctx->ops_info[nlcmd].hdr_policy_loaded = 1;
+ nlctx->ops_info[nlcmd].hdr_flags = policy_ctx.flag_mask;
+ return 0;
+}
+
+u32 get_stats_flag(struct nl_context *nlctx, unsigned int nlcmd,
+ unsigned int hdrattr)
+{
+ if (!nlctx->ctx->show_stats)
+ return 0;
+ if (nlcmd > ETHTOOL_MSG_USER_MAX ||
+ !(nlctx->ops_info[nlcmd].op_flags & GENL_CMD_CAP_HASPOL))
+ return 0;
+
+ if (read_flags_policy(nlctx, nlctx->ethnl_socket, nlcmd, hdrattr) < 0)
+ return 0;
+
+ return nlctx->ops_info[nlcmd].hdr_flags & ETHTOOL_FLAG_STATS;
+}
+
+/* initialization */
+
+static int genl_read_ops(struct nl_context *nlctx,
+ const struct nlattr *ops_attr)
+{
+ struct nl_op_info *ops_info;
+ struct nlattr *op_attr;
+ int ret;
+
+ ops_info = calloc(__ETHTOOL_MSG_USER_CNT, sizeof(ops_info[0]));
+ if (!ops_info)
+ return -ENOMEM;
+
+ mnl_attr_for_each_nested(op_attr, ops_attr) {
+ const struct nlattr *tb[CTRL_ATTR_OP_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ uint32_t op_id;
+
+ ret = mnl_attr_parse_nested(op_attr, attr_cb, &tb_info);
+ if (ret < 0)
+ goto err;
+
+ if (!tb[CTRL_ATTR_OP_ID] || !tb[CTRL_ATTR_OP_FLAGS])
+ continue;
+ op_id = mnl_attr_get_u32(tb[CTRL_ATTR_OP_ID]);
+ if (op_id >= __ETHTOOL_MSG_USER_CNT)
+ continue;
+
+ ops_info[op_id].op_flags =
+ mnl_attr_get_u32(tb[CTRL_ATTR_OP_FLAGS]);
+ }
+
+ nlctx->ops_info = ops_info;
+ return 0;
+err:
+ free(ops_info);
+ return ret;
+}
+
+static void find_mc_group(struct nl_context *nlctx, struct nlattr *nest)
{
const struct nlattr *grp_tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {};
DECLARE_ATTR_TB_INFO(grp_tb);
@@ -116,9 +343,9 @@ static void find_mc_group(struct nlattr *nest, struct fam_info *info)
!grp_tb[CTRL_ATTR_MCAST_GRP_ID])
continue;
if (strcmp(mnl_attr_get_str(grp_tb[CTRL_ATTR_MCAST_GRP_NAME]),
- info->grp_name))
+ ETHTOOL_MCGRP_MONITOR_NAME))
continue;
- info->grp_id =
+ nlctx->ethnl_mongrp =
mnl_attr_get_u32(grp_tb[CTRL_ATTR_MCAST_GRP_ID]);
return;
}
@@ -126,16 +353,22 @@ static void find_mc_group(struct nlattr *nest, struct fam_info *info)
static int family_info_cb(const struct nlmsghdr *nlhdr, void *data)
{
- struct fam_info *info = data;
+ struct nl_context *nlctx = data;
struct nlattr *attr;
+ int ret;
mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) {
switch (mnl_attr_get_type(attr)) {
case CTRL_ATTR_FAMILY_ID:
- info->fam_id = mnl_attr_get_u16(attr);
+ nlctx->ethnl_fam = mnl_attr_get_u16(attr);
+ break;
+ case CTRL_ATTR_OPS:
+ ret = genl_read_ops(nlctx, attr);
+ if (ret < 0)
+ return MNL_CB_ERROR;
break;
case CTRL_ATTR_MCAST_GROUPS:
- find_mc_group(attr, info);
+ find_mc_group(nlctx, attr);
break;
}
}
@@ -144,41 +377,37 @@ static int family_info_cb(const struct nlmsghdr *nlhdr, void *data)
}
#ifdef TEST_ETHTOOL
-static int get_genl_family(struct nl_socket *nlsk, struct fam_info *info)
+static int get_genl_family(struct nl_context *nlctx, struct nl_socket *nlsk)
{
return 0;
}
#else
-static int get_genl_family(struct nl_socket *nlsk, struct fam_info *info)
+static int get_genl_family(struct nl_context *nlctx, struct nl_socket *nlsk)
{
struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
int ret;
- nlsk->nlctx->suppress_nlerr = 2;
+ nlctx->suppress_nlerr = 2;
ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETFAMILY,
NLM_F_REQUEST | NLM_F_ACK, 1);
if (ret < 0)
goto out;
ret = -EMSGSIZE;
- if (ethnla_put_strz(msgbuff, CTRL_ATTR_FAMILY_NAME, info->fam_name))
+ if (ethnla_put_strz(msgbuff, CTRL_ATTR_FAMILY_NAME, ETHTOOL_GENL_NAME))
goto out;
nlsock_sendmsg(nlsk, NULL);
- nlsock_process_reply(nlsk, family_info_cb, info);
- ret = info->fam_id ? 0 : -EADDRNOTAVAIL;
+ nlsock_process_reply(nlsk, family_info_cb, nlctx);
+ ret = nlctx->ethnl_fam ? 0 : -EADDRNOTAVAIL;
out:
- nlsk->nlctx->suppress_nlerr = 0;
+ nlctx->suppress_nlerr = 0;
return ret;
}
#endif
int netlink_init(struct cmd_context *ctx)
{
- struct fam_info info = {
- .fam_name = ETHTOOL_GENL_NAME,
- .grp_name = ETHTOOL_MCGRP_MONITOR_NAME,
- };
struct nl_context *nlctx;
int ret;
@@ -189,11 +418,9 @@ int netlink_init(struct cmd_context *ctx)
ret = nlsock_init(nlctx, &nlctx->ethnl_socket, NETLINK_GENERIC);
if (ret < 0)
goto out_free;
- ret = get_genl_family(nlctx->ethnl_socket, &info);
+ ret = get_genl_family(nlctx, nlctx->ethnl_socket);
if (ret < 0)
goto out_nlsk;
- nlctx->ethnl_fam = info.fam_id;
- nlctx->ethnl_mongrp = info.grp_id;
ctx->nlctx = nlctx;
return 0;
@@ -201,16 +428,93 @@ int netlink_init(struct cmd_context *ctx)
out_nlsk:
nlsock_done(nlctx->ethnl_socket);
out_free:
+ free(nlctx->ops_info);
free(nlctx);
return ret;
}
-void netlink_done(struct cmd_context *ctx)
+static void netlink_done(struct cmd_context *ctx)
{
- if (!ctx->nlctx)
+ struct nl_context *nlctx = ctx->nlctx;
+
+ if (!nlctx)
return;
- free(ctx->nlctx);
+ nlsock_done(nlctx->ethnl_socket);
+ nlsock_done(nlctx->ethnl2_socket);
+ nlsock_done(nlctx->rtnl_socket);
+ free(nlctx->ops_info);
+ free(nlctx);
ctx->nlctx = NULL;
cleanup_all_strings();
}
+
+/**
+ * netlink_run_handler() - run netlink handler for subcommand
+ * @ctx: command context
+ * @nlfunc: subcommand netlink handler to call
+ * @no_fallback: there is no ioctl fallback handler
+ *
+ * This function returns only if ioctl() handler should be run as fallback.
+ * Otherwise it exits with appropriate return code.
+ */
+void netlink_run_handler(struct cmd_context *ctx, nl_func_t nlfunc,
+ bool no_fallback)
+{
+ bool wildcard = ctx->devname && !strcmp(ctx->devname, WILDCARD_DEVNAME);
+ bool wildcard_unsupported, ioctl_fallback;
+ struct nl_context *nlctx;
+ const char *reason;
+ int ret;
+
+ if (ctx->devname && strlen(ctx->devname) >= ALTIFNAMSIZ) {
+ fprintf(stderr, "device name '%s' longer than %u characters\n",
+ ctx->devname, ALTIFNAMSIZ - 1);
+ exit(1);
+ }
+
+ if (!nlfunc) {
+ reason = "ethtool netlink support for subcommand missing";
+ goto no_support;
+ }
+ if (netlink_init(ctx)) {
+ reason = "netlink interface initialization failed";
+ goto no_support;
+ }
+ nlctx = ctx->nlctx;
+
+ ret = nlfunc(ctx);
+ wildcard_unsupported = nlctx->wildcard_unsupported;
+ ioctl_fallback = nlctx->ioctl_fallback;
+ netlink_done(ctx);
+
+ if (no_fallback || ret != -EOPNOTSUPP || !ioctl_fallback) {
+ if (wildcard_unsupported)
+ fprintf(stderr, "%s\n",
+ "subcommand does not support wildcard dump");
+ exit(ret >= 0 ? ret : 1);
+ }
+ if (wildcard_unsupported)
+ reason = "subcommand does not support wildcard dump";
+ else
+ reason = "kernel netlink support for subcommand missing";
+
+no_support:
+ if (no_fallback) {
+ fprintf(stderr, "%s, subcommand not supported by ioctl\n",
+ reason);
+ exit(1);
+ }
+ if (wildcard) {
+ fprintf(stderr, "%s, wildcard dump not supported\n", reason);
+ exit(1);
+ }
+ if (ctx->devname && strlen(ctx->devname) >= IFNAMSIZ) {
+ fprintf(stderr,
+ "%s, device name longer than %u not supported\n",
+ reason, IFNAMSIZ - 1);
+ exit(1);
+ }
+
+ /* fallback to ioctl() */
+}
diff --git a/netlink/netlink.h b/netlink/netlink.h
index db078d2..c025585 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -16,6 +16,21 @@
#define WILDCARD_DEVNAME "*"
#define CMDMASK_WORDS DIV_ROUND_UP(__ETHTOOL_MSG_KERNEL_CNT, 32)
+enum link_mode_class {
+ LM_CLASS_UNKNOWN,
+ LM_CLASS_REAL,
+ LM_CLASS_AUTONEG,
+ LM_CLASS_PORT,
+ LM_CLASS_PAUSE,
+ LM_CLASS_FEC,
+};
+
+struct nl_op_info {
+ uint32_t op_flags;
+ uint32_t hdr_flags;
+ uint8_t hdr_policy_loaded:1;
+};
+
struct nl_context {
struct cmd_context *ctx;
void *cmd_private;
@@ -25,6 +40,7 @@ struct nl_context {
unsigned int suppress_nlerr;
uint16_t ethnl_fam;
uint32_t ethnl_mongrp;
+ struct nl_op_info *ops_info;
struct nl_socket *ethnl_socket;
struct nl_socket *ethnl2_socket;
struct nl_socket *rtnl_socket;
@@ -35,7 +51,9 @@ struct nl_context {
const char *cmd;
const char *param;
char **argp;
- int argc;
+ unsigned int argc;
+ bool ioctl_fallback;
+ bool wildcard_unsupported;
};
struct attr_tb_info {
@@ -49,13 +67,70 @@ struct attr_tb_info {
int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data);
int attr_cb(const struct nlattr *attr, void *data);
+int netlink_init(struct cmd_context *ctx);
+bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd,
+ bool allow_wildcard);
const char *get_dev_name(const struct nlattr *nest);
int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname);
+u32 get_stats_flag(struct nl_context *nlctx, unsigned int nlcmd,
+ unsigned int hdrattr);
int linkmodes_reply_cb(const struct nlmsghdr *nlhdr, void *data);
int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data);
int wol_reply_cb(const struct nlmsghdr *nlhdr, void *data);
int debug_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int features_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int privflags_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int rings_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int channels_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int coalesce_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int pause_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int eee_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int cable_test_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int cable_test_ntf_cb(const struct nlmsghdr *nlhdr, void *data);
+int cable_test_tdr_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int cable_test_tdr_ntf_cb(const struct nlmsghdr *nlhdr, void *data);
+
+/* dump helpers */
+
+int dump_link_modes(struct nl_context *nlctx, const struct nlattr *bitset,
+ bool mask, unsigned int class, const char *before,
+ const char *between, const char *after,
+ const char *if_none);
+
+static inline void show_u32(const struct nlattr *attr, const char *label)
+{
+ if (attr)
+ printf("%s%u\n", label, mnl_attr_get_u32(attr));
+ else
+ printf("%sn/a\n", label);
+}
+
+static inline const char *u8_to_bool(const uint8_t *val)
+{
+ if (val)
+ return *val ? "on" : "off";
+ else
+ return "n/a";
+}
+
+static inline void show_bool_val(const char *key, const char *fmt, uint8_t *val)
+{
+ if (is_json_context()) {
+ if (val)
+ print_bool(PRINT_JSON, key, NULL, val);
+ } else {
+ print_string(PRINT_FP, NULL, fmt, u8_to_bool(val));
+ }
+}
+
+static inline void show_bool(const char *key, const char *fmt,
+ const struct nlattr *attr)
+{
+ show_bool_val(key, fmt, attr ? mnl_attr_get_payload(attr) : NULL);
+}
+
+/* misc */
static inline void copy_devname(char *dst, const char *src)
{
diff --git a/netlink/nlsock.c b/netlink/nlsock.c
index 22abb68..0ec2738 100644
--- a/netlink/nlsock.c
+++ b/netlink/nlsock.c
@@ -168,30 +168,30 @@ static void debug_msg(struct nl_socket *nlsk, const void *msg, unsigned int len,
*
* Return: error code extracted from the message
*/
-static int nlsock_process_ack(struct nlmsghdr *nlhdr, ssize_t len,
+static int nlsock_process_ack(struct nlmsghdr *nlhdr, unsigned long len,
unsigned int suppress_nlerr, bool pretty)
{
const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
DECLARE_ATTR_TB_INFO(tb);
+ unsigned int err_offset = 0;
unsigned int tlv_offset;
struct nlmsgerr *nlerr;
bool silent;
- if (len < NLMSG_HDRLEN + sizeof(*nlerr))
+ if ((len < NLMSG_HDRLEN + sizeof(*nlerr)) || (len < nlhdr->nlmsg_len))
return -EFAULT;
nlerr = mnl_nlmsg_get_payload(nlhdr);
- silent = (!(nlhdr->nlmsg_flags & NLM_F_ACK_TLVS) ||
- suppress_nlerr >= 2 ||
- (suppress_nlerr && nlerr->error == -EOPNOTSUPP));
- if (silent)
- goto out;
+ silent = suppress_nlerr >= 2 ||
+ (suppress_nlerr && nlerr->error == -EOPNOTSUPP);
+ if (silent || !(nlhdr->nlmsg_flags & NLM_F_ACK_TLVS))
+ goto tlv_done;
tlv_offset = sizeof(*nlerr);
if (!(nlhdr->nlmsg_flags & NLM_F_CAPPED))
tlv_offset += MNL_ALIGN(mnl_nlmsg_get_payload_len(&nlerr->msg));
-
if (mnl_attr_parse(nlhdr, tlv_offset, attr_cb, &tb_info) < 0)
- goto out;
+ goto tlv_done;
+
if (tb[NLMSGERR_ATTR_MSG]) {
const char *msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
@@ -202,24 +202,21 @@ static int nlsock_process_ack(struct nlmsghdr *nlhdr, ssize_t len,
mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]));
fputc('\n', stderr);
}
+ if (tb[NLMSGERR_ATTR_OFFS])
+ err_offset = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
- if (nlerr->error && pretty) {
- unsigned int err_offset = 0;
-
- if (tb[NLMSGERR_ATTR_OFFS])
- err_offset = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
+tlv_done:
+ if (nlerr->error && !silent) {
+ errno = -nlerr->error;
+ perror("netlink error");
+ }
+ if (pretty && !(nlhdr->nlmsg_flags & NLM_F_CAPPED) &&
+ nlhdr->nlmsg_len >= NLMSG_HDRLEN + nlerr->msg.nlmsg_len) {
fprintf(stderr, "offending message%s:\n",
err_offset ? " and attribute" : "");
pretty_print_genlmsg(&nlerr->msg, ethnl_umsg_desc,
ethnl_umsg_n_desc, err_offset);
}
-
-out:
- if (nlerr->error) {
- errno = -nlerr->error;
- if (!silent)
- perror("netlink error");
- }
return nlerr->error;
}
@@ -258,12 +255,12 @@ int nlsock_process_reply(struct nl_socket *nlsk, mnl_cb_t reply_cb, void *data)
nlhdr = (struct nlmsghdr *)buff;
if (nlhdr->nlmsg_type == NLMSG_ERROR) {
- bool silent = nlsk->nlctx->suppress_nlerr;
+ unsigned int suppress = nlsk->nlctx->suppress_nlerr;
bool pretty;
pretty = debug_on(nlsk->nlctx->ctx->debug,
DEBUG_NL_PRETTY_MSG);
- return nlsock_process_ack(nlhdr, len, silent, pretty);
+ return nlsock_process_ack(nlhdr, len, suppress, pretty);
}
msgbuff->nlhdr = nlhdr;
@@ -398,8 +395,11 @@ out_msgbuff:
*/
void nlsock_done(struct nl_socket *nlsk)
{
+ if (!nlsk)
+ return;
if (nlsk->sk)
mnl_socket_close(nlsk->sk);
msgbuff_done(&nlsk->msgbuff);
memset(nlsk, '\0', sizeof(*nlsk));
+ free(nlsk);
}
diff --git a/netlink/parser.c b/netlink/parser.c
index 40eb4a5..c2eae93 100644
--- a/netlink/parser.c
+++ b/netlink/parser.c
@@ -54,6 +54,22 @@ static bool __prefix_0x(const char *p)
return p[0] == '0' && (p[1] == 'x' || p[1] == 'X');
}
+static float parse_float(const char *arg, float *result, float min,
+ float max)
+{
+ char *endptr;
+ float val;
+
+ if (!arg || !arg[0])
+ return -EINVAL;
+ val = strtof(arg, &endptr);
+ if (*endptr || val < min || val > max)
+ return -EINVAL;
+
+ *result = val;
+ return 0;
+}
+
static int __parse_u32(const char *arg, uint32_t *result, uint32_t min,
uint32_t max, int base)
{
@@ -139,8 +155,9 @@ static int lookup_u8(const char *arg, uint8_t *result,
/* Parser handler for a flag. Expects a name (with no additional argument),
* generates NLA_FLAG or sets a bool (if the name was present).
*/
-int nl_parse_flag(struct nl_context *nlctx, uint16_t type, const void *data,
- struct nl_msg_buff *msgbuff, void *dest)
+int nl_parse_flag(struct nl_context *nlctx __maybe_unused, uint16_t type,
+ const void *data __maybe_unused, struct nl_msg_buff *msgbuff,
+ void *dest)
{
if (dest)
*(bool *)dest = true;
@@ -150,7 +167,8 @@ int nl_parse_flag(struct nl_context *nlctx, uint16_t type, const void *data,
/* Parser handler for null terminated string. Expects a string argument,
* generates NLA_NUL_STRING or fills const char *
*/
-int nl_parse_string(struct nl_context *nlctx, uint16_t type, const void *data,
+int nl_parse_string(struct nl_context *nlctx, uint16_t type,
+ const void *data __maybe_unused,
struct nl_msg_buff *msgbuff, void *dest)
{
const char *arg = *nlctx->argp;
@@ -167,8 +185,8 @@ int nl_parse_string(struct nl_context *nlctx, uint16_t type, const void *data,
* (may use 0x prefix), generates NLA_U32 or fills an uint32_t.
*/
int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
- const void *data, struct nl_msg_buff *msgbuff,
- void *dest)
+ const void *data __maybe_unused,
+ struct nl_msg_buff *msgbuff, void *dest)
{
const char *arg = *nlctx->argp;
uint32_t val;
@@ -191,8 +209,8 @@ int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
* (may use 0x prefix), generates NLA_U32 or fills an uint32_t.
*/
int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
- const void *data, struct nl_msg_buff *msgbuff,
- void *dest)
+ const void *data __maybe_unused,
+ struct nl_msg_buff *msgbuff, void *dest)
{
const char *arg = *nlctx->argp;
uint8_t val;
@@ -211,10 +229,37 @@ int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
return (type && ethnla_put_u8(msgbuff, type, val)) ? -EMSGSIZE : 0;
}
+/* Parser handler for float meters and convert it to cm. Generates
+ * NLA_U32 or fills an uint32_t.
+ */
+int nl_parse_direct_m2cm(struct nl_context *nlctx, uint16_t type,
+ const void *data __maybe_unused,
+ struct nl_msg_buff *msgbuff, void *dest)
+{
+ const char *arg = *nlctx->argp;
+ float meters;
+ uint32_t cm;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ ret = parse_float(arg, &meters, 0, 150);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ return ret;
+ }
+
+ cm = (uint32_t)(meters * 100 + 0.5);
+ if (dest)
+ *(uint32_t *)dest = cm;
+ return (type && ethnla_put_u32(msgbuff, type, cm)) ? -EMSGSIZE : 0;
+}
+
/* Parser handler for (tri-state) bool. Expects "name on|off", generates
* NLA_U8 which is 1 for "on" and 0 for "off".
*/
-int nl_parse_u8bool(struct nl_context *nlctx, uint16_t type, const void *data,
+int nl_parse_u8bool(struct nl_context *nlctx, uint16_t type,
+ const void *data __maybe_unused,
struct nl_msg_buff *msgbuff, void *dest)
{
const char *arg = *nlctx->argp;
@@ -421,8 +466,9 @@ err:
* error_parser_params (error message, return value and number of extra
* arguments to skip).
*/
-int nl_parse_error(struct nl_context *nlctx, uint16_t type, const void *data,
- struct nl_msg_buff *msgbuff, void *dest)
+int nl_parse_error(struct nl_context *nlctx, uint16_t type __maybe_unused,
+ const void *data, struct nl_msg_buff *msgbuff __maybe_unused,
+ void *dest __maybe_unused)
{
const struct error_parser_data *parser_data = data;
unsigned int skip = parser_data->extra_args;
@@ -558,7 +604,7 @@ static int parse_numeric_bitset(struct nl_context *nlctx, uint16_t type,
parser_err_invalid_value(nlctx, arg);
return -EINVAL;
}
- len1 = maskptr ? (maskptr - arg) : strlen(arg);
+ len1 = maskptr ? (unsigned int)(maskptr - arg) : strlen(arg);
nwords = DIV_ROUND_UP(len1, 8);
nbits = 0;
@@ -584,8 +630,10 @@ static int parse_numeric_bitset(struct nl_context *nlctx, uint16_t type,
}
value = calloc(nwords, sizeof(uint32_t));
- if (!value)
+ if (!value) {
+ free(mask);
return -ENOMEM;
+ }
ret = __parse_num_string(arg, len1, value, force_hex1);
if (ret < 0) {
parser_err_invalid_value(nlctx, arg);
@@ -872,7 +920,7 @@ static void __parser_set(uint64_t *map, unsigned int idx)
}
struct tmp_buff {
- struct nl_msg_buff msgbuff;
+ struct nl_msg_buff *msgbuff;
unsigned int id;
unsigned int orig_len;
struct tmp_buff *next;
@@ -903,7 +951,12 @@ static struct tmp_buff *tmp_buff_find_or_create(struct tmp_buff **phead,
if (!new_buff)
return NULL;
new_buff->id = id;
- msgbuff_init(&new_buff->msgbuff);
+ new_buff->msgbuff = malloc(sizeof(*new_buff->msgbuff));
+ if (!new_buff->msgbuff) {
+ free(new_buff);
+ return NULL;
+ }
+ msgbuff_init(new_buff->msgbuff);
new_buff->next = NULL;
*pbuff = new_buff;
@@ -917,7 +970,10 @@ static void tmp_buff_destroy(struct tmp_buff *head)
while (buff) {
next = buff->next;
- msgbuff_done(&buff->msgbuff);
+ if (buff->msgbuff) {
+ msgbuff_done(buff->msgbuff);
+ free(buff->msgbuff);
+ }
free(buff);
buff = next;
}
@@ -932,13 +988,22 @@ static void tmp_buff_destroy(struct tmp_buff *head)
* param_parser::offset)
* @group_style: defines if identifiers in .group represent separate messages,
* nested attributes or are not allowed
+ * @msgbuffs: (only used for @group_style = PARSER_GROUP_MSG) array to store
+ * pointers to composed messages; caller must make sure this
+ * array is sufficient, i.e. that it has at least as many entries
+ * as the number of different .group values in params array;
+ * entries are filled from the start, remaining entries are not
+ * modified; caller should zero initialize the array before
+ * calling nl_parser()
*/
int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
- void *dest, enum parser_group_style group_style)
+ void *dest, enum parser_group_style group_style,
+ struct nl_msg_buff **msgbuffs)
{
struct nl_socket *nlsk = nlctx->ethnl_socket;
const struct param_parser *parser;
struct tmp_buff *buffs = NULL;
+ unsigned int n_msgbuffs = 0;
struct tmp_buff *buff;
unsigned int n_params;
uint64_t *params_seen;
@@ -956,20 +1021,20 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
buff = tmp_buff_find_or_create(&buffs, parser->group);
if (!buff)
goto out_free_buffs;
- msgbuff = &buff->msgbuff;
+ msgbuff = buff->msgbuff;
+ ret = msg_init(nlctx, msgbuff, parser->group,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ goto out_free_buffs;
switch (group_style) {
case PARSER_GROUP_NEST:
ret = -EMSGSIZE;
- nest = ethnla_nest_start(&buff->msgbuff, parser->group);
+ nest = ethnla_nest_start(buff->msgbuff, parser->group);
if (!nest)
goto out_free_buffs;
break;
case PARSER_GROUP_MSG:
- ret = msg_init(nlctx, msgbuff, parser->group,
- NLM_F_REQUEST | NLM_F_ACK);
- if (ret < 0)
- goto out_free_buffs;
if (ethnla_fill_header(msgbuff,
ETHTOOL_A_LINKINFO_HEADER,
nlctx->devname, 0))
@@ -1014,17 +1079,24 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
buff = NULL;
if (parser->group)
buff = tmp_buff_find(buffs, parser->group);
- msgbuff = buff ? &buff->msgbuff : &nlsk->msgbuff;
+ msgbuff = buff ? buff->msgbuff : &nlsk->msgbuff;
- param_dest = dest ? (dest + parser->dest_offset) : NULL;
+ param_dest = dest ? ((char *)dest + parser->dest_offset) : NULL;
ret = parser->handler(nlctx, parser->type, parser->handler_data,
msgbuff, param_dest);
if (ret < 0)
goto out_free;
}
+ if (group_style == PARSER_GROUP_MSG) {
+ ret = -EOPNOTSUPP;
+ for (buff = buffs; buff; buff = buff->next)
+ if (msgbuff_len(buff->msgbuff) > buff->orig_len &&
+ netlink_cmd_check(nlctx->ctx, buff->id, false))
+ goto out_free;
+ }
for (buff = buffs; buff; buff = buff->next) {
- struct nl_msg_buff *msgbuff = &buff->msgbuff;
+ struct nl_msg_buff *msgbuff = buff->msgbuff;
if (group_style == PARSER_GROUP_NONE ||
msgbuff_len(msgbuff) == buff->orig_len)
@@ -1037,12 +1109,8 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
goto out_free;
break;
case PARSER_GROUP_MSG:
- ret = nlsock_sendmsg(nlsk, msgbuff);
- if (ret < 0)
- goto out_free;
- ret = nlsock_process_reply(nlsk, nomsg_reply_cb, NULL);
- if (ret < 0)
- goto out_free;
+ msgbuffs[n_msgbuffs++] = msgbuff;
+ buff->msgbuff = NULL;
break;
default:
break;
diff --git a/netlink/parser.h b/netlink/parser.h
index 3cc26d2..28f26cc 100644
--- a/netlink/parser.h
+++ b/netlink/parser.h
@@ -111,6 +111,10 @@ int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
const void *data, struct nl_msg_buff *msgbuff,
void *dest);
+/* NLA_U32 represented as float number of meters, converted to cm. */
+int nl_parse_direct_m2cm(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest);
/* NLA_U8 represented as on | off */
int nl_parse_u8bool(struct nl_context *nlctx, uint16_t type, const void *data,
struct nl_msg_buff *msgbuff, void *dest);
@@ -139,6 +143,7 @@ int nl_parse_char_bitset(struct nl_context *nlctx, uint16_t type,
/* main entry point called to parse the command line */
int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
- void *dest, enum parser_group_style group_style);
+ void *dest, enum parser_group_style group_style,
+ struct nl_msg_buff **msgbuffs);
#endif /* ETHTOOL_NETLINK_PARSER_H__ */
diff --git a/netlink/pause.c b/netlink/pause.c
new file mode 100644
index 0000000..867d0da
--- /dev/null
+++ b/netlink/pause.c
@@ -0,0 +1,308 @@
+/*
+ * pause.c - netlink implementation of pause commands
+ *
+ * Implementation of "ethtool -a <dev>" and "ethtool -A <dev> ..."
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* PAUSE_GET */
+
+struct pause_autoneg_status {
+ bool pause;
+ bool asym_pause;
+};
+
+static void pause_autoneg_walker(unsigned int idx,
+ const char *name __maybe_unused, bool val,
+ void *data)
+{
+ struct pause_autoneg_status *status = data;
+
+ if (idx == ETHTOOL_LINK_MODE_Pause_BIT)
+ status->pause = val;
+ if (idx == ETHTOOL_LINK_MODE_Asym_Pause_BIT)
+ status->asym_pause = val;
+}
+
+static int pause_autoneg_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct pause_autoneg_status ours = {};
+ struct pause_autoneg_status peer = {};
+ struct nl_context *nlctx = data;
+ uint8_t rx_status = false;
+ uint8_t tx_status = false;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+
+ if (!tb[ETHTOOL_A_LINKMODES_OURS] || !tb[ETHTOOL_A_LINKMODES_PEER])
+ return MNL_CB_OK;
+ ret = walk_bitset(tb[ETHTOOL_A_LINKMODES_OURS], NULL,
+ pause_autoneg_walker, &ours);
+ if (ret < 0)
+ return err_ret;
+ ret = walk_bitset(tb[ETHTOOL_A_LINKMODES_PEER], NULL,
+ pause_autoneg_walker, &peer);
+ if (ret < 0)
+ return err_ret;
+
+ if (ours.pause && peer.pause) {
+ rx_status = true;
+ tx_status = true;
+ } else if (ours.asym_pause && peer.asym_pause) {
+ if (ours.pause)
+ rx_status = true;
+ else if (peer.pause)
+ tx_status = true;
+ }
+
+ open_json_object("negotiated");
+ show_bool_val("rx", "RX negotiated: %s\n", &rx_status);
+ show_bool_val("tx", "TX negotiated: %s\n", &tx_status);
+ close_json_object();
+
+ return MNL_CB_OK;
+}
+
+static int show_pause_autoneg_status(struct nl_context *nlctx)
+{
+ const char *saved_devname;
+ int ret;
+
+ saved_devname = nlctx->ctx->devname;
+ nlctx->ctx->devname = nlctx->devname;
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ goto out;
+
+ ret = nlsock_prep_get_request(nlctx->ethnl2_socket,
+ ETHTOOL_MSG_LINKMODES_GET,
+ ETHTOOL_A_LINKMODES_HEADER,
+ ETHTOOL_FLAG_COMPACT_BITSETS);
+ if (ret < 0)
+ goto out;
+ ret = nlsock_send_get_request(nlctx->ethnl2_socket, pause_autoneg_cb);
+
+out:
+ nlctx->ctx->devname = saved_devname;
+ return ret;
+}
+
+static int show_pause_stats(const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHTOOL_A_PAUSE_STAT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ static const struct {
+ unsigned int attr;
+ char *name;
+ } stats[] = {
+ { ETHTOOL_A_PAUSE_STAT_TX_FRAMES, "tx_pause_frames" },
+ { ETHTOOL_A_PAUSE_STAT_RX_FRAMES, "rx_pause_frames" },
+ };
+ bool header = false;
+ unsigned int i;
+ size_t n;
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ open_json_object("statistics");
+ for (i = 0; i < ARRAY_SIZE(stats); i++) {
+ char fmt[32];
+
+ if (!tb[stats[i].attr])
+ continue;
+
+ if (!header && !is_json_context()) {
+ printf("Statistics:\n");
+ header = true;
+ }
+
+ if (mnl_attr_validate(tb[stats[i].attr], MNL_TYPE_U64)) {
+ fprintf(stderr, "malformed netlink message (statistic)\n");
+ goto err_close_stats;
+ }
+
+ n = snprintf(fmt, sizeof(fmt), " %s: %%" PRIu64 "\n",
+ stats[i].name);
+ if (n >= sizeof(fmt)) {
+ fprintf(stderr, "internal error - malformed label\n");
+ goto err_close_stats;
+ }
+
+ print_u64(PRINT_ANY, stats[i].name, fmt,
+ mnl_attr_get_u64(tb[stats[i].attr]));
+ }
+ close_json_object();
+
+ return 0;
+
+err_close_stats:
+ close_json_object();
+ return -1;
+}
+
+int pause_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_PAUSE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_PAUSE_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ print_nl();
+
+ open_json_object(NULL);
+
+ print_string(PRINT_ANY, "ifname", "Pause parameters for %s:\n",
+ nlctx->devname);
+
+ show_bool("autonegotiate", "Autonegotiate:\t%s\n",
+ tb[ETHTOOL_A_PAUSE_AUTONEG]);
+ show_bool("rx", "RX:\t\t%s\n", tb[ETHTOOL_A_PAUSE_RX]);
+ show_bool("tx", "TX:\t\t%s\n", tb[ETHTOOL_A_PAUSE_TX]);
+
+ if (!nlctx->is_monitor && tb[ETHTOOL_A_PAUSE_AUTONEG] &&
+ mnl_attr_get_u8(tb[ETHTOOL_A_PAUSE_AUTONEG])) {
+ ret = show_pause_autoneg_status(nlctx);
+ if (ret < 0)
+ goto err_close_dev;
+ }
+ if (tb[ETHTOOL_A_PAUSE_STATS]) {
+ ret = show_pause_stats(tb[ETHTOOL_A_PAUSE_STATS]);
+ if (ret < 0)
+ goto err_close_dev;
+ }
+ if (!silent)
+ print_nl();
+
+ close_json_object();
+
+ return MNL_CB_OK;
+
+err_close_dev:
+ close_json_object();
+ return err_ret;
+}
+
+int nl_gpause(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ u32 flags;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PAUSE_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ flags = get_stats_flag(nlctx, ETHTOOL_MSG_PAUSE_GET,
+ ETHTOOL_A_PAUSE_HEADER);
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PAUSE_GET,
+ ETHTOOL_A_PAUSE_HEADER, flags);
+ if (ret < 0)
+ return ret;
+
+ new_json_obj(ctx->json);
+ ret = nlsock_send_get_request(nlsk, pause_reply_cb);
+ delete_json_obj();
+ return ret;
+}
+
+/* PAUSE_SET */
+
+static const struct param_parser spause_params[] = {
+ {
+ .arg = "autoneg",
+ .type = ETHTOOL_A_PAUSE_AUTONEG,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx",
+ .type = ETHTOOL_A_PAUSE_RX,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx",
+ .type = ETHTOOL_A_PAUSE_TX,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_spause(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PAUSE_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-A";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PAUSE_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_PAUSE_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, spause_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 76;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 76;
+}
diff --git a/netlink/prettymsg.c b/netlink/prettymsg.c
index 9e62beb..0a1fae3 100644
--- a/netlink/prettymsg.c
+++ b/netlink/prettymsg.c
@@ -9,6 +9,7 @@
#include <errno.h>
#include <stdint.h>
#include <limits.h>
+#include <inttypes.h>
#include <linux/genetlink.h>
#include <linux/rtnetlink.h>
#include <linux/if_link.h>
@@ -110,6 +111,9 @@ static int pretty_print_attr(const struct nlattr *attr,
case NLA_U32:
printf("%u", mnl_attr_get_u32(attr));
break;
+ case NLA_U64:
+ printf("%" PRIu64, mnl_attr_get_u64(attr));
+ break;
case NLA_X8:
printf("0x%02x", mnl_attr_get_u8(attr));
break;
@@ -119,6 +123,9 @@ static int pretty_print_attr(const struct nlattr *attr,
case NLA_X32:
printf("0x%08x", mnl_attr_get_u32(attr));
break;
+ case NLA_X64:
+ printf("%" PRIx64, mnl_attr_get_u64(attr));
+ break;
case NLA_S8:
printf("%d", (int)mnl_attr_get_u8(attr));
break;
@@ -128,6 +135,9 @@ static int pretty_print_attr(const struct nlattr *attr,
case NLA_S32:
printf("%d", (int)mnl_attr_get_u32(attr));
break;
+ case NLA_S64:
+ printf("%" PRId64, (int64_t)mnl_attr_get_u64(attr));
+ break;
case NLA_STRING:
printf("\"%.*s\"", alen, (const char *)adata);
break;
@@ -137,6 +147,15 @@ static int pretty_print_attr(const struct nlattr *attr,
case NLA_BOOL:
printf("%s", mnl_attr_get_u8(attr) ? "on" : "off");
break;
+ case NLA_U32_ENUM: {
+ uint32_t val = mnl_attr_get_u32(attr);
+
+ if (adesc && val < adesc->n_names && adesc->names[val])
+ printf("%s", adesc->names[val]);
+ else
+ printf("%u", val);
+ break;
+ }
default:
if (alen <= __DUMP_LINE)
__print_binary_short(adata, alen);
@@ -202,7 +221,7 @@ static void rtm_link_summary(const struct ifinfomsg *ifinfo)
printf(" ifindex=%d", ifinfo->ifi_index);
if (ifinfo->ifi_flags)
printf(" flags=0x%x", ifinfo->ifi_flags);
- if (ifinfo->ifi_flags)
+ if (ifinfo->ifi_change)
printf(" change=0x%x", ifinfo->ifi_change);
}
diff --git a/netlink/prettymsg.h b/netlink/prettymsg.h
index b5e5f73..25990cc 100644
--- a/netlink/prettymsg.h
+++ b/netlink/prettymsg.h
@@ -17,24 +17,34 @@ enum pretty_nla_format {
NLA_U8,
NLA_U16,
NLA_U32,
+ NLA_U64,
NLA_X8,
NLA_X16,
NLA_X32,
+ NLA_X64,
NLA_S8,
NLA_S16,
NLA_S32,
+ NLA_S64,
NLA_STRING,
NLA_FLAG,
NLA_BOOL,
NLA_NESTED,
NLA_ARRAY,
+ NLA_U32_ENUM,
};
struct pretty_nla_desc {
enum pretty_nla_format format;
const char *name;
- const struct pretty_nla_desc *children;
- unsigned int n_children;
+ union {
+ const struct pretty_nla_desc *children;
+ const char *const *names;
+ };
+ union {
+ unsigned int n_children;
+ unsigned int n_names;
+ };
};
struct pretty_nlmsg_desc {
@@ -55,12 +65,15 @@ struct pretty_nlmsg_desc {
#define NLATTR_DESC_U8(_name) NLATTR_DESC(_name, NLA_U8)
#define NLATTR_DESC_U16(_name) NLATTR_DESC(_name, NLA_U16)
#define NLATTR_DESC_U32(_name) NLATTR_DESC(_name, NLA_U32)
+#define NLATTR_DESC_U64(_name) NLATTR_DESC(_name, NLA_U64)
#define NLATTR_DESC_X8(_name) NLATTR_DESC(_name, NLA_X8)
#define NLATTR_DESC_X16(_name) NLATTR_DESC(_name, NLA_X16)
#define NLATTR_DESC_X32(_name) NLATTR_DESC(_name, NLA_X32)
+#define NLATTR_DESC_X64(_name) NLATTR_DESC(_name, NLA_X64)
#define NLATTR_DESC_S8(_name) NLATTR_DESC(_name, NLA_U8)
#define NLATTR_DESC_S16(_name) NLATTR_DESC(_name, NLA_U16)
#define NLATTR_DESC_S32(_name) NLATTR_DESC(_name, NLA_U32)
+#define NLATTR_DESC_S64(_name) NLATTR_DESC(_name, NLA_S64)
#define NLATTR_DESC_STRING(_name) NLATTR_DESC(_name, NLA_STRING)
#define NLATTR_DESC_FLAG(_name) NLATTR_DESC(_name, NLA_FLAG)
#define NLATTR_DESC_BOOL(_name) NLATTR_DESC(_name, NLA_BOOL)
@@ -81,6 +94,13 @@ struct pretty_nlmsg_desc {
.children = __ ## _children_desc ## _desc, \
.n_children = 1, \
}
+#define NLATTR_DESC_U32_ENUM(_name, _names_table) \
+ [_name] = { \
+ .format = NLA_U32_ENUM, \
+ .name = #_name, \
+ .names = __ ## _names_table ## _names, \
+ .n_children = ARRAY_SIZE(__ ## _names_table ## _names), \
+ }
#define NLMSG_DESC(_name, _attrs) \
[_name] = { \
diff --git a/netlink/privflags.c b/netlink/privflags.c
new file mode 100644
index 0000000..299ccdc
--- /dev/null
+++ b/netlink/privflags.c
@@ -0,0 +1,158 @@
+/*
+ * privflags.c - netlink implementation of private flags commands
+ *
+ * Implementation of "ethtool --show-priv-flags <dev>" and
+ * "ethtool --set-priv-flags <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "strset.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* PRIVFLAGS_GET */
+
+static void privflags_maxlen_walk_cb(unsigned int idx, const char *name,
+ bool val __maybe_unused, void *data)
+{
+ unsigned int *maxlen = data;
+ unsigned int len, n;
+
+ if (name)
+ len = strlen(name);
+ else {
+ len = 3; /* strlen("bit") */
+ for (n = idx ?: 1; n; n /= 10)
+ len++; /* plus number of ditigs */
+ }
+ if (len > *maxlen)
+ *maxlen = len;
+}
+
+static void privflags_dump_walk_cb(unsigned int idx, const char *name, bool val,
+ void *data)
+{
+ unsigned int *maxlen = data;
+ char buff[16];
+
+ if (!name) {
+ snprintf(buff, sizeof(buff) - 1, "bit%u", idx);
+ name = buff;
+ }
+ printf("%-*s: %s\n", *maxlen, name, val ? "on" : "off");
+}
+
+int privflags_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_PRIVFLAGS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const struct stringset *flag_names = NULL;
+ struct nl_context *nlctx = data;
+ unsigned int maxlen = 0;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0 || !tb[ETHTOOL_A_PRIVFLAGS_FLAGS])
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_PRIVFLAGS_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS])) {
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return err_ret;
+ flag_names = perdev_stringset(nlctx->devname, ETH_SS_PRIV_FLAGS,
+ nlctx->ethnl2_socket);
+ }
+
+ ret = walk_bitset(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], flag_names,
+ privflags_maxlen_walk_cb, &maxlen);
+ if (ret < 0)
+ return err_ret;
+ if (silent)
+ putchar('\n');
+ printf("Private flags for %s:\n", nlctx->devname);
+ ret = walk_bitset(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], flag_names,
+ privflags_dump_walk_cb, &maxlen);
+ return (ret < 0) ? err_ret : MNL_CB_OK;
+}
+
+int nl_gprivflags(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PRIVFLAGS_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PRIVFLAGS_GET,
+ ETHTOOL_A_PRIVFLAGS_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, privflags_reply_cb);
+}
+
+/* PRIVFLAGS_SET */
+
+static const struct bitset_parser_data privflags_parser_data = {
+ .force_hex = false,
+ .no_mask = false,
+};
+
+int nl_sprivflags(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PRIVFLAGS_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "--set-priv-flags";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PRIVFLAGS_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_PRIVFLAGS_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parse_bitset(nlctx, ETHTOOL_A_PRIVFLAGS_FLAGS,
+ &privflags_parser_data, msgbuff, NULL);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 2;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 1;
+}
diff --git a/netlink/rings.c b/netlink/rings.c
new file mode 100644
index 0000000..b8c458f
--- /dev/null
+++ b/netlink/rings.c
@@ -0,0 +1,141 @@
+/*
+ * rings.c - netlink implementation of ring commands
+ *
+ * Implementation of "ethtool -g <dev>" and "ethtool -G <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+/* RINGS_GET */
+
+int rings_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_RINGS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_RINGS_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ putchar('\n');
+ printf("Ring parameters for %s:\n", nlctx->devname);
+ printf("Pre-set maximums:\n");
+ show_u32(tb[ETHTOOL_A_RINGS_RX_MAX], "RX:\t\t");
+ show_u32(tb[ETHTOOL_A_RINGS_RX_MINI_MAX], "RX Mini:\t");
+ show_u32(tb[ETHTOOL_A_RINGS_RX_JUMBO_MAX], "RX Jumbo:\t");
+ show_u32(tb[ETHTOOL_A_RINGS_TX_MAX], "TX:\t\t");
+ printf("Current hardware settings:\n");
+ show_u32(tb[ETHTOOL_A_RINGS_RX], "RX:\t\t");
+ show_u32(tb[ETHTOOL_A_RINGS_RX_MINI], "RX Mini:\t");
+ show_u32(tb[ETHTOOL_A_RINGS_RX_JUMBO], "RX Jumbo:\t");
+ show_u32(tb[ETHTOOL_A_RINGS_TX], "TX:\t\t");
+
+ return MNL_CB_OK;
+}
+
+int nl_gring(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_RINGS_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_RINGS_GET,
+ ETHTOOL_A_RINGS_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, rings_reply_cb);
+}
+
+/* RINGS_SET */
+
+static const struct param_parser sring_params[] = {
+ {
+ .arg = "rx",
+ .type = ETHTOOL_A_RINGS_RX,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-mini",
+ .type = ETHTOOL_A_RINGS_RX_MINI,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-jumbo",
+ .type = ETHTOOL_A_RINGS_RX_JUMBO,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx",
+ .type = ETHTOOL_A_RINGS_TX,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_sring(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_RINGS_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-G";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_RINGS_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_RINGS_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, sring_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 81;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 81;
+}
diff --git a/netlink/settings.c b/netlink/settings.c
index 0212465..90c28b1 100644
--- a/netlink/settings.c
+++ b/netlink/settings.c
@@ -17,15 +17,6 @@
/* GET_SETTINGS */
-enum link_mode_class {
- LM_CLASS_UNKNOWN,
- LM_CLASS_REAL,
- LM_CLASS_AUTONEG,
- LM_CLASS_PORT,
- LM_CLASS_PAUSE,
- LM_CLASS_FEC,
-};
-
struct link_mode_info {
enum link_mode_class class;
u32 speed;
@@ -37,6 +28,21 @@ static const char *const names_duplex[] = {
[DUPLEX_FULL] = "Full",
};
+static const char *const names_master_slave_state[] = {
+ [MASTER_SLAVE_STATE_UNKNOWN] = "unknown",
+ [MASTER_SLAVE_STATE_MASTER] = "master",
+ [MASTER_SLAVE_STATE_SLAVE] = "slave",
+ [MASTER_SLAVE_STATE_ERR] = "resolution error",
+};
+
+static const char *const names_master_slave_cfg[] = {
+ [MASTER_SLAVE_CFG_UNKNOWN] = "unknown",
+ [MASTER_SLAVE_CFG_MASTER_PREFERRED] = "preferred master",
+ [MASTER_SLAVE_CFG_SLAVE_PREFERRED] = "preferred slave",
+ [MASTER_SLAVE_CFG_MASTER_FORCE] = "forced master",
+ [MASTER_SLAVE_CFG_SLAVE_FORCE] = "forced slave",
+};
+
static const char *const names_port[] = {
[PORT_TP] = "Twisted Pair",
[PORT_AUI] = "AUI",
@@ -58,158 +64,113 @@ static const char *const names_transceiver[] = {
* there is little chance of getting them separated any time soon so let's
* sort them out ourselves
*/
+#define __REAL(_speed) \
+ { .class = LM_CLASS_REAL, .speed = _speed, .duplex = DUPLEX_FULL }
+#define __HALF_DUPLEX(_speed) \
+ { .class = LM_CLASS_REAL, .speed = _speed, .duplex = DUPLEX_HALF }
+#define __SPECIAL(_class) \
+ { .class = LM_CLASS_ ## _class }
+
static const struct link_mode_info link_modes[] = {
- [ETHTOOL_LINK_MODE_10baseT_Half_BIT] =
- { LM_CLASS_REAL, 10, DUPLEX_HALF },
- [ETHTOOL_LINK_MODE_10baseT_Full_BIT] =
- { LM_CLASS_REAL, 10, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100baseT_Half_BIT] =
- { LM_CLASS_REAL, 100, DUPLEX_HALF },
- [ETHTOOL_LINK_MODE_100baseT_Full_BIT] =
- { LM_CLASS_REAL, 100, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] =
- { LM_CLASS_REAL, 1000, DUPLEX_HALF },
- [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] =
- { LM_CLASS_REAL, 1000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_Autoneg_BIT] =
- { LM_CLASS_AUTONEG },
- [ETHTOOL_LINK_MODE_TP_BIT] =
- { LM_CLASS_PORT },
- [ETHTOOL_LINK_MODE_AUI_BIT] =
- { LM_CLASS_PORT },
- [ETHTOOL_LINK_MODE_MII_BIT] =
- { LM_CLASS_PORT },
- [ETHTOOL_LINK_MODE_FIBRE_BIT] =
- { LM_CLASS_PORT },
- [ETHTOOL_LINK_MODE_BNC_BIT] =
- { LM_CLASS_PORT },
- [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] =
- { LM_CLASS_REAL, 10000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_Pause_BIT] =
- { LM_CLASS_PAUSE },
- [ETHTOOL_LINK_MODE_Asym_Pause_BIT] =
- { LM_CLASS_PAUSE },
- [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] =
- { LM_CLASS_REAL, 2500, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_Backplane_BIT] =
- { LM_CLASS_PORT },
- [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] =
- { LM_CLASS_REAL, 1000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] =
- { LM_CLASS_REAL, 10000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] =
- { LM_CLASS_REAL, 10000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] =
- { LM_CLASS_REAL, 10000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] =
- { LM_CLASS_REAL, 20000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] =
- { LM_CLASS_REAL, 20000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] =
- { LM_CLASS_REAL, 40000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] =
- { LM_CLASS_REAL, 40000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] =
- { LM_CLASS_REAL, 40000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] =
- { LM_CLASS_REAL, 40000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] =
- { LM_CLASS_REAL, 56000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] =
- { LM_CLASS_REAL, 56000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] =
- { LM_CLASS_REAL, 56000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] =
- { LM_CLASS_REAL, 56000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] =
- { LM_CLASS_REAL, 25000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] =
- { LM_CLASS_REAL, 25000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] =
- { LM_CLASS_REAL, 25000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] =
- { LM_CLASS_REAL, 50000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] =
- { LM_CLASS_REAL, 50000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] =
- { LM_CLASS_REAL, 100000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] =
- { LM_CLASS_REAL, 100000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] =
- { LM_CLASS_REAL, 100000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] =
- { LM_CLASS_REAL, 100000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] =
- { LM_CLASS_REAL, 50000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] =
- { LM_CLASS_REAL, 1000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] =
- { LM_CLASS_REAL, 10000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] =
- { LM_CLASS_REAL, 10000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] =
- { LM_CLASS_REAL, 10000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] =
- { LM_CLASS_REAL, 10000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] =
- { LM_CLASS_REAL, 10000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] =
- { LM_CLASS_REAL, 2500, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] =
- { LM_CLASS_REAL, 5000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_FEC_NONE_BIT] =
- { LM_CLASS_FEC },
- [ETHTOOL_LINK_MODE_FEC_RS_BIT] =
- { LM_CLASS_FEC },
- [ETHTOOL_LINK_MODE_FEC_BASER_BIT] =
- { LM_CLASS_FEC },
- [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] =
- { LM_CLASS_REAL, 50000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] =
- { LM_CLASS_REAL, 50000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] =
- { LM_CLASS_REAL, 50000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] =
- { LM_CLASS_REAL, 50000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] =
- { LM_CLASS_REAL, 50000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] =
- { LM_CLASS_REAL, 100000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] =
- { LM_CLASS_REAL, 100000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] =
- { LM_CLASS_REAL, 100000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] =
- { LM_CLASS_REAL, 100000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] =
- { LM_CLASS_REAL, 100000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] =
- { LM_CLASS_REAL, 200000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] =
- { LM_CLASS_REAL, 200000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] =
- { LM_CLASS_REAL, 200000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] =
- { LM_CLASS_REAL, 200000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] =
- { LM_CLASS_REAL, 200000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_100baseT1_Full_BIT] =
- { LM_CLASS_REAL, 100, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] =
- { LM_CLASS_REAL, 1000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] =
- { LM_CLASS_REAL, 400000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] =
- { LM_CLASS_REAL, 400000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] =
- { LM_CLASS_REAL, 400000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] =
- { LM_CLASS_REAL, 400000, DUPLEX_FULL },
- [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] =
- { LM_CLASS_REAL, 400000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = __HALF_DUPLEX(10),
+ [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = __REAL(10),
+ [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = __HALF_DUPLEX(100),
+ [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = __REAL(100),
+ [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = __HALF_DUPLEX(1000),
+ [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = __REAL(1000),
+ [ETHTOOL_LINK_MODE_Autoneg_BIT] = __SPECIAL(AUTONEG),
+ [ETHTOOL_LINK_MODE_TP_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_AUI_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_MII_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_FIBRE_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_BNC_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_Pause_BIT] = __SPECIAL(PAUSE),
+ [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = __SPECIAL(PAUSE),
+ [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = __REAL(2500),
+ [ETHTOOL_LINK_MODE_Backplane_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = __REAL(1000),
+ [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = __REAL(20000),
+ [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = __REAL(20000),
+ [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = __REAL(40000),
+ [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = __REAL(40000),
+ [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = __REAL(40000),
+ [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = __REAL(40000),
+ [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = __REAL(56000),
+ [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = __REAL(56000),
+ [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = __REAL(56000),
+ [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = __REAL(56000),
+ [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = __REAL(25000),
+ [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = __REAL(25000),
+ [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = __REAL(25000),
+ [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = __REAL(1000),
+ [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = __REAL(2500),
+ [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = __REAL(5000),
+ [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = __SPECIAL(FEC),
+ [ETHTOOL_LINK_MODE_FEC_RS_BIT] = __SPECIAL(FEC),
+ [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = __SPECIAL(FEC),
+ [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_100baseT1_Full_BIT] = __REAL(100),
+ [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] = __REAL(1000),
+ [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_FEC_LLRS_BIT] = __SPECIAL(FEC),
+ [ETHTOOL_LINK_MODE_100000baseKR_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseSR_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseCR_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseDR_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_100baseFX_Half_BIT] = __HALF_DUPLEX(100),
+ [ETHTOOL_LINK_MODE_100baseFX_Full_BIT] = __REAL(100),
};
const unsigned int link_modes_count = ARRAY_SIZE(link_modes);
+#undef __REAL
+#undef __HALF_DUPLEX
+#undef __SPECIAL
+
static bool lm_class_match(unsigned int mode, enum link_mode_class class)
{
unsigned int mode_class = (mode < link_modes_count) ?
@@ -261,25 +222,33 @@ static void print_banner(struct nl_context *nlctx)
nlctx->no_banner = true;
}
-static int dump_link_modes(struct nl_context *nlctx,
- const struct nlattr *bitset, bool mask,
- unsigned int class, const char *before,
- const char *between, const char *after,
- const char *if_none)
+int dump_link_modes(struct nl_context *nlctx, const struct nlattr *bitset,
+ bool mask, unsigned int class, const char *before,
+ const char *between, const char *after, const char *if_none)
{
const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {};
DECLARE_ATTR_TB_INFO(bitset_tb);
const unsigned int before_len = strlen(before);
+ unsigned int prev = UINT_MAX - 1;
const struct nlattr *bits;
const struct nlattr *bit;
bool first = true;
- int prev = -2;
+ bool nomask;
int ret;
ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
- bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
if (ret < 0)
goto err_nonl;
+
+ nomask = bitset_tb[ETHTOOL_A_BITSET_NOMASK];
+ /* Trying to print the mask of a "no mask" bitset doesn't make sense */
+ if (mask && nomask) {
+ ret = -EFAULT;
+ goto err_nonl;
+ }
+
+ bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
+
if (!bits) {
const struct stringset *lm_strings;
unsigned int count;
@@ -317,7 +286,7 @@ static int dump_link_modes(struct nl_context *nlctx,
if (first)
first = false;
/* ugly hack to preserve old output format */
- if (class == LM_CLASS_REAL && (prev == idx - 1) &&
+ if (class == LM_CLASS_REAL && (idx == prev + 1) &&
prev < link_modes_count &&
link_modes[prev].class == LM_CLASS_REAL &&
link_modes[prev].duplex == DUPLEX_HALF)
@@ -348,7 +317,7 @@ static int dump_link_modes(struct nl_context *nlctx,
if (!tb[ETHTOOL_A_BITSET_BIT_INDEX] ||
!tb[ETHTOOL_A_BITSET_BIT_NAME])
goto err;
- if (!mask && !tb[ETHTOOL_A_BITSET_BIT_VALUE])
+ if (!mask && !nomask && !tb[ETHTOOL_A_BITSET_BIT_VALUE])
continue;
idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);
@@ -359,7 +328,7 @@ static int dump_link_modes(struct nl_context *nlctx,
first = false;
} else {
/* ugly hack to preserve old output format */
- if ((class == LM_CLASS_REAL) && (prev == idx - 1) &&
+ if ((class == LM_CLASS_REAL) && (idx == prev + 1) &&
(prev < link_modes_count) &&
(link_modes[prev].class == LM_CLASS_REAL) &&
(link_modes[prev].duplex == DUPLEX_HALF))
@@ -463,9 +432,9 @@ static int dump_peer_modes(struct nl_context *nlctx, const struct nlattr *attr)
printf("\tLink partner advertised auto-negotiation: %s\n",
autoneg ? "Yes" : "No");
- ret = dump_link_modes(nlctx, attr, true, LM_CLASS_FEC,
+ ret = dump_link_modes(nlctx, attr, false, LM_CLASS_FEC,
"Link partner advertised FEC modes: ",
- " ", "\n", "No");
+ " ", "\n", "Not reported");
return ret;
}
@@ -518,6 +487,25 @@ int linkmodes_reply_cb(const struct nlmsghdr *nlhdr, void *data)
printf("\tAuto-negotiation: %s\n",
(autoneg == AUTONEG_DISABLE) ? "off" : "on");
}
+ if (tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]) {
+ uint8_t val;
+
+ val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]);
+
+ print_banner(nlctx);
+ print_enum(names_master_slave_cfg,
+ ARRAY_SIZE(names_master_slave_cfg), val,
+ "master-slave cfg");
+ }
+ if (tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE]) {
+ uint8_t val;
+
+ val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE]);
+ print_banner(nlctx);
+ print_enum(names_master_slave_state,
+ ARRAY_SIZE(names_master_slave_state), val,
+ "master-slave status");
+ }
return MNL_CB_OK;
err:
@@ -579,6 +567,149 @@ int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data)
return MNL_CB_OK;
}
+static const char *get_enum_string(const char *const *string_table, unsigned int n_string_table,
+ unsigned int val)
+{
+ if (val >= n_string_table || !string_table[val])
+ return NULL;
+ else
+ return string_table[val];
+}
+
+static const char *const names_link_ext_state[] = {
+ [ETHTOOL_LINK_EXT_STATE_AUTONEG] = "Autoneg",
+ [ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE] = "Link training failure",
+ [ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH] = "Logical mismatch",
+ [ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY] = "Bad signal integrity",
+ [ETHTOOL_LINK_EXT_STATE_NO_CABLE] = "No cable",
+ [ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE] = "Cable issue",
+ [ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE] = "EEPROM issue",
+ [ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE] = "Calibration failure",
+ [ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED] = "Power budget exceeded",
+ [ETHTOOL_LINK_EXT_STATE_OVERHEAT] = "Overheat",
+};
+
+static const char *const names_autoneg_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED] =
+ "No partner detected",
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED] =
+ "Ack not received",
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED] =
+ "Next page exchange failed",
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE] =
+ "No partner detected during force mode",
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE] =
+ "FEC mismatch during override",
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD] =
+ "No HCD",
+};
+
+static const char *const names_link_training_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED] =
+ "KR frame lock not acquired",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT] =
+ "KR link inhibit timeout",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY] =
+ "KR Link partner did not set receiver ready",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT] =
+ "Remote side is not ready yet",
+};
+
+static const char *const names_link_logical_mismatch_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK] =
+ "PCS did not acquire block lock",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK] =
+ "PCS did not acquire AM lock",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS] =
+ "PCS did not get align_status",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED] =
+ "FC FEC is not locked",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED] =
+ "RS FEC is not locked",
+};
+
+static const char *const names_bad_signal_integrity_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS] =
+ "Large number of physical errors",
+ [ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE] =
+ "Unsupported rate",
+};
+
+static const char *const names_cable_issue_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE] =
+ "Unsupported cable",
+ [ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE] =
+ "Cable test failure",
+};
+
+static const char *link_ext_substate_get(uint8_t link_ext_state_val, uint8_t link_ext_substate_val)
+{
+ switch (link_ext_state_val) {
+ case ETHTOOL_LINK_EXT_STATE_AUTONEG:
+ return get_enum_string(names_autoneg_link_ext_substate,
+ ARRAY_SIZE(names_autoneg_link_ext_substate),
+ link_ext_substate_val);
+ case ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE:
+ return get_enum_string(names_link_training_link_ext_substate,
+ ARRAY_SIZE(names_link_training_link_ext_substate),
+ link_ext_substate_val);
+ case ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH:
+ return get_enum_string(names_link_logical_mismatch_link_ext_substate,
+ ARRAY_SIZE(names_link_logical_mismatch_link_ext_substate),
+ link_ext_substate_val);
+ case ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY:
+ return get_enum_string(names_bad_signal_integrity_link_ext_substate,
+ ARRAY_SIZE(names_bad_signal_integrity_link_ext_substate),
+ link_ext_substate_val);
+ case ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE:
+ return get_enum_string(names_cable_issue_link_ext_substate,
+ ARRAY_SIZE(names_cable_issue_link_ext_substate),
+ link_ext_substate_val);
+ default:
+ return NULL;
+ }
+}
+
+static void linkstate_link_ext_substate_print(const struct nlattr *tb[],
+ uint8_t link_ext_state_val)
+{
+ uint8_t link_ext_substate_val;
+ const char *link_ext_substate_str;
+
+ if (!tb[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE])
+ return;
+
+ link_ext_substate_val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE]);
+
+ link_ext_substate_str = link_ext_substate_get(link_ext_state_val, link_ext_substate_val);
+ if (!link_ext_substate_str)
+ printf(", %u", link_ext_substate_val);
+ else
+ printf(", %s", link_ext_substate_str);
+}
+
+static void linkstate_link_ext_state_print(const struct nlattr *tb[])
+{
+ uint8_t link_ext_state_val;
+ const char *link_ext_state_str;
+
+ if (!tb[ETHTOOL_A_LINKSTATE_EXT_STATE])
+ return;
+
+ link_ext_state_val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_EXT_STATE]);
+
+ link_ext_state_str = get_enum_string(names_link_ext_state,
+ ARRAY_SIZE(names_link_ext_state),
+ link_ext_state_val);
+ if (!link_ext_state_str)
+ printf(" (%u", link_ext_state_val);
+ else
+ printf(" (%s", link_ext_state_str);
+
+ linkstate_link_ext_substate_print(tb, link_ext_state_val);
+ printf(")");
+}
+
int linkstate_reply_cb(const struct nlmsghdr *nlhdr, void *data)
{
const struct nlattr *tb[ETHTOOL_A_LINKSTATE_MAX + 1] = {};
@@ -599,13 +730,32 @@ int linkstate_reply_cb(const struct nlmsghdr *nlhdr, void *data)
uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_LINK]);
print_banner(nlctx);
- printf("\tLink detected: %s\n", val ? "yes" : "no");
+ printf("\tLink detected: %s", val ? "yes" : "no");
+ linkstate_link_ext_state_print(tb);
+ printf("\n");
+ }
+
+ if (tb[ETHTOOL_A_LINKSTATE_SQI]) {
+ uint32_t val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_SQI]);
+
+ print_banner(nlctx);
+ printf("\tSQI: %u", val);
+
+ if (tb[ETHTOOL_A_LINKSTATE_SQI_MAX]) {
+ uint32_t max;
+
+ max = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_SQI_MAX]);
+ printf("/%u\n", max);
+ } else {
+ printf("\n");
+ }
}
return MNL_CB_OK;
}
-void wol_modes_cb(unsigned int idx, const char *name, bool val, void *data)
+void wol_modes_cb(unsigned int idx, const char *name __maybe_unused, bool val,
+ void *data)
{
struct ethtool_wolinfo *wol = data;
@@ -653,7 +803,8 @@ int wol_reply_cb(const struct nlmsghdr *nlhdr, void *data)
return MNL_CB_OK;
}
-void msgmask_cb(unsigned int idx, const char *name, bool val, void *data)
+void msgmask_cb(unsigned int idx, const char *name __maybe_unused, bool val,
+ void *data)
{
u32 *msg_mask = data;
@@ -663,7 +814,8 @@ void msgmask_cb(unsigned int idx, const char *name, bool val, void *data)
*msg_mask |= (1U << idx);
}
-void msgmask_cb2(unsigned int idx, const char *name, bool val, void *data)
+void msgmask_cb2(unsigned int idx __maybe_unused, const char *name,
+ bool val, void *data __maybe_unused)
{
if (val)
printf(" %s", name);
@@ -726,6 +878,13 @@ int nl_gset(struct cmd_context *ctx)
struct nl_socket *nlsk = nlctx->ethnl_socket;
int ret;
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_LINKMODES_GET, true) ||
+ netlink_cmd_check(ctx, ETHTOOL_MSG_LINKINFO_GET, true) ||
+ netlink_cmd_check(ctx, ETHTOOL_MSG_WOL_GET, true) ||
+ netlink_cmd_check(ctx, ETHTOOL_MSG_DEBUG_GET, true) ||
+ netlink_cmd_check(ctx, ETHTOOL_MSG_LINKSTATE_GET, true))
+ return -EOPNOTSUPP;
+
nlctx->suppress_nlerr = 1;
ret = gset_request(nlsk, ETHTOOL_MSG_LINKMODES_GET,
@@ -818,6 +977,14 @@ static const struct lookup_entry_u32 duplex_values[] = {
{}
};
+static const struct lookup_entry_u8 master_slave_values[] = {
+ { .arg = "preferred-master", .val = MASTER_SLAVE_CFG_MASTER_PREFERRED },
+ { .arg = "preferred-slave", .val = MASTER_SLAVE_CFG_SLAVE_PREFERRED },
+ { .arg = "forced-master", .val = MASTER_SLAVE_CFG_MASTER_FORCE },
+ { .arg = "forced-slave", .val = MASTER_SLAVE_CFG_SLAVE_FORCE },
+ {}
+};
+
char wol_bit_chars[WOL_MODE_COUNT] = {
[WAKE_PHY_BIT] = 'p',
[WAKE_UCAST_BIT] = 'u',
@@ -909,6 +1076,14 @@ static const struct param_parser sset_params[] = {
.min_argc = 1,
},
{
+ .arg = "master-slave",
+ .group = ETHTOOL_MSG_LINKMODES_SET,
+ .type = ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG,
+ .handler = nl_parse_lookup_u8,
+ .handler_data = master_slave_values,
+ .min_argc = 1,
+ },
+ {
.arg = "wol",
.group = ETHTOOL_MSG_WOL_SET,
.type = ETHTOOL_A_WOL_MODES,
@@ -935,9 +1110,103 @@ static const struct param_parser sset_params[] = {
{}
};
+/* Maximum number of request messages sent to kernel; must be equal to the
+ * number of different .group values in sset_params[] array.
+ */
+#define SSET_MAX_MSGS 4
+
+static int linkmodes_reply_advert_all_cb(const struct nlmsghdr *nlhdr,
+ void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_msg_buff *req_msgbuff = data;
+ const struct nlattr *ours_attr;
+ struct nlattr *req_bitset;
+ uint32_t *supported_modes;
+ unsigned int modes_count;
+ unsigned int i;
+ int ret;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+ ours_attr = tb[ETHTOOL_A_LINKMODES_OURS];
+ if (!ours_attr)
+ return MNL_CB_ERROR;
+ modes_count = bitset_get_count(tb[ETHTOOL_A_LINKMODES_OURS], &ret);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+ supported_modes = get_compact_bitset_mask(tb[ETHTOOL_A_LINKMODES_OURS]);
+ if (!supported_modes)
+ return MNL_CB_ERROR;
+
+ /* keep only "real" link modes */
+ for (i = 0; i < modes_count; i++)
+ if (!lm_class_match(i, LM_CLASS_REAL))
+ supported_modes[i / 32] &= ~((uint32_t)1 << (i % 32));
+
+ req_bitset = ethnla_nest_start(req_msgbuff, ETHTOOL_A_LINKMODES_OURS);
+ if (!req_bitset)
+ return MNL_CB_ERROR;
+
+ if (ethnla_put_u32(req_msgbuff, ETHTOOL_A_BITSET_SIZE, modes_count) ||
+ ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_VALUE,
+ DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t),
+ supported_modes) ||
+ ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_MASK,
+ DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t),
+ supported_modes)) {
+ ethnla_nest_cancel(req_msgbuff, req_bitset);
+ return MNL_CB_ERROR;
+ }
+
+ ethnla_nest_end(req_msgbuff, req_bitset);
+ return MNL_CB_OK;
+}
+
+/* For compatibility reasons with ioctl-based ethtool, when "autoneg on" is
+ * specified without "advertise", "speed" and "duplex", we need to query the
+ * supported link modes from the kernel and advertise all the "real" ones.
+ */
+static int nl_sset_compat_linkmodes(struct nl_context *nlctx,
+ struct nl_msg_buff *msgbuff)
+{
+ const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ ret = mnl_attr_parse(msgbuff->nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ if (!tb[ETHTOOL_A_LINKMODES_AUTONEG] || tb[ETHTOOL_A_LINKMODES_OURS] ||
+ tb[ETHTOOL_A_LINKMODES_SPEED] || tb[ETHTOOL_A_LINKMODES_DUPLEX])
+ return 0;
+ if (!mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_AUTONEG]))
+ return 0;
+
+ /* all conditions satisfied, create ETHTOOL_A_LINKMODES_OURS */
+ if (netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_GET, false) ||
+ netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_SET, false))
+ return -EOPNOTSUPP;
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_LINKMODES_GET,
+ ETHTOOL_A_LINKMODES_HEADER,
+ ETHTOOL_FLAG_COMPACT_BITSETS);
+ if (ret < 0)
+ return ret;
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return ret;
+ return nlsock_process_reply(nlsk, linkmodes_reply_advert_all_cb,
+ msgbuff);
+}
+
int nl_sset(struct cmd_context *ctx)
{
+ struct nl_msg_buff *msgbuffs[SSET_MAX_MSGS] = {};
struct nl_context *nlctx = ctx->nlctx;
+ unsigned int i;
int ret;
nlctx->cmd = "-s";
@@ -945,11 +1214,34 @@ int nl_sset(struct cmd_context *ctx)
nlctx->argc = ctx->argc;
nlctx->devname = ctx->devname;
- ret = nl_parser(nlctx, sset_params, NULL, PARSER_GROUP_MSG);
- if (ret < 0)
- return 1;
+ ret = nl_parser(nlctx, sset_params, NULL, PARSER_GROUP_MSG, msgbuffs);
+ if (ret < 0) {
+ ret = 1;
+ goto out_free;
+ }
- if (ret == 0)
- return 0;
+ for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) {
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+
+ if (msgbuffs[i]->genlhdr->cmd == ETHTOOL_MSG_LINKMODES_SET) {
+ ret = nl_sset_compat_linkmodes(nlctx, msgbuffs[i]);
+ if (ret < 0)
+ goto out_free;
+ }
+ ret = nlsock_sendmsg(nlsk, msgbuffs[i]);
+ if (ret < 0)
+ goto out_free;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, NULL);
+ if (ret < 0)
+ goto out_free;
+ }
+
+out_free:
+ for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) {
+ msgbuff_done(msgbuffs[i]);
+ free(msgbuffs[i]);
+ }
+ if (ret >= 0)
+ return ret;
return nlctx->exit_code ?: 75;
}
diff --git a/netlink/tsinfo.c b/netlink/tsinfo.c
new file mode 100644
index 0000000..c6571ff
--- /dev/null
+++ b/netlink/tsinfo.c
@@ -0,0 +1,124 @@
+/*
+ * tsinfo.c - netlink implementation of timestamping commands
+ *
+ * Implementation of "ethtool -T <dev>"
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+
+/* TSINFO_GET */
+
+static void tsinfo_dump_cb(unsigned int idx, const char *name, bool val,
+ void *data __maybe_unused)
+{
+ if (!val)
+ return;
+
+ if (name)
+ printf("\t%s\n", name);
+ else
+ printf("\tbit%u\n", idx);
+}
+
+static int tsinfo_dump_list(struct nl_context *nlctx, const struct nlattr *attr,
+ const char *label, const char *if_empty,
+ unsigned int stringset_id)
+{
+ const struct stringset *strings = NULL;
+ int ret;
+
+ printf("%s:", label);
+ ret = 0;
+ if (!attr || bitset_is_empty(attr, false, &ret)) {
+ printf("%s\n", if_empty);
+ return ret;
+ }
+ putchar('\n');
+ if (ret < 0)
+ return ret;
+
+ if (bitset_is_compact(attr)) {
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return ret;
+ strings = global_stringset(stringset_id, nlctx->ethnl2_socket);
+ }
+ return walk_bitset(attr, strings, tsinfo_dump_cb, NULL);
+}
+
+int tsinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_TSINFO_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_TSINFO_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ putchar('\n');
+ printf("Time stamping parameters for %s:\n", nlctx->devname);
+
+ ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_TIMESTAMPING],
+ "Capabilities", "", ETH_SS_SOF_TIMESTAMPING);
+ if (ret < 0)
+ return err_ret;
+
+ printf("PTP Hardware Clock: ");
+ if (tb[ETHTOOL_A_TSINFO_PHC_INDEX])
+ printf("%d\n",
+ mnl_attr_get_u32(tb[ETHTOOL_A_TSINFO_PHC_INDEX]));
+ else
+ printf("none\n");
+
+ ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_TX_TYPES],
+ "Hardware Transmit Timestamp Modes", " none",
+ ETH_SS_TS_TX_TYPES);
+ if (ret < 0)
+ return err_ret;
+
+ ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_RX_FILTERS],
+ "Hardware Receive Filter Modes", " none",
+ ETH_SS_TS_RX_FILTERS);
+ if (ret < 0)
+ return err_ret;
+
+ return MNL_CB_OK;
+}
+
+int nl_tsinfo(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_TSINFO_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_TSINFO_GET,
+ ETHTOOL_A_TSINFO_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, tsinfo_reply_cb);
+}
diff --git a/netlink/tunnels.c b/netlink/tunnels.c
new file mode 100644
index 0000000..b464046
--- /dev/null
+++ b/netlink/tunnels.c
@@ -0,0 +1,236 @@
+/*
+ * tunnel.c - device tunnel information
+ *
+ * Implementation of "ethtool --show-tunnels <dev>"
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+
+#include "../common.h"
+#include "../internal.h"
+
+#include "bitset.h"
+#include "netlink.h"
+#include "strset.h"
+
+/* TUNNEL_INFO_GET */
+
+static int
+tunnel_info_strings_load(struct nl_context *nlctx,
+ const struct stringset **strings)
+{
+ int ret;
+
+ if (*strings)
+ return 0;
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return ret;
+ *strings = global_stringset(ETH_SS_UDP_TUNNEL_TYPES,
+ nlctx->ethnl2_socket);
+ return 0;
+}
+
+static int
+tunnel_info_dump_udp_entry(struct nl_context *nlctx, const struct nlattr *entry,
+ const struct stringset **strings)
+{
+ const struct nlattr *entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(entry_tb);
+ const struct nlattr *attr;
+ unsigned int port, type;
+ int ret;
+
+ if (tunnel_info_strings_load(nlctx, strings))
+ return 1;
+
+ ret = mnl_attr_parse_nested(entry, attr_cb, &entry_tb_info);
+ if (ret < 0) {
+ fprintf(stderr, "malformed netlink message (udp entry)\n");
+ return 1;
+ }
+
+ attr = entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT];
+ if (!attr || mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
+ fprintf(stderr, "malformed netlink message (port)\n");
+ return 1;
+ }
+ port = ntohs(mnl_attr_get_u16(attr));
+
+ attr = entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE];
+ if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ fprintf(stderr, "malformed netlink message (tunnel type)\n");
+ return 1;
+ }
+ type = mnl_attr_get_u32(attr);
+
+ printf(" port %d, %s\n", port, get_string(*strings, type));
+
+ return 0;
+}
+
+static void tunnel_info_dump_cb(unsigned int idx, const char *name, bool val,
+ void *data)
+{
+ bool *first = data;
+
+ if (!val)
+ return;
+
+ if (!*first)
+ putchar(',');
+ *first = false;
+
+ if (name)
+ printf(" %s", name);
+ else
+ printf(" bit%u", idx);
+}
+
+static int
+tunnel_info_dump_list(struct nl_context *nlctx, const struct nlattr *attr,
+ const struct stringset **strings)
+{
+ bool first = true;
+ int ret;
+
+ if (bitset_is_empty(attr, false, &ret) || ret) {
+ if (ret)
+ return 1;
+
+ printf(" Types: none (static entries)\n");
+ return 0;
+ }
+
+ if (bitset_is_compact(attr) &&
+ tunnel_info_strings_load(nlctx, strings))
+ return 1;
+
+ printf(" Types:");
+ ret = walk_bitset(attr, *strings, tunnel_info_dump_cb, &first);
+ putchar('\n');
+ return ret;
+}
+
+static int
+tunnel_info_dump_udp_table(const struct nlattr *table, struct nl_context *nlctx,
+ const struct stringset **strings)
+{
+ const struct nlattr *table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(table_tb);
+ const struct nlattr *attr;
+ int i, ret;
+
+ ret = mnl_attr_parse_nested(table, attr_cb, &table_tb_info);
+ if (ret < 0) {
+ fprintf(stderr, "malformed netlink message (udp table)\n");
+ return 1;
+ }
+
+ attr = table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE];
+ if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ fprintf(stderr, "malformed netlink message (table size)\n");
+ return 1;
+ }
+ printf(" Size: %d\n", mnl_attr_get_u32(attr));
+
+ attr = table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES];
+ if (!attr || tunnel_info_dump_list(nlctx, attr, strings)) {
+ fprintf(stderr, "malformed netlink message (types)\n");
+ return 1;
+ }
+
+ if (!table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY]) {
+ printf(" No entries\n");
+ return 0;
+ }
+
+ i = 0;
+ mnl_attr_for_each_nested(attr, table) {
+ if (mnl_attr_get_type(attr) == ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY)
+ i++;
+ }
+
+ printf(" Entries (%d):\n", i++);
+ mnl_attr_for_each_nested(attr, table) {
+ if (mnl_attr_get_type(attr) == ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY)
+ if (tunnel_info_dump_udp_entry(nlctx, attr, strings))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+tunnel_info_dump_udp(const struct nlattr *udp_ports, struct nl_context *nlctx)
+{
+ const struct stringset *strings = NULL;
+ const struct nlattr *table;
+ int i, err;
+
+ i = 0;
+ mnl_attr_for_each_nested(table, udp_ports) {
+ if (mnl_attr_get_type(table) != ETHTOOL_A_TUNNEL_UDP_TABLE)
+ continue;
+
+ printf(" UDP port table %d: \n", i++);
+ err = tunnel_info_dump_udp_table(table, nlctx, &strings);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int tunnel_info_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_TUNNEL_INFO_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const struct nlattr *udp_ports;
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_TUNNEL_INFO_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ printf("Tunnel information for %s:\n", nlctx->devname);
+
+ udp_ports = tb[ETHTOOL_A_TUNNEL_INFO_UDP_PORTS];
+ if (udp_ports)
+ if (tunnel_info_dump_udp(udp_ports, nlctx))
+ return err_ret;
+
+ return MNL_CB_OK;
+}
+
+int nl_gtunnels(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_TUNNEL_INFO_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_TUNNEL_INFO_GET,
+ ETHTOOL_A_TUNNEL_INFO_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, tunnel_info_reply_cb);
+}
diff --git a/qsfp-dd.c b/qsfp-dd.c
new file mode 100644
index 0000000..900bbb5
--- /dev/null
+++ b/qsfp-dd.c
@@ -0,0 +1,333 @@
+/**
+ * Description:
+ *
+ * This module adds QSFP-DD support to ethtool. The changes are similar to
+ * the ones already existing in qsfp.c, but customized to use the memory
+ * addresses and logic as defined in the specification's document.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include "internal.h"
+#include "sff-common.h"
+#include "qsfp-dd.h"
+
+static void qsfp_dd_show_identifier(const __u8 *id)
+{
+ sff8024_show_identifier(id, QSFP_DD_ID_OFFSET);
+}
+
+static void qsfp_dd_show_connector(const __u8 *id)
+{
+ sff8024_show_connector(id, QSFP_DD_CTOR_OFFSET);
+}
+
+static void qsfp_dd_show_oui(const __u8 *id)
+{
+ sff8024_show_oui(id, QSFP_DD_VENDOR_OUI_OFFSET);
+}
+
+/**
+ * Print the revision compliance. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 45, section 1.7.2.1, Table 18
+ * [2] CMIS Rev. 4, pag. 81, section 8.2.1, Table 8-2
+ */
+static void qsfp_dd_show_rev_compliance(const __u8 *id)
+{
+ __u8 rev = id[QSFP_DD_REV_COMPLIANCE_OFFSET];
+ int major = (rev >> 4) & 0x0F;
+ int minor = rev & 0x0F;
+
+ printf("\t%-41s : Rev. %d.%d\n", "Revision compliance", major, minor);
+}
+
+/**
+ * Print information about the device's power consumption.
+ * Relevant documents:
+ * [1] CMIS Rev. 3, pag. 59, section 1.7.3.9, Table 30
+ * [2] CMIS Rev. 4, pag. 94, section 8.3.9, Table 8-18
+ * [3] QSFP-DD Hardware Rev 5.0, pag. 22, section 4.2.1
+ */
+static void qsfp_dd_show_power_info(const __u8 *id)
+{
+ float max_power = 0.0f;
+ __u8 base_power = 0;
+ __u8 power_class;
+
+ /* Get the power class (first 3 most significat bytes) */
+ power_class = (id[QSFP_DD_PWR_CLASS_OFFSET] >> 5) & 0x07;
+
+ /* Get the base power in multiples of 0.25W */
+ base_power = id[QSFP_DD_PWR_MAX_POWER_OFFSET];
+ max_power = base_power * 0.25f;
+
+ printf("\t%-41s : %d\n", "Power class", power_class + 1);
+ printf("\t%-41s : %.02fW\n", "Max power", max_power);
+}
+
+/**
+ * Print the cable assembly length, for both passive copper and active
+ * optical or electrical cables. The base length (bits 5-0) must be
+ * multiplied with the SMF length multiplier (bits 7-6) to obtain the
+ * correct value. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 59, section 1.7.3.10, Table 31
+ * [2] CMIS Rev. 4, pag. 94, section 8.3.10, Table 8-19
+ */
+static void qsfp_dd_show_cbl_asm_len(const __u8 *id)
+{
+ static const char *fn = "Cable assembly length";
+ float mul = 1.0f;
+ float val = 0.0f;
+
+ /* Check if max length */
+ if (id[QSFP_DD_CBL_ASM_LEN_OFFSET] == QSFP_DD_6300M_MAX_LEN) {
+ printf("\t%-41s : > 6.3km\n", fn);
+ return;
+ }
+
+ /* Get the multiplier from the first two bits */
+ switch (id[QSFP_DD_CBL_ASM_LEN_OFFSET] & QSFP_DD_LEN_MUL_MASK) {
+ case QSFP_DD_MULTIPLIER_00:
+ mul = 0.1f;
+ break;
+ case QSFP_DD_MULTIPLIER_01:
+ mul = 1.0f;
+ break;
+ case QSFP_DD_MULTIPLIER_10:
+ mul = 10.0f;
+ break;
+ case QSFP_DD_MULTIPLIER_11:
+ mul = 100.0f;
+ break;
+ default:
+ break;
+ }
+
+ /* Get base value from first 6 bits and multiply by mul */
+ val = (id[QSFP_DD_CBL_ASM_LEN_OFFSET] & QSFP_DD_LEN_VAL_MASK);
+ val = (float)val * mul;
+ printf("\t%-41s : %0.2fm\n", fn, val);
+}
+
+/**
+ * Print the length for SMF fiber. The base length (bits 5-0) must be
+ * multiplied with the SMF length multiplier (bits 7-6) to obtain the
+ * correct value. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 63, section 1.7.4.2, Table 39
+ * [2] CMIS Rev. 4, pag. 99, section 8.4.2, Table 8-27
+ */
+static void qsfp_dd_print_smf_cbl_len(const __u8 *id)
+{
+ static const char *fn = "Length (SMF)";
+ float mul = 1.0f;
+ float val = 0.0f;
+
+ /* Get the multiplier from the first two bits */
+ switch (id[QSFP_DD_SMF_LEN_OFFSET] & QSFP_DD_LEN_MUL_MASK) {
+ case QSFP_DD_MULTIPLIER_00:
+ mul = 0.1f;
+ break;
+ case QSFP_DD_MULTIPLIER_01:
+ mul = 1.0f;
+ break;
+ default:
+ break;
+ }
+
+ /* Get base value from first 6 bits and multiply by mul */
+ val = (id[QSFP_DD_SMF_LEN_OFFSET] & QSFP_DD_LEN_VAL_MASK);
+ val = (float)val * mul;
+ printf("\t%-41s : %0.2fkm\n", fn, val);
+}
+
+/**
+ * Print relevant signal integrity control properties. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 71, section 1.7.4.10, Table 46
+ * [2] CMIS Rev. 4, pag. 105, section 8.4.10, Table 8-34
+ */
+static void qsfp_dd_show_sig_integrity(const __u8 *id)
+{
+ /* CDR Bypass control: 2nd bit from each byte */
+ printf("\t%-41s : ", "Tx CDR bypass control");
+ printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_TX_OFFSET] & 0x02));
+
+ printf("\t%-41s : ", "Rx CDR bypass control");
+ printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_RX_OFFSET] & 0x02));
+
+ /* CDR Implementation: 1st bit from each byte */
+ printf("\t%-41s : ", "Tx CDR");
+ printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_TX_OFFSET] & 0x01));
+
+ printf("\t%-41s : ", "Rx CDR");
+ printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_RX_OFFSET] & 0x01));
+}
+
+/**
+ * Print relevant media interface technology info. Relevant documents:
+ * [1] CMIS Rev. 3
+ * --> pag. 61, section 1.7.3.14, Table 36
+ * --> pag. 64, section 1.7.4.3, 1.7.4.4
+ * [2] CMIS Rev. 4
+ * --> pag. 97, section 8.3.14, Table 8-24
+ * --> pag. 98, section 8.4, Table 8-25
+ * --> page 100, section 8.4.3, 8.4.4
+ */
+static void qsfp_dd_show_mit_compliance(const __u8 *id)
+{
+ static const char *cc = " (Copper cable,";
+
+ printf("\t%-41s : 0x%02x", "Transmitter technology",
+ id[QSFP_DD_MEDIA_INTF_TECH_OFFSET]);
+
+ switch (id[QSFP_DD_MEDIA_INTF_TECH_OFFSET]) {
+ case QSFP_DD_850_VCSEL:
+ printf(" (850 nm VCSEL)\n");
+ break;
+ case QSFP_DD_1310_VCSEL:
+ printf(" (1310 nm VCSEL)\n");
+ break;
+ case QSFP_DD_1550_VCSEL:
+ printf(" (1550 nm VCSEL)\n");
+ break;
+ case QSFP_DD_1310_FP:
+ printf(" (1310 nm FP)\n");
+ break;
+ case QSFP_DD_1310_DFB:
+ printf(" (1310 nm DFB)\n");
+ break;
+ case QSFP_DD_1550_DFB:
+ printf(" (1550 nm DFB)\n");
+ break;
+ case QSFP_DD_1310_EML:
+ printf(" (1310 nm EML)\n");
+ break;
+ case QSFP_DD_1550_EML:
+ printf(" (1550 nm EML)\n");
+ break;
+ case QSFP_DD_OTHERS:
+ printf(" (Others/Undefined)\n");
+ break;
+ case QSFP_DD_1490_DFB:
+ printf(" (1490 nm DFB)\n");
+ break;
+ case QSFP_DD_COPPER_UNEQUAL:
+ printf("%s unequalized)\n", cc);
+ break;
+ case QSFP_DD_COPPER_PASS_EQUAL:
+ printf("%s passive equalized)\n", cc);
+ break;
+ case QSFP_DD_COPPER_NF_EQUAL:
+ printf("%s near and far end limiting active equalizers)\n", cc);
+ break;
+ case QSFP_DD_COPPER_F_EQUAL:
+ printf("%s far end limiting active equalizers)\n", cc);
+ break;
+ case QSFP_DD_COPPER_N_EQUAL:
+ printf("%s near end limiting active equalizers)\n", cc);
+ break;
+ case QSFP_DD_COPPER_LINEAR_EQUAL:
+ printf("%s linear active equalizers)\n", cc);
+ break;
+ }
+
+ if (id[QSFP_DD_MEDIA_INTF_TECH_OFFSET] >= QSFP_DD_COPPER_UNEQUAL) {
+ printf("\t%-41s : %udb\n", "Attenuation at 5GHz",
+ id[QSFP_DD_COPPER_ATT_5GHZ]);
+ printf("\t%-41s : %udb\n", "Attenuation at 7GHz",
+ id[QSFP_DD_COPPER_ATT_7GHZ]);
+ printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
+ id[QSFP_DD_COPPER_ATT_12P9GHZ]);
+ printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz",
+ id[QSFP_DD_COPPER_ATT_25P8GHZ]);
+ } else {
+ printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
+ (((id[QSFP_DD_NOM_WAVELENGTH_MSB] << 8) |
+ id[QSFP_DD_NOM_WAVELENGTH_LSB]) * 0.05));
+ printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
+ (((id[QSFP_DD_WAVELENGTH_TOL_MSB] << 8) |
+ id[QSFP_DD_WAVELENGTH_TOL_LSB]) * 0.005));
+ }
+}
+
+/*
+ * 2-byte internal temperature conversions:
+ * First byte is a signed 8-bit integer, which is the temp decimal part
+ * Second byte is a multiple of 1/256th of a degree, which is added to
+ * the dec part.
+ */
+#define OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
+
+/**
+ * Print relevant module level monitoring values. Relevant documents:
+ * [1] CMIS Rev. 3:
+ * --> pag. 50, section 1.7.2.4, Table 22
+ *
+ * [2] CMIS Rev. 4:
+ * --> pag. 84, section 8.2.4, Table 8-6
+ */
+static void qsfp_dd_show_mod_lvl_monitors(const __u8 *id)
+{
+ PRINT_TEMP("Module temperature",
+ OFFSET_TO_TEMP(QSFP_DD_CURR_TEMP_OFFSET));
+ PRINT_VCC("Module voltage",
+ OFFSET_TO_U16(QSFP_DD_CURR_CURR_OFFSET));
+}
+
+/**
+ * Print relevant info about the maximum supported fiber media length
+ * for each type of fiber media at the maximum module-supported bit rate.
+ * Relevant documents:
+ * [1] CMIS Rev. 3, page 64, section 1.7.4.2, Table 39
+ * [2] CMIS Rev. 4, page 99, section 8.4.2, Table 8-27
+ */
+static void qsfp_dd_show_link_len(const __u8 *id)
+{
+ qsfp_dd_print_smf_cbl_len(id);
+ sff_show_value_with_unit(id, QSFP_DD_OM5_LEN_OFFSET,
+ "Length (OM5)", 2, "m");
+ sff_show_value_with_unit(id, QSFP_DD_OM4_LEN_OFFSET,
+ "Length (OM4)", 2, "m");
+ sff_show_value_with_unit(id, QSFP_DD_OM3_LEN_OFFSET,
+ "Length (OM3 50/125um)", 2, "m");
+ sff_show_value_with_unit(id, QSFP_DD_OM2_LEN_OFFSET,
+ "Length (OM2 50/125um)", 1, "m");
+}
+
+/**
+ * Show relevant information about the vendor. Relevant documents:
+ * [1] CMIS Rev. 3, page 56, section 1.7.3, Table 27
+ * [2] CMIS Rev. 4, page 91, section 8.2, Table 8-15
+ */
+static void qsfp_dd_show_vendor_info(const __u8 *id)
+{
+ sff_show_ascii(id, QSFP_DD_VENDOR_NAME_START_OFFSET,
+ QSFP_DD_VENDOR_NAME_END_OFFSET, "Vendor name");
+ qsfp_dd_show_oui(id);
+ sff_show_ascii(id, QSFP_DD_VENDOR_PN_START_OFFSET,
+ QSFP_DD_VENDOR_PN_END_OFFSET, "Vendor PN");
+ sff_show_ascii(id, QSFP_DD_VENDOR_REV_START_OFFSET,
+ QSFP_DD_VENDOR_REV_END_OFFSET, "Vendor rev");
+ sff_show_ascii(id, QSFP_DD_VENDOR_SN_START_OFFSET,
+ QSFP_DD_VENDOR_SN_END_OFFSET, "Vendor SN");
+ sff_show_ascii(id, QSFP_DD_DATE_YEAR_OFFSET,
+ QSFP_DD_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+
+ if (id[QSFP_DD_CLEI_PRESENT_BYTE] & QSFP_DD_CLEI_PRESENT_MASK)
+ sff_show_ascii(id, QSFP_DD_CLEI_START_OFFSET,
+ QSFP_DD_CLEI_END_OFFSET, "CLEI code");
+}
+
+void qsfp_dd_show_all(const __u8 *id)
+{
+ qsfp_dd_show_identifier(id);
+ qsfp_dd_show_power_info(id);
+ qsfp_dd_show_connector(id);
+ qsfp_dd_show_cbl_asm_len(id);
+ qsfp_dd_show_sig_integrity(id);
+ qsfp_dd_show_mit_compliance(id);
+ qsfp_dd_show_mod_lvl_monitors(id);
+ qsfp_dd_show_link_len(id);
+ qsfp_dd_show_vendor_info(id);
+ qsfp_dd_show_rev_compliance(id);
+}
diff --git a/qsfp-dd.h b/qsfp-dd.h
new file mode 100644
index 0000000..f589c4e
--- /dev/null
+++ b/qsfp-dd.h
@@ -0,0 +1,125 @@
+#ifndef QSFP_DD_H__
+#define QSFP_DD_H__
+
+/* Identifier and revision compliance (Page 0) */
+#define QSFP_DD_ID_OFFSET 0x00
+#define QSFP_DD_REV_COMPLIANCE_OFFSET 0x01
+
+#define QSFP_DD_MODULE_TYPE_OFFSET 0x55
+#define QSFP_DD_MT_MMF 0x01
+#define QSFP_DD_MT_SMF 0x02
+
+/* Module-Level Monitors (Page 0) */
+#define QSFP_DD_CURR_TEMP_OFFSET 0x0E
+#define QSFP_DD_CURR_CURR_OFFSET 0x10
+
+#define QSFP_DD_CTOR_OFFSET 0xCB
+
+/* Vendor related information (Page 0) */
+#define QSFP_DD_VENDOR_NAME_START_OFFSET 0x81
+#define QSFP_DD_VENDOR_NAME_END_OFFSET 0x90
+
+#define QSFP_DD_VENDOR_OUI_OFFSET 0x91
+
+#define QSFP_DD_VENDOR_PN_START_OFFSET 0x94
+#define QSFP_DD_VENDOR_PN_END_OFFSET 0xA3
+
+#define QSFP_DD_VENDOR_REV_START_OFFSET 0xA4
+#define QSFP_DD_VENDOR_REV_END_OFFSET 0xA5
+
+#define QSFP_DD_VENDOR_SN_START_OFFSET 0xA6
+#define QSFP_DD_VENDOR_SN_END_OFFSET 0xB5
+
+#define QSFP_DD_DATE_YEAR_OFFSET 0xB6
+#define QSFP_DD_DATE_VENDOR_LOT_OFFSET 0xBC
+
+/* CLEI Code (Page 0) */
+#define QSFP_DD_CLEI_PRESENT_BYTE 0x02
+#define QSFP_DD_CLEI_PRESENT_MASK 0x20
+#define QSFP_DD_CLEI_START_OFFSET 0xBE
+#define QSFP_DD_CLEI_END_OFFSET 0xC7
+
+/* Cable assembly length */
+#define QSFP_DD_CBL_ASM_LEN_OFFSET 0xCA
+#define QSFP_DD_6300M_MAX_LEN 0xFF
+
+/* Cable length with multiplier */
+#define QSFP_DD_MULTIPLIER_00 0x00
+#define QSFP_DD_MULTIPLIER_01 0x40
+#define QSFP_DD_MULTIPLIER_10 0x80
+#define QSFP_DD_MULTIPLIER_11 0xC0
+#define QSFP_DD_LEN_MUL_MASK 0xC0
+#define QSFP_DD_LEN_VAL_MASK 0x3F
+
+/* Module power characteristics */
+#define QSFP_DD_PWR_CLASS_OFFSET 0xC8
+#define QSFP_DD_PWR_MAX_POWER_OFFSET 0xC9
+#define QSFP_DD_PWR_CLASS_MASK 0xE0
+#define QSFP_DD_PWR_CLASS_1 0x00
+#define QSFP_DD_PWR_CLASS_2 0x01
+#define QSFP_DD_PWR_CLASS_3 0x02
+#define QSFP_DD_PWR_CLASS_4 0x03
+#define QSFP_DD_PWR_CLASS_5 0x04
+#define QSFP_DD_PWR_CLASS_6 0x05
+#define QSFP_DD_PWR_CLASS_7 0x06
+#define QSFP_DD_PWR_CLASS_8 0x07
+
+/* Copper cable attenuation */
+#define QSFP_DD_COPPER_ATT_5GHZ 0xCC
+#define QSFP_DD_COPPER_ATT_7GHZ 0xCD
+#define QSFP_DD_COPPER_ATT_12P9GHZ 0xCE
+#define QSFP_DD_COPPER_ATT_25P8GHZ 0xCF
+
+/* Cable assembly lane */
+#define QSFP_DD_CABLE_ASM_NEAR_END_OFFSET 0xD2
+#define QSFP_DD_CABLE_ASM_FAR_END_OFFSET 0xD3
+
+/* Media interface technology */
+#define QSFP_DD_MEDIA_INTF_TECH_OFFSET 0xD4
+#define QSFP_DD_850_VCSEL 0x00
+#define QSFP_DD_1310_VCSEL 0x01
+#define QSFP_DD_1550_VCSEL 0x02
+#define QSFP_DD_1310_FP 0x03
+#define QSFP_DD_1310_DFB 0x04
+#define QSFP_DD_1550_DFB 0x05
+#define QSFP_DD_1310_EML 0x06
+#define QSFP_DD_1550_EML 0x07
+#define QSFP_DD_OTHERS 0x08
+#define QSFP_DD_1490_DFB 0x09
+#define QSFP_DD_COPPER_UNEQUAL 0x0A
+#define QSFP_DD_COPPER_PASS_EQUAL 0x0B
+#define QSFP_DD_COPPER_NF_EQUAL 0x0C
+#define QSFP_DD_COPPER_F_EQUAL 0x0D
+#define QSFP_DD_COPPER_N_EQUAL 0x0E
+#define QSFP_DD_COPPER_LINEAR_EQUAL 0x0F
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x01: contains advertising fields that define properties
+ * that are unique to active modules and cable assemblies.
+ * RealOffset = 1 * 0x80 + LocalOffset
+ */
+#define PAG01H_UPPER_OFFSET (0x01 * 0x80)
+
+/* Supported Link Length (Page 1) */
+#define QSFP_DD_SMF_LEN_OFFSET (PAG01H_UPPER_OFFSET + 0x84)
+#define QSFP_DD_OM5_LEN_OFFSET (PAG01H_UPPER_OFFSET + 0x85)
+#define QSFP_DD_OM4_LEN_OFFSET (PAG01H_UPPER_OFFSET + 0x86)
+#define QSFP_DD_OM3_LEN_OFFSET (PAG01H_UPPER_OFFSET + 0x87)
+#define QSFP_DD_OM2_LEN_OFFSET (PAG01H_UPPER_OFFSET + 0x88)
+
+/* Wavelength (Page 1) */
+#define QSFP_DD_NOM_WAVELENGTH_MSB (PAG01H_UPPER_OFFSET + 0x8A)
+#define QSFP_DD_NOM_WAVELENGTH_LSB (PAG01H_UPPER_OFFSET + 0x8B)
+#define QSFP_DD_WAVELENGTH_TOL_MSB (PAG01H_UPPER_OFFSET + 0x8C)
+#define QSFP_DD_WAVELENGTH_TOL_LSB (PAG01H_UPPER_OFFSET + 0x8D)
+
+/* Signal integrity controls */
+#define QSFP_DD_SIG_INTEG_TX_OFFSET (PAG01H_UPPER_OFFSET + 0xA1)
+#define QSFP_DD_SIG_INTEG_RX_OFFSET (PAG01H_UPPER_OFFSET + 0xA2)
+
+#define YESNO(x) (((x) != 0) ? "Yes" : "No")
+#define ONOFF(x) (((x) != 0) ? "On" : "Off")
+
+void qsfp_dd_show_all(const __u8 *id);
+
+#endif /* QSFP_DD_H__ */
diff --git a/qsfp.c b/qsfp.c
index d0774b0..3c1a300 100644
--- a/qsfp.c
+++ b/qsfp.c
@@ -58,6 +58,7 @@
#include "internal.h"
#include "sff-common.h"
#include "qsfp.h"
+#include "qsfp-dd.h"
#define MAX_DESC_SIZE 42
@@ -375,6 +376,107 @@ static void sff8636_show_transceiver(const __u8 *id)
printf("%s 100G Ethernet: 100G ACC or 25GAUI C2M ACC with worst BER of 10^(-12)\n",
pfx);
break;
+ case SFF8636_ETHERNET_100GE_DWDM2:
+ printf("%s 100GE-DWDM2 (DWDM transceiver using 2 wavelengths on a 1550 nm DWDM grid with a reach up to 80 km)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_100G_1550NM_WDM:
+ printf("%s 100G 1550nm WDM (4 wavelengths)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_10G_BASET_SR:
+ printf("%s 10GBASE-T Short Reach (30 meters)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_5G_BASET:
+ printf("%s 5GBASE-T\n", pfx);
+ break;
+ case SFF8636_ETHERNET_2HALFG_BASET:
+ printf("%s 2.5GBASE-T\n", pfx);
+ break;
+ case SFF8636_ETHERNET_40G_SWDM4:
+ printf("%s 40G SWDM4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_SWDM4:
+ printf("%s 100G SWDM4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_PAM4_BIDI:
+ printf("%s 100G PAM4 BiDi\n", pfx);
+ break;
+ case SFF8636_ETHERNET_4WDM10_MSA:
+ printf("%s 4WDM-10 MSA (10km version of 100G CWDM4 with same RS(528,514) FEC in host system)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_4WDM20_MSA:
+ printf("%s 4WDM-20 MSA (20km version of 100GBASE-LR4 with RS(528,514) FEC in host system)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_4WDM40_MSA:
+ printf("%s 4WDM-40 MSA (40km reach with APD receiver and RS(528,514) FEC in host system)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_100G_DR:
+ printf("%s 100GBASE-DR (clause 140), CAUI-4 (no FEC)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_FR_NOFEC:
+ printf("%s 100G-FR or 100GBASE-FR1 (clause 140), CAUI-4 (no FEC)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_LR_NOFEC:
+ printf("%s 100G-LR or 100GBASE-LR1 (clause 140), CAUI-4 (no FEC)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_ACC1:
+ printf("%s Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_200G_AOC1:
+ printf("%s Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_200G_ACC2:
+ printf("%s Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_200G_A0C2:
+ printf("%s Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_200G_CR4:
+ printf("%s 50GBASE-CR, 100GBASE-CR2, or 200GBASE-CR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_SR4:
+ printf("%s 50GBASE-SR, 100GBASE-SR2, or 200GBASE-SR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_DR4:
+ printf("%s 50GBASE-FR or 200GBASE-DR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_FR4:
+ printf("%s 200GBASE-FR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_PSM4:
+ printf("%s 200G 1550 nm PSM4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_50G_LR:
+ printf("%s 50GBASE-LR\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_LR4:
+ printf("%s 200GBASE-LR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_64G_EA:
+ printf("%s 64GFC EA\n", pfx);
+ break;
+ case SFF8636_ETHERNET_64G_SW:
+ printf("%s 64GFC SW\n", pfx);
+ break;
+ case SFF8636_ETHERNET_64G_LW:
+ printf("%s 64GFC LW\n", pfx);
+ break;
+ case SFF8636_ETHERNET_128FC_EA:
+ printf("%s 128GFC EA\n", pfx);
+ break;
+ case SFF8636_ETHERNET_128FC_SW:
+ printf("%s 128GFC SW\n", pfx);
+ break;
+ case SFF8636_ETHERNET_128FC_LW:
+ printf("%s 128GFC LW\n", pfx);
+ break;
default:
printf("%s (reserved or unknown)\n", pfx);
break;
@@ -478,9 +580,9 @@ static void sff8636_show_rate_identifier(const __u8 *id)
id[SFF8636_EXT_RS_OFFSET]);
}
-static void sff8636_show_oui(const __u8 *id)
+static void sff8636_show_oui(const __u8 *id, int id_offset)
{
- sff8024_show_oui(id, SFF8636_VENDOR_OUI_OFFSET);
+ sff8024_show_oui(id, id_offset);
}
static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id)
@@ -561,38 +663,7 @@ static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id)
static void sff8636_show_revision_compliance(const __u8 *id)
{
- static const char *pfx =
- "\tRevision Compliance :";
-
- switch (id[SFF8636_REV_COMPLIANCE_OFFSET]) {
- case SFF8636_REV_UNSPECIFIED:
- printf("%s Revision not specified\n", pfx);
- break;
- case SFF8636_REV_8436_48:
- printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
- break;
- case SFF8636_REV_8436_8636:
- printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
- break;
- case SFF8636_REV_8636_13:
- printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx);
- break;
- case SFF8636_REV_8636_14:
- printf("%s SFF-8636 Rev 1.4\n", pfx);
- break;
- case SFF8636_REV_8636_15:
- printf("%s SFF-8636 Rev 1.5\n", pfx);
- break;
- case SFF8636_REV_8636_20:
- printf("%s SFF-8636 Rev 2.0\n", pfx);
- break;
- case SFF8636_REV_8636_27:
- printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx);
- break;
- default:
- printf("%s Unallocated\n", pfx);
- break;
- }
+ sff_show_revision_compliance(id, SFF8636_REV_COMPLIANCE_OFFSET);
}
/*
@@ -745,10 +816,15 @@ static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len)
sff_show_thresholds(sd);
}
-
}
+
void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
{
+ if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) {
+ qsfp_dd_show_all(id);
+ return;
+ }
+
sff8636_show_identifier(id);
if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) ||
(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) ||
@@ -773,7 +849,7 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
sff8636_show_wavelength_or_copper_compliance(id);
sff_show_ascii(id, SFF8636_VENDOR_NAME_START_OFFSET,
SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name");
- sff8636_show_oui(id);
+ sff8636_show_oui(id, SFF8636_VENDOR_OUI_OFFSET);
sff_show_ascii(id, SFF8636_VENDOR_PN_START_OFFSET,
SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN");
sff_show_ascii(id, SFF8636_VENDOR_REV_START_OFFSET,
diff --git a/qsfp.h b/qsfp.h
index b623174..9636b0c 100644
--- a/qsfp.h
+++ b/qsfp.h
@@ -31,14 +31,6 @@
#define SFF8636_ID_OFFSET 0x00
#define SFF8636_REV_COMPLIANCE_OFFSET 0x01
-#define SFF8636_REV_UNSPECIFIED 0x00
-#define SFF8636_REV_8436_48 0x01
-#define SFF8636_REV_8436_8636 0x02
-#define SFF8636_REV_8636_13 0x03
-#define SFF8636_REV_8636_14 0x04
-#define SFF8636_REV_8636_15 0x05
-#define SFF8636_REV_8636_20 0x06
-#define SFF8636_REV_8636_27 0x07
#define SFF8636_STATUS_2_OFFSET 0x02
/* Flat Memory:0- Paging, 1- Page 0 only */
@@ -496,6 +488,42 @@
#define SFF8636_ETHERNET_100G_AOC2 0x18
#define SFF8636_ETHERNET_100G_ACC2 0x19
+#define SFF8636_ETHERNET_100GE_DWDM2 0x1A
+#define SFF8636_ETHERNET_100G_1550NM_WDM 0x1B
+#define SFF8636_ETHERNET_10G_BASET_SR 0x1C
+#define SFF8636_ETHERNET_5G_BASET 0x1D
+#define SFF8636_ETHERNET_2HALFG_BASET 0x1E
+#define SFF8636_ETHERNET_40G_SWDM4 0x1F
+#define SFF8636_ETHERNET_100G_SWDM4 0x20
+#define SFF8636_ETHERNET_100G_PAM4_BIDI 0x21
+#define SFF8636_ETHERNET_4WDM10_MSA 0x22
+#define SFF8636_ETHERNET_4WDM20_MSA 0x23
+#define SFF8636_ETHERNET_4WDM40_MSA 0x24
+#define SFF8636_ETHERNET_100G_DR 0x25
+#define SFF8636_ETHERNET_100G_FR_NOFEC 0x26
+#define SFF8636_ETHERNET_100G_LR_NOFEC 0x27
+/* 28h-2Fh reserved */
+#define SFF8636_ETHERNET_200G_ACC1 0x30
+#define SFF8636_ETHERNET_200G_AOC1 0x31
+#define SFF8636_ETHERNET_200G_ACC2 0x32
+#define SFF8636_ETHERNET_200G_A0C2 0x33
+/* 34h-3Fh reserved */
+#define SFF8636_ETHERNET_200G_CR4 0x40
+#define SFF8636_ETHERNET_200G_SR4 0x41
+#define SFF8636_ETHERNET_200G_DR4 0x42
+#define SFF8636_ETHERNET_200G_FR4 0x43
+#define SFF8636_ETHERNET_200G_PSM4 0x44
+#define SFF8636_ETHERNET_50G_LR 0x45
+#define SFF8636_ETHERNET_200G_LR4 0x46
+/* 47h-4Fh reserved */
+#define SFF8636_ETHERNET_64G_EA 0x50
+#define SFF8636_ETHERNET_64G_SW 0x51
+#define SFF8636_ETHERNET_64G_LW 0x52
+#define SFF8636_ETHERNET_128FC_EA 0x53
+#define SFF8636_ETHERNET_128FC_SW 0x54
+#define SFF8636_ETHERNET_128FC_LW 0x55
+/* 56h-5Fh reserved */
+
#define SFF8636_OPTION_2_OFFSET 0xC1
/* Rx output amplitude */
#define SFF8636_O2_RX_OUTPUT_AMP (1 << 0)
diff --git a/realtek.c b/realtek.c
index d10cfd4..ee0c611 100644
--- a/realtek.c
+++ b/realtek.c
@@ -241,7 +241,7 @@ print_intr_bits(u16 mask)
}
int
-realtek_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+realtek_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *data = (u32 *) regs->data;
diff --git a/rxclass.c b/rxclass.c
index 7997265..6cf81fd 100644
--- a/rxclass.c
+++ b/rxclass.c
@@ -348,8 +348,9 @@ int rxclass_rule_getall(struct cmd_context *ctx)
{
struct ethtool_rxnfc *nfccmd;
__u32 *rule_locs;
- int err, i;
+ unsigned int i;
__u32 count;
+ int err;
/* determine rule count */
err = rxclass_get_dev_info(ctx, &count, NULL);
@@ -481,8 +482,9 @@ static int rmgr_find_empty_slot(struct rmgr_ctrl *rmgr,
static int rmgr_init(struct cmd_context *ctx, struct rmgr_ctrl *rmgr)
{
struct ethtool_rxnfc *nfccmd;
- int err, i;
__u32 *rule_locs;
+ unsigned int i;
+ int err;
/* clear rule manager settings */
memset(rmgr, 0, sizeof(*rmgr));
@@ -941,7 +943,7 @@ static int rxclass_get_long(char *str, long long *val, int size)
static int rxclass_get_ulong(char *str, unsigned long long *val, int size)
{
- long long max = ~0ULL >> (64 - size);
+ unsigned long long max = ~0ULL >> (64 - size);
char *endp;
errno = 0;
diff --git a/sfc.c b/sfc.c
index f56243d..340800e 100644
--- a/sfc.c
+++ b/sfc.c
@@ -3890,7 +3890,8 @@ print_complex_table(unsigned revision, const struct efx_nic_reg_table *table,
}
int
-sfc_dump_regs(struct ethtool_drvinfo *info maybe_unused, struct ethtool_regs *regs)
+sfc_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
{
const struct efx_nic_reg *reg;
const struct efx_nic_reg_table *table;
diff --git a/sff-common.c b/sff-common.c
index 7700cbe..5285645 100644
--- a/sff-common.c
+++ b/sff-common.c
@@ -136,6 +136,9 @@ void sff8024_show_identifier(const __u8 *id, int id_offset)
case SFF8024_ID_MICRO_QSFP:
printf(" (microQSFP)\n");
break;
+ case SFF8024_ID_QSFP_DD:
+ printf(" (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))\n");
+ break;
default:
printf(" (reserved or unknown)\n");
break;
@@ -203,6 +206,18 @@ void sff8024_show_connector(const __u8 *id, int ctor_offset)
case SFF8024_CTOR_MXC_2x16:
printf(" (MXC 2x16)\n");
break;
+ case SFF8024_CTOR_CS_OPTICAL:
+ printf(" (CS optical connector)\n");
+ break;
+ case SFF8024_CTOR_CS_OPTICAL_MINI:
+ printf(" (Mini CS optical connector)\n");
+ break;
+ case SFF8024_CTOR_MPO_2X12:
+ printf(" (MPO 2x12)\n");
+ break;
+ case SFF8024_CTOR_MPO_1X16:
+ printf(" (MPO 1x16)\n");
+ break;
default:
printf(" (reserved or unknown)\n");
break;
@@ -302,3 +317,39 @@ void sff_show_thresholds(struct sff_diags sd)
PRINT_xX_PWR("Laser rx power low warning threshold",
sd.rx_power[LWARN]);
}
+
+void sff_show_revision_compliance(const __u8 *id, int rev_offset)
+{
+ static const char *pfx =
+ "\tRevision Compliance :";
+
+ switch (id[rev_offset]) {
+ case SFF8636_REV_UNSPECIFIED:
+ printf("%s Revision not specified\n", pfx);
+ break;
+ case SFF8636_REV_8436_48:
+ printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
+ break;
+ case SFF8636_REV_8436_8636:
+ printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
+ break;
+ case SFF8636_REV_8636_13:
+ printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx);
+ break;
+ case SFF8636_REV_8636_14:
+ printf("%s SFF-8636 Rev 1.4\n", pfx);
+ break;
+ case SFF8636_REV_8636_15:
+ printf("%s SFF-8636 Rev 1.5\n", pfx);
+ break;
+ case SFF8636_REV_8636_20:
+ printf("%s SFF-8636 Rev 2.0\n", pfx);
+ break;
+ case SFF8636_REV_8636_27:
+ printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx);
+ break;
+ default:
+ printf("%s Unallocated\n", pfx);
+ break;
+ }
+}
diff --git a/sff-common.h b/sff-common.h
index 5562b4d..cfb5d0e 100644
--- a/sff-common.h
+++ b/sff-common.h
@@ -26,7 +26,17 @@
#include <stdio.h>
#include "internal.h"
-#define SFF8024_ID_OFFSET 0x00
+/* Revision compliance */
+#define SFF8636_REV_UNSPECIFIED 0x00
+#define SFF8636_REV_8436_48 0x01
+#define SFF8636_REV_8436_8636 0x02
+#define SFF8636_REV_8636_13 0x03
+#define SFF8636_REV_8636_14 0x04
+#define SFF8636_REV_8636_15 0x05
+#define SFF8636_REV_8636_20 0x06
+#define SFF8636_REV_8636_27 0x07
+
+#define SFF8024_ID_OFFSET 0x00
#define SFF8024_ID_UNKNOWN 0x00
#define SFF8024_ID_GBIC 0x01
#define SFF8024_ID_SOLDERED_MODULE 0x02
@@ -51,7 +61,8 @@
#define SFF8024_ID_HD8X_FANOUT 0x15
#define SFF8024_ID_CDFP_S3 0x16
#define SFF8024_ID_MICRO_QSFP 0x17
-#define SFF8024_ID_LAST SFF8024_ID_MICRO_QSFP
+#define SFF8024_ID_QSFP_DD 0x18
+#define SFF8024_ID_LAST SFF8024_ID_QSFP_DD
#define SFF8024_ID_UNALLOCATED_LAST 0x7F
#define SFF8024_ID_VENDOR_START 0x80
#define SFF8024_ID_VENDOR_LAST 0xFF
@@ -76,8 +87,14 @@
#define SFF8024_CTOR_RJ45 0x22
#define SFF8024_CTOR_NO_SEPARABLE 0x23
#define SFF8024_CTOR_MXC_2x16 0x24
-#define SFF8024_CTOR_LAST SFF8024_CTOR_MXC_2x16
-#define SFF8024_CTOR_UNALLOCATED_LAST 0x7F
+#define SFF8024_CTOR_CS_OPTICAL 0x25
+#define SFF8024_CTOR_CS_OPTICAL_MINI 0x26
+#define SFF8024_CTOR_MPO_2X12 0x27
+#define SFF8024_CTOR_MPO_1X16 0x28
+#define SFF8024_CTOR_LAST SFF8024_CTOR_MPO_1X16
+
+#define SFF8024_CTOR_NO_SEP_QSFP_DD 0x6F
+#define SFF8024_CTOR_UNALLOCATED_LAST 0x7F
#define SFF8024_CTOR_VENDOR_START 0x80
#define SFF8024_CTOR_VENDOR_LAST 0xFF
@@ -185,5 +202,6 @@ void sff8024_show_oui(const __u8 *id, int id_offset);
void sff8024_show_identifier(const __u8 *id, int id_offset);
void sff8024_show_connector(const __u8 *id, int ctor_offset);
void sff8024_show_encoding(const __u8 *id, int encoding_offset, int sff_type);
+void sff_show_revision_compliance(const __u8 *id, int rev_offset);
#endif /* SFF_COMMON_H__ */
diff --git a/sfpdiag.c b/sfpdiag.c
index fa41651..1fa8b7b 100644
--- a/sfpdiag.c
+++ b/sfpdiag.c
@@ -190,8 +190,8 @@ static float befloattoh(const __u32 *source)
static void sff8472_calibration(const __u8 *id, struct sff_diags *sd)
{
- int i;
__u16 rx_reading;
+ unsigned int i;
/* Calibration should occur for all values (threshold and current) */
for (i = 0; i < ARRAY_SIZE(sd->bias_cur); ++i) {
diff --git a/sfpid.c b/sfpid.c
index ded3be7..da2b3f4 100644
--- a/sfpid.c
+++ b/sfpid.c
@@ -191,8 +191,76 @@ static void sff8079_show_transceiver(const __u8 *id)
printf("%s Extended: 100G AOC or 25GAUI C2M AOC with worst BER of 10^(-12)\n", pfx);
if (id[36] == 0x19)
printf("%s Extended: 100G ACC or 25GAUI C2M ACC with worst BER of 10^(-12)\n", pfx);
+ if (id[36] == 0x1a)
+ printf("%s Extended: 100GE-DWDM2 (DWDM transceiver using 2 wavelengths on a 1550 nm DWDM grid with a reach up to 80 km)\n",
+ pfx);
+ if (id[36] == 0x1b)
+ printf("%s Extended: 100G 1550nm WDM (4 wavelengths)\n", pfx);
if (id[36] == 0x1c)
printf("%s Extended: 10Gbase-T Short Reach\n", pfx);
+ if (id[36] == 0x1d)
+ printf("%s Extended: 5GBASE-T\n", pfx);
+ if (id[36] == 0x1e)
+ printf("%s Extended: 2.5GBASE-T\n", pfx);
+ if (id[36] == 0x1f)
+ printf("%s Extended: 40G SWDM4\n", pfx);
+ if (id[36] == 0x20)
+ printf("%s Extended: 100G SWDM4\n", pfx);
+ if (id[36] == 0x21)
+ printf("%s Extended: 100G PAM4 BiDi\n", pfx);
+ if (id[36] == 0x22)
+ printf("%s Extended: 4WDM-10 MSA (10km version of 100G CWDM4 with same RS(528,514) FEC in host system)\n",
+ pfx);
+ if (id[36] == 0x23)
+ printf("%s Extended: 4WDM-20 MSA (20km version of 100GBASE-LR4 with RS(528,514) FEC in host system)\n",
+ pfx);
+ if (id[36] == 0x24)
+ printf("%s Extended: 4WDM-40 MSA (40km reach with APD receiver and RS(528,514) FEC in host system)\n",
+ pfx);
+ if (id[36] == 0x25)
+ printf("%s Extended: 100GBASE-DR (clause 140), CAUI-4 (no FEC)\n", pfx);
+ if (id[36] == 0x26)
+ printf("%s Extended: 100G-FR or 100GBASE-FR1 (clause 140), CAUI-4 (no FEC)\n", pfx);
+ if (id[36] == 0x27)
+ printf("%s Extended: 100G-LR or 100GBASE-LR1 (clause 140), CAUI-4 (no FEC)\n", pfx);
+ if (id[36] == 0x30)
+ printf("%s Extended: Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n",
+ pfx);
+ if (id[36] == 0x31)
+ printf("%s Extended: Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n",
+ pfx);
+ if (id[36] == 0x32)
+ printf("%s Extended: Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n",
+ pfx);
+ if (id[36] == 0x33)
+ printf("%s Extended: Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n",
+ pfx);
+ if (id[36] == 0x40)
+ printf("%s Extended: 50GBASE-CR, 100GBASE-CR2, or 200GBASE-CR4\n", pfx);
+ if (id[36] == 0x41)
+ printf("%s Extended: 50GBASE-SR, 100GBASE-SR2, or 200GBASE-SR4\n", pfx);
+ if (id[36] == 0x42)
+ printf("%s Extended: 50GBASE-FR or 200GBASE-DR4\n", pfx);
+ if (id[36] == 0x43)
+ printf("%s Extended: 200GBASE-FR4\n", pfx);
+ if (id[36] == 0x44)
+ printf("%s Extended: 200G 1550 nm PSM4\n", pfx);
+ if (id[36] == 0x45)
+ printf("%s Extended: 50GBASE-LR\n", pfx);
+ if (id[36] == 0x46)
+ printf("%s Extended: 200GBASE-LR4\n", pfx);
+ if (id[36] == 0x50)
+ printf("%s Extended: 64GFC EA\n", pfx);
+ if (id[36] == 0x51)
+ printf("%s Extended: 64GFC SW\n", pfx);
+ if (id[36] == 0x52)
+ printf("%s Extended: 64GFC LW\n", pfx);
+ if (id[36] == 0x53)
+ printf("%s Extended: 128GFC EA\n", pfx);
+ if (id[36] == 0x54)
+ printf("%s Extended: 128GFC SW\n", pfx);
+ if (id[36] == 0x55)
+ printf("%s Extended: 128GFC LW\n", pfx);
}
static void sff8079_show_encoding(const __u8 *id)
diff --git a/smsc911x.c b/smsc911x.c
index bafee21..b643504 100644
--- a/smsc911x.c
+++ b/smsc911x.c
@@ -2,7 +2,7 @@
#include <string.h>
#include "internal.h"
-int smsc911x_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int smsc911x_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
unsigned int *smsc_reg = (unsigned int *)regs->data;
diff --git a/stmmac.c b/stmmac.c
index 98d9058..5847120 100644
--- a/stmmac.c
+++ b/stmmac.c
@@ -18,7 +18,7 @@
#define GMAC_REG_NUM 55
#define GMAC_DMA_REG_NUM 23
-int st_mac100_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int st_mac100_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
int i;
@@ -51,7 +51,7 @@ int st_mac100_dump_regs(struct ethtool_drvinfo *info maybe_unused,
return 0;
}
-int st_gmac_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int st_gmac_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
int i;
diff --git a/tg3.c b/tg3.c
index 8698391..ebdef2d 100644
--- a/tg3.c
+++ b/tg3.c
@@ -4,10 +4,10 @@
#define TG3_MAGIC 0x669955aa
-int tg3_dump_eeprom(struct ethtool_drvinfo *info maybe_unused,
+int tg3_dump_eeprom(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_eeprom *ee)
{
- int i;
+ unsigned int i;
if (ee->magic != TG3_MAGIC) {
fprintf(stderr, "Magic number 0x%08x does not match 0x%08x\n",
@@ -23,10 +23,10 @@ int tg3_dump_eeprom(struct ethtool_drvinfo *info maybe_unused,
return 0;
}
-int tg3_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int tg3_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
- int i;
+ unsigned int i;
u32 reg;
fprintf(stdout, "Offset\tValue\n");
diff --git a/tse.c b/tse.c
index e5241ee..fb00d21 100644
--- a/tse.c
+++ b/tse.c
@@ -25,7 +25,7 @@ bitset(u32 val, int bit)
return 0;
}
-int altera_tse_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int altera_tse_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
int i;
diff --git a/uapi/linux/ethtool.h b/uapi/linux/ethtool.h
index 1619806..052689b 100644
--- a/uapi/linux/ethtool.h
+++ b/uapi/linux/ethtool.h
@@ -577,6 +577,76 @@ struct ethtool_pauseparam {
__u32 tx_pause;
};
+/**
+ * enum ethtool_link_ext_state - link extended state
+ */
+enum ethtool_link_ext_state {
+ ETHTOOL_LINK_EXT_STATE_AUTONEG,
+ ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
+ ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
+ ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY,
+ ETHTOOL_LINK_EXT_STATE_NO_CABLE,
+ ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
+ ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE,
+ ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE,
+ ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED,
+ ETHTOOL_LINK_EXT_STATE_OVERHEAT,
+};
+
+/**
+ * enum ethtool_link_ext_substate_autoneg - more information in addition to
+ * ETHTOOL_LINK_EXT_STATE_AUTONEG.
+ */
+enum ethtool_link_ext_substate_autoneg {
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED = 1,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD,
+};
+
+/**
+ * enum ethtool_link_ext_substate_link_training - more information in addition to
+ * ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE.
+ */
+enum ethtool_link_ext_substate_link_training {
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED = 1,
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT,
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY,
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT,
+};
+
+/**
+ * enum ethtool_link_ext_substate_logical_mismatch - more information in addition
+ * to ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH.
+ */
+enum ethtool_link_ext_substate_link_logical_mismatch {
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK = 1,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED,
+};
+
+/**
+ * enum ethtool_link_ext_substate_bad_signal_integrity - more information in
+ * addition to ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY.
+ */
+enum ethtool_link_ext_substate_bad_signal_integrity {
+ ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS = 1,
+ ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE,
+};
+
+/**
+ * enum ethtool_link_ext_substate_cable_issue - more information in
+ * addition to ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE.
+ */
+enum ethtool_link_ext_substate_cable_issue {
+ ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE = 1,
+ ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE,
+};
+
#define ETH_GSTRING_LEN 32
/**
@@ -594,6 +664,10 @@ struct ethtool_pauseparam {
* @ETH_SS_LINK_MODES: link mode names
* @ETH_SS_MSG_CLASSES: debug message class names
* @ETH_SS_WOL_MODES: wake-on-lan modes
+ * @ETH_SS_SOF_TIMESTAMPING: SOF_TIMESTAMPING_* flags
+ * @ETH_SS_TS_TX_TYPES: timestamping Tx types
+ * @ETH_SS_TS_RX_FILTERS: timestamping Rx filters
+ * @ETH_SS_UDP_TUNNEL_TYPES: UDP tunnel types
*/
enum ethtool_stringset {
ETH_SS_TEST = 0,
@@ -608,6 +682,10 @@ enum ethtool_stringset {
ETH_SS_LINK_MODES,
ETH_SS_MSG_CLASSES,
ETH_SS_WOL_MODES,
+ ETH_SS_SOF_TIMESTAMPING,
+ ETH_SS_TS_TX_TYPES,
+ ETH_SS_TS_RX_FILTERS,
+ ETH_SS_UDP_TUNNEL_TYPES,
/* add new constants above here */
ETH_SS_COUNT
@@ -1328,6 +1406,7 @@ enum ethtool_fec_config_bits {
ETHTOOL_FEC_OFF_BIT,
ETHTOOL_FEC_RS_BIT,
ETHTOOL_FEC_BASER_BIT,
+ ETHTOOL_FEC_LLRS_BIT,
};
#define ETHTOOL_FEC_NONE (1 << ETHTOOL_FEC_NONE_BIT)
@@ -1335,6 +1414,7 @@ enum ethtool_fec_config_bits {
#define ETHTOOL_FEC_OFF (1 << ETHTOOL_FEC_OFF_BIT)
#define ETHTOOL_FEC_RS (1 << ETHTOOL_FEC_RS_BIT)
#define ETHTOOL_FEC_BASER (1 << ETHTOOL_FEC_BASER_BIT)
+#define ETHTOOL_FEC_LLRS (1 << ETHTOOL_FEC_LLRS_BIT)
/* CMDs currently supported */
#define ETHTOOL_GSET 0x00000001 /* DEPRECATED, Get settings.
@@ -1519,7 +1599,24 @@ enum ethtool_link_mode_bit_indices {
ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71,
ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT = 72,
ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT = 73,
-
+ ETHTOOL_LINK_MODE_FEC_LLRS_BIT = 74,
+ ETHTOOL_LINK_MODE_100000baseKR_Full_BIT = 75,
+ ETHTOOL_LINK_MODE_100000baseSR_Full_BIT = 76,
+ ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT = 77,
+ ETHTOOL_LINK_MODE_100000baseCR_Full_BIT = 78,
+ ETHTOOL_LINK_MODE_100000baseDR_Full_BIT = 79,
+ ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT = 80,
+ ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT = 81,
+ ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT = 82,
+ ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT = 83,
+ ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT = 84,
+ ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT = 85,
+ ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT = 86,
+ ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT = 87,
+ ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT = 88,
+ ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT = 89,
+ ETHTOOL_LINK_MODE_100baseFX_Half_BIT = 90,
+ ETHTOOL_LINK_MODE_100baseFX_Full_BIT = 91,
/* must be last entry */
__ETHTOOL_LINK_MODE_MASK_NBITS
};
@@ -1656,6 +1753,18 @@ static __inline__ int ethtool_validate_duplex(__u8 duplex)
return 0;
}
+#define MASTER_SLAVE_CFG_UNSUPPORTED 0
+#define MASTER_SLAVE_CFG_UNKNOWN 1
+#define MASTER_SLAVE_CFG_MASTER_PREFERRED 2
+#define MASTER_SLAVE_CFG_SLAVE_PREFERRED 3
+#define MASTER_SLAVE_CFG_MASTER_FORCE 4
+#define MASTER_SLAVE_CFG_SLAVE_FORCE 5
+#define MASTER_SLAVE_STATE_UNSUPPORTED 0
+#define MASTER_SLAVE_STATE_UNKNOWN 1
+#define MASTER_SLAVE_STATE_MASTER 2
+#define MASTER_SLAVE_STATE_SLAVE 3
+#define MASTER_SLAVE_STATE_ERR 4
+
/* Which connector port. */
#define PORT_TP 0x00
#define PORT_AUI 0x01
@@ -1894,7 +2003,9 @@ struct ethtool_link_settings {
__u8 eth_tp_mdix_ctrl;
__s8 link_mode_masks_nwords;
__u8 transceiver;
- __u8 reserved1[3];
+ __u8 master_slave_cfg;
+ __u8 master_slave_state;
+ __u8 reserved1[1];
__u32 reserved[7];
__u32 link_mode_masks[0];
/* layout of link_mode_masks fields:
diff --git a/uapi/linux/ethtool_netlink.h b/uapi/linux/ethtool_netlink.h
index ad6d3a0..c022883 100644
--- a/uapi/linux/ethtool_netlink.h
+++ b/uapi/linux/ethtool_netlink.h
@@ -2,7 +2,7 @@
/*
* include/uapi/linux/ethtool_netlink.h - netlink interface for ethtool
*
- * See Documentation/networking/ethtool-netlink.txt in kernel source tree for
+ * See Documentation/networking/ethtool-netlink.rst in kernel source tree for
* doucumentation of the interface.
*/
@@ -24,6 +24,24 @@ enum {
ETHTOOL_MSG_DEBUG_SET,
ETHTOOL_MSG_WOL_GET,
ETHTOOL_MSG_WOL_SET,
+ ETHTOOL_MSG_FEATURES_GET,
+ ETHTOOL_MSG_FEATURES_SET,
+ ETHTOOL_MSG_PRIVFLAGS_GET,
+ ETHTOOL_MSG_PRIVFLAGS_SET,
+ ETHTOOL_MSG_RINGS_GET,
+ ETHTOOL_MSG_RINGS_SET,
+ ETHTOOL_MSG_CHANNELS_GET,
+ ETHTOOL_MSG_CHANNELS_SET,
+ ETHTOOL_MSG_COALESCE_GET,
+ ETHTOOL_MSG_COALESCE_SET,
+ ETHTOOL_MSG_PAUSE_GET,
+ ETHTOOL_MSG_PAUSE_SET,
+ ETHTOOL_MSG_EEE_GET,
+ ETHTOOL_MSG_EEE_SET,
+ ETHTOOL_MSG_TSINFO_GET,
+ ETHTOOL_MSG_CABLE_TEST_ACT,
+ ETHTOOL_MSG_CABLE_TEST_TDR_ACT,
+ ETHTOOL_MSG_TUNNEL_INFO_GET,
/* add new constants above here */
__ETHTOOL_MSG_USER_CNT,
@@ -43,6 +61,25 @@ enum {
ETHTOOL_MSG_DEBUG_NTF,
ETHTOOL_MSG_WOL_GET_REPLY,
ETHTOOL_MSG_WOL_NTF,
+ ETHTOOL_MSG_FEATURES_GET_REPLY,
+ ETHTOOL_MSG_FEATURES_SET_REPLY,
+ ETHTOOL_MSG_FEATURES_NTF,
+ ETHTOOL_MSG_PRIVFLAGS_GET_REPLY,
+ ETHTOOL_MSG_PRIVFLAGS_NTF,
+ ETHTOOL_MSG_RINGS_GET_REPLY,
+ ETHTOOL_MSG_RINGS_NTF,
+ ETHTOOL_MSG_CHANNELS_GET_REPLY,
+ ETHTOOL_MSG_CHANNELS_NTF,
+ ETHTOOL_MSG_COALESCE_GET_REPLY,
+ ETHTOOL_MSG_COALESCE_NTF,
+ ETHTOOL_MSG_PAUSE_GET_REPLY,
+ ETHTOOL_MSG_PAUSE_NTF,
+ ETHTOOL_MSG_EEE_GET_REPLY,
+ ETHTOOL_MSG_EEE_NTF,
+ ETHTOOL_MSG_TSINFO_GET_REPLY,
+ ETHTOOL_MSG_CABLE_TEST_NTF,
+ ETHTOOL_MSG_CABLE_TEST_TDR_NTF,
+ ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY,
/* add new constants above here */
__ETHTOOL_MSG_KERNEL_CNT,
@@ -55,9 +92,12 @@ enum {
#define ETHTOOL_FLAG_COMPACT_BITSETS (1 << 0)
/* provide optional reply for SET or ACT requests */
#define ETHTOOL_FLAG_OMIT_REPLY (1 << 1)
+/* request statistics, if supported by the driver */
+#define ETHTOOL_FLAG_STATS (1 << 2)
#define ETHTOOL_FLAG_ALL (ETHTOOL_FLAG_COMPACT_BITSETS | \
- ETHTOOL_FLAG_OMIT_REPLY)
+ ETHTOOL_FLAG_OMIT_REPLY | \
+ ETHTOOL_FLAG_STATS)
enum {
ETHTOOL_A_HEADER_UNSPEC,
@@ -185,6 +225,8 @@ enum {
ETHTOOL_A_LINKMODES_PEER, /* bitset */
ETHTOOL_A_LINKMODES_SPEED, /* u32 */
ETHTOOL_A_LINKMODES_DUPLEX, /* u8 */
+ ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG, /* u8 */
+ ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE, /* u8 */
/* add new constants above here */
__ETHTOOL_A_LINKMODES_CNT,
@@ -197,6 +239,10 @@ enum {
ETHTOOL_A_LINKSTATE_UNSPEC,
ETHTOOL_A_LINKSTATE_HEADER, /* nest - _A_HEADER_* */
ETHTOOL_A_LINKSTATE_LINK, /* u8 */
+ ETHTOOL_A_LINKSTATE_SQI, /* u32 */
+ ETHTOOL_A_LINKSTATE_SQI_MAX, /* u32 */
+ ETHTOOL_A_LINKSTATE_EXT_STATE, /* u8 */
+ ETHTOOL_A_LINKSTATE_EXT_SUBSTATE, /* u8 */
/* add new constants above here */
__ETHTOOL_A_LINKSTATE_CNT,
@@ -228,6 +274,360 @@ enum {
ETHTOOL_A_WOL_MAX = __ETHTOOL_A_WOL_CNT - 1
};
+/* FEATURES */
+
+enum {
+ ETHTOOL_A_FEATURES_UNSPEC,
+ ETHTOOL_A_FEATURES_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_FEATURES_HW, /* bitset */
+ ETHTOOL_A_FEATURES_WANTED, /* bitset */
+ ETHTOOL_A_FEATURES_ACTIVE, /* bitset */
+ ETHTOOL_A_FEATURES_NOCHANGE, /* bitset */
+
+ /* add new constants above here */
+ __ETHTOOL_A_FEATURES_CNT,
+ ETHTOOL_A_FEATURES_MAX = __ETHTOOL_A_FEATURES_CNT - 1
+};
+
+/* PRIVFLAGS */
+
+enum {
+ ETHTOOL_A_PRIVFLAGS_UNSPEC,
+ ETHTOOL_A_PRIVFLAGS_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_PRIVFLAGS_FLAGS, /* bitset */
+
+ /* add new constants above here */
+ __ETHTOOL_A_PRIVFLAGS_CNT,
+ ETHTOOL_A_PRIVFLAGS_MAX = __ETHTOOL_A_PRIVFLAGS_CNT - 1
+};
+
+/* RINGS */
+
+enum {
+ ETHTOOL_A_RINGS_UNSPEC,
+ ETHTOOL_A_RINGS_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_RINGS_RX_MAX, /* u32 */
+ ETHTOOL_A_RINGS_RX_MINI_MAX, /* u32 */
+ ETHTOOL_A_RINGS_RX_JUMBO_MAX, /* u32 */
+ ETHTOOL_A_RINGS_TX_MAX, /* u32 */
+ ETHTOOL_A_RINGS_RX, /* u32 */
+ ETHTOOL_A_RINGS_RX_MINI, /* u32 */
+ ETHTOOL_A_RINGS_RX_JUMBO, /* u32 */
+ ETHTOOL_A_RINGS_TX, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_RINGS_CNT,
+ ETHTOOL_A_RINGS_MAX = (__ETHTOOL_A_RINGS_CNT - 1)
+};
+
+/* CHANNELS */
+
+enum {
+ ETHTOOL_A_CHANNELS_UNSPEC,
+ ETHTOOL_A_CHANNELS_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_CHANNELS_RX_MAX, /* u32 */
+ ETHTOOL_A_CHANNELS_TX_MAX, /* u32 */
+ ETHTOOL_A_CHANNELS_OTHER_MAX, /* u32 */
+ ETHTOOL_A_CHANNELS_COMBINED_MAX, /* u32 */
+ ETHTOOL_A_CHANNELS_RX_COUNT, /* u32 */
+ ETHTOOL_A_CHANNELS_TX_COUNT, /* u32 */
+ ETHTOOL_A_CHANNELS_OTHER_COUNT, /* u32 */
+ ETHTOOL_A_CHANNELS_COMBINED_COUNT, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_CHANNELS_CNT,
+ ETHTOOL_A_CHANNELS_MAX = (__ETHTOOL_A_CHANNELS_CNT - 1)
+};
+
+/* COALESCE */
+
+enum {
+ ETHTOOL_A_COALESCE_UNSPEC,
+ ETHTOOL_A_COALESCE_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_COALESCE_RX_USECS, /* u32 */
+ ETHTOOL_A_COALESCE_RX_MAX_FRAMES, /* u32 */
+ ETHTOOL_A_COALESCE_RX_USECS_IRQ, /* u32 */
+ ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, /* u32 */
+ ETHTOOL_A_COALESCE_TX_USECS, /* u32 */
+ ETHTOOL_A_COALESCE_TX_MAX_FRAMES, /* u32 */
+ ETHTOOL_A_COALESCE_TX_USECS_IRQ, /* u32 */
+ ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, /* u32 */
+ ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, /* u32 */
+ ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, /* u8 */
+ ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, /* u8 */
+ ETHTOOL_A_COALESCE_PKT_RATE_LOW, /* u32 */
+ ETHTOOL_A_COALESCE_RX_USECS_LOW, /* u32 */
+ ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, /* u32 */
+ ETHTOOL_A_COALESCE_TX_USECS_LOW, /* u32 */
+ ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, /* u32 */
+ ETHTOOL_A_COALESCE_PKT_RATE_HIGH, /* u32 */
+ ETHTOOL_A_COALESCE_RX_USECS_HIGH, /* u32 */
+ ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, /* u32 */
+ ETHTOOL_A_COALESCE_TX_USECS_HIGH, /* u32 */
+ ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, /* u32 */
+ ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_COALESCE_CNT,
+ ETHTOOL_A_COALESCE_MAX = (__ETHTOOL_A_COALESCE_CNT - 1)
+};
+
+/* PAUSE */
+
+enum {
+ ETHTOOL_A_PAUSE_UNSPEC,
+ ETHTOOL_A_PAUSE_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_PAUSE_AUTONEG, /* u8 */
+ ETHTOOL_A_PAUSE_RX, /* u8 */
+ ETHTOOL_A_PAUSE_TX, /* u8 */
+ ETHTOOL_A_PAUSE_STATS, /* nest - _PAUSE_STAT_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_PAUSE_CNT,
+ ETHTOOL_A_PAUSE_MAX = (__ETHTOOL_A_PAUSE_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_PAUSE_STAT_UNSPEC,
+ ETHTOOL_A_PAUSE_STAT_PAD,
+
+ ETHTOOL_A_PAUSE_STAT_TX_FRAMES,
+ ETHTOOL_A_PAUSE_STAT_RX_FRAMES,
+
+ /* add new constants above here */
+ __ETHTOOL_A_PAUSE_STAT_CNT,
+ ETHTOOL_A_PAUSE_STAT_MAX = (__ETHTOOL_A_PAUSE_STAT_CNT - 1)
+};
+
+/* EEE */
+
+enum {
+ ETHTOOL_A_EEE_UNSPEC,
+ ETHTOOL_A_EEE_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_EEE_MODES_OURS, /* bitset */
+ ETHTOOL_A_EEE_MODES_PEER, /* bitset */
+ ETHTOOL_A_EEE_ACTIVE, /* u8 */
+ ETHTOOL_A_EEE_ENABLED, /* u8 */
+ ETHTOOL_A_EEE_TX_LPI_ENABLED, /* u8 */
+ ETHTOOL_A_EEE_TX_LPI_TIMER, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_EEE_CNT,
+ ETHTOOL_A_EEE_MAX = (__ETHTOOL_A_EEE_CNT - 1)
+};
+
+/* TSINFO */
+
+enum {
+ ETHTOOL_A_TSINFO_UNSPEC,
+ ETHTOOL_A_TSINFO_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_TSINFO_TIMESTAMPING, /* bitset */
+ ETHTOOL_A_TSINFO_TX_TYPES, /* bitset */
+ ETHTOOL_A_TSINFO_RX_FILTERS, /* bitset */
+ ETHTOOL_A_TSINFO_PHC_INDEX, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_TSINFO_CNT,
+ ETHTOOL_A_TSINFO_MAX = (__ETHTOOL_A_TSINFO_CNT - 1)
+};
+
+/* CABLE TEST */
+
+enum {
+ ETHTOOL_A_CABLE_TEST_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_HEADER, /* nest - _A_HEADER_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_CABLE_TEST_CNT,
+ ETHTOOL_A_CABLE_TEST_MAX = __ETHTOOL_A_CABLE_TEST_CNT - 1
+};
+
+/* CABLE TEST NOTIFY */
+enum {
+ ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC,
+ ETHTOOL_A_CABLE_RESULT_CODE_OK,
+ ETHTOOL_A_CABLE_RESULT_CODE_OPEN,
+ ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT,
+ ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT,
+};
+
+enum {
+ ETHTOOL_A_CABLE_PAIR_A,
+ ETHTOOL_A_CABLE_PAIR_B,
+ ETHTOOL_A_CABLE_PAIR_C,
+ ETHTOOL_A_CABLE_PAIR_D,
+};
+
+enum {
+ ETHTOOL_A_CABLE_RESULT_UNSPEC,
+ ETHTOOL_A_CABLE_RESULT_PAIR, /* u8 ETHTOOL_A_CABLE_PAIR_ */
+ ETHTOOL_A_CABLE_RESULT_CODE, /* u8 ETHTOOL_A_CABLE_RESULT_CODE_ */
+
+ __ETHTOOL_A_CABLE_RESULT_CNT,
+ ETHTOOL_A_CABLE_RESULT_MAX = (__ETHTOOL_A_CABLE_RESULT_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_FAULT_LENGTH_UNSPEC,
+ ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, /* u8 ETHTOOL_A_CABLE_PAIR_ */
+ ETHTOOL_A_CABLE_FAULT_LENGTH_CM, /* u32 */
+
+ __ETHTOOL_A_CABLE_FAULT_LENGTH_CNT,
+ ETHTOOL_A_CABLE_FAULT_LENGTH_MAX = (__ETHTOOL_A_CABLE_FAULT_LENGTH_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED,
+ ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED
+};
+
+enum {
+ ETHTOOL_A_CABLE_NEST_UNSPEC,
+ ETHTOOL_A_CABLE_NEST_RESULT, /* nest - ETHTOOL_A_CABLE_RESULT_ */
+ ETHTOOL_A_CABLE_NEST_FAULT_LENGTH, /* nest - ETHTOOL_A_CABLE_FAULT_LENGTH_ */
+ __ETHTOOL_A_CABLE_NEST_CNT,
+ ETHTOOL_A_CABLE_NEST_MAX = (__ETHTOOL_A_CABLE_NEST_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_TEST_NTF_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_NTF_HEADER, /* nest - ETHTOOL_A_HEADER_* */
+ ETHTOOL_A_CABLE_TEST_NTF_STATUS, /* u8 - _STARTED/_COMPLETE */
+ ETHTOOL_A_CABLE_TEST_NTF_NEST, /* nest - of results: */
+
+ __ETHTOOL_A_CABLE_TEST_NTF_CNT,
+ ETHTOOL_A_CABLE_TEST_NTF_MAX = (__ETHTOOL_A_CABLE_TEST_NTF_CNT - 1)
+};
+
+/* CABLE TEST TDR */
+
+enum {
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST, /* u32 */
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST, /* u32 */
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP, /* u32 */
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR, /* u8 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_CABLE_TEST_TDR_CFG_CNT,
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX = __ETHTOOL_A_CABLE_TEST_TDR_CFG_CNT - 1
+};
+
+enum {
+ ETHTOOL_A_CABLE_TEST_TDR_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_TDR_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_CABLE_TEST_TDR_CFG, /* nest - *_TDR_CFG_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_CABLE_TEST_TDR_CNT,
+ ETHTOOL_A_CABLE_TEST_TDR_MAX = __ETHTOOL_A_CABLE_TEST_TDR_CNT - 1
+};
+
+/* CABLE TEST TDR NOTIFY */
+
+enum {
+ ETHTOOL_A_CABLE_AMPLITUDE_UNSPEC,
+ ETHTOOL_A_CABLE_AMPLITUDE_PAIR, /* u8 */
+ ETHTOOL_A_CABLE_AMPLITUDE_mV, /* s16 */
+
+ __ETHTOOL_A_CABLE_AMPLITUDE_CNT,
+ ETHTOOL_A_CABLE_AMPLITUDE_MAX = (__ETHTOOL_A_CABLE_AMPLITUDE_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_PULSE_UNSPEC,
+ ETHTOOL_A_CABLE_PULSE_mV, /* s16 */
+
+ __ETHTOOL_A_CABLE_PULSE_CNT,
+ ETHTOOL_A_CABLE_PULSE_MAX = (__ETHTOOL_A_CABLE_PULSE_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_STEP_UNSPEC,
+ ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE, /* u32 */
+ ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, /* u32 */
+ ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, /* u32 */
+
+ __ETHTOOL_A_CABLE_STEP_CNT,
+ ETHTOOL_A_CABLE_STEP_MAX = (__ETHTOOL_A_CABLE_STEP_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_TDR_NEST_UNSPEC,
+ ETHTOOL_A_CABLE_TDR_NEST_STEP, /* nest - ETHTTOOL_A_CABLE_STEP */
+ ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE, /* nest - ETHTOOL_A_CABLE_AMPLITUDE */
+ ETHTOOL_A_CABLE_TDR_NEST_PULSE, /* nest - ETHTOOL_A_CABLE_PULSE */
+
+ __ETHTOOL_A_CABLE_TDR_NEST_CNT,
+ ETHTOOL_A_CABLE_TDR_NEST_MAX = (__ETHTOOL_A_CABLE_TDR_NEST_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_TEST_TDR_NTF_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_TDR_NTF_HEADER, /* nest - ETHTOOL_A_HEADER_* */
+ ETHTOOL_A_CABLE_TEST_TDR_NTF_STATUS, /* u8 - _STARTED/_COMPLETE */
+ ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST, /* nest - of results: */
+
+ /* add new constants above here */
+ __ETHTOOL_A_CABLE_TEST_TDR_NTF_CNT,
+ ETHTOOL_A_CABLE_TEST_TDR_NTF_MAX = __ETHTOOL_A_CABLE_TEST_TDR_NTF_CNT - 1
+};
+
+/* TUNNEL INFO */
+
+enum {
+ ETHTOOL_UDP_TUNNEL_TYPE_VXLAN,
+ ETHTOOL_UDP_TUNNEL_TYPE_GENEVE,
+ ETHTOOL_UDP_TUNNEL_TYPE_VXLAN_GPE,
+
+ __ETHTOOL_UDP_TUNNEL_TYPE_CNT
+};
+
+enum {
+ ETHTOOL_A_TUNNEL_UDP_ENTRY_UNSPEC,
+
+ ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT, /* be16 */
+ ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_TUNNEL_UDP_ENTRY_CNT,
+ ETHTOOL_A_TUNNEL_UDP_ENTRY_MAX = (__ETHTOOL_A_TUNNEL_UDP_ENTRY_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_TUNNEL_UDP_TABLE_UNSPEC,
+
+ ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE, /* u32 */
+ ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES, /* bitset */
+ ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY, /* nest - _UDP_ENTRY_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_TUNNEL_UDP_TABLE_CNT,
+ ETHTOOL_A_TUNNEL_UDP_TABLE_MAX = (__ETHTOOL_A_TUNNEL_UDP_TABLE_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_TUNNEL_UDP_UNSPEC,
+
+ ETHTOOL_A_TUNNEL_UDP_TABLE, /* nest - _UDP_TABLE_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_TUNNEL_UDP_CNT,
+ ETHTOOL_A_TUNNEL_UDP_MAX = (__ETHTOOL_A_TUNNEL_UDP_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_TUNNEL_INFO_UNSPEC,
+ ETHTOOL_A_TUNNEL_INFO_HEADER, /* nest - _A_HEADER_* */
+
+ ETHTOOL_A_TUNNEL_INFO_UDP_PORTS, /* nest - _UDP_TABLE */
+
+ /* add new constants above here */
+ __ETHTOOL_A_TUNNEL_INFO_CNT,
+ ETHTOOL_A_TUNNEL_INFO_MAX = (__ETHTOOL_A_TUNNEL_INFO_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/uapi/linux/genetlink.h b/uapi/linux/genetlink.h
index 1317119..9fa720e 100644
--- a/uapi/linux/genetlink.h
+++ b/uapi/linux/genetlink.h
@@ -48,6 +48,7 @@ enum {
CTRL_CMD_NEWMCAST_GRP,
CTRL_CMD_DELMCAST_GRP,
CTRL_CMD_GETMCAST_GRP, /* unused */
+ CTRL_CMD_GETPOLICY,
__CTRL_CMD_MAX,
};
@@ -62,6 +63,9 @@ enum {
CTRL_ATTR_MAXATTR,
CTRL_ATTR_OPS,
CTRL_ATTR_MCAST_GROUPS,
+ CTRL_ATTR_POLICY,
+ CTRL_ATTR_OP_POLICY,
+ CTRL_ATTR_OP,
__CTRL_ATTR_MAX,
};
@@ -83,6 +87,15 @@ enum {
__CTRL_ATTR_MCAST_GRP_MAX,
};
+enum {
+ CTRL_ATTR_POLICY_UNSPEC,
+ CTRL_ATTR_POLICY_DO,
+ CTRL_ATTR_POLICY_DUMP,
+
+ __CTRL_ATTR_POLICY_DUMP_MAX,
+ CTRL_ATTR_POLICY_DUMP_MAX = __CTRL_ATTR_POLICY_DUMP_MAX - 1
+};
+
#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
diff --git a/uapi/linux/if_link.h b/uapi/linux/if_link.h
index cb88bcb..307e5c2 100644
--- a/uapi/linux/if_link.h
+++ b/uapi/linux/if_link.h
@@ -7,24 +7,23 @@
/* This struct should be in sync with struct rtnl_link_stats64 */
struct rtnl_link_stats {
- __u32 rx_packets; /* total packets received */
- __u32 tx_packets; /* total packets transmitted */
- __u32 rx_bytes; /* total bytes received */
- __u32 tx_bytes; /* total bytes transmitted */
- __u32 rx_errors; /* bad packets received */
- __u32 tx_errors; /* packet transmit problems */
- __u32 rx_dropped; /* no space in linux buffers */
- __u32 tx_dropped; /* no space available in linux */
- __u32 multicast; /* multicast packets received */
+ __u32 rx_packets;
+ __u32 tx_packets;
+ __u32 rx_bytes;
+ __u32 tx_bytes;
+ __u32 rx_errors;
+ __u32 tx_errors;
+ __u32 rx_dropped;
+ __u32 tx_dropped;
+ __u32 multicast;
__u32 collisions;
-
/* detailed rx_errors: */
__u32 rx_length_errors;
- __u32 rx_over_errors; /* receiver ring buff overflow */
- __u32 rx_crc_errors; /* recved pkt with crc error */
- __u32 rx_frame_errors; /* recv'd frame alignment error */
- __u32 rx_fifo_errors; /* recv'r fifo overrun */
- __u32 rx_missed_errors; /* receiver missed packet */
+ __u32 rx_over_errors;
+ __u32 rx_crc_errors;
+ __u32 rx_frame_errors;
+ __u32 rx_fifo_errors;
+ __u32 rx_missed_errors;
/* detailed tx_errors */
__u32 tx_aborted_errors;
@@ -37,29 +36,200 @@ struct rtnl_link_stats {
__u32 rx_compressed;
__u32 tx_compressed;
- __u32 rx_nohandler; /* dropped, no handler found */
+ __u32 rx_nohandler;
};
-/* The main device statistics structure */
+/**
+ * struct rtnl_link_stats64 - The main device statistics structure.
+ *
+ * @rx_packets: Number of good packets received by the interface.
+ * For hardware interfaces counts all good packets received from the device
+ * by the host, including packets which host had to drop at various stages
+ * of processing (even in the driver).
+ *
+ * @tx_packets: Number of packets successfully transmitted.
+ * For hardware interfaces counts packets which host was able to successfully
+ * hand over to the device, which does not necessarily mean that packets
+ * had been successfully transmitted out of the device, only that device
+ * acknowledged it copied them out of host memory.
+ *
+ * @rx_bytes: Number of good received bytes, corresponding to @rx_packets.
+ *
+ * For IEEE 802.3 devices should count the length of Ethernet Frames
+ * excluding the FCS.
+ *
+ * @tx_bytes: Number of good transmitted bytes, corresponding to @tx_packets.
+ *
+ * For IEEE 802.3 devices should count the length of Ethernet Frames
+ * excluding the FCS.
+ *
+ * @rx_errors: Total number of bad packets received on this network device.
+ * This counter must include events counted by @rx_length_errors,
+ * @rx_crc_errors, @rx_frame_errors and other errors not otherwise
+ * counted.
+ *
+ * @tx_errors: Total number of transmit problems.
+ * This counter must include events counter by @tx_aborted_errors,
+ * @tx_carrier_errors, @tx_fifo_errors, @tx_heartbeat_errors,
+ * @tx_window_errors and other errors not otherwise counted.
+ *
+ * @rx_dropped: Number of packets received but not processed,
+ * e.g. due to lack of resources or unsupported protocol.
+ * For hardware interfaces this counter should not include packets
+ * dropped by the device which are counted separately in
+ * @rx_missed_errors (since procfs folds those two counters together).
+ *
+ * @tx_dropped: Number of packets dropped on their way to transmission,
+ * e.g. due to lack of resources.
+ *
+ * @multicast: Multicast packets received.
+ * For hardware interfaces this statistic is commonly calculated
+ * at the device level (unlike @rx_packets) and therefore may include
+ * packets which did not reach the host.
+ *
+ * For IEEE 802.3 devices this counter may be equivalent to:
+ *
+ * - 30.3.1.1.21 aMulticastFramesReceivedOK
+ *
+ * @collisions: Number of collisions during packet transmissions.
+ *
+ * @rx_length_errors: Number of packets dropped due to invalid length.
+ * Part of aggregate "frame" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices this counter should be equivalent to a sum
+ * of the following attributes:
+ *
+ * - 30.3.1.1.23 aInRangeLengthErrors
+ * - 30.3.1.1.24 aOutOfRangeLengthField
+ * - 30.3.1.1.25 aFrameTooLongErrors
+ *
+ * @rx_over_errors: Receiver FIFO overflow event counter.
+ *
+ * Historically the count of overflow events. Such events may be
+ * reported in the receive descriptors or via interrupts, and may
+ * not correspond one-to-one with dropped packets.
+ *
+ * The recommended interpretation for high speed interfaces is -
+ * number of packets dropped because they did not fit into buffers
+ * provided by the host, e.g. packets larger than MTU or next buffer
+ * in the ring was not available for a scatter transfer.
+ *
+ * Part of aggregate "frame" errors in `/proc/net/dev`.
+ *
+ * This statistics was historically used interchangeably with
+ * @rx_fifo_errors.
+ *
+ * This statistic corresponds to hardware events and is not commonly used
+ * on software devices.
+ *
+ * @rx_crc_errors: Number of packets received with a CRC error.
+ * Part of aggregate "frame" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices this counter must be equivalent to:
+ *
+ * - 30.3.1.1.6 aFrameCheckSequenceErrors
+ *
+ * @rx_frame_errors: Receiver frame alignment errors.
+ * Part of aggregate "frame" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices this counter should be equivalent to:
+ *
+ * - 30.3.1.1.7 aAlignmentErrors
+ *
+ * @rx_fifo_errors: Receiver FIFO error counter.
+ *
+ * Historically the count of overflow events. Those events may be
+ * reported in the receive descriptors or via interrupts, and may
+ * not correspond one-to-one with dropped packets.
+ *
+ * This statistics was used interchangeably with @rx_over_errors.
+ * Not recommended for use in drivers for high speed interfaces.
+ *
+ * This statistic is used on software devices, e.g. to count software
+ * packet queue overflow (can) or sequencing errors (GRE).
+ *
+ * @rx_missed_errors: Count of packets missed by the host.
+ * Folded into the "drop" counter in `/proc/net/dev`.
+ *
+ * Counts number of packets dropped by the device due to lack
+ * of buffer space. This usually indicates that the host interface
+ * is slower than the network interface, or host is not keeping up
+ * with the receive packet rate.
+ *
+ * This statistic corresponds to hardware events and is not used
+ * on software devices.
+ *
+ * @tx_aborted_errors:
+ * Part of aggregate "carrier" errors in `/proc/net/dev`.
+ * For IEEE 802.3 devices capable of half-duplex operation this counter
+ * must be equivalent to:
+ *
+ * - 30.3.1.1.11 aFramesAbortedDueToXSColls
+ *
+ * High speed interfaces may use this counter as a general device
+ * discard counter.
+ *
+ * @tx_carrier_errors: Number of frame transmission errors due to loss
+ * of carrier during transmission.
+ * Part of aggregate "carrier" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices this counter must be equivalent to:
+ *
+ * - 30.3.1.1.13 aCarrierSenseErrors
+ *
+ * @tx_fifo_errors: Number of frame transmission errors due to device
+ * FIFO underrun / underflow. This condition occurs when the device
+ * begins transmission of a frame but is unable to deliver the
+ * entire frame to the transmitter in time for transmission.
+ * Part of aggregate "carrier" errors in `/proc/net/dev`.
+ *
+ * @tx_heartbeat_errors: Number of Heartbeat / SQE Test errors for
+ * old half-duplex Ethernet.
+ * Part of aggregate "carrier" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices possibly equivalent to:
+ *
+ * - 30.3.2.1.4 aSQETestErrors
+ *
+ * @tx_window_errors: Number of frame transmission errors due
+ * to late collisions (for Ethernet - after the first 64B of transmission).
+ * Part of aggregate "carrier" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices this counter must be equivalent to:
+ *
+ * - 30.3.1.1.10 aLateCollisions
+ *
+ * @rx_compressed: Number of correctly received compressed packets.
+ * This counters is only meaningful for interfaces which support
+ * packet compression (e.g. CSLIP, PPP).
+ *
+ * @tx_compressed: Number of transmitted compressed packets.
+ * This counters is only meaningful for interfaces which support
+ * packet compression (e.g. CSLIP, PPP).
+ *
+ * @rx_nohandler: Number of packets received on the interface
+ * but dropped by the networking stack because the device is
+ * not designated to receive packets (e.g. backup link in a bond).
+ */
struct rtnl_link_stats64 {
- __u64 rx_packets; /* total packets received */
- __u64 tx_packets; /* total packets transmitted */
- __u64 rx_bytes; /* total bytes received */
- __u64 tx_bytes; /* total bytes transmitted */
- __u64 rx_errors; /* bad packets received */
- __u64 tx_errors; /* packet transmit problems */
- __u64 rx_dropped; /* no space in linux buffers */
- __u64 tx_dropped; /* no space available in linux */
- __u64 multicast; /* multicast packets received */
+ __u64 rx_packets;
+ __u64 tx_packets;
+ __u64 rx_bytes;
+ __u64 tx_bytes;
+ __u64 rx_errors;
+ __u64 tx_errors;
+ __u64 rx_dropped;
+ __u64 tx_dropped;
+ __u64 multicast;
__u64 collisions;
/* detailed rx_errors: */
__u64 rx_length_errors;
- __u64 rx_over_errors; /* receiver ring buff overflow */
- __u64 rx_crc_errors; /* recved pkt with crc error */
- __u64 rx_frame_errors; /* recv'd frame alignment error */
- __u64 rx_fifo_errors; /* recv'r fifo overrun */
- __u64 rx_missed_errors; /* receiver missed packet */
+ __u64 rx_over_errors;
+ __u64 rx_crc_errors;
+ __u64 rx_frame_errors;
+ __u64 rx_fifo_errors;
+ __u64 rx_missed_errors;
/* detailed tx_errors */
__u64 tx_aborted_errors;
@@ -71,8 +241,7 @@ struct rtnl_link_stats64 {
/* for cslip etc */
__u64 rx_compressed;
__u64 tx_compressed;
-
- __u64 rx_nohandler; /* dropped, no handler found */
+ __u64 rx_nohandler;
};
/* The struct should be in sync with struct ifmap */
@@ -170,12 +339,22 @@ enum {
IFLA_PROP_LIST,
IFLA_ALT_IFNAME, /* Alternative ifname */
IFLA_PERM_ADDRESS,
+ IFLA_PROTO_DOWN_REASON,
__IFLA_MAX
};
#define IFLA_MAX (__IFLA_MAX - 1)
+enum {
+ IFLA_PROTO_DOWN_REASON_UNSPEC,
+ IFLA_PROTO_DOWN_REASON_MASK, /* u32, mask for reason bits */
+ IFLA_PROTO_DOWN_REASON_VALUE, /* u32, reason bit value */
+
+ __IFLA_PROTO_DOWN_REASON_CNT,
+ IFLA_PROTO_DOWN_REASON_MAX = __IFLA_PROTO_DOWN_REASON_CNT - 1
+};
+
/* backwards compatibility for userspace */
#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
@@ -341,6 +520,8 @@ enum {
IFLA_BRPORT_NEIGH_SUPPRESS,
IFLA_BRPORT_ISOLATED,
IFLA_BRPORT_BACKUP_PORT,
+ IFLA_BRPORT_MRP_RING_OPEN,
+ IFLA_BRPORT_MRP_IN_OPEN,
__IFLA_BRPORT_MAX
};
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
@@ -461,6 +642,7 @@ enum {
IFLA_MACSEC_REPLAY_PROTECT,
IFLA_MACSEC_VALIDATION,
IFLA_MACSEC_PAD,
+ IFLA_MACSEC_OFFLOAD,
__IFLA_MACSEC_MAX,
};
@@ -487,6 +669,7 @@ enum macsec_validation_type {
enum macsec_offload {
MACSEC_OFFLOAD_OFF = 0,
MACSEC_OFFLOAD_PHY = 1,
+ MACSEC_OFFLOAD_MAC = 2,
__MACSEC_OFFLOAD_END,
MACSEC_OFFLOAD_MAX = __MACSEC_OFFLOAD_END - 1,
};
@@ -901,7 +1084,14 @@ enum {
#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
-/* HSR section */
+/* HSR/PRP section, both uses same interface */
+
+/* Different redundancy protocols for hsr device */
+enum {
+ HSR_PROTOCOL_HSR,
+ HSR_PROTOCOL_PRP,
+ HSR_PROTOCOL_MAX,
+};
enum {
IFLA_HSR_UNSPEC,
@@ -911,6 +1101,9 @@ enum {
IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */
IFLA_HSR_SEQ_NR,
IFLA_HSR_VERSION, /* HSR version */
+ IFLA_HSR_PROTOCOL, /* Indicate different protocol than
+ * HSR. For example PRP.
+ */
__IFLA_HSR_MAX,
};
@@ -970,11 +1163,12 @@ enum {
#define XDP_FLAGS_SKB_MODE (1U << 1)
#define XDP_FLAGS_DRV_MODE (1U << 2)
#define XDP_FLAGS_HW_MODE (1U << 3)
+#define XDP_FLAGS_REPLACE (1U << 4)
#define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \
XDP_FLAGS_DRV_MODE | \
XDP_FLAGS_HW_MODE)
#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \
- XDP_FLAGS_MODES)
+ XDP_FLAGS_MODES | XDP_FLAGS_REPLACE)
/* These are stored into IFLA_XDP_ATTACHED on dump. */
enum {
@@ -994,6 +1188,7 @@ enum {
IFLA_XDP_DRV_PROG_ID,
IFLA_XDP_SKB_PROG_ID,
IFLA_XDP_HW_PROG_ID,
+ IFLA_XDP_EXPECTED_FD,
__IFLA_XDP_MAX,
};
diff --git a/uapi/linux/net_tstamp.h b/uapi/linux/net_tstamp.h
index f96e650..7ed0b3d 100644
--- a/uapi/linux/net_tstamp.h
+++ b/uapi/linux/net_tstamp.h
@@ -98,6 +98,9 @@ enum hwtstamp_tx_types {
* receive a time stamp via the socket error queue.
*/
HWTSTAMP_TX_ONESTEP_P2P,
+
+ /* add new constants above here */
+ __HWTSTAMP_TX_CNT
};
/* possible values for hwtstamp_config->rx_filter */
@@ -140,6 +143,9 @@ enum hwtstamp_rx_filters {
/* NTP, UDP, all versions and packet modes */
HWTSTAMP_FILTER_NTP_ALL,
+
+ /* add new constants above here */
+ __HWTSTAMP_FILTER_CNT
};
/* SCM_TIMESTAMPING_PKTINFO control message */
diff --git a/uapi/linux/netlink.h b/uapi/linux/netlink.h
index 2c28d32..dfef006 100644
--- a/uapi/linux/netlink.h
+++ b/uapi/linux/netlink.h
@@ -129,6 +129,7 @@ struct nlmsgerr {
* @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to
* be used - in the success case - to identify a created
* object or operation or similar (binary)
+ * @NLMSGERR_ATTR_POLICY: policy for a rejected attribute
* @__NLMSGERR_ATTR_MAX: number of attributes
* @NLMSGERR_ATTR_MAX: highest attribute number
*/
@@ -137,6 +138,7 @@ enum nlmsgerr_attrs {
NLMSGERR_ATTR_MSG,
NLMSGERR_ATTR_OFFS,
NLMSGERR_ATTR_COOKIE,
+ NLMSGERR_ATTR_POLICY,
__NLMSGERR_ATTR_MAX,
NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
@@ -245,4 +247,109 @@ struct nla_bitfield32 {
__u32 selector;
};
+/*
+ * policy descriptions - it's specific to each family how this is used
+ * Normally, it should be retrieved via a dump inside another attribute
+ * specifying where it applies.
+ */
+
+/**
+ * enum netlink_attribute_type - type of an attribute
+ * @NL_ATTR_TYPE_INVALID: unused
+ * @NL_ATTR_TYPE_FLAG: flag attribute (present/not present)
+ * @NL_ATTR_TYPE_U8: 8-bit unsigned attribute
+ * @NL_ATTR_TYPE_U16: 16-bit unsigned attribute
+ * @NL_ATTR_TYPE_U32: 32-bit unsigned attribute
+ * @NL_ATTR_TYPE_U64: 64-bit unsigned attribute
+ * @NL_ATTR_TYPE_S8: 8-bit signed attribute
+ * @NL_ATTR_TYPE_S16: 16-bit signed attribute
+ * @NL_ATTR_TYPE_S32: 32-bit signed attribute
+ * @NL_ATTR_TYPE_S64: 64-bit signed attribute
+ * @NL_ATTR_TYPE_BINARY: binary data, min/max length may be specified
+ * @NL_ATTR_TYPE_STRING: string, min/max length may be specified
+ * @NL_ATTR_TYPE_NUL_STRING: NUL-terminated string,
+ * min/max length may be specified
+ * @NL_ATTR_TYPE_NESTED: nested, i.e. the content of this attribute
+ * consists of sub-attributes. The nested policy and maxtype
+ * inside may be specified.
+ * @NL_ATTR_TYPE_NESTED_ARRAY: nested array, i.e. the content of this
+ * attribute contains sub-attributes whose type is irrelevant
+ * (just used to separate the array entries) and each such array
+ * entry has attributes again, the policy for those inner ones
+ * and the corresponding maxtype may be specified.
+ * @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute
+ */
+enum netlink_attribute_type {
+ NL_ATTR_TYPE_INVALID,
+
+ NL_ATTR_TYPE_FLAG,
+
+ NL_ATTR_TYPE_U8,
+ NL_ATTR_TYPE_U16,
+ NL_ATTR_TYPE_U32,
+ NL_ATTR_TYPE_U64,
+
+ NL_ATTR_TYPE_S8,
+ NL_ATTR_TYPE_S16,
+ NL_ATTR_TYPE_S32,
+ NL_ATTR_TYPE_S64,
+
+ NL_ATTR_TYPE_BINARY,
+ NL_ATTR_TYPE_STRING,
+ NL_ATTR_TYPE_NUL_STRING,
+
+ NL_ATTR_TYPE_NESTED,
+ NL_ATTR_TYPE_NESTED_ARRAY,
+
+ NL_ATTR_TYPE_BITFIELD32,
+};
+
+/**
+ * enum netlink_policy_type_attr - policy type attributes
+ * @NL_POLICY_TYPE_ATTR_UNSPEC: unused
+ * @NL_POLICY_TYPE_ATTR_TYPE: type of the attribute,
+ * &enum netlink_attribute_type (U32)
+ * @NL_POLICY_TYPE_ATTR_MIN_VALUE_S: minimum value for signed
+ * integers (S64)
+ * @NL_POLICY_TYPE_ATTR_MAX_VALUE_S: maximum value for signed
+ * integers (S64)
+ * @NL_POLICY_TYPE_ATTR_MIN_VALUE_U: minimum value for unsigned
+ * integers (U64)
+ * @NL_POLICY_TYPE_ATTR_MAX_VALUE_U: maximum value for unsigned
+ * integers (U64)
+ * @NL_POLICY_TYPE_ATTR_MIN_LENGTH: minimum length for binary
+ * attributes, no minimum if not given (U32)
+ * @NL_POLICY_TYPE_ATTR_MAX_LENGTH: maximum length for binary
+ * attributes, no maximum if not given (U32)
+ * @NL_POLICY_TYPE_ATTR_POLICY_IDX: sub policy for nested and
+ * nested array types (U32)
+ * @NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: maximum sub policy
+ * attribute for nested and nested array types, this can
+ * in theory be < the size of the policy pointed to by
+ * the index, if limited inside the nesting (U32)
+ * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the
+ * bitfield32 type (U32)
+ * @NL_POLICY_TYPE_ATTR_MASK: mask of valid bits for unsigned integers (U64)
+ * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment
+ */
+enum netlink_policy_type_attr {
+ NL_POLICY_TYPE_ATTR_UNSPEC,
+ NL_POLICY_TYPE_ATTR_TYPE,
+ NL_POLICY_TYPE_ATTR_MIN_VALUE_S,
+ NL_POLICY_TYPE_ATTR_MAX_VALUE_S,
+ NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
+ NL_POLICY_TYPE_ATTR_MAX_VALUE_U,
+ NL_POLICY_TYPE_ATTR_MIN_LENGTH,
+ NL_POLICY_TYPE_ATTR_MAX_LENGTH,
+ NL_POLICY_TYPE_ATTR_POLICY_IDX,
+ NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
+ NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
+ NL_POLICY_TYPE_ATTR_PAD,
+ NL_POLICY_TYPE_ATTR_MASK,
+
+ /* keep last */
+ __NL_POLICY_TYPE_ATTR_MAX,
+ NL_POLICY_TYPE_ATTR_MAX = __NL_POLICY_TYPE_ATTR_MAX - 1
+};
+
#endif /* __LINUX_NETLINK_H */
diff --git a/uapi/linux/rtnetlink.h b/uapi/linux/rtnetlink.h
index 9d802cd..5ad84e6 100644
--- a/uapi/linux/rtnetlink.h
+++ b/uapi/linux/rtnetlink.h
@@ -257,12 +257,12 @@ enum {
/* rtm_protocol */
-#define RTPROT_UNSPEC 0
-#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects;
- not used by current IPv4 */
-#define RTPROT_KERNEL 2 /* Route installed by kernel */
-#define RTPROT_BOOT 3 /* Route installed during boot */
-#define RTPROT_STATIC 4 /* Route installed by administrator */
+#define RTPROT_UNSPEC 0
+#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects;
+ not used by current IPv4 */
+#define RTPROT_KERNEL 2 /* Route installed by kernel */
+#define RTPROT_BOOT 3 /* Route installed during boot */
+#define RTPROT_STATIC 4 /* Route installed by administrator */
/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel;
they are just passed from user and back as is.
@@ -271,22 +271,23 @@ enum {
avoid conflicts.
*/
-#define RTPROT_GATED 8 /* Apparently, GateD */
-#define RTPROT_RA 9 /* RDISC/ND router advertisements */
-#define RTPROT_MRT 10 /* Merit MRT */
-#define RTPROT_ZEBRA 11 /* Zebra */
-#define RTPROT_BIRD 12 /* BIRD */
-#define RTPROT_DNROUTED 13 /* DECnet routing daemon */
-#define RTPROT_XORP 14 /* XORP */
-#define RTPROT_NTK 15 /* Netsukuku */
-#define RTPROT_DHCP 16 /* DHCP client */
-#define RTPROT_MROUTED 17 /* Multicast daemon */
-#define RTPROT_BABEL 42 /* Babel daemon */
-#define RTPROT_BGP 186 /* BGP Routes */
-#define RTPROT_ISIS 187 /* ISIS Routes */
-#define RTPROT_OSPF 188 /* OSPF Routes */
-#define RTPROT_RIP 189 /* RIP Routes */
-#define RTPROT_EIGRP 192 /* EIGRP Routes */
+#define RTPROT_GATED 8 /* Apparently, GateD */
+#define RTPROT_RA 9 /* RDISC/ND router advertisements */
+#define RTPROT_MRT 10 /* Merit MRT */
+#define RTPROT_ZEBRA 11 /* Zebra */
+#define RTPROT_BIRD 12 /* BIRD */
+#define RTPROT_DNROUTED 13 /* DECnet routing daemon */
+#define RTPROT_XORP 14 /* XORP */
+#define RTPROT_NTK 15 /* Netsukuku */
+#define RTPROT_DHCP 16 /* DHCP client */
+#define RTPROT_MROUTED 17 /* Multicast daemon */
+#define RTPROT_KEEPALIVED 18 /* Keepalived daemon */
+#define RTPROT_BABEL 42 /* Babel daemon */
+#define RTPROT_BGP 186 /* BGP Routes */
+#define RTPROT_ISIS 187 /* ISIS Routes */
+#define RTPROT_OSPF 188 /* OSPF Routes */
+#define RTPROT_RIP 189 /* RIP Routes */
+#define RTPROT_EIGRP 192 /* EIGRP Routes */
/* rtm_scope
@@ -609,11 +610,17 @@ enum {
TCA_HW_OFFLOAD,
TCA_INGRESS_BLOCK,
TCA_EGRESS_BLOCK,
+ TCA_DUMP_FLAGS,
__TCA_MAX
};
#define TCA_MAX (__TCA_MAX - 1)
+#define TCA_DUMP_FLAGS_TERSE (1 << 0) /* Means that in dump user gets only basic
+ * data necessary to identify the objects
+ * (handle, cookie, etc.) and stats.
+ */
+
#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg))))
#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg))
@@ -769,6 +776,7 @@ enum {
#define RTEXT_FILTER_BRVLAN (1 << 1)
#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)
#define RTEXT_FILTER_SKIP_STATS (1 << 3)
+#define RTEXT_FILTER_MRP (1 << 4)
/* End of information exported to user level */
diff --git a/vioc.c b/vioc.c
index ef163ab..c04a6dc 100644
--- a/vioc.c
+++ b/vioc.c
@@ -11,7 +11,7 @@ struct regs_line {
#define VIOC_REGS_LINE_SIZE sizeof(struct regs_line)
-int vioc_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+int vioc_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
unsigned int i;
diff --git a/vmxnet3.c b/vmxnet3.c
index c972145..6872682 100644
--- a/vmxnet3.c
+++ b/vmxnet3.c
@@ -3,7 +3,7 @@
#include "internal.h"
int
-vmxnet3_dump_regs(struct ethtool_drvinfo *info maybe_unused,
+vmxnet3_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
struct ethtool_regs *regs)
{
u32 *regs_buff = (u32 *)regs->data;